【2020年1月】令和だし本格的にVSCodeのRemote Containerで、爆速の"開発コンテナ"始めよう

# VSCode の Remote Conainer で"開発環境+プロジェクト全部入りのコンテナ"からスタートダッシュをキメるッ！？

開発でVS Code の Remote Conainer使っていますか？単に既存のコンテナに入るだけなら Remote SSH でも構いませんが、"ローカル開発環境の一部"として、いやむしろ**ローカルの開発環境＝Remote Container**として、ビンビンにRemote Container使っていきましょう。令和だし！~~(すでに２年だけどね・・・？)~~

特にMacを使っていると最初からPythonやらPHPやらRubyやらが入ってしまっているので**開発環境がある**のですが、これらは割とmacOSのエコシステムに組み込まれているので不要にパッケージの追加削除、できないのですよ。`brew`とか意外とあっさり壊れますしね・・・。特に**バージョンアップなんてもってのほか**です。全然、余裕でおかしくなります。
そんなわけでMacに入っているPythonやRubyでプログラミングをバリバリしていると・・・**ふと、後戻りできない**状況になったりするわけです。
そんなことにならないためにも、"Remote Containerでの開発"に入門しましょう~!

## "Dev Container" 機能のご紹介

Remote Container の本家サイトと本家Githubで"Try a dev container"と言う項目とリポジトリがあるの、ご存知でしょうか？

- [Developing inside a Container using Visual Studio Code Remote Development # Try a dev container](https://code.visualstudio.com/docs/remote/containers#_quick-start-try-a-dev-container)

"dev container"は開発環境入りコンテナが付属したプロジェクトのサンプルで、以下の各言語向けにdev-containerのサンプルが用意されています。

- Node.js, Javascript
- Python
- Go
- Java
- .Net Core
- PHP
- Rust
- C++

例えば、node.js、Javascript用のサンプルは以下のようなツリーになっております。

```zsh
% git clone https://github.com/Microsoft/vscode-remote-try-node nodejs-dev-sample
% cd nodejs-dev-sample
% tree -a -I ".git"
.
├── .devcontainer
│   ├── Dockerfile
│   └── devcontainer.json
├── .eslintrc.json
├── .gitattributes
├── .gitignore
├── .vscode
│   └── launch.json
├── LICENSE
├── README.md
├── package.json
├── server.js
└── yarn.lock
```
`tree`コマンドで`.git`だけ除外して全て表示すると上記のようになります。
ここで気になるのが・・・`.devcontainer`ですよね？！
中身の`Dockerfile`と`devcontainer.json`は以下のようになっております。

```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 node:10

# The node image includes a non-root user with sudo access. Use the "remoteUser"
# property in devcontainer.json to use it. On Linux, the container user's GID/UIDs
# will be updated to match your local UID/GID (when using the dockerFile property).
# See https://aka.ms/vscode-remote/containers/non-root-user for details.
ARG USERNAME=node
ARG USER_UID=1000
ARG USER_GID=$USER_UID

# Avoid warnings by switching to noninteractive
ENV DEBIAN_FRONTEND=noninteractive

# Configure apt and install packages
RUN apt-get update \
    && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \ 
    #
    # Verify git and needed tools are installed
    && apt-get -y install git iproute2 procps \
    #
    # Remove outdated yarn from /opt and install via package 
    # so it can be easily updated via apt-get upgrade yarn
    && rm -rf /opt/yarn-* \
    && rm -f /usr/local/bin/yarn \
    && rm -f /usr/local/bin/yarnpkg \
    && apt-get install -y curl apt-transport-https lsb-release \
    && curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
    && echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
    && apt-get update \
    && apt-get -y install --no-install-recommends yarn \
    #
    # Install eslint globally
    && npm install -g eslint \
    #
    # [Optional] Update a non-root user to UID/GID if needed.
    && if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \
        groupmod --gid $USER_GID $USERNAME \
        && usermod --uid $USER_UID --gid $USER_GID $USERNAME \
        && chown -R $USER_UID:$USER_GID /home/$USERNAME; \
    fi \
    # [Optional] Add add sudo support for non-root user
    && apt-get install -y sudo \
    && echo node ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
    && chmod 0440 /etc/sudoers.d/$USERNAME \
    #
    # Clean up
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*

# Switch back to dialog for any ad-hoc use of apt-get
ENV DEBIAN_FRONTEND=dialog
```
上記の解説は後に回して、続いて`devcontainer.json`の中身は・・・

```json:devcontainer.json
{
    "name": "Node.js Sample",
    "dockerFile": "Dockerfile",

    // Use 'appPort' to create a container with published ports. If the port isn't working, be sure
    // your server accepts connections from all interfaces (0.0.0.0 or '*'), not just localhost.
    "appPort": [3000],

    // Comment out the next line to run as root instead.
    "remoteUser": "node",

    // Use 'settings' to set *default* container specific settings.json values on container create. 
    // You can edit these settings after create using File > Preferences > Settings > Remote.
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash"
    },

    // Specifies a command that should be run after the container has been created.
    "postCreateCommand": "yarn install",

    // Add the IDs of extensions you want installed when the container is created in the array below.
    "extensions": [
        "dbaeumer.vscode-eslint"
    ]
}
```
と、コンテナの設定とvscodeのsettings.jsonの混ざったような形式のファイルとなっております。
この設定がなんなのかはだいたい、想像がつくかと思いますが、このフォルダをVSCodeで開くと何が起こるのでしょうか…試してみましょう！
その前に、Dockerデーモンが立ち上がってない方は事前にDockerデーモンを立ち上げてください。WindowsやMacの方はDocker for Desktopを起動しておいてください。

Dockerが起動したらVSCodeを立ち上げて `nodejs-dev-sample` を開いてみます。
すると・・・
<img width="697" alt="スクリーンショット 2020-01-03 14.10.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/ef966934-a25f-3f41-37ea-94ed24849cec.png">

`dev container configurationが見つかったからコンテナで開くか？`と言う問い合わせが出ました！
そして `Reopen in Container`をクリックすると・・・
<img width="931" alt="スクリーンショット 2020-01-03 14.22.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/187da491-46be-98a3-2087-2bc9d14ffd99.png">
しばらく時間が経って…開きました！左下のステータスバーがグリーンに変わってRemote接続中であることと、"Dev Container: Node.js Sample"の文字が眩しいですね！
そうなんです。`.devcontainer`フォルダと`devcontainer.json`の設定と然るべき`Dockerfile`が揃っていればプロジェクトフォルダ内のファイルを丸ごとマウントしたコンテナの自動生成とプロジェクトをVSCodeで開くのを勝手にやってくれるのです！

また、ここでのDockerfileの特筆するべき点はdockerに問題の"root"ユーザー問題を宜しく解決してくれている点です。
"コンテナの中で開発する"のは聞こえはいいですが、大抵のイメージはそのまま実行するとユーザーが"root"になってしまうのが多いので、コンテナの中で更新されるファイルのオーナーが"root"になってしまってウザい問題がありました。自前でDockerfile書けばなんとでもなるのですがいちいち書いてられないし、いちいちDockerfile書くくらいなら開発環境汚れても別にいいじゃん**会社のPCだし**、みたいなことになってますよね！？
このDockerfileでは丁寧にそこのところをサポートしてくれているので、rootユーザー問題が無事に解決されています。

ついでにコンテナの管理も VSCode からできるので…
<img width="931" alt="スクリーンショット 2020-01-03 14.23.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/b7a79212-b76d-9f0a-d8b8-3c40d412adf1.png">
このようにどのプロジェクトでどのコンテナ使っているかは、VSCodeから管理できます。
特に**DockerではどのフォルダのDockerfileで立ち上げたコンテナかがわからない**(と言うかイメージを作ってコンテナ起動するのでどこのフォルダのDockerfileで作成したイメージかどうかは本来は関係ないハズ)のでこれは便利です。

## どうやって使うのか？

まずは本家リポジトリのご紹介をしておきましょう。
リポジトリ名からどの言語向けのサンプルプロジェクトかわかると思います。

- [microsoft/vscode-remote-try-python](https://github.com/microsoft/vscode-remote-try-python)
- [microsoft/vscode-remote-try-node](https://github.com/microsoft/vscode-remote-try-node)
- [microsoft/vscode-remote-try-go](https://github.com/microsoft/vscode-remote-try-go)
- [microsoft/vscode-remote-try-java](https://github.com/microsoft/vscode-remote-try-java)
- [microsoft/vscode-remote-try-cpp](https://github.com/microsoft/vscode-remote-try-cpp)
- [microsoft/vscode-remote-try-dotnetcore](https://github.com/microsoft/vscode-remote-try-dotnetcore)
- [microsoft/vscode-remote-try-rust](https://github.com/microsoft/vscode-remote-try-rust)
- [microsoft/vscode-remote-try-php](https://github.com/microsoft/vscode-remote-try-php)

実際には`.devcontainer`フォルダと`devcontainer.json`の設定と使用される`Dockerfile`の３つが揃っていれば自動的にやってくれますので、これらのファイルのみを本家リポジトリからコピペで作成するのもアリです。
とは言え毎度コピペするのも面倒なので、以下のように手順を整えてしまいましょう。

## 1. 雛形としてクローン

上記のリポジトリを以下のように"プロジェクトの名前"でクローンしてきます。

```shell
$ git clone https://github.com/microsoft/vscode-remote-try-php my-first-php
```
リポジトリ名の後ろの引数が作成されるフォルダ名で、別名でクローンするのが第１のミソです。

## 2. 履歴の削除と再作成

当然、cloneしたばかりでは本家リポジトリのコミット履歴を全て含んでおります。
このまま再利用しても全然、良いのですが・・・大抵の場合は気になるでしょう。
そこで `.git` 以下をざっくり削除します。

```shell
$ rm -rf .git
```
これで過去のコミット履歴は綺麗さっぱり忘れました。
ついでに不要なファイルは削除しておきましょう。
なんなら`.devcontainer`以外は不要です。`.vscode`はお好みにお任せいたします。

で、お掃除が完了したところで新規のリポジトリとして初期化します。

```shell
$ git init
...
```
これで新たな開発コンテナプロジェクトとして第一歩が始まりました！

## 3. Dockerfile のカスタマイズ

実際の案件ではDBやAngular、Aws、Firebase、AzureなどのCLIなどなどなどを入れる必要があったりするのでDockerfileがそのまま使えることはほぼないです。
よってDockerfileに予め必要なツールをインストールするコマンドを入れておきます。

Dockerfileの記述に関してはここでは割愛させていただきます。。。

## 4. devcontainer.json のカスタマイズ

`devcontainer.json`ではプロジェクト名や転送するポート、必要なVSCodeのプラグインなどの設定ができ、とても重要なファイルです。
設定値のリストは以下の箇所にあります。

- [devcontainer.json reference](https://code.visualstudio.com/docs/remote/containers#_devcontainerjson-reference)

サンプルリポジトリの中で使われている設定をいくつか抜き出してみますと・・・

- settings … これはコンテナの中で使用される VSCodeのsetting.jsonの設定値となります。
- appPort … 転送するポートです
- postCreateCommand … コンテナ作成後に実行されるコマンド
- extensions … リモート側にインストールされるVSCodeプラグイン

あたりが重要なカスタマイズするポイントでしょうか。

また、リファレンスによると `docker-compose.yml`も利用可能な模様です。

これらを設定後に、VSCodeからフォルダを開けば、即座に開発環境込みのプロジェクトのスタートです！

## まとめ

VScode Remoteコンテナの機能の入門編を書いてみるにあたって、公式ドキュメントを見直しましたが…ボリュームが半端ないですね。
これをどこまで深堀りするべきか悩みましたが、入門編ということで`ほとんど触れない`ようにいたしました(笑)
本文中に飛び飛びでリンクを貼っていますが、Remote Containerの公式ヘルプは驚愕の１ページです。

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

これだけで本書けそうなボリュームですよ・・・

というところで、今回はここまでといたします！