DockerとVisual Studio Codeで開発環境を作る

##はじめに
表題の通りです。
今までDockerもVSCodeも別々に使っていました。
今更ですがDockerでVSCodeを使えるようにしていきます。

[Remote Development with VS Code](https://code.visualstudio.com/blogs/2019/05/02/remote-development)

この機能を使うと、以下の3つの環境にVSCodeからリモート接続して開発できるようになります。

##その前に
まず、使わないやり方
・[ローカルで開発したソースをDockerにコピーする](#ローカルで開発したソースをDockerにコピーする)
・[ローカルの特定ディレクトリをDockerにマウントする](#ローカルの特定ディレクトリをDockerにマウントする)

###ローカルで開発したソースをDockerにコピーする
以前、使わないやり方でRust公式ドキュメントを試したことがあるので内容はこちらを参考にしてください。
> [Rust公式ドキュメントをDockerコンテナ上で進めてみる（1. 事始め, 2.数当てゲーム）](https://qiita.com/c3drive/items/0008c05998374b6315b4)

こちらでは、以下の役割分担となっていました。


|  作業  |  ローカル環境(PC）  | 開発環境(Docker）  |
| ---- | ---- | ---- |-
| Rustのインストール | ー | ○ | 
| Rustのコーディング | ○ | ー |
| Rustのビルド | ー | ○ |
| Rustの実行 | ー | ○ |

使っていたDockerfile

 ```Dockerfile
FROM rust:latest
WORKDIR /usr/src/projects
COPY . .

# 2.数当てゲームをプログラミングする
RUN cargo init guessing_game --bin && \
    cd /usr/src/projects/guessing_game && \
    cargo build
WORKDIR /usr/src/projects/guessing_game
CMD ["cargo", "run"]
```
ローカルでuessing_game/main.rcを作成しディレクトリごと`COPY . .`でDocker側にコピーしています。
ローカルを汚さないコンセプトでやっていたのでVSCodeにRust拡張を入れることもしませんでした。
公式ドキュメントをなぞるだけであれば問題ないですが、通常は動作確認が不可になるのでローカルにもRustを入れてビルド実行できるようにするかと思います。
以下はWeb上でRustの動作確認ができますので、ビルドが通ることの確認だけでしたらばローカルにIDEを入れる必要もないですがきちんとサービスを開発するのには向かないです。
https://play.rust-lang.org/

###ローカルの特定ディレクトリをDockerにマウントする
開発とDocker内での動作確認（ビルド）を並行できる点が利点。
コピーとの違いは、Dockerfileから`COPY`と`CMD`を削除し、run時に以下を実行

```bash
# フルパスにしないとマウントされないので注意（空のフォルダがコンテナに作成されるだけ）
$ docker run -it -v /Users/yukokanai/work/docker/bootstrap/mounted_folder/{開発ごとに作成}:/new_dir {image名}
```

--
どちらの方法もターミナルウィンドウをいくつか準備したりとちょっと手間でした。


##インストール
こちらの手順からインストールを進めます。
[Developing inside a Container　- Installation](https://code.visualstudio.com/docs/remote/containers#_installation)

VSCodeとDockerはローカル端末に入っているので以下の手順はスキップします。
>1.Install and configure Docker for your operating system.
>2.Install Visual Studio Code or Visual Studio Code Insiders.

3.Install the Remote Development extension pack.
VSCodeにて、「Remote Development」を選択し、インストールします。
![スクリーンショット 2021-08-01 14.58.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/de5aab4b-a950-be4c-c740-b9f9e1fc0f4b.png)

##設定
まずは、適用するプロジェクトを新規に用意します。
※すでにDockerfileを使っているものへの適用も可能です。

予めGitHubから適用するプロジェクト作成しクローンします。

```bash
$ git clone git@github.com:c3drive/smart_dog.git
```

####お試しでクイックスタートに従って進める
インストール同様こちらの手順から進めます。
[Developing inside a Container　- Quick start](https://code.visualstudio.com/docs/remote/containers#_quick-start-open-an-existing-folder-in-a-container)

VSCodeを開き、「Remote-Containers: Open Folder in Container...」を選択します。
![スクリーンショット 2021-08-01 16.19.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/af5c4cbe-53ab-0067-a2e6-b3a07d67e56c.png)
先ほどクローンしたプロジェクトを選択します。
コンテナのOSイメージはrustを使いたかったので、「Show All Definions...」を選択します。

![スクリーンショット 2021-08-01 16.30.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/d4124027-1416-75c9-de85-f2595f54e30f.png)
rustを選択すると設定が開始します。
完了後の画面は以下。VSCodeのTERMINALにてcargoが入っていることを確認します。
![スクリーンショット 2021-08-01 17.03.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/60369774-92c0-6c1e-7d55-f9cb3a0c25b8.png)
ちなみに私のローカル環境にrustやcargoはインストールしていません。以下はローカルのターミナル画面。
コンテナが起動していることもついでに確認してみます。
![スクリーンショット 2021-08-01 17.08.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/62dc13fb-d433-237e-8ad1-998ad213110d.png)
問題なく、動いていそうです。

##ファイルを作成・変更してみる
まずは、VSCodeのTERMINAL（コンテナ）からcargo new
EXPLORERにhello_cargoフォルダが作成されました。
![スクリーンショット 2021-08-01 17.11.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/d4ba633a-8ed3-1e79-01d2-2a1dc0ed235c.png)
ローカル環境でも作成されています。
![スクリーンショット 2021-08-01 17.15.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/332c33c2-bf01-7290-a203-a2fea9779c66.png)

次にVSCodeエディタからファイル変更してみます。
src/main.rcに赤線部分を追記しました。
![スクリーンショット 2021-08-01 17.17.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/055b2711-e2cf-f609-5ff1-60133a6b846a.png)
まずはローカル環境で確認
![スクリーンショット 2021-08-01 17.19.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/1d714096-5726-26c9-88d6-bf3206911839.png)

VSCodeではビルド実行までしてみます。
![スクリーンショット 2021-08-01 17.23.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/e8802028-cbe2-323c-9f1b-ccf5787ccd00.png)

問題なさそうです。

##実践
動作がわかったところで、自身が使うDockerfileを使った環境にしていきます。
プロジェクトは先ほどのものを使いますが、Remote周りで作成されたものはすべて削除し、Dockerfileを作成します。
![スクリーンショット 2021-08-01 17.41.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/74113115-3e90-5dac-58bc-0d08f00d03ac.png)
（CMDがあると何か起きるかと思いましたが特に何も起きませんでした）
先ほどと同様に左下のマークから「Remote-Containers: Open Folder in Container...」を選択します。
OSイメージの選択画面が、先ほどとは違う表示となり、Dockerfileの選択肢に表示されているので選択します。
![スクリーンショット 2021-08-01 17.49.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/f100ee68-6a5e-d1f2-8521-16e44aca4fb5.png)
無事起動しました。
![スクリーンショット 2021-08-01 18.00.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/1a133445-9e1e-ee4b-3e18-8fcde7da577b.png)

先ほどとはEXPLORERの構成と`.devcontainer/devcontainer.json`が異なります。
![スクリーンショット 2021-08-01 18.24.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/bd54c809-7b42-5836-4f13-67bf09391262.png)
![スクリーンショット 2021-08-01 18.27.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/4f9c8170-fff3-67fe-a675-fe7359f0ec80.png)
`.devcontainer/devcontainer.json`は、今後変更する必要が出てきそうでしたのでコードでも残しておきます。

```json:用意されたrustを選択した場合
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.187.0/containers/rust
{
	"name": "Rust",
	"build": {
		"dockerfile": "Dockerfile"
	},
	"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],

	// Set *default* container specific settings.json values on container create.
	"settings": { 
		"lldb.executable": "/usr/bin/lldb",
		// VS Code don't watch files under ./target
		"files.watcherExclude": {
			"**/target/**": true
		}
	},

	// Add the IDs of extensions you want installed when the container is created.
	"extensions": [
		"rust-lang.rust",
		"bungcip.better-toml",
		"vadimcn.vscode-lldb",
		"mutantdino.resourcemonitor"
	],

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],

	// Use 'postCreateCommand' to run commands after the container is created.
	// "postCreateCommand": "rustc --version",

	// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
	"remoteUser": "vscode"
}
```
```json:既存のDockerfileを選択した場合
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.187.0/containers/docker-existing-dockerfile
{
	"name": "Existing Dockerfile",

	// Sets the run context to one level up instead of the .devcontainer folder.
	"context": "..",

	// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
	"dockerFile": "../Dockerfile",

	// Set *default* container specific settings.json values on container create.
	"settings": {},
	
	// Add the IDs of extensions you want installed when the container is created.
	"extensions": []

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],

	// Uncomment the next line to run commands after the container is created - for example installing curl.
	// "postCreateCommand": "apt-get update && apt-get install -y curl",

	// Uncomment when using a ptrace-based debugger like C++, Go, and Rust
	// "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],

	// Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker.
	// "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ],

	// Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
	// "remoteUser": "vscode"
}

```

##コンテナの設定を変更した時
Dockerfileを変更した時などは、リビルドを行います。
コンテナを起動した状態で左下の緑を選択するとこちらの選択肢が表示されますので、「Rebuild Container」を選択してください。
![スクリーンショット 2021-08-01 18.37.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/a8ca67ac-557f-a429-9e25-833f015e844c.png)

##コンテナのコネクション切断と再接続
コンテナを起動した状態で左下の緑を選択した後の画面をスクロールし、「Close Remote Connection」でコネクションが切れプロジェクトが閉じます。
![スクリーンショット 2021-08-01 18.42.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/de10ad1e-e405-ffaf-a419-2ad3a97a5619.png)
この時、コンテナも停止しします。（わかりづらいですが、今回NAMES「modest_chatelet」のコンテナを利用しており、ないことがわかります）
![スクリーンショット 2021-08-01 18.42.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/28b43e6f-e2bd-b98b-b2c8-40d60cfe06a4.png)
再度コネクションするには、再度左下の緑を選択して、「Open Folder in Container」を選択し、先ほどまでコネクションしていたプロジェクトを選択します。![スクリーンショット 2021-08-01 18.47.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/bd571e69-0155-a3e3-fa5e-927c8005f50e.png)
つながりました。
![スクリーンショット 2021-08-01 18.47.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/7889106a-0bd8-f687-db9c-90b68ffcf536.png)

##その他
REMOTE EXPLORERにて、右クリック「Stop Container」を選択すると、停止後リロードを要求され、リロード後即起動されて停止することはできません。
![スクリーンショット 2021-08-01 18.53.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/24ff61c4-ee43-bd93-91cd-e49868868cea.png)
この挙動は、ローカルのターミナルから停止コマンドを実行しても同様でした。
再接続の挙動でもVSCodeでContainer接続可能なプロジェクトを開いた瞬間勝手にコネクションされていましたのでそのVSCodeを開いている限り停止はできないと思います。

せっかくなのでコンテナを削除したらどうなるかみておきます。
一度、「modest_chatelet」のコンテナを削除します。
その後、左下の緑を選択して、「Open Folder in Container」を選択し、先ほどまでコネクションしていたプロジェクトを選択します。
![スクリーンショット 2021-08-01 19.17.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/865824c9-cfe1-8ec7-f7fe-d623bb56d275.png)
ビルドが走り、勝手にコネクションされました。NAMESは「competent_allen」となり新しいコンテナで起動しています。

また、コネクション切断をせずにVSCodeを終了させるとコンテナは起動し続けます。

##おわりに
簡単にDockerでの開発ができるようになったのでこれから、Webアプリを作ろうと思います。
ここまで、読んでいただきありがとうございました。

##[追記]Docker+VSCode環境にて、RustでWebアプリ作りました。
https://qiita.com/c3drive/items/71dda219f0193ae72069

こちらにはGitHubのソースパスも貼っていますが、Docker-Composeを使いました。


##参考
今回は、以下サイトを参考にさせていただきました。

- [Developing inside a Container](https://code.visualstudio.com/docs/remote/containers)
- [Dockerで立ち上げた開発環境をVS Codeで開く!](https://qiita.com/yoskeoka/items/01c52c069123e0298660)
- [Visual Studio CodeのRemote DevelopmentとDockerで快適な開発環境をゲット](https://tech-lab.sios.jp/archives/18677)