VS CodeのRemote Containersを使ってコンテナ内で開発する

この記事は[品川 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/shinagawa)の18日目です。

「マシンを変えてもすぐに開発できるようにしたい」と思うことがあって個人的に調べた結果、VS CodeのRemote Containersにたどり着きました。

Remote Containersを使った記事はちょくちょくあるので、この記事では知らない人向けの紹介と実際に使ってみた感想みたいなものを書きたいと思います。
コンテナ内で開発したいなぁと思っている人の参考になれば幸いです。

## Remote Containersとは
VS Codeの拡張機能の1つです。これを使うとDockerコンテナ内で開発できます。
詳しくはドキュメントを読んでください🙇

- [Developing inside a Container using Visual Studio Code Remote Development](https://code.visualstudio.com/docs/remote/containers)

## 開発環境をDocker化するメリット
改めて説明するまでもないかもしれませんが、一応メリットを整理しておきます。

1. 開発環境が簡単に構築できる
2. チームで同じ環境を共有できる
3. Dockerfileなどによって構築手順・設定がコード化される

## Remote Containersを使ってみる
Remote Containersを使って、ローカルマシン上のコンテナ内で開発してみます。
素晴らしいことにサンプルが公開されています。ここではFlaskを使ったWebアプリケーションを動かしてみます。

### 事前準備
- Gitがインストール済み
- Dockerがインストール済み
- VS Code（および拡張機能のRemote Containers）がインストール済み

### 手順
1. サンプルリポジトリ[^1]を`git clone`する
2. cloneしたリポジトリのディレクトリをVS Codeで開く
3. コマンドパレットで「`Remote-Containers: Reopen Folder in Container`」を実行する

### ちょっとだけ説明
上記の手順を実行すると

- イメージのビルド
  - `.devcontainer/Dockerfile`を使ってビルドします。
- コンテナの作成・起動
- ~/.gitconfigのコピーと拡張機能のインストール
  - `.devcontainer/devcontainer.json` に記載されている拡張機能（このサンプルでは`ms-python.python`）をインストールします。
- ディレクトリをコンテナにマウントし、VS Codeで表示
  - このVS Codeは、コンテナ内部のVS Code Serverと通信しており（正直この辺りはよく分かってませんが...）、コンテナ内部で起動しているかのように扱えます。

までやってくれます。後は今までとおり開発できます。やったぜ！

![2019-12-17_2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543047/7e77302f-fffa-15c6-c55d-e374f0383b35.gif)

あと、ホストマシンのアクセスはポートを公開することで可能です。
このサンプルでは9000でポートが公開されているので、Flaskをポートを9000に指定して起動すればホストマシンからでもアクセスできます[^2]。

## Remote Containersを使ってみて
Remote Containersを使ってみた感想とかをまとまりなく書いてみます。

### Gitがそのまま使えて便利
- コンテナ作成時に~/.gitconfigがコンテナ内に自動でコピーされるようです[^3]。
- 当然といえば当然ですが、コンテナ内でGitが使える必要があります。

### ホストマシンとほぼ同じように使える
- コンテナ作成時に実行するコマンドを指定できます。
- なので、シェルスクリプトなどを指定して、~/.ssh/configやdotfiles、コマンド履歴などをコンテナ内にコピーすれば、ホストマシンとほぼ同じように使えます。

### Macだとファイルの読み書きが遅い
- ホストマシンのワークスペースを開発用コンテナに丸ごとマウントするわけですが、Macだと遅すぎてツライです[^4]。
- これはマウントする際のオプションに `cached` をつけることで、だいぶ改善されます。

### コンテナ再作成に時間がかかる
- 作成したコンテナに対し、上記の~/.gitconfigのコピーや拡張機能のインストールを行います。これらはキャッシュされないので、新しいDockerイメージになるたびに実行されます。
- 一応、コンテナ内のVS Code Serverのデータディレクトリをマウントすることで拡張機能のインストールを避けることができます。以下の記事が参考になります。
  - [Advanced Container Configuration](https://code.visualstudio.com/docs/remote/containers-advanced#_avoiding-extension-reinstalls-on-container-rebuild)

### パッケージはコンテナ作成時にワークスペース内にインストールすると良さそう
- ここで言うパッケージとは、PythonでいうとpipやPoetryとかで、Node.jsならnpmとかyarnとかでインストールするパッケージのことです。
- 具体的には、パッケージをENTRYPOINTでインストールします。インストール先をワークスペースにすることでvolumeに載ります。
- こうしておくと、パッケージをインストール（アンインストール）してもDockerイメージをビルドし直す必要がなくなり、気軽にパッケージを試すことができます。

## まとめ
ちょっと重いと感じるときもありますが、docker-compose.ymlを使って複数のコンテナを同時に起動すること（例えば、MySQLとFlaskの2つを起動するとか）もできるので、個人的にはかなり便利だと思います。
新しく開発環境を構築する機会があるなら、ぜひやってみてください！


[^1]: [microsoft/vs code-remote-try-python: Python sample project for trying out the VS Code Remote - Containers extension](https://github.com/Microsoft/vscode-remote-try-python)
[^2]: このサンプルでは`devcontainer.json`で設定されていますが、docker-compose.ymlを使う場合は、通常どおり、docker-compose.ymlに`ports`を書くだけです。
[^3]: ~/.gitconfigがシンボリックリンクだとダメなので注意してください。コンテナ作成時にやっていることは「Dev Containers」ターミナルにログが出力されるので、眺めてみるといいかもしれません。
[^4]: 「Docker Mac volumes 遅い」でググってみると色々出てきます。