VS Code Remote - Containers を Docker Compose で使うのだー!

VS Code の Remote - Containers プラグインを使うと Docker の中で開発ができて最高だよ、という記事を読んで面白そうなので触ってみた。

www.keisuke69.net

ちょっとぐぐったら、このプラグインに関する記事がいっぱい出てきた。結構前から人気なのね。知らなかったや。

## Remote - Containers って何なの?

は、だいたいこんな印象。Remote - Containers プラグインVS Code Server をコンテナの中にインストールして、ローカル側の VS Code がその VS Code Server とコミュニケーションする。

f:id:bufferings:20200608000048p:plain

それによって、実際はコンテナの中にある開発環境が、あたかもローカルにあるみたいな気持ちで VS Codeを使うことができる。だから、ローカル側に例えば PHP を入れてなくても、VS Code では PHPプラグインを動かして、コード補完やデバッグ実行などができる。便利。

  • VS Code の UI 拡張はローカル側で、Workspace 拡張はコンテナ側で動くらしい。雰囲気は分かる。
  • Docker のポートをローカル側にマッピングしておけば、Webアプリの動作確認なども可能。ですよね。

## Dockerfile or docker-compose.yml ?

最初に触ってみたときは少し戸惑った。

ローカルのソースコードは自動でコンテナの中にマウントされるっぽいのにされないし、ポートは特に devcontainer.json に書かなくてもマッピングされてそうだし、んー?ってなった。

けど、しばらく触ってみて、あぁ、これって Dockerfile を使ってる場合と docker-compose.yml を使ってる場合で設定の方法が違うっぽいな。と気づいた。僕は docker-compose.yml を使ってた。

もうしばらく触ってみて分かったのは、Dockerfile だけではできない部分を devcontainer.json で設定できたり、 Remote - Containers プラグインが自動的にサポートしたりしてるってこと。ポートのマッピングとか、ボリュームのマウントとか、環境変数の指定とか。ほほー。

docker-compose.yml を使う場合は、そういうサポートをほとんど必要としない。元々 Docker Compose にそういう機能がだいたい備わってるから。

じゃ、どっちを使おうかなと考えたんだけど、 docker-compose.yml を使えば Dockerfile でできることはカバーできるだろうから、 Dockerfile よりも docker-compose.yml を使う方向で進めてみることにした。

## やらないこと

  • メインで使う言語は、ローカルマシンで環境を整えたいなと思うので、このプラグインでは僕は使わないだろうなと思った。なので Java で使うつもりはない。今回は PHP で試してみてる。
  • 実行中のコンテナや k8s のコンテナにアタッチすることもできるみたいだけど、やらない。docker-compose.yml を用意してコンテナをビルドするー。
  • 複数プロジェクトで1つのコンテナ設定を共有することもできるみたいだけど、やらない。プロジェクトごとにコンテナ設定を用意する。
  • 設定は .devcontainer/devcontainer.json.devcontainer.json のどちらかが選べるけど、後者は使わない。ディレクトリを用意する。中に色々置きたいから。

さて、進もう。

## Docker Compose を使う場合の情報

を知りたいなという観点で、公式ドキュメントを読んだ。で、満足した。2020-06-07 時点の情報。

code.visualstudio.com

code.visualstudio.com

こういう観点でメモを残しておこうと思う。ほぼ忘れているであろう来月の自分のため。

  • (1) devcontainer.json の仕様 (for docker-compose.yml)
  • (2) Dockerfile を使った場合と docker-compose.yml を使った場合の違い
  • (3) その他、覚えておきたいことのメモ

## (1) devcontainer.json の仕様 (for docker-compose.yml)

https://code.visualstudio.com/docs/remote/containers#_devcontainerjson-reference

に仕様が書いてある。docker-compose.yml を使う場合は、以下の部分だけを知ってれば良さそう:

"Docker Compose" の部分

dockerComposeFile

  • 文字列または配列。必須。
  • devcontainer.json からの相対パスで docker-compose.yml を指定
  • 元々存在しているファイルなどを書き換えずに拡張したい場合には、複数ファイルを指定することが可能で、その場合は配列で指定。
  • プロジェクトのルートから .env を読み込むっぽい。docker-compose.yml の env_file で別のファイルを指定できるっぽい。

感想

  • たぶん僕は配列での複数ファイル指定は使わないし、基本的には .devcontainer の下に docker-compose.yml を置くと思うので docker-compose.yml と書くだけになりそう。
  • Laravel みたいに .env を使ってる場合は、変な感じにならないといいんだけどな。覚えとこ。

service

  • 文字列。必須。
  • docker-compose.yml の中のどのサービスに VS Code が接続するかを指定

感想

  • この設定自体は問題ないんだけど、ここで指定した以外のサービスは起動するのかな?どうなのかな?と思ったら次に書いてあった。

runServices

  • 配列
  • docker-compose.yml の中のどのサービスを実行するか
  • デフォルトは全てのサービス

感想

  • 基本的に docker-compose.yml には起動したいサービスを書くので、この設定は書かないだろう。デフォルトの動きで問題ない。

workspaceFolder

  • 文字列
  • VS Code がコンテナに接続するときに開くパス(コンテナの中のパス)
  • デフォルトは"/"
  • 普通はマウントしたソースコードのフォルダーを指定する

感想

  • そだね。マウントしたソースコードのフォルダーを指定すると思う

remoteEnv

  • name-value ペアのオブジェクト
  • リモート側の VS CodeVS Code が起動するプロセス(ターミナルなど)で利用できる環境変数を上書き。コンテナ全体の環境変数ではない。

感想

  • 今の所、必要なさそう。

remoteUser

  • 文字列
  • リモート側の VS Code を実行するユーザー
  • デフォルトはコンテナが使うユーザーと同じ

感想

  • Linux を使う場合は気にしておいたほうが良さそうだな。ファイルのオーナーが root にならないように。MacWindows だとその辺りは気にしなくて良さそう。

shutdownAction

  • none または stopComposestopCompose がデフォルト。
  • VS Code (Remote) が閉じられたときに、コンテナを停止するかどうか

感想

  • VS Code (Remote) を閉じたら docker-compose は停止してほしいので、デフォルトのままでいいな。

General の部分

General の部分にはこれ以外にも指定できる項目があるけど、 Docker Compose 使うならこれくらいで大丈夫だと思う:

name

  • 文字列
  • 表示名

感想

  • 適当で大丈夫。

extensions

感想

  • コンテナ側で開いた後にプラグインを右クリックすると devcontainer.json に追加できるので、そこから追加するのが楽
  • 例えば PHP だと PHP Extension Pack を追加したりとかそういうの

settings

  • VS Code の設定を書いておける

感想

  • PHP Extension Pack の設定とかあったら書いておくと便利なのかな

postCreateCommand

  • 文字列または配列
  • コンテナの作成が終わって起動するときに実行したいコマンドを書いておくことができる

感想

  • ソースコードもマウントされた状態で実行されるので composer install とか書くと良さそう
  • 毎回なのかな?1回だけなのかな?と思ってみてみたら1回だけっぽいな。 /root/.vscode-server/data/Machine/.postCreateCommandMarker というファイルがなければ実行してそのファイルを作成するっぽい。

結局

のところ、書くのはこれくらいだな:

{
  "name": "My PHP Application",

  "dockerComposeFile": "docker-compose.yml",
  "service": "php-fpm",
  "workspaceFolder": "/workspace",
  "extensions": [
    "felixfbecker.php-pack"
  ],
  "settings": [],
  "postCreateCommand": "composer install"
}

.devcontainer の中身

.devcontainer ディレクトリの中に docker-compose.yml を入れて、その隣にビルドに必要な Dockerfile などを置いておくのが好きだなと思った。こんな感じ:

❯ tree .devcontainer 
.devcontainer
├── devcontainer.json
├── docker
│   ├── app
│   │   └── Dockerfile
│   └── web
│       └── default.conf
└── docker-compose.yml

## (2) Dockerfile を使った場合と docker-compose.yml を使った場合の違い

以下の設定は docker-compose.yml を使う場合は devcontainer.json じゃなくて docker-compose.yml の設定で書く

  • ポートマッピング
  • 環境変数
  • ボリュームマウント
  • コンテナのユーザー
  • コンテナの起動コマンドや引数

でも、デフォルトの動作が Dockerfile を使う場合と異なり、 docker-compose.yml では明示的に指定しないといけないこともある。

起動コマンド

Dockerfile の場合はコンテナのデフォルトコマンドを上書きして /bin/sh -c "while sleep 1000; do :; done" にしてしまうらしい。コンテナが終了してしまわないように。

でも docker-compose.yml の場合は自分で command にこれを指定しないといけない。

# プロセスが終了してコンテナが終了してしまわないように上書きする
command: /bin/sh -c "while sleep 1000; do :; done"

ただ、今回僕が使ってみたのは php-fpm のコンテナでコマンドが終了しないので、これはやらなくても良さそう。

ボリュームマウントのタイプ

MacOS の場合は Remote - Containers プラグインはデフォルトでボリュームマウントの consistency に cached を使うらしいんだけど、docker-compose.yml を使う場合は明示的に指定しないといけない(んだと思う):

volumes:
  - ..:/workspace:cached

cached はホストOS側の変更がコンテナ側に伝わるのに遅延があるけど、パフォーマンスが良くなる。

Use bind mounts | Docker Documentation

コンテナユーザーの UID/GID

僕は Ubuntu を使ってるんだけど Linux の場合はボリュームマウントを使ったときに、コンテナのユーザーの UID/GID でファイルが書き込まれてしまうから例えば root ユーザーを使うと全部 root で書き込まれてしまう。Mac だとホスト側のユーザーになるんだけどね。Windows は知らないけど Mac と同じなんじゃないかなと予想。

なので Linux でボリュームマウントを使うときは UID/GID を指定しておかないといけない。Remote - Containers で Dockerfile を使ってる場合は自動でホスト側の UID/GID を使ってくれるみたい。いいなぁ。 docker-compose.yml の場合はそういう対応はないので、自分で指定する。僕のユーザーは 1000 だからユーザーさえ指定してれば基本的には大丈夫だけど。

## (3) その他、覚えておきたいことのメモ

Git

https://code.visualstudio.com/docs/remote/containers#_sharing-git-credentials-with-your-container

  • Git はコンテナの中に入れておくと良さそう。VS Code が変更点とかを表示してくれるから。
  • でも push とかの操作はローカル側からやろうかな。SSH キーのことを考えたり、コンテナの中のターミナルを自分に合うようにするの面倒だし。

とはいえ、コンテナの中から Git を使うのに便利なように考えてくれてる。

  • ~/.gitconfig は自動的にマウントされる
  • SSH Agent を使ってると自動的にフォワードされる

なので、特に気にせずに Git を VS Code (Remote) の中で利用できそうではある。

Named Volume

https://code.visualstudio.com/docs/remote/containers-advanced#_improving-container-disk-performance

MacWindows のボリュームマウントは遅いので、 vendor や node_modules のようにファイルがたくさん置かれるものを bind してしまうと読み込みがすごく遅くなってしまう。

かといってボリュームを使わなかったら毎回クリアされてしまって、毎回 composer install でライブラリーを引っ張ってくるのは、ない。

ということで、 named local volume を使う。そうすれば bind はされないけどコンテナを再起動しても情報は残る:

version: '3'
services:
    app:
        build: docker/app
        working_dir: /workspace
        volumes:
            - ..:/workspace:cached
            - app-vendor:/workspace/vendor

(...)

volumes:
    app-vendor:

root 以外でコンテナを実行している場合は postCreateCommand で初回にオーナーを更新しておく:

"postCreateCommand": "sudo chown user-name-goes-here vendor"

だいたいこれくらい知っておけば良さそうかなぁ。