docker-compose

Laravel Sail でコンテナのshellを使う

こんばんは、ウチイダです。

Laravel Sail便利ですよね。docker-compose のラッパーみたいなものなので、学習コストも低くてとても良い感じです。

docker-composeでできることは何でもできるので、困ったらdocker-composeのコマンドを調べるのですが、なかなか覚えられません。

ちょくちょく使うのが、コンテナに入るコマンドです。

# Laravelアプリケーションのコンテナのbashを開く
$ docker-compose exec laravel.test bash

dockerコマンドみたいに、-it とかオプション付けなくてもいいので、まだいいですが、長いですよね。

あるときふとReadoubleを見直してみたら、sail コマンドからコンテナ接続するサブコマンドがちゃんと用意されていました。

# sail コマンドから、Laravel アプリケーソンのコンテナのbashをひらく
$ sail shell

# root ユーザーで接続する場合
$ sail root-shell

# Readoubleにはないけど、以下でもOK
$ sail bash

すごく短くなります。

sailコマンドの中身(vendor/bin/bash)は難しくないshellスクリプトなので、中身を見てみるといろいろ発見があって面白いです。

以上です。あなたのお役に立てれば幸いです。

WSL上のDocker Composeで作成したコンテナに、Windows 11ホストから接続する

こんにちは、ウチイダです。

ここしばらく、Windwos 11のWSL Ubuntu での環境構築を進めてきました。

前回の記事で、Docker Composeのインストールを紹介しています。

今回はその続きとして、シンプルなPHP開発環境をDocker Compose を用いて作成し、Windows ホストのブラウザからアクセスできるように設定していきます。

これができると、ついにWindows 11でWSLを使った実用的なWeb開発ができるようになります。

なお、動作環境は以下の通りです。

  • Windwos 11 Pro (21H2 ビルド 22000.120)
  • WSL カーネル バージョン: 5.10.16
  • Windows Terminal バージョン: 1.9.1942.0
  • Docker バージョン 20.10.8, build 3967b7d
  • Docker Compose バージョン 1.29.2, build 5becea4c

前準備①:Docker デーモンを起動する

WSLのディストリビューションは、サービスの自動起動などを行いません。

WSLを起動した後、最初にDockerを利用する場合は、デーモンを起動するのを忘れないようにしましょう。

$ sudo service docker start
[sudo] password for y-uchiida:
 * Starting Docker: docker                                 [ OK ]

## Docker デーモンの起動を確認します
$ service docker status
 * Docker is running

前準備②:開発環境をつくるディレクトリを作成

続いて、Docker に関するファイルや、PHPなどのスクリプトを置くディレクトリを作成します。

例として、「sample_docker-compose_on_wsl」を作成します。

また、PHPやJavaScriptなどのデータを置くための「public」も作成しておきます。

## とりあえずホームディレクトリに作ることにします
## 開発環境をまとめておく場所を決めている方は、そちらに作成してください
$ mkdir ~/sample_docker-compose_on_wsl

## 公開用ファイルを設置するpublic ディレクトリを作成します
$ mkdir ~/sample_docker-compose_on_wsl/public 

前準備③:ファイル編集とDocker実行の準備

この後の作業で、yamlファイルを編集したりdocker-compose コマンドを実行したりするので、作成した開発環境用のディレクトリに移動しておきます。

また、エディタも用意します。以下の例では、VS Codeでsample_docker-compose_on_wsl ディレクトリを開いています。

## docker-compose コマンドを使うので、カレントディレクトリを移動しておきます
$ cd ~/sample_docker-compose_on_wsl

## Visual Studio Code(VS Code) を利用している方は、code コマンドでVS Codeで開くことができます
## docker-compose.ymlの編集などはVS Code
$ code .

docker-compose.ymlを作成する

準備が整いましたので、Docker Compose でコンテナを生成・起動するためのファイル docker-compose.ymlを作っていきます。

sample_docker-compose_on_wsl ディレクトリ配下に作成します。

完成コードは以下です。

ディスク容量を圧迫しないように、alpine ベースのイメージを選択しました。

version: '3.8' # 現時点での最新のバージョンを指定しておきます
services: 

  # Webサーバとして、nginx を利用します
  web: 
    container_name: "web"
    image: nginx:1.21.1-alpine
    ports:
      - "8080:80" # ポート8080を、ポート80 へフォワーディングします
    links:
      - app # php 実行コンテナ(app)に接続できるようにします
    volumes:
      - ./public:/var/www/public # WSL側のpublic ディレクトリを、web コンテナにマウントします
      - ./default.conf:/etc/nginx/conf.d/default.conf # nginx の設定ファイルを、web コンテナにマウントします

  app:
    container_name: "app"
    image: php:7.4-fpm-alpine3.14
    volumes:
      - ./public:/var/www/public # web コンテナと同じパスにアクセスできるように、app コンテナにもpublic ディレクトリをマウントします

ここでのポイントは2点あります。

ひとつは、links でweb コンテナから app コンテナに接続できるように設定したことです。

これにより、phpファイルが呼び出された際にapp コンテナへ処理を引き渡すことができるようになります。

もうひとつは、web と app の両方の volumes に./publicをマウントするように記述したことです。

同じファイルパスで参照できるようになるので、スムーズに処理の引き渡しができます。

コンテナにマウントするファイルを作成する

続いて、コンテナにマウントするファイルを作っていきます。

最低限の用意として、2つのファイルを作成します。

phpの処理をapp コンテナに引き渡すための設定を記述したnginxのconfigファイル「default.conf」と、publicルートにアクセスした際に処理される「index.php」です。

default.confの作成

まずは、nginxの設定ファイルから見ていきます。

sample_docker-compose_on_wsl ディレクトリの直下に、以下のdefault.conf を作成してください。

server {
    listen 80;
    server_name localhost;

    root /var/www/public;
    index index.php;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # PHP ファイルへアクセスされた場合に、app コンテナのfast CGIへ処理を回します
    location ~ \.php$ {
        include fastcgi_params;

        # app コンテナのポート9000でFast CGI がListenしているので、そちらへパスします
        fastcgi_pass app:9000;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_index index.php;

        # nginx がアクセスされたファイルパスを、app コンテナで処理すべきファイルパスとして渡します
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;

        try_files $uri = 404;
    }
}

ポイントは、 location ~ \.php のディレクティブです。

ここで、php ファイルにアクセスされた際にapp コンテナへ処理を移すための設定を記載しています。

app コンテナのポート9000でFast CGI がListenしているので、fastcgi_pass はapp:9000 を指定します。

また、SCRIPT_FILENAME で、nginx がアクセスされたファイルパス($dcoument_root$fastcgi_script_name)を渡しています。

docker-compose.yml では、2つのコンテナにpublic ディレクトリを同じパスにマウントしました。

コンテナ同士のphpファイルのパスを一致させているため、このように記述できます。

index.php の作成

最後に、index.php を作成します。

これは、phpが動作していることが分かれば何でもよいです。

public ディレクトリ内に作成します。

<?php
    echo "hello from docker on wsl!!<br>\r\n";

内容は、好きなように設定してください。

コンテナの起動と動作確認

準備が整いましたので、docker コンテナを起動して動作を見ていきます。

まずはコンテナの起動です。

## dockerコンテナの起動には、sudo が必要になると思います
## コンテナ起動後にほかのコマンドを使いたいので、最後に & をつけてバックグラウンドで処理させます
$ sudo docker-compose up &
Creating app ... done
Creating web ... done
Attaching to app, web
app    | [09-Aug-2021 09:19:39] NOTICE: fpm is running, pid 1
app    | [09-Aug-2021 09:19:39] NOTICE: ready to handle connections
web    | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
web    | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
web    | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
web    | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
web    | 10-listen-on-ipv6-by-default.sh: info: /etc/nginx/conf.d/default.conf differs from the packaged version
web    | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
web    | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
web    | /docker-entrypoint.sh: Configuration complete; ready for start up
web    | 2021/08/09 09:19:40 [notice] 1#1: using the "epoll" event method
web    | 2021/08/09 09:19:40 [notice] 1#1: nginx/1.21.1
web    | 2021/08/09 09:19:40 [notice] 1#1: built by gcc 10.3.1 20210424 (Alpine 10.3.1_git20210424)
web    | 2021/08/09 09:19:40 [notice] 1#1: OS: Linux 5.10.16.3-microsoft-standard-WSL2
web    | 2021/08/09 09:19:40 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
web    | 2021/08/09 09:19:40 [notice] 1#1: start worker processes
web    | 2021/08/09 09:19:40 [notice] 1#1: start worker process 32
web    | 2021/08/09 09:19:40 [notice] 1#1: start worker process 33
web    | 2021/08/09 09:19:40 [notice] 1#1: start worker process 34
web    | 2021/08/09 09:19:40 [notice] 1#1: start worker process 35

コンテナ起動時のメッセージがたくさん出ます。最後にstart worker process (番号) と表示されれば成功です。

コンテナの起動コマンドの最後に 「&」 をつけておくと、コンテナの起動がバックグラウンドで処理されます。

バックグラウンド処理にしておくことで、up コマンド実行後も同じターミナルで別のコマンドが実行できます。

WSLからコンテナにアクセス

まずはDocker コンテナのホストであるWSL から、コンテナへアクセスしてみます。

8080ポートをフォワーディングしているので、localhost:8080でコンテナ内のnginxへアクセスできます。

curl コマンドを用いて、レスポンスを確認します。

$ curl http://localhost:8080/
hello from docker on wsl!!<br>
app    | 172.22.0.3 -  09/Aug/2021:10:07:27 +0000 "GET /index.php" 200
web    | 172.22.0.1 - - [09/Aug/2021:10:07:27 +0000] "GET / HTTP/1.1" 200 43 "-" "curl/7.68.0"

index.php でechoした文字列「hello from docker on wsl!!」が表示されています。

また、web コンテナ、app コンテナそれぞれがメッセージを出してきました。

このような表示になれば、うまく設定できています。

index.php の内容を変更・保存し、再度curl コマンドを実行すると、表示内容が変わります。

Windows ホストのブラウザからコンテナにアクセス

最後に、Windows上のブラウザからコンテナにアクセスしてみます。

以前はWindows ホストからWSLにアクセスするには設定が必要だったのですが、今ではとくに何もしなくてもよいようです。

ブラウザを開き、localhost:8080にアクセスしてみます。

あっさり接続できてしまいました。

WSLのDockerで、こんなに簡単にPHP開発環境が作れるとは驚きです。

Docker コンテナを終了する

一通り動作確認をしたら、Docker コンテナを終了します。

$ sudo docker-compose down
Stopping web ...
Stopping app ...
web    | 2021/08/09 10:45:04 [notice] 1#1: signal 3 (SIGQUIT) received, shutting down
web    | 2021/08/09 10:45:04 [notice] 33#33: gracefully shutting down
web    | 2021/08/09 10:45:04 [notice] 34#34: gracefully shutting down

... 中略 ...

Removing web ... done
Removing app ... done
Removing network sample_docker-compose_default
[1]+  Done                    sudo docker-compose up

またコンテナにアクセスしたい場合は、 docker-compose up を実行してください。

まとめ:WSLでも、スムーズにPHP開発環境が構築できるようになりました

今回はこれで終了です。

2021年8月時点では、とても簡単にWSLを使ったPHP開発環境が構築できるようになっていました。感動。

Docker Compose を使いたかったので長くなってしまいましたが、Windows ホストからWSL 上のDocker コンテナにつなげるだけだったら、もっと簡単にできそうです。

PHP のほかにも、node.js やPython の環境も、同じ要領で構築できそうです。

ここから先はDocker の領域に入っていきますので、Docker に関する情報などをあたってみるとよいかと思います。

以上、あなたのお役に立てれば幸いです。

Windows 11 のWSL2(Ubuntu 20.04)にDocker Composeをインストールする

こんにちは、ウチイダです。

前回のDocker インストールに引き続き、Windows 11のWSL2にDocker Composeをインストールして、開発環境を分割しやすくしていきます。

WSL2 のUbuntu上に、Docker がインストールされている前提で手順を見ていきます。

なお、動作環境は以下の通りです。

  • Windwos 11 Pro (21H2 ビルド 22000.120)
  • WSL カーネル バージョン: 5.10.16
  • Windows Terminal バージョン: 1.9.1942.0

GitHub リポジトリから、Docker Composeの最新版をインストール

Docker Composeは、aptなどのパッケージ管理ツールからインストールできないようです。

公式サイトの記載に従って、最新版のリリースをcurlで取得します。

https://github.com/docker/compose/releases

2021年8月時点の最新版は、1.29.2 です。

## GitHubでの最新リリースのバージョン番号を指定してバイナリを取得
$ sudo curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

## ダウンロードしたバイナリに、実行権限を付与
$ sudo chmod +x /usr/local/bin/docker-compose

## バージョン情報を表示して、インストールされたかを確認する
$ docker-compose --version
docker-compose version 1.29.2, build 5becea4c

Docker Composeのコマンド補完をインストール

続いて、公式サイトにオプションとして記載されているコマンド補完機能を導入します。

docker-compose と入力した後にTab キーを押すと、利用できるサブコマンドが表示されたりと、非常に快適になります。

## コマンドライン補完のスクリプトをインストール
$ curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose

## ウチイダの場合、直接 /etc/bash_completion.d/ にファイルを置こうとするとうまくいかなかったので、以下のコマンドで行いました
$ curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose > ~/docker-compose
$ chmod 0644 ~/docker-compose
$ sudo root:root ~/docker-compose
$ sudo mv ~/docker-compose /etc/bash_completion.d

## シェルを再読み込み
$ exec $SHELL -l

## docker-compose のコマンド補完が動作することを確認
$ docker-compose [Tab キーを2回押す]
build    create   events   help     kill     pause    ps       push     rm       scale    stop     unpause  version
config   down     exec     images   logs     port     pull     restart  run      start    top      up

以上で、インストール作業は完了です。

次回はWeb開発のための環境を、Docker Compose で動かしてみることにします。

以上、あなたのお役に立てればうれしいです。

docker-composeからDockerfileへ変数を渡す

docker-compose.ymlで管理するサービスのイメージを自分でビルドするときに、Dockerfileへ変数を渡すのに苦労したのでメモしておきます。

ymlを書き換えるのではなくて、.envを書き換えるだけで済むようにしたかったのです。

コマンド入力するときにオプションで渡すのも面倒だったし…

最終的に、以下のようにしたらできました。

  • .envファイルに変数名=値 の形式で列挙
  • さらに、args に変数を列挙して、Dockerfileに変数を届ける
  • Dockerfile側で、ARGS で変数の利用を宣言する

動作テストのため、以下のdocker-compose.ymlとDockerfikeを作成しました。

まずはdocker-compose.ymlから。

version: "3.9"

services:
  env_test:
    container_name: "env_test"
    build:
      context: "./build"
      dockerfile: "Dockerfile"
      args: # .env で宣言している変数を、イメージビルド時に使えるようにします
       - ENV_FROM_COMPOSE=$ENV_FROM_COMPOSE

つづいて、Dockerfileです。

FROM alpine:latest
ARG ENV_FROM_COMPOSE

RUN echo "env value: ${ENV_FROM_COMPOSE}"

CMD ["/bin/bash"]

.envには変数を設定。

ENV_FROM_COMPOSE=hello_from_docker-image-building!!

ビルドしてみます。

$ docker-compose build
Building env_test
[+] Building 0.5s (6/6) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                           0.0s
 => => transferring dockerfile: 37B                                                                                                                                            0.0s
 => [internal] load .dockerignore                                                                                                                                              0.0s
 => => transferring context: 2B                                                                                                                                                0.0s
 => [internal] load metadata for docker.io/library/alpine:latest                                                                                                               0.0s
 => CACHED [1/2] FROM docker.io/library/alpine:latest                                                                                                                          0.0s
 => [2/2] RUN echo "env value: hello_from_docker-image-building!!"                                                                                                             0.3s
 => exporting to image                                                                                                                                                         0.0s
 => => exporting layers                                                                                                                                                        0.0s
 => => writing image sha256:fc3c6c7aff6c5670a461dd324a2c81b2029389e5e1f0145110e6b50dbbd1b956                                                                                   0.0s
 => => naming to docker.io/library/docker-env-test_env_test

ステージ [2/2]で、データが$ENV_FROM_COMPOSE が展開されて、値が表示されています。

ビルドステージでechoしても、こちらには表示されませんけど…

ビルド時にdocker-composeから値を渡したいときは、この記述方法が使えそうです。

注意点:docker-compose.yml のenv_fileは動作が違う!

2021.08.12 追記です。

docker-compose.ymlには、「env_file」という要素もあります。

これは、docker-compose 経由で立ち上げられたコンテナの中で利用できる環境変数を、外部ファイルに列挙しておくためのものです。

ここでファイルを指定しても、docker-compose.ymlの中で利用することはできません。

たとえば、「.another_env」というファイルを作って環境変数を列挙しておき、docker-compose.ymlにenv_fileで指定しても、.another_envに書かれた変数はdocker-compose.ymlからは利用できませんが、ビルドしたコンテナ内では利用できます。

version: "3.9"

services:
  env_test:
    container_name: "env_test"
    env_file:
      # .another_envに記載された変数は、docker-compose.ymlの中では利用できない!!
      - ./.another_env
    build:
      context: "./build"
      dockerfile: "Dockerfile"

.env 以外のファイルを読み込んでdocker-compose.yml内で利用する方法は、ちょっと調べただけではわかりませんでした。後日、また調査しておきます。。。

以上です。あなたのお役に立てればうれしいです。

docker-compose でホスト側の任意のディレクトリをマウントできない

動作環境

  • Windows 10 Pro
  • WSL2 ubuntu
  • Docker Desktop for Windows

dockerが自動で作ってくれるディレクトリではなく、ホスト側で任意に作成したディレクトリをマウントさせたいという状況がありました。

調べてみたところ、トップレベルでvolumesを定義するとよいみたいです。

しかし、いろんなところに書いてあるやり方でymlを記述してもエラーがでます。。。

version: "3.9"

services:
  mount_test:
    container_name: "mount_test"
      image: alpine
      tty: true
      volumes:
        - volume_test:/tmp/mounted_from_host

volumes:
  volume_test:
   driver_opts:
     type: none
     device: /var/docker_test/volume # このディレクトリはあらかじめ作成されており、いくつかのファイルが入っている
     o: bind

エラーの内容は以下のような感じ。

$ docker-compose up -d
Creating network "docker-compose-test_mount_from_host_default" with the default driver
Creating volume "docker-compose-test_mount_from_host_volume_test" with default driver
Creating mount_test ... error

ERROR: for mount_test  Cannot start service mount_test: error while mounting volume '/var/lib/docker/volumes/docker-compose-test_mount_from_host_volume_test/_data': failed to mount local volume: mount /var/docker_test/volume:/var/lib/docker/volumes/docker-compose-test_mount_from_host_volume_test/_data, flags: 0x1000: no such file or directory

ERROR: for mount_test  Cannot start service mount_test: error while mounting volume '/var/lib/docker/volumes/docker-compose-test_mount_from_host_volume_test/_data': failed to mount local volume: mount /var/docker_test/volume:/var/lib/docker/volumes/docker-compose-test_mount_from_host_volume_test/_data, flags: 0x1000: no such file or directory
ERROR: Encountered errors while bringing up the project.

なぜかdockerが自動生成したディレクトリにマウントしようとしています。

修正:ボリュームの定義にdriverを追加する

「driver: local」と追加しました。この項目が必要なようです。いつからか仕様が変わったんですかね…

version: "3.9"

services:
  mount_test:
    container_name: "mount_test"
      image: alpine
      tty: true
      volumes:
        - volume_test:/tmp/mounted_from_host

volumes:
  volume_test:
   driver: local # ここを追加
   driver_opts:
     type: none
     device: /var/docker_test/volume
     o: bind

修正したymlで再度試したら、ちゃんとできました。

実行前に、前回のエラーで作成されたネットワークとボリュームを片付けておきます。

$ docker-compose down && docker volume rm docker-compose-test_mount_from_host_volume_test
Removing mount_test ... done
Removing network docker-compose-test_mount_from_host_default
docker-compose-test_mount_from_host_volume_test
$
$ docker-compose up -d
Creating network "docker-compose-test_mount_from_host_default" with the default driver
Creating volume "docker-compose-test_mount_from_host_volume_test" with local driver
Creating mount_test ... done
$
$ docker-compose exec mount_test cat /tmp/mounted_from_host/helloworld.txt
hello world from mounted from host!!

以上、あなたのお役に立てれば幸いです。