VSCode Remote Containers を利用して最強のローカル開発環境を作りたい

# はじめに
VSCodeの神拡張機能であるRemote Containersの自分なりの設定の紹介です
[公式サンプル](https://github.com/Microsoft/vscode-dev-containers)は公開されていますが、そのままだと流石に使いずいので自分なりに使いやすいように編集した設定を紹介します
なお、本記事で紹介する設定ファイルは全て以下のリポジトリで公開しています(紹介していない環境のものも入っています)
https://github.com/sabure500/remote-container-sample

また、Remote Containersを使ってみて良いなと思ったので色々使いやすいように設定を弄っていますが、本記事は最強のローカル環境を「作りたい」なので、ここをこうした方が良いといった案があったら是非教えてくれると嬉しいです

# VSCode Remote Containers とは
VSCodeの拡張機能であり、使用することでコンテナの中でVSCodeを開いて作業を行うことができるようになる
コンテナの中で直接VSCodeを開いて作業ができるようになるため、開発環境をサンドボックス化してローカルマシン上には全く影響しないところで開発を行うことができる
類似の拡張機能シリーズでこれ以外にも「Remote SSH」と「Remote WSL」が存在し、これはそれぞれSSH接続先またはWSLの中でVSCodeを開いて作業を行うことができるようになる
それぞれの詳細は [公式サイト](https://code.visualstudio.com/docs/remote/remote-overview) を参照

# インストール
VSCode Remote Containers で開発環境を作る場合は以下の2つのインストールが必要です
逆にいうと、以下の2つがあればローカルマシンには他に何も入れずにNode,python,Go,Java等の環境が作れます
* Visual Studia Code
* Docker Desktop for Windows or Mac

## Docker Desktop for Windows or Mac
以下の公式ページからインストーラをダウンロードする
https://www.docker.com/products/docker-desktop

## Visual Studio Code
### VSCode本体のインストール
以下の公式ページからダウンロードする
https://code.visualstudio.com/docs

### Remote Containersの導入
Remote Containersは通常の拡張機能なので、VSCodeインストール後に起動して左のタブから拡張機能を選択し｢Remote Container｣と検索することで一覧に出てくるのでそこからインストールできる
もしくは以下のマーケットプレースのページからインストールしても良い
https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers

### Remote Containersの起動
後ほど紹介するRemoteContainersの設定ファイルがある場所をWorkspaceとして起動後に、VSCode左下の緑の｢><」マークをクリックし、｢Remote-Containers : Reopen in Container｣を選択する

# 環境構築設定の紹介
Remote Containersにおける環境構築は.devcontainerディレクトリ上にdevcontainer.jsonというRemote Containers用の設定ファイルとDockerfile(もしくは、docker-compose.yaml等)を配置することで行う
環境毎の設定を紹介していく

## GoogleCloudSDK
ローカル環境でGoogleCloudSDKのコマンドを利用するときもRemote Containersを利用している
設定ファイルは以下で公開しており、基本的にはこれをそのまま使うことで誰でもすぐに同じGoogleCloudSDKの環境を利用できる
その環境構築用の設定を記述していく
全体のディレクトリ構成は以下のようになっている

```
.
├ .devcontainer
    ├ devcontainer.json
    ├ Dockerfile
    ├ .config/fish/config.fish
    └ .local/share/fish/fish_history
```

### Dockerfile
実際に開発環境として使うコンテナを作成する用のファイル
最初に全体像を示し、その後各行を解説する

```Dockerfile:Dockerfile
FROM google/cloud-sdk:297.0.1-alpine

# ===== common area =====
RUN apk add --no-cache fish git openssh curl
COPY .config/fish/config.fish /root/.config/fish/config.fish
# =======================

# ===== kubernetes resource install =====
ENV KUBECTL_VERSION 1.18.4
ENV KUSTOMIZE_VERSION 3.1.0
ENV ARGOCD_VERSION 1.5.2
RUN curl -sfL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl \
    && curl -sfL -o /usr/local/bin/kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64 \
    && curl -sfL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/v${ARGOCD_VERSION}/argocd-linux-amd64 \
    && chmod +x /usr/local/bin/kubectl /usr/local/bin/kustomize /usr/local/bin/argocd
# =======================
```

* ベースイメージ

    ```Dockerfile
    FROM google/cloud-sdk:297.0.1-alpine
    ```

    ベースイメージは google/cloud-sdk:297.0.1-alpine を利用する

* 環境で利用する汎用的なパッケージのインストール

    ```Dockerfile
    RUN apk add --no-cache fish git openssh curl
    ```

    ベースイメージがalpineなので、apkを利用して開発環境上で利用したいパッケージをインストールする
    ここではfish,git,ssh,curlを入れているが、bashを使いたい場合はfishではなくbashを導入する等各自カスタマイズする

* fishシェル用の設定

    ```Dockerfile
    COPY .config/fish/config.fish /root/.config/fish/config.fish
    ```
    ```.config/fish/config.fish
    set normal (set_color normal)
    set magenta (set_color magenta)
    set yellow (set_color yellow)
    set green (set_color green)
    set red (set_color red)
    set gray (set_color -o black)

    # Fish git prompt
    set __fish_git_prompt_showdirtystate 'yes'
    set __fish_git_prompt_showstashstate 'yes'
    set __fish_git_prompt_showuntrackedfiles 'yes'
    set __fish_git_prompt_showupstream 'yes'
    set __fish_git_prompt_color_branch yellow
    set __fish_git_prompt_color_upstream_ahead green
    set __fish_git_prompt_color_upstream_behind red

    # Status Chars
    set __fish_git_prompt_char_dirtystate '⚡'
    set __fish_git_prompt_char_stagedstate '→'
    set __fish_git_prompt_char_untrackedfiles '☡'
    set __fish_git_prompt_char_stashstate '↩'
    set __fish_git_prompt_char_upstream_ahead '+'
    set __fish_git_prompt_char_upstream_behind '-'

    function fish_prompt
      set last_status $status

      set_color $fish_color_cwd
      printf '%s' (prompt_pwd)
      set_color normal

      printf '%s ' (__fish_git_prompt)

      set_color normal
    end
    ```

    作業用のシェルとしてはfishシェルを利用する
    初期設定のままでは使いづらいので、Gitのブランチを表示する等のプロンプトを変更する設定ファイルをコンテナ上にコピーして配置する
    fishの設定ファイルは以下のブログの記事を参考にさせてもらっています
    https://www.martinklepsch.org/posts/git-prompt-for-fish-shell.html

* GoogleCloudSDkと一緒に使うコマンド類のインストール

    ```Dockerfile
    # ===== kubernetes resource install =====
    ENV KUBECTL_VERSION 1.18.4
    ENV KUSTOMIZE_VERSION 3.1.0
    ENV ARGOCD_VERSION 1.5.2
    RUN curl -sfL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl \
        && curl -sfL -o /usr/local/bin/kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64 \
        && curl -sfL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/v${ARGOCD_VERSION}/argocd-linux-amd64 \
        && chmod +x /usr/local/bin/kubectl /usr/local/bin/kustomize /usr/local/bin/argocd
    # =======================
    ```

    GCPのリソースとして専らGKEを使うことが多いので、Kubernetes関連のリソースをコンテナ内にインストールしています

### devcontainer.json
VSCodeからコンテナを開く際の設定ファイル
利用するDockerfileやコンテナ上でVSCodeを利用する際の拡張機能、またローカル環境からのVolume等を記述する
他に何ができるかの詳細は[公式のリファレンス](https://code.visualstudio.com/docs/remote/containers#_devcontainerjson-reference)を参照
最初に全体像を示し、その後各行を解説する

```json:devcontainer.json
{
    "name": "Google Cloud SDK Remote-Container",
    "build" : {
        "dockerfile": "Dockerfile"
    },
    "settings": {
        "terminal.integrated.shell.linux": "/usr/bin/fish",
    },
    "extensions": [
        "alefragnani.bookmarks",
        "mhutchie.git-graph",
        "redhat.vscode-yaml",
        "zainchen.json"
    ],
    "mounts": [
        "source=${localEnv:HOME}/.ssh/,target=/root/.ssh/,type=bind,consistency=cached",
        "source=${localEnv:HOME}/.gitconfig,target=/root/.gitconfig,type=bind,consistency=cached",
        "source=${localWorkspaceFolder}/.devcontainer/.local/share/fish/fish_history,target=/root/.local/share/fish/fish_history,type=bind,consistency=cached",
        "source=${localEnv:HOME}/.config/gcloud/,target=/root/.config/gcloud/,type=bind,consistency=cached",
        "source=${localEnv:HOME}/.kube/,target=/root/.kube/,type=bind,consistency=cached",
    ],
}
```

* 利用するコンテナイメージ

    ```json
        "build" : {
            "dockerfile": "Dockerfile"
        },
    ```
    Dockerfileの置いてある場所を指定する
    最初にディレクトリ構造で示した通り、同じディレクトリ上にあるのでそのまま"Dockerfile"と書いている

* コンテナ固有のVSCodeの設定

    ```json
        "settings": {
            "terminal.integrated.shell.linux": "/usr/bin/fish",
        },
    ```
    コンテナ上独自で設定したいVSCodeの設定を記載する
    例えば、ローカル上では導入しないがコンテナ上では導入するExtension用の設定等
    settingsの記述に関してはローカル上で書かれていることは改めてdevcontainer.json上で書かなくてもコンテナ上で引き継がれる
    ここではコンテナ上ではターミナルのシェルはfishを利用することだけ記述している

* コンテナ環境上のVSCodeで利用する拡張機能の設定

    ```json
        "extensions": [
            "alefragnani.bookmarks",
            "mhutchie.git-graph",
            "redhat.vscode-yaml",
            "zainchen.json"
        ],
    ```
    コンテナ環境上で利用したい拡張機能を記述する
    拡張機能に関してはsettingsの設定と違い、ローカル上で導入されていてもdevcontainer.json上で書かれていないものはコンテナ上で導入されないので注意  

* ローカル環境からのマウント

    ```json
        "mounts": [ 
            "source=${localEnv:HOME}/.ssh/,target=/root/.ssh/,type=bind,consistency=cached",
            "source=${localEnv:HOME}/.gitconfig,target=/root/.gitconfig,type=bind,consistency=cached",
            "source=${localWorkspaceFolder}/.devcontainer/.local/share/fish/fish_history,target=/root/.local/share/fish/fish_history,type=bind,consistency=cached",
            "source=${localEnv:HOME}/.config/gcloud/,target=/root/.config/gcloud/,type=bind,consistency=cached",
            "source=${localEnv:HOME}/.kube/,target=/root/.kube/,type=bind,consistency=cached",
        ],
    ```
    ローカル環境上のファイルを使いたい、または、コンテナを再起動しても消去されて欲しくないファイルをマウントする
    "source="でローカルのパス、"target="でコンテナ上のパスを指定している
    また、${localEnv:XXXX}と書くことで、ローカル環境上で環境変数「XXXX」を利用できる
    ここでは5つのファイル・ディレクトリをマウントしている
    1. sshの設定
    sshの設定は複数の環境から利用されるため、ローカル環境からマウントして利用する
    2. gitの設定
    gitの設定も複数の環境から利用されるため、ローカル環境からマウントして利用する
    3. fishの操作履歴
    ここが結構ポイントで、コンテナ上で作業しているとコンテナを停止すると操作履歴が全て削除されてしまう
    自分は作業をするときにカーソル↑等を利用して過去のhistoryのコマンド履歴を利用することが多く消されると不便だったので、消えないようにWorkspaces上でマウントしておく
    4. gcpの設定
    GCPログイン情報等も毎回消えると面倒なのでローカル上からマウントする
    5. kubectlの設定
    同じくGKEのクラスタ登録等も毎回消えると面倒なのでローカル上からマウントする

## Java
Java用の環境として、OpenJDK+Wildfly+maven+Gradleが入った環境を使用している
このJava用の環境も上で紹介したGoogleCloudSDKの環境と相違点に絞って紹介する
全体のディレクトリ構成は以下のようになっている

```
.
├ .devcontainer
    ├ devcontainer.json
    ├ docker-compose.yaml
    ├ Dockerfile
    ├ .m2/
    ├ .gradle/
    ├ .config/fish/config.fish
    └ .local/share/fish/fish_history
```

### docker-compose
Javaの環境は別にコンテナで立てるDB環境と接続するためにDockerのNetworkを利用するためにDockerfileではなく、docker-composeを利用する  
最初に全体像を示し、その後各行を解説する

```yaml:docker-compose.yaml
version: "3"
services:
  jdk-wildfly-maven:
    build: .
    ports:
      - "8080:8080"
      - "9990:9990"
    command: /bin/sh -c "while sleep 1000; do :; done"
    volumes: 
      - $HOME/.ssh:/root/.ssh
      - $HOME/.gitconfig:/root/.gitconfig
      - .local/share/fish/fish_history:/root/.local/share/fish/fish_history
      - ..:/workspace
      - ./jboss_home/configuration/standalone.xml:/opt/wildfly/standalone/configuration/standalone.xml
      - .m2:/root/.m2
      - .gradle:/root/.gradle
    networks:
      - remote-container_common-network
networks: 
  remote-container_common-network:
    external: true
```

* ローカル環境からコンテナ環境へのポートフォワード

    ```yaml
    ports:
      - "8080:8080"
      - "9990:9990"
    ```
    Wildflyのデフォルトポートである8080と9990に対して、ローカル環境で対象のポートにアクセスした際にコンテナ上にアクセスするようにする

* コンテナのデフォルトコマンドの上書き

    ```yaml
    command: /bin/sh -c "while sleep 1000; do :; done"
    ```
    コンテナ起動時のデフォルトコマンドが失敗したり終了したりした場合にコンテナが停止しないように、デフォルトコマンドを上書きする
    ここで記述しているコマンドはdocker-composeを利用しない場合のRemote Containersのデフォルト設定
    docker-composeを利用する場合は明示的に書いてあげる必要がある

* ローカル環境からのマウント

    ```yaml
    volumes: 
      - $HOME/.ssh:/root/.ssh
      - $HOME/.gitconfig:/root/.gitconfig
      - .local/share/fish/fish_history:/root/.local/share/fish/fish_history
      - ..:/workspace
      - ./jboss_home/configuration/standalone.xml:/opt/wildfly/standalone/configuration/standalone.xml
      - .m2:/root/.m2
      - .gradle:/root/.gradle
    ```
    ローカル環境からのマウントはdevcontainer.jsonではなくdocker-compose.yamlで書く必要がある
    Dockerfileの場合と違うところは、"..:/workspace"と書いているようにworkspace自体を明示的に指定してマウントしている
    java環境独自の設定として".m2:/root/.m2"や".gradle:/root/.gradle"でGradleやMavenの設定やリポジトリをマウントしている。これはこの環境でしか利用しないものなので、ユーザーホーム($HOME)ではなくWorkSpace上に直接マウントしている
    またWildflyの設定ファイルであるstandalone.xmlについては、データソースの設定等が開発中に常に変更される可能性があり、コンテナ再起動のたびに削除されても困るので"./jboss_home/configuration/standalone.xml:/opt/wildfly/standalone/configuration/standalone.xml"といった形でマウントしている

* docker networkの利用

    ```yaml
        networks:
          - remote-container_common-network
    networks: 
      remote-container_common-network:
        external: true
    ```
    Javaはアプリケーションの実行環境として利用するのでDBと接続をしたい
    DBに関してもローカル環境ではコンテナとして起動するので他のコンテナと接続するためにDocker Networkを作成してそれを利用する
    そのためこの環境を利用するためには以下のコマンドで事前にネットワークを作成する必要がある

    ```bash
    docker network create --driver bridge remote-container_common-network
    ```

### Dockerfile
docker-compose.yamlで指定しているOpenJDK+Wildfly+maven+Gradle環境を作成するDockerfile
ベースイメージとしてはJBoss公式イメージ"jboss/wildfly"はサイズが大きく使いずらかったりalpineベースのイメージを使いたかったこともあり、Adoptopenjdkを元にして作成している

```Dockerfile:Dockerfile
FROM adoptopenjdk/openjdk11:alpine-slim

# ===== common area =====
ENV WORKSPACE_DIR "/workspace"
RUN apk add --no-cache fish git openssh curl wget tar unzip\
    && mkdir -p $WORKSPACE_DIR
COPY .config/fish/config.fish /root/.config/fish/config.fish
# =======================

# ==== wildfly install =====
ENV JBOSS_HOME "/opt/wildfly"
ENV WILDFLY_VERSION "20.0.0.Final"

RUN wget -P /opt http://download.jboss.org/wildfly/${WILDFLY_VERSION}/wildfly-${WILDFLY_VERSION}.tar.gz \
    && tar -zxvf /opt/wildfly-${WILDFLY_VERSION}.tar.gz -C /opt \
    && rm /opt/wildfly-${WILDFLY_VERSION}.tar.gz \
    && mv /opt/wildfly-${WILDFLY_VERSION} ${JBOSS_HOME} \
    && $JBOSS_HOME/bin/add-user.sh admin admin --silent
# =======================

# ==== maven install =====
ENV MAVEN_HOME "/opt/maven"
ENV MAVEN_VERSION 3.6.3
ENV PATH "$PATH:$MAVEN_HOME/bin"
ENV MAVEN_CONFIG "$HOME/.m2"
RUN curl -fsSL -o /opt/apache-maven-${MAVEN_VERSION}-bin.tar.gz http://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
    && tar -zxvf /opt/apache-maven-${MAVEN_VERSION}-bin.tar.gz -C /opt \
    && rm /opt/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
    && mv /opt/apache-maven-${MAVEN_VERSION} /opt/maven
# =======================

# ==== gradle install ====
ENV GRADLE_HOME "/opt/gradle"
ENV GRADLE_VERSION 6.5
ENV PATH "$PATH:$GRADLE_HOME/bin"
RUN curl -fsSL -o /opt/gradle-${GRADLE_VERSION}-bin.zip https://downloads.gradle-dn.com/distributions/gradle-${GRADLE_VERSION}-bin.zip \
    && unzip -d /opt /opt/gradle-${GRADLE_VERSION}-bin.zip \
    && rm /opt/gradle-${GRADLE_VERSION}-bin.zip \
    && mv /opt/gradle-${GRADLE_VERSION} /opt/gradle
# =======================
```

### devcontainer.json
Dockerfileを利用する場合とdocker-composeを利用する場合で、devcontainer.jsonのデフォルトの設定や利用できる設定が変わっている(例えば、マウントはdevcontainer.jsonには記述しても効かなくなり、docker-composeにて記述しなければならなくなる)
詳細は[公式リファレンス](https://code.visualstudio.com/docs/remote/containers#_devcontainerjson-reference)参照

```json:devcontainer.json
{
    "name": "JDK&Wildfly&Maven&Gradle Remote-Container",
    "dockerComposeFile": "docker-compose.yaml",
    "service" : "jdk-wildfly-maven",
    "workspaceFolder": "/workspace",
    "settings": {
        "terminal.integrated.shell.linux": "/usr/bin/fish",
        "java.home": "/opt/java/openjdk",
        "maven.executable.preferMavenWrapper": false,
        "maven.executable.path": "/opt/maven/bin",
        "maven.terminal.useJavaHome": true,
        "java.jdt.ls.vmargs": "-noverify -Xmx1G -XX:+UseG1GC -XX:+UseStringDeduplication -javaagent:\"/root/.vscode/extensions/gabrielbb.vscode-lombok-1.0.1/server/lombok.jar\"",
    },
    "extensions": [
        "alefragnani.bookmarks",
        "mhutchie.git-graph",
        "vscjava.vscode-java-pack",
        "shengchen.vscode-checkstyle",
        "gabrielbb.vscode-lombok",
        "naco-siren.gradle-langua"
    ],
    "shutdownAction": "stopCompose"
}
```

* 利用するイメージとサービス

    ```json
        "dockerComposeFile": "docker-compose.yaml",
        "service" : "jdk-wildfly-maven",
    ```

    docker-composeを利用する場合は"dockerComposeFile"を利用してdocker-compose.yamlの場所を指定する
    また、docker-composeの場合は複数のコンテナが起動している可能性があるので、serviceでどのコンテナでVSCodeを開くのかも指定する

* workspaceフォルダの指定

    ```json
        "workspaceFolder": "/workspace",
    ```

    Dockerfileの場合と違い、docker-composeの場合は明示的にマウントするworkspaceの場所を指定する必要がある
    この場合に存在しないディレクトリは指定できないため、Dockerfile上で先に/workspaceといったディレクトリを作っておく

* java特有の設定

    ```json
        "settings": {
            "terminal.integrated.shell.linux": "/usr/bin/fish",
            "java.home": "/opt/java/openjdk",
            "maven.executable.preferMavenWrapper": false,
            "maven.executable.path": "/opt/maven/bin",
            "maven.terminal.useJavaHome": true,
            "java.jdt.ls.vmargs": "-noverify -Xmx1G -XX:+UseG1GC -XX:+UseStringDeduplication -javaagent:\"/root/.vscode/extensions/gabrielbb.vscode-lombok-1.0.1/server/lombok.jar\"",
        },
        "extensions": [
            "alefragnani.bookmarks",
            "mhutchie.git-graph",
            "vscjava.vscode-java-pack",
            "shengchen.vscode-checkstyle",
            "gabrielbb.vscode-lombok",
            "naco-siren.gradle-langua"
        ],
    ```

    ローカル環境のVSCode上では入れていないが、Java環境では利用したい拡張機能を指定している
    また、その拡張機能に対応したコンテナ上でのみ適用したいsettingsの内容を記述している

## MySQL
前の章で作成したJavaから利用するDBもコンテナで作成する
この環境はあまりRemote Containersで作成する意味はないが、統一するために一応Remote Containersで作っている
単純にdocker-composeで起動しても特に問題はない
他のコンテナ(Java環境)と接続するためにこの環境を作る前に以下のコマンドでDocker networkを作っておくこと
(すでにある場合は問題なし)

```bash
docker network create --driver bridge remote-container_common-network
```

全体のディレクトリ構成や設定ファイルは以下のようになっている

```
.
├ .devcontainer
│  ├ devcontainer.json
│  ├ docker-compose.yaml
│  └ my.cnf
└ db
```

```yaml:docker-compose.yaml
version: "3"
services:
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: pass
      TZ: "Asia/Tokyo"
    ports:
      - "3306:3306"
    volumes: 
      - ../db/data:/var/lib/mysql
      - ./my.cnf:/etc/mysql/conf.d/my.cnf
    networks:
      - remote-container_common-network
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      PMA_ARBITRARY: 1
    ports:
      - 3307:80
    depends_on:
      - mysql
    networks:
      - remote-container_common-network
networks: 
  remote-container_common-network:
    external: true
```

```json:devcontainer.json
{
    "name": "MySQL Remote-Container",
    "dockerComposeFile": "docker-compose.yaml",
    "service" : "mysql",
    "workspaceFolder": "/root",
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash",
    },
    "extensions": [],
    "shutdownAction": "stopCompose"
}
```

## 補足
今回はあくまで自分用のローカル環境の構築なので、複数のアプリケーションを1つの環境で扱うことを想定してRemote Containersの設定を作っている
つまり以下のようなこと

```
.
├ .devcontainer
├ application1
│  ├ .git
│  └ source code
├ application2
│  ├ .git
│  └ source code
```

上記のような構成ではなく、1つのアプリケーションに対して1つのRemote Containersの設定を作る方がgit上でそのアプリの開発環境も管理でき開発者全員が同じ環境を使える等のメリットがあるため良いと思われる
つまり以下のようなこと

```
.
├ application1
│  ├ .devcontainer
│  ├ .git
│  └ source code
├ application2
│  ├ .devcontainer
│  ├ .git
│  └ source code
```

ただし、このような管理の仕方の場合は今回紹介したようなfishの独自設定をぶち込んだりすると戦争が起りかねないのでチームでよく相談してどうするかを決定した方が良いと思われます


# VSCode Remote Containers のメリット・デメリット
最後に使っていて思ったメリット・デメリットをまとめて終わりにする

## メリット
* 自分のローカル環境が汚れない  
VSCodeとDockerさえあれば余計なものを何も入れなくて良い点がすごく良い  
* 開発環境の設定もアプリ開発者の間で共有できる  
同じ開発環境を共有していることになるので人によっては動く動かないといった事象の発生をなくせる  
また、そもそもDockerfileが必須になるため本番環境もDockerfileで作れば開発と本番での差異を限りなく少なくできる  
ただし、あくまで開発環境用のDockerfileと本番環境用のDockerfileは別で作った方が良い  
(本記事では紹介していないがRemote Containersのextendの機能を利用して本番環境のDockerfileを元に開発にい必要なパッケージの導入部分のみ上書いて開発環境で利用することもできる)
* 開発環境で作ったイメージがCI/CDで流用できる  
開発環境上でツールを使ってチェック等をしている時は、CI/CDの時にも同様のチェックをすることは多い  
この時に今のCICDツールはJOBのコンテナ上での実行をサポートしていることが多いので、開発環境作成時に作ったコンテナをそのままであったり一部だけ改良して利用できたりする

## デメリット
* VSCodeでしか使えない  
VSCodeの拡張機能なので、それ以外のIDEでは使えない  
かなり有用な機能なので自分が知らないだけで他のIDEでも同じようなことができるのかもしれないですが...  
* コンテナ上では利用できない拡張機能もある  
Remote Containersで起動したコンテナ上のVSCodeでは利用できない拡張機能が一部存在する  
* 利用するツールについてある程度理解していないと環境構築できない  
Dockerfileを作成することが必須であり、このツールの設定ファイルはどこに展開されるかといったことやマウントした方が良い部分とそうでない部分といったことを理解して意識する必要がある  
ただし、設定を自作するのではなく他人が作成した設定ファイルをそのまま利用する場合は今までよりさらに何も知らなくても使えます  
* 一つのコンテナ環境にどこまで詰め込んで良いか分かりづらい  
今回紹介したものだとJavaとGoogleCloudSDKで分けているが、やろうと思えばこれはさらに細分化できるし、逆に1つにまとめることもできる  
アプリケーションの単位でremote-containerの設定を作るなら分かりやすいが、GoogleCloudSDKのように自分用のローカル環境として作る場合は境目が難しく基準となる答えはまだ持っていない

# 参考
* VSCode Remote Containers公式サンプル : https://github.com/Microsoft/vscode-dev-containers
* VSCode Remote Containers公式ドキュメント : https://code.visualstudio.com/docs/remote/remote-overview
* fishプロンプト設定の参考ブログ : https://www.martinklepsch.org/posts/git-prompt-for-fish-shell.html