[VSCode]Remote Containersで開発環境を効率的に作成する

## RemoteContainerとは？

- VSCodeでコンテナ開発環境(devcontainer)を作成するための拡張機能
  - [Remote - Containers - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)

- 利点
  - Dockerで開発する利点とVSCodeで開発する利点のどちらも享受できる
  - Dockerで開発する利点
        - OS依存がない
        - 特定の人だけ実行できないなどの環境依存がない
        - 使用するライブラリなどのバージョンが統一される
  - VSCodeで開発する利点
        - Linter、Formetterなどの設定をプロジェクトで共通化
            - それらの設定の自動適用
        - 開発で使用するVSCode拡張機能を自動インストール
            - 開発するのに必須な拡張機能などをプロジェクトで統一可能(Vue.jsで言うところのVeturなど)
                - 開発環境構築毎に、拡張機能を入れるのでHOST汚染がない
- 欠点
  - VSCodeを使用していないと使えない


## 使い方

- RemoteContainersのインストール

    ```bash
    code --install-extension ms-vscode-remote.remote-containers
    ```

- RemoteContainerの起動方法
  - VSCodeで`command＋Shift＋P`を入力後、`Remote-Containers: Add Development Container Configuration Files`と入力
  - 何のDocker環境を構築するか選択する画面になるので、Python3を選択します
 ![2021-11-29-17-17-52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169887/3f0ab67e-de57-0fba-2945-f822ac5e8569.png)

  - その後は適当に最新版を選択後、OKを押下すると`devcontainer.json`が作成されます
  - 先程までなかった`.devcontainer`が作成されているはずなので、VSCodeで`command＋Shift＋P`を入力後、`Remote-Containers: Open Folder in Container`と入力
  - `.devcontainer`が存在するディレクトリでOKを押下
  - VSCodeが新規画面を開き、コンテナをビルドするのでしばらく待つ
![2021-11-29-17-25-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169887/bc769290-e44d-263e-e1f0-d39589f4d0bd.png)

  - ビルドが完了するとVSCodeの左下に`DEV Container`と表示され、VSCodeでコンテナ内に入っていることがわかります

![2021-11-29-17-29-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169887/d8632f7b-e944-74da-3c18-da7d3c29c19c.png)

  - TeminalもDocker内部なので、最新版のPythonがインストールされていることがわかります
           - ログインユーザーもvscodeになっています
![2021-11-29-17-35-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169887/231600b6-0928-6e81-1da7-f8ee8d2b1b36.png)

  - 以上でRemoteContainerの起動成功です

## 説明

- `devcontainer.json`でRemoteContainerの設定を行っているので内容を解説します

  ```json:devcontainer.json
  {
    "name": "Python 3",
    "build": {
      "dockerfile": "Dockerfile",
      "context": "..",
      "args": { 
        "VARIANT": "3.10-bullseye",
        "NODE_VERSION": "lts/*"
      }
    },
    "settings": { 
      "python.defaultInterpreterPath": "/usr/local/bin/python",
      "python.linting.enabled": true,
      "python.linting.pylintEnabled": true,
      "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
      "python.formatting.blackPath": "/usr/local/py-utils/bin/black",
      "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
      "python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
      "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
      "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
      "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
      "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
      "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
    },
    "extensions": [
      "ms-python.python",
      "ms-python.vscode-pylance"
    ],
    "forwardPorts": [],
    "postCreateCommand": "pip3 install --user -r requirements.txt",
    "remoteUser": "vscode"
  }
  ```

  - 各設定項目
    - name
      - RemoteContainerの環境名
    - build
      - どのファイルを元にコンテナをビルドするか設定
      - dockerfile
            - Dockerfileを指定。相対パスなどで指定可能
      - dockerComposeFile
            - docker−composeを使用する場合、dockerfileではなく、こちらで設定する
      - context
            - ビルドを行う相対パスの指定
      - args
            - `"args": {"NODE_VERSION": "lts/*"} `のようにしてコンテナ内に設定したい環境変数を指定
    - settings
      - VSCodeの設定を記載。`command + ,`を使用した時の設定内容のJSON
            - ここで適切な設定を行うことで、LinterやFormetterをVSCode保存時自動実行などが可能になる
    - extensions
      - VSCodeの拡張機能のIDを記載すれば、RemoteContainer起動時にコンテナ内に自動的にインストールされる
            - 拡張機能のIDは拡張機能インストール画面で設定からコピー可能
![2021-11-29-18-55-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169887/f61e3820-1cff-5903-7f42-9d0ed39dde47.png)
![2021-11-29-18-55-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169887/64a549c6-a5e7-503a-7aea-fa434652b591.png)

      - 自分のVSCodeで使用している拡張機能IDを一括で出力する場合のコマンド

        ```bash
        code --list-extensions | xargs -L 1 echo code --install-extension
        ```

    - forwardPorts
      - portを開ける
    - postCreateCommand
      - コンテナ起動後に、自動実行したいコマンドなどを入力できる
    - remoteUser
      - コンテナのログインユーザーの指定。デフォルトはvscode
      - homebrewなどを使用する際にはAdmin権限でコンテナを使用できないので必要


## docker-composeでRemoteContainer

- 説明ではDockerfile単体でRemoteContainerを起動する方法を説明致しましたが、下記のリポジトリのように、docker-composeでRemoteContainerを起動することも可能です
  - [selenium-docker-env/devcontainer.json at main · I-s-23/selenium-docker-env](https://github.com/I-s-23/selenium-docker-env/blob/main/.devcontainer/devcontainer.json)


## devcontainerで環境変数を定義

-  .envファイルを読み込ませる
   - devcontainer.jsonに以下のように書けば、Docker内部で環境変数が読み込まれます
     - [VSCodeのdevcontainer.jsonのremoteEnvでハマった話 | by GALACTIC1969 | GALACTIC1969 | Medium](https://medium.com/galactic1969/vscode%E3%81%AEdevcontainer-json%E3%81%AEremoteenv%E3%81%A7%E3%83%8F%E3%83%9E%E3%81%A3%E3%81%9F%E8%A9%B1-b46158e76d2f)

     ```json:devcontainer.json
     "runArgs": [
         "--env-file",
         ".env"
       ],
     ```

- envファイルを使わない場合、このように定義することも可能です

  ```json:devcontainer.json
  "runArgs": [
      "-e BIND_PORT=80",
      "-e BIND_ADDRESS=0.0.0.0",
      "-e BIND_ADDRESS=${ADRESS}",
    ],
  ```

## マルチステージングビルド

- マルチステージングビルドの環境もRemoteContainerで開けます
  - 以下のようにdevとprodでマルチステージングビルドをする場合、`docker-compose.yaml`に`target: dev`と書いた状態でRemoteContainerを起動すれば、devのTagrgetでビルドが止まるので開発が可能になります


    ```Dockerfile
    FROM python:3.10 as dev
    ENV APP_HOME /workspace
    WORKDIR $APP_HOME
    COPY . .
    RUN python -m pip install -U pip setuptools \
        && pip install -r requirements.txt \
        && apt-get clean \
        && rm -rf /var/lib/apt/lists/*

    FROM python:3.10-slim-buster as prod
    COPY . .
    COPY --from=dev /root/.cache/pip /root/.cache/pip
    COPY --from=dev /workspace/requirements.txt .
    RUN python -m pip install -U pip setuptools \
        && pip install -r requirements.txt
    ```

    ```yml:docker-compose.yaml
    version: "3.9"
    services:
      auto:
        build:
          context: ../
          dockerfile: Dockerfile
          target: dev
        env_file: .env
        tty: true
        stdin_open: true
        ports:
          - 8099:8099
        volumes:
          - ..:/workspace:cached
    ```


## 自分が今まで作成したdevcontainer

- 個人開発で使用している物なのでご自由にお使い下さい
  - IaC
        - [I-s-23/IaC-env at develop](https://github.com/I-s-23/IaC-env/tree/develop)
            - Terraform,aws-cli,cloudSDKなどIaCを行いたい場合などに役立たせて下さい
  - React
        - [I-s-23/react-docker-development-environment](https://github.com/I-s-23/react-docker-development-environment)
  - Python
        - Selenium
            - [I-s-23/selenium-docker-env: Selenium web browser automation docker env andmy custom functions](https://github.com/I-s-23/selenium-docker-env)
                - Python3.9+Selenium+ChromeDriver+pyautoGUIが入ったDocker環境です。ブラウザの自動操作にお使い下さい
  - Go
        - [I-s-23/golang-docker-dev: golang dev docker with VS Code extend remote-containers](https://github.com/I-s-23/golang-docker-dev)
  - SAM
        - [I-s-23/sam-local-invoke-in-docker](https://github.com/I-s-23/sam-local-invoke-in-docker)
            - AWS Lambdaをlocalでテスト実行可能になっている環境


## Features

- `RemoteContainers`に`features`というPreview機能が追加されました
  - 簡単に説明すると`devcontainer.json`に以下のような記載をするだけで、その機能が使用可能という物です
        - 例. devcontainer内でGitとGitHubCLIの最新版を使用したい場合

        ```json:devcontainer.json
        　"features": {
                "git": "latest",
                "github-cli": "latest"
        }
        ```

	- 以下が現状`features`で対応している機能です
    	- Terraform、Docker in Docker などIaCに必要な物が用意されているのは嬉しいですね
    	- その他、GitHubCLI、homebrewなど最新版のインストールが地味に面倒な物をこの設定だけでインストールできるのが有り難いです

        ```json:devcontainer.json
        "features": {
            "docker-in-docker": "latest",
            "docker-from-docker": "latest",
            "kubectl-helm-minikube": "latest",
            "terraform": "latest",
            "git": "latest",
            "github-cli": "latest",
            "azure-cli": "latest",
            "sshd": "latest",
            "desktop-lite": "latest",
            "homebrew": "latest",
            "python": "latest",
            "golang": "latest",
            "java": "lts",
            "maven": "latest",
            "gradle": "latest",
            "ruby": "latest",
            "rust": "latest",
            "powershell": "latest"
        }
        ```

    - この機能の注意点として、対応していないBaseコンテナがあること(homebrewをfeaturesでDebian、AmazonLinux2などにインストールしようとするとエラーになる)
      - もう1点、`features`はコンテナの構築後にインストールされる仕様なのか、`features`で`homebrew`などをインストールした場合、Dockerfile内で`RUN brew install`などを行うとエラーになります
        - コンテナ内に`homebrew`がない段階で`brew install`を行おうとしたからだと思われる
    - `features`の詳細に関しては詳しく書かれている方がいらっしゃいますのでそちらを参考にしてください
      - [Dev container features (preview) が便利すぎる - Qiita](https://qiita.com/frozenbonito/items/6d73418a7e6c2d3746e4)

## 感想

- RemoteContainersは`.devcontainer`をリポジトリにPushしなければ個人で利用することも可能で、Pushしてチームで共有すれば、開発環境をチームで展開できる。など自由度が高いところがいいところだと思っております

## refarence

- [Dev container features (preview) が便利すぎる - Qiita](https://qiita.com/frozenbonito/items/6d73418a7e6c2d3746e4)
- [VSCode Remote Containers を利用して最強のローカル開発環境を作りたい - Qiita](https://qiita.com/sabure500/items/a117b8a1733193be455f#googlecloudsdk)