VS CodeでDocker開発コンテナを便利に使おう

# はじめに

- ローカル環境で開発し、Linux環境にデプロイしてテストするのが面倒
- Dockerを使っていい感じに開発環境を作りたい
- しかし色々設定や構築が面倒

そんな方のためにDockerコンテナを用いた開発環境をVS Codeから便利に構築、運用できる拡張機能「Remote-Containers」の使い方のご紹介です。

# この拡張機能の素晴らしさ

VS Codeの拡張機能「Remote-Containers」はコンテナ内でVS Codeを立ち上げ、ホストマシンのVS Codeと通信させることであたかもローカル環境で開発しているような操作感でコンテナ内開発が行えるというものです。

詳しい構成は公式ドキュメントに図があります。

![](https://code.visualstudio.com/assets/docs/remote/containers/architecture-containers.png)
(https://code.visualstudio.com/assets/docs/remote/containers/)

また、複数の開発環境をVS Code上から管理して、ワンクリックでコンテナを立ち上げることが可能です。

![](https://code.visualstudio.com/assets/docs/remote/containers/containers-explorer-python.png)
(https://code.visualstudio.com/assets/docs/remote/containers/)

そのために開発を始める際にコンテナをコマンドから立ち上げ、シェルをアタッチしてコンテナ内に入るなどの作業が必要なくなります。

ローカル環境でVS Codeを開いて開発を始めるのとほぼ同じ感覚でコンテナ内での開発が始められるのです。

システム要件は以下になります。

> - Windows: Docker Desktop 2.0+ on Windows 10 Pro/Enterprise. (Docker Toolbox is not supported.)
> - macOS: Docker Desktop 2.0+.
> - Linux: Docker CE/EE 18.06+ and Docker Compose 1.21+. (The Ubuntu snap package is not supported.)

# 環境構築のための環境構築

## Docker

まずDockerをインストールしますが、ここは省略します。

筆者はWindows環境にDocker Desktopをインストールしましたが、想像を絶する艱難辛苦を味わいました。

## VS Code 拡張機能

VS Code上でctrl + shift + Xで拡張機能メニューを開き、「Remote-Containers」を検索してインストールします。

Microsoft公式なので安心(?)です。

Remote-Containers / Remote-SSH / Remote-WSLをひとつにしたRemote Developmentという拡張機能もあるのでそちらでも大丈夫です。

## git

GithubのMicrosoft公式リポジトリに設定ファイルのサンプルがあるので、それをcloneしてくるとスムーズです。

なのでgitをインストールしましょう(手順省略)。

サンプルのリポジトリは以下です。

[vscode-remote-try-python](https://github.com/Microsoft/vscode-remote-try-python)

今回はPythonを使いますが、node.jsやjava、goなどもあります。

必要なのは.devcontainerディレクトリ配下のDockerfileとdevcontainer.jsonなので、そこだけ持ってきても構いません。

Remote-Containersの機能で「Try a Sample」というのがあり、cloneしなくてもこれらのリポジトリを使って試すことができますが突然docker imageのビルドが始まるのでやや面喰います。


# 開発環境構築

## プロジェクト構成

例えばPythonのアプリケーションの開発環境を作るとします。

ディレクトリ構成を以下のようにしてVS Codeからprojectディレクトリを開きます。

```
project/
    └ .devcontainer/
        ├ Dockerfile
        └ devcontainer.json
    └ .git/
    └ package/
        ├ __init__.py
        ├ __main__.py
        └  module.py
    ├ requirements.txt
    └ .gitignore
```

f1メニューまたは左下に現れる緑色のアイコンをクリックして「Remote-Containers: Open Folder in Container...」を選択します。

![](https://code.visualstudio.com/assets/docs/remote/common/remote-dev-status-bar.png)
(https://code.visualstudio.com/assets/docs/remote/containers/)

するとVS Codeが.devcontainer配下のDockerfileとdevcontainer.jsonを読み込み、その設定に従ってDockerコンテナを立ち上げてくれるという寸法です。

それでは、Dockerfileとdevcontainer.jsonの中身を見て具体的に何が起こっているのかを理解しましょう。

## Dockerfile

ここは普通のDockerfileで、特別なことはありませんがユーザー権限回りの設定をいい感じにやってくれています。

```Dockerfile
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------

FROM python:3
```

Docker imageをビルドするための元となるイメージを指定する項目です。

pythonのバージョンを詳しく指定したい場合はここをpython:3.7などにします。

```Dockerfile
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN apt-get update \
    && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \

    && apt-get -y install git iproute2 procps lsb-release \

    ~~~

    && groupadd --gid $USER_GID $USERNAME \
    && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \

    && apt-get install -y sudo \
    && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\
    && chmod 0440 /etc/sudoers.d/$USERNAME \
```

ここでapt-getの設定とユーザーの権限回りを設定しています。

要はsudoできる権限を持ったvscodeというユーザーでコンテナを扱えるようにしているようです。

## devcontainer.json

こちらがこの拡張機能のミソです。

```json
	"name": "Python 3",
	"context": "..",
	"dockerFile": "Dockerfile",

	"settings": { 
		"terminal.integrated.shell.linux": "/bin/bash",
		"python.pythonPath": "/usr/local/bin/python",
		"python.linting.enabled": true,
		"python.linting.pylintEnabled": true,
		"python.linting.pylintPath": "/usr/local/bin/pylint"
	},

	"appPort": [ 9000 ],

	"postCreateCommand": "sudo pip install -r requirements.txt",

	"remoteUser": "vscode",

	"extensions": [
		"ms-python.python"
	]
}
```
ここにjson形式で書かれている項目がRemote-Containers拡張機能の設定となります。

- name
    - Dev Containerの表示名を指定できます(Dockerコンテナ名とは別物です)。
- context
    - プロジェクトのルートを指定します。
    - この場合devcontainer.jsonがいる.devcontainerディレクトリの一つ上なので".."が指定されています。
- settings
    - コンテナ側のVS Codeにおける各種設定を行う項目です。シェルやPythonのパスなど。
- appPort
    - ホストマシンからアクセスできるコンテナのポート番号を指定します。
    - docker runするときの-pオプションで9000:9000と指定するのと同じです。
    - つまりホストマシンのブラウザでlocalhost:9000を叩くとコンテナのlocalhost:9000につながります。
- postCreateCommand
    - イメージをビルドした後にコンテナ内で実行されるコマンドを書くことができます。
    - この場合はプロジェクトのルートにあるrequirements.txtに書かれたパッケージがインストールされます。
- extensions
    - コンテナ側のVS Codeにインストールされる拡張機能を指定できます。

公式ドキュメントによると、その他にも色々設定できる項目があります。

デフォルトではプロジェクトのルートディレクトリがコンテナの/workspaceにバインドされますが、

```json
{	
    "workspaceFolder": "/home/vscode",
    "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/home/vscode/project"
}
```
のようにすれば、/home/vscodeにバインドされてVS Codeで開くデフォルトのディレクトリもそこになります。

```json
{	
    "containerEnv": {
        "WORKSPACE": "/home/vscode"
    }
}
```
containerEnv項目を指定すると、コンテナ内で使える環境変数を設定できます。

```json
{	
    "runArgs": [
		"--name=project_dev_container"
    ]
}
```
runArgsの項目で、コンテナ立ち上げ時のオプションを直接指定することもできます。

実際にはVS Codeがこのdevcontainer.jsonを読み込んで各種オプションをつけてdocker runしてコンテナを立ち上げています。

その際にrunArgsの項目で指定した文字列のリストがスペース区切りで追加されるわけです。

さらに詳しくはこちら : [Developing inside a Container - devcontainer.json reference](https://code.visualstudio.com/docs/remote/containers#_devcontainerjson-reference)

## その他個別設定

### git

コンテナにバインドするプロジェクトディレクトリに.git/が入っていると、そのままコンテナ内のVS Codeのバージョン管理機能が使えます。

VS Codeのバージョン管理機能は大変便利で、git addやgit commit、git pushなどがGUIで行えます。

しかしながらコンテナ内でモートリポジトリと通信しようとすると、毎回Githubの認証が必要になってしまいます。

ですがそこは我らがVS Code、gitの認証情報をホストマシンと共有できる機能が付いています。

[Developing inside a Container - Sharing Git credentials with your container](https://code.visualstudio.com/docs/remote/containers#_sharing-git-credentials-with-your-container)

まずホストマシンとでGithubのユーザー名やメールアドレスを.gitconfigファイルに保存しておきます。

```bash
$ git config --global user.name "Your Name"
$ git config --global user.email "your.email@address"
```

ユーザールートにある.gitconfigにこれらの設定が書き込まれていますが、これをVS Codeが自動でコンテナにコピーしてくれるようです。

次にパスワードなどの認証情報ですが、二通り設定方法があります。

https通信を使ってidとパスワードで認証している場合、gitのcredential helperにパスワードを保存しておけばその設定がコンテナと同期されます。

[Caching your GitHub password in Git](https://help.github.com/en/github/using-git/caching-your-github-password-in-git)

```bash
$ git config --global credential.helper wincred
```

sshでの認証では、ホストマシンでSSH agentにGithub用の公開鍵を登録しておくとその設定が同期されるようです。

PowerShellで

```PowerShell
ssh-add $HOME/.ssh/github_rsa
```
とするとSSH agentに鍵が登録されます。

しかしながらSSH agentが起動していない場合が多く、そのときは管理者権限でPowerShellに入って

```PowerShell
Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
Get-Service ssh-agent
```

するか、GUIでサービス > OpenSSH Authentication Agentのプロパティから設定できます。

詳しくは[Windows 10のssh-agentをコマンド プロンプト、WSL、Git Bashで使ってみた#ssh-agent-の有効化](https://qiita.com/q1701/items/3cdc2d7ef7a3539f351d#ssh-agent-%E3%81%AE%E6%9C%89%E5%8A%B9%E5%8C%96)など。

### AWS Access Key

コンテナ内からAWS S3と通信しようとすると、アクセスキーの問題が発生します。

ホストマシンのユーザールートにある.awsディレクトリ内にアクセスキーの情報があり、それをコンテナ内でも読み込みたいものです。

しかしながらgitの場合とは異なりそこは自動的に読み込んではくれないようです。

それゆえに一度コンテナの外からdocker cpを使ってコンテナにコピーしてくる必要があります。

```bash
docker cp $HOME/.aws {コンテナ名}:home/vscode
```

ここで、Remote-Containersで立ち上げるコンテナに名前がついてると便利です。

先ほどのdevcontainer.jsonのrunArgsの項目で、

```json
{	
    "runArgs": [
		"--name=project_dev_container"
    ]
}
```

のようにしてコンテナ名をつけるようにすると良いでしょう。



# 素晴らしい航海

実際に使ってみると、普通にVS Codeで開発するのとほぼ同じ感覚で開発コンテナを扱うことができます。

また、Pythonで開発する際には仮想環境を用意するのが普通ですがコンテナ内で環境を構築することでその必要がなくなります。

なぜなら、各プロジェクトごとに違うコンテナを使っているのでパッケージをグローバルにインストールしても環境を汚染しないからです。

そして、Pythonのバージョンごとにイメージが用意されているので本番環境に合わせてそれを指定することもできます。

(この辺りはRemote-Containersというよりdockerを使うことのメリットですが)

こうした開発環境をワンクリックで開いたり、切り替えたりできるわけです。

また、この拡張機能はポートのバインドも自動で行ってくれるため、フロントエンド開発でのローカルサーバーもストレスなく使うことができます。

ただし、例えばnode.jsなどは3000番ポート指定でローカルサーバーが立ち上がるのでそのポートをローカルマシンに公開しなければなりません。

その場合はdevcontainer.jsonで

```json
{	
    "appPort": [ 3000 ]
}
```
のように設定しておきましょう。

# 参考

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

公式ドキュメント、長い

- [【2020年1月】令和だし本格的にVSCodeのRemote Containerで、爆速の"開発コンテナ"始めよう](https://qiita.com/koinori/items/084a0770c1f9e72e0c14)

この記事を見てRemote-Containersを使い始めました。感謝！