VSCodeで"Remote - Containers"拡張機能を試してみる

#**はじめに**
今日は、VSCodeで"Remote - Containers"拡張機能を試してみたいと思います。今回は、Windows10仮想マシン上のDocker環境で試します。
ちなみに、"Remote - Containers"拡張機能とはどんなものかと言いますと、コンテナの中身をあたかもローカルにあるもののようにVSCode上で操作できる機能です。これにより、コンテナ内のファイルをローカルファイル感覚で直接編集できるようになります。コンテナ内のファイル編集が楽になりますし、修正ファイルをアップロードする手間も省けますので、大変便利です。また、拡張機能もdevcontainerというリモートアクセス単位で必要なものだけインストールできるので、インストールする拡張機能が増えていってもゴチャゴチャせず、管理しやすい状態を維持しやすくなります。
私の経験談ですが、開発の現場では、商用環境の他に、テスト環境なるものが大体は用意されており、この環境はリリース前の最終確認環境として開発者みんなと共有する事が多かった印象です。この形だと他の開発者も使っているかもしれないので開発中の機能を気軽にテストできません。そこで、大体は、自分のPC内部になんちゃってテスト環境を作って、そこで開発初期段階のテストを行います。なんちゃってテスト環境なので、実際の商用環境や共用のテスト環境とはかなり環境的な相違があり、なんちゃってテスト環境では動作するのにテスト環境や商用環境ではうまく動作しないといったトラブルに遭遇した経験が結構ありました。
この"Remote - Containers"拡張機能を使うと、テスト環境（場合によっては商用環境）とほぼ同等の環境が自分のPC内部にコンテナ型仮想環境として用意できます。ですので、なんちゃってテスト環境は不要になりますし、コンテナ内のテスト環境を自分だけで占有でき、かつプログラミング等の開発作業もコンテナ内のテスト環境のファイルをあたかもローカルファイルを扱う感覚で編集する事ができます。しかも、人員が増えてもコンテナ環境を共有すればすぐに同じ環境が構築できます。
開発者にとっては夢のような話ですが、この拡張機能とDocker、VSCodeを使えばそれが実現できるのです。
今日はそういった話です。

#**環境周りの情報**
"Remote - Containers"拡張機能は、スタンドアロン環境で試します。

* Windows10仮想マシン
* VSCodeインストール済み

※詳細は以前投稿した下記を参照して下さい

https://qiita.com/829yasubee/items/e556293639ae5bcad6cb

#**環境構築手順**

###**"Remote - Containers"拡張機能のインストール**
VSCodeに"Remote - Containers"拡張機能をインストールします。
左側タブで[拡張機能]タブに切り替え、"Remote"というキーワードで検索。すると、検索結果に"Remote - Containers"拡張機能が現れるので、[インストール]ボタンをクリック。
![スクリーンショット1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/ceab66f7-fb03-64f2-2ee0-5e3aae98c5f1.png)
この拡張機能はインストールが完了しても、左側タブに新たなタブは出現しません。
![スクリーンショット2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/4aebca5b-4f59-fd53-fc06-dddbe2d8996e.png)
[リモート エクスプローラー]タブ内に機能が組み込まれます。つまり、"Remote - SSH"拡張機能とタブが統合されます。確認してみます。
[リモートエクスプローラー]タブに切り替えると、画面左上部に[SSH Targets]が選択されているプルダウンがありますので、[Containers]を選択します。
![スクリーンショット3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/aa39777a-eb4e-9ff9-fbea-09bc5f0b843f.png)
すると、リモートエクスプローラーの表示が、SSH TARGETSからCONTAINERSに切り替わります。ちなみに、以前試しに作成したコンテナは削除してしまったので、今は何も表示されません。
![スクリーンショット4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/b5a13583-ad9c-02e9-3953-14ca87cf6864.png)

###**※急遽予定変更**
ここで少し話が脱線します。
急遽ですが、Windows仮想マシン上のDockerではなく、Ubuntu仮想マシン上のDockerで"Remote - Containers"拡張機能を試す事にします。Ubuntu仮想マシン上のDocker環境については、以前の投稿を参照して下さい。

https://qiita.com/829yasubee/items/2eaf1dc517c1f3f7b64b

この環境を構築した後、Remote-SSHできるようにSSHサーバー設定を追加で行いました。その方が後々便利だろうと思ったからです。ついでに、レンタルサーバーにもSSHサーバー設定を行いました。なので、今時点でVSCodeのRemote-SSHを見るとこんな感じになっております（Ubuntu仮想マシンへのSSH Target名は"Docker"で、レンタルサーバーの方は"RentalServer"にしました）。
![スクリーンショット5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/6d887b71-5127-2a8d-6eb3-8a946c50f117.png)
んで、"Remote - Containers"拡張機能の使い方を色々探っていた時、Remote-SSHで"Docker"ターゲットにSSH接続したら、その状態のVSCodeでも"Remote-SSH"と"Remote-Containers"拡張機能が使用可能な状態になっていました。"Docker"ターゲット上のVSCodeにはその拡張機能を入れた覚えがなかったので不思議に思いました。
![スクリーンショット6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/24e5e314-c0bf-41e6-f364-a1271f11876f.png)
私の今までの理解では、拡張機能はローカルとリモートで別々にインストールしないといけないと思っていましたが、拡張機能によっては別々にインストールしなくても、ローカルとリモートのどちらでも使える（グローバル）タイプがあるのです。知らなかった・・・。拡張機能の説明にこのような記載がある場合はそのタイプです。"Remote-Containers"拡張機能をリモート側にインストールしようとしても"ローカルにインストール"となってて、リモートにインストールできなかったのは、"Remote-Containers"拡張機能はグローバルタイプだったからなのですね。
![スクリーンショット7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/076f294f-16e4-f8cf-f91f-9a9cabe6108d.png)
という事は、先日、一旦諦めていた、Ubuntu仮想マシン上のDockerで"Remote - Containers"拡張機能を機能させる事が、実はできるのではないかと思い始めました。以前調べた時、この拡張機能のデフォルト設定はlocalhostをリモート接続先とするようになっており、その設定を変えるにはsetting.jsonに接続先を変更する記載をする必要があるとの情報をキャッチしたので試してみましたがうまく出来ませんでした。しかし、そんな設定は必要なくて、Remote-SSHで"Docker"ターゲットに接続すれば、その状況のVSCodeではUbuntu仮想マシンがローカルになるわけですから、そのVSCode上で"Remote - Containers"拡張機能を使って、Ubuntu仮想マシン上のDockerで稼働しているコンテナ内に対して、ローカルファイル感覚でアクセスできるようになります。私としては、この方がしっくりきます。
話が少し長くなりましたが、これが急遽予定変更に至った経緯です。それでは話を元に戻します。

:::note warn
これ以降のVSCode上の操作は、全てRemote-SSHで"Docker"ターゲットに接続した状態からの操作を前提としていますので、ご注意下さい。
:::

###**devcontainer.jsonの定義**
"Remote - Containers"拡張機能を機能させるには、生成したいdevcontainer単位に、決められたディレクトリ構成でdevcontainer.jsonを作成 ＆ 配置する必要があります。私の場合は、Ubuntu仮想マシンのログインユーザーのhomeディレクトリ配下にprojectsディレクトリを作成し、その配下にdevcontainer単位のディレクトリを作成していく事にしました。赤枠部分が１つのdevcontainerに相当する定義です。
![スクリーンショット8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/3fa9848d-a09b-ee6a-7200-2e98e071d4f4.png)
そして、ここからがルールなのですが、まず、作成したいdevcontainerの起点となるディレクトリを作成します。名前は自由です。今回はlinuc資格勉強用devcontainerを作成するつもりなのでlinucディレクトリにしました。
![スクリーンショット9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/6911ff62-f22a-c87e-8e48-79439c825fb8.png)
次に、その配下に.devcontainerディレクトリを作成します。この名前で作成して下さい。
![スクリーンショット10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/6c150dfb-94bc-158b-d2b2-b0e4df11fc32.png)
さらに、その配下にdevcontainer.jsonファイルを作成します。この名前で作成して下さい。
![スクリーンショット11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/010fa924-2847-4ba6-bdea-f9c1c9fe71f1.png)
それでは、devcontainer.jsonファイルを定義していきます。
devcontainer.jsonは、VSCodeで生成 & アクセスするdevcontainerを定義するファイルです。定義内容は、生成するコンテナ環境、初期表示するコンテナやディレクトリ、devcontainerアクセスした状況下のVSCodeに対して行う「VSCodeの設定変更内容」や「追加インストールしたい拡張機能」等、多岐に渡ります。今回の定義内容は次のようにしました。
![スクリーンショット12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/8564feab-e197-c78f-d65d-7439a8dd8780.png)
各項目の意味は次の通りです。

|項目|意味|
|:-|:-|
|name|devcontainerを識別する為の名前。短い名前にした方がいいです（理由は後述）|
|dockerComposeFile|Docker Compose定義ファイルのファイルパス|
|service|devcontainerアクセス時、VSCodeで開くデフォルトのサービス（コンテナ）|
|workspaceFolder|devcontainerアクセス時、VSCodeで開くデフォルトのサービス内のディレクトリ|

今回の定義内容を平たく言うと、「VSCodeで"linuc"devcontainerを開く際、"docker-compose.yml"の定義内容に従ってコンテナ環境を生成し、生成したコンテナ環境の内、"os"コンテナ内のルートディレクトリを初期表示する」という意味になります。尚、devcontainer.jsonやdocker-compose.ymlでは、コンテナはサービスと呼称します。指し示している対象は同じと考えて問題ないかと思います。

###**docker-compose.ymlの定義**
まず、.devcontainerディレクトリ直下にdocker-compose.ymlファイルを作成します。この名前で作成して下さい。
![スクリーンショット13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/dfbdc20f-68db-f151-db34-0789f831998a.png)

:::note info
格納ディレクトリはdevcontainerの起点ディレクトリ内であればどこでも大丈夫ですが、"Remote - Containers"拡張機能の紹介用サンプルを何個か見た限りでは、.devcontainerディレクトリ直下に配置するのが一般的なようでしたので、私もそれに習いました。
:::

次に、docker-compose.ymlファイルを定義していきます。
docker-compose.ymlは、生成するコンテナ環境の生成内容や生成手順を定義するファイルです。定義内容は、指定イメージを使ったコンテナの生成、コンテナの所属するネットワークの生成、コンテナにマウントするボリュームの生成、等になります。今回の定義内容は次のようにしました。
![スクリーンショット14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/eed8e4f1-5743-fbcd-7835-3f9a7a1ba99b.png)
各項目の意味は次の通りです。

|項目|サブ項目|意味|
|:-|:-|:-|
|version|-|docker-compose.ymlの記述フォーマットのバージョン|
|services|-|生成するサービス群（コンテナ群）。今回は"os"コンテナ１つだけ|
|services|image|コンテナ生成に使用するイメージ。今回は"ubuntu"イメージの最新版|
|services|networks|コンテナを所属させるネットワーク群。今回は"net"ネットワークにだけ所属させる
|services|tty|コンテナに標準入出力（キーボード等）を取り付けるか否か|
|networks|-|生成するネットワーク群。今回は"net"ネットワーク１つだけ|

:::note info
サービス名やネットワーク名はなるべく短い名前にした方が良いと思いました。というのも、"Remote - Containers"拡張機能やDocker Composeで生成されるサービスやネットワークの名称は、ここで指定した名前に余計な文字が自動付与されます。
例えば、今回生成されるサービス名は"os"ではなく"linuc_devcontainer-os-1"、ネットワーク名は"net"ではなく"linuc_devcontainer_net"となります。devcontainer名や"devcontainer"という固定文字列、枝番が連結された名称になってしまいまいます。
:::

:::note info
ttyサブ項目を定義しなかった場合、生成されたubuntuコンテナの起動に失敗します。これを分かりやすく言うと、キーボードもディスプレイも繋いていないubuntuパソコンを生成したようなものです。これでは何も操作できないので、ubuntuイメージを使用する場合はttyサブ項目をtrueで定義して下さい。
mysqlイメージ等、サーバー用途（デーモン）前提のイメージを使用する場合はttyサブ項目の定義は不要です。これは私の勝手なイメージですが、サーバーってラック等の棚に電源ONになっている本体だけ鎮座していてリモートアクセスできる状態であれば良いイメージありませんか。だから、別にキーボードやディスプレイが繋がっていなくてもOKなんだと思います。
:::

###**devcontainerの生成 & アクセス**
これで準備は完了です。それでは、devcontainerを生成し、VSCodeでアクセスしてみます。
画面左下の[><]マークのようなボタンをクリックし、画面上部に表示される選択肢から[Open Folder in Container]を選択します。既に、VSCodeで、devcontainerの起点ディレクトリを開いている状態であれば、[Reopen in Container]を選択しても構いません。
![スクリーンショット15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/09f00d18-5ec3-90fd-2289-03ca24da6a10.png)
すると、homeディレクトリ内のディレクトリが一覧表示されますので、[projects]→[linuc]と順番に選択し、devcontainer起点ディレクトリまで指定したら[OK]ボタンをクリックします。
![スクリーンショット16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/72425515-29ae-3ea6-af22-9cc0493665a6.png)
devcontainerの生成が始まり、生成が完了するとVSCodeが"linuc"devcontainerに切り替わります。
無事、切り替わり、ルートディレクトリが初期表示され、ターミナルも繋がっています。本当にローカル環境を触っているかのような感覚でディレクトリやファイル操作ができます。すごい！
![スクリーンショット17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/df94ed8f-cec4-631b-f537-e68a97635a72.png)
一応、コンテナ等の生成状況も確認しておきます。
devcontainerから抜ける場合は、画面左下の[><]マークのようなボタンをクリックし、画面上部に表示される選択肢から[Reopen Folder in SSH]を選択します。
![スクリーンショット17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/bb00e7ec-67c3-b316-18ef-cc534ee64e2b.png)
元の"docker"SSHターゲットに接続している状態のVSCodeに戻った事を確認したら、画面左側タブより[Docker]タブをクリックします。ubuntuイメージもダウンロードされ、コンテナやネットワークもちゃんと生成されている事が確認できます。
![スクリーンショット18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/ef6322b9-fc3c-a46a-edb1-0c26a2a12c9a.png)

:::note info
"Docker"拡張機能は、"Docker"SSHターゲット側にだけインストールし直しました。今回の予定変更で、Windows10仮想マシン上ではなく、Ubuntu仮想マシン上のDockerでコンテナ管理するようにしたからです。必要なSSHターゲット内だけに必要な拡張機能をインストールできるのは非常にありがたいです。
:::

![スクリーンショット19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/54d21e43-59d5-d416-f8d2-7f6a71af7a99.png)

###**WordPressのdevcontainerも生成**
今度はもう少しだけ複雑なdevcontainerを生成してみます。題材はお馴染みのWordPressです。今の私にとっては丁度良い難易度です。

それでは各種定義ファイルを定義していきます。ディレクトリ構成はこのような感じです。先程は無かった.envファイルが増えていますが、これに関しては後で説明します。
![スクリーンショット20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/eaa84ec3-5a10-3315-7f25-b5f92046d63c.png)
まず、devcontainer.jsonファイルを定義します。定義内容は次のようにしました。先程と殆ど同じ項目定義なので大体分かると思いますが、extensions項目は初登場です。
![スクリーンショット21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/62743878-fb3d-c996-c1ae-e1c1605975fe.png)
項目の意味は次の通りです。

|項目|意味|
|:-|:-|
|extentions|devcontainer生成時に、追加する拡張機能。複数指定もできます|

:::note info
設定値はかなり長い文字列ですが、拡張機能毎に暗記する必要はありません。VSCodeでdevcontainer.jsonを開いている状態で、画面左側タブの[拡張機能]タブに切り替えて、お目当ての拡張機能を検索し、検索結果に表示された拡張機能を右クリックして[Add to devcontainer.json]をクリックすれば、自動で定義が追加されます。
:::

![スクリーンショット22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/f2df2c54-0e2f-a7f3-5e02-cc939d756d8a.png)
次に、docker-compose.ymlファイルを定義します。定義内容は次のようにしました。
初登場項目が結構多く、定義内容もだいぶ長くなりました。定義内容のアウトラインとしましては、"app"コンテナ（WordPress本体）と"db"コンテナ（MySQLデータベース）の2コンテナ構成で、WordPress本体のプログラムやMySQLデータベースのデータに関しては、コンテナを再生成しても消えないようにボリュームマウントしています。
![スクリーンショット23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/9b54052a-0725-2884-ab88-3c40e072f1d8.png)
それでは初登場の項目に関してだけピックアップします。

|項目|サブ項目|意味|
|:-|:-|:-|
|services|volumes|コンテナにマウントするボリューム群。WordPress本体のプログラムは"app"ボリュームに、MySQLデータベースのデータは"db"ボリュームにそれぞれマウント|
|services|ports|ポートフォワード設定。ローカルポート:リモートポートの形式で定義|
|services|restart|コンテナ停止時の再試行ポリシー。今回はどちらのコンテナもalways（必ず再起動）|
|services|environment|コンテナに定義する環境変数群。WorPressイメージとMySQLイメージでは、定義が必要な環境変数が決まっています。詳細はDocker Hubの該当イメージ説明を参照の事|
|services|depends_on|自身が依存するコンテナ。"app"コンテナは"db"コンテナに依存|
|volumes|-|生成するボリューム群。今回は"app"と"db"ボリュームの２つ|

:::note info
今回のdocker-compose.ymlファイルの定義内容では、${変数名}形式の記述を多用しました。環境依存する設定値を変数化して外部ファイルへ逃がして、管理しやすくしたかったからです。どこへ逃しているかと言うと、それが次に説明する.envファイルです。
:::

最後に、.envファイルを定義します。.envファイルは、docker-compose.ymlと同じディレクトリに配置すると、docker-compose.ymlファイルから参照できるようになります。同じディレクトリに配置しない場合には、docker-compose.ymlにenv_file項目として.envファイルパスを指定する必要があります。その場合、ファイル名も自由に決めて良いみたいです。
.envファイルの中身はお見せできませんが、「変数名=値」形式で定義し、改行して複数定義する事もできます。

これで準備は完了です。それでは先程と同様の手順で、devcontainerを生成し、VSCodeでアクセスしてみます。
![スクリーンショット24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/305a4af0-892c-a547-003d-9d572f19632a.png)
無事、devcontainerの生成 & アクセスができました。
各コンテナの生成状況も確認してみます。
![スクリーンショット25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/f401a7bf-3ba8-d6e0-a667-9f82e9f41822.png)
無事、意図した通りに作成されました。
最後に、WordPressの初期設定画面が表示されるか確認して、今回は終わりにしたいと思います。
![スクリーンショット26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/c198e39e-f455-1e47-889f-87518486c1dd.png)
無事、表示されました。
お疲れ様でした！