掲載内容は個人の見解であり、所属する企業を代表するものではありません.
Visual Studio Code の Dev Container を利用して開発環境を整えるようにすると、操作している端末の環境を汚さずに使えるので、大変便利ですよね。 並行して携わっているお仕事のランタイムバージョンが異なっているのを忘れて、うっかりインストールして開発環境がぶち壊しになるとかいう苦い歴史とはそろそろお別れしたいものです。
Dev Container はざっくり言うと ソースコードが格納されたディレクトリを、開発に必要な SDK がインストールされたコンテナにマウントして、VS Code から接続した状態で開くことができる というモノですね。 で、この Dev Container にインストールされた SDK で造られたアプリケーションの実行環境もコンテナである場合、この開発コンテナから直接 docker コマンドを実行したい、というのが人情です。 まあやり方などは上記のドキュメントにも書いてあるんですが、いちいち調べるのも面倒ですし、何度やっても覚えられないのでこれは備忘録です。
私が試している環境は以下のようになります。
まずは素直に Dev Container 内でアプリを直接動作させることを考えます。
さて Dev Container から docker を実行するにあたっては 2 つの方法が考えられます。
目的としては開発・実行環境がソフトウェア的に分離できればいいので、個人的には Docker outside of Docker のパターンがいいかなと思っています。
まず Dev Container を用意するわけですが、これもいくつか方法が考えられますが、個人的には一番先頭の DooD セットアップ済みが安定するかなと思っています。
前置きが長くなりましたがここからが実際の手順というか備忘録になります。
Remote-Containers: Add Development Container Configuration Files
を探して選択Docker from Docker
を選ぶ作業が終わると .devcontainer
というディレクトリ配下に、 devcontainer.json
と Dockerfile
というファイルが生成されているはずです。
一度 Visual Studio Code を閉じて再度同じディレクトリを開くと、Dev Container の設定がされてるけど コンテナ環境で開きなおすか? というような感じの通知がでますので、そこで Reopen を選択すれば作業開始です。
ただこの通知、しばらくすると消えてしまって見逃しがちですので、コマンドパレット(Ctrl + Shift + P)から Remote-Containers: Rebuild and Reopen in Container
を実行する方が確実です。またコンテナマウントした状態ではなく、ホストマシン上で普通に開きなおしたい場合はコマンドパレットから Remote-Containers: Reopen Folder Locally
を実行すると戻ることができます。
まずは作成した環境で DooD が出来てることを確認しておきましょう。
下記のようにホスト側で使っていたコンテナイメージに加え、vsc
から始まるコンテナおよびイメージが確認できます。
これが Visual Stuido が Remote Container 機能で接続している Dev Conatainer とそのイメージにです。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
vsc-docker-outside-of-vscode-devcontainer-6db317e7d257ca334df89b964a5b54b8 latest 65999a18db5e About an hour ago 1.12GB
pystrsample latest ce350b283efa 45 hours ago 917MB
python 3.8 e7d3be492e61 8 days ago 883MB
ubuntu latest 7e0aa2d69a15 5 weeks ago 72.7MB
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6793b3914ce7 vsc-docker-outside-of-vscode-devcontainer-6db317e7d257ca334df89b964a5b54b8 "/bin/sh -c 'echo Co…" 9 minutes ago Up 9 minutes brave_euler
docker コマンドをコンテナの中から実行しているのに、そのコンテナを外から見ている状態なのはなんだか不思議な気分です。
さて、この時点ではベースイメージとして選択したコンテナイメージに含まれるツール群(git とか)しか使えません。
このためパッケージマネージャ等を使ってインストールしていくことになりますが、毎回 VS Code 開くたびに毎回そんなことはやってられません。
.devcontainer/Dockerfile
に SDK インストールを記載しておくことで、次回以降はビルド済みのイメージが使えるわけですね。
たとえば .NET 5 の SDK をインストールするなら下記のような内容を追記します。
# .NET Core 5.0 SDK をインストール
RUN wget https://packages.microsoft.com/config/ubuntu/20.10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \
dpkg -i packages-microsoft-prod.deb && \
apt-get update && \
apt-get install -y apt-transport-https && \
apt-get update && \
apt-get install -y dotnet-sdk-5.0
もちろん Dockerfile を書き換えただけでは反映されませんので、コマンドパレット(Ctrl + Shift + P)から Remote-Containers: Reopen Folder Locally
を選んで一度コンテナから抜け、
再度コマンドパレットから Remote-Containers: Rebuild and Reopen in Container
を実行して、イメージを最新化した上でコンテナに接続しなおします。
最初のうちはこの Dev Container を育てていくフェーズがなので、Rebuild と Reopen が頻繁にあって面倒ですが、いずれ頻度は下がっていくでしょう。 アプリケーションのソースコードと一緒に Dev Container の定義もソースコードリポジトリにまとめてコミットして管理することで、git pull するだけで全員が常に統一された環境で開発作業ができるわけですから、ここは頑張りどころですよね。
さて実際にアプリを作ってみましょう。 前述の通り Dev Container では .NET SDK が使えるようになっていますので、例えば下記のようなコマンドでアプリの開発を始められるわけです。
$ mkdir src
$ dotnet new web -o src
$ dotnet new gitignore -o src
$ cd src
# いろいろコーディングする
この Dev Container はホストマシンのディレクトリをマウントしているわけですので、コンテナが終了してもソースコードがちゃんとホストマシンに残るわけですね。 まあもちろんソースコードリポジトリにちゃんと保存しておくべきでしょう。 この方法で作った Dev Container であれば git コマンドも使えますので、コンテナの中からでも外からでもバージョン管理は可能です
$ dotnet run
Building...
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to bind to https://localhost:5001 on the IPv6 loopback interface: 'Cannot assign requested address'.
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to bind to http://localhost:5000 on the IPv6 loopback interface: 'Cannot assign requested address'.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: /workspaces/ainaba-csa-blog/docker-outside-of-vscode-devcontainer/src
さて勢いあまって Dev Container 内でアプリを実行しましたが、ASP.NET Core の開発サーバーが Port 5000, 5001 を使用して待機しています。 Linux コンテナで実行しているので curl などでテストしてもいいのですが、Dev Container の場合はコンテナ内部でポートを使って待ち受けるプロセスが起動すると、自動的にポートフォワードが行われ、ホスト側から接続できるようになります。
なにこれ便利。
VS Code が Dev Container 内で起動したアプリプロセスに対して自動的にポートフォワードしてくれるというのは、開発中に Try & Error している時は大変便利です。 とはいえ、Dev Contaienr をそのまま実運用環境にデプロイすることはないでしょうから、実際には構成の異なる環境でのテストをしていることになります。 実運用環境にデプロイするためのコンテナ開発は別途必要になるわけですが、その作業もアプリ開発をしている Dev Container の中でまとめてやってしまいたいなと思います。
さてランタイムのみのイメージにアプリをデプロイして動作させたいわけですから、例えばこちらのような別の Dockerfile を用意することになります。 で、それをビルドして実行するわけですね。
$ cd src
$ docker build -t app-container .
## 省略 ##
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
app-container latest e3574686992b 17 minutes ago 205MB
vsc-docker-outside-of-vscode-devcontainer-6db317e7d257ca334df89b964a5b54b8 latest 65999a18db5e 2 hours ago 1.12GB
pystrsample latest ce350b283efa 45 hours ago 917MB
python 3.8 e7d3be492e61 8 days ago 883MB
ubuntu latest 7e0aa2d69a15 5 weeks ago 72.7MB
$ docker run -d --rm -p 8080:80 app-container
038025e44bcab23074c3312ea8c60c8fae3c334d9d5aeb83282244405ae682e0
さて前述の通りこのアプリコンテナはホストの Docker Daemon から起動されてるので、Dev Container 内部でプロセスが起動したわけではありません。
つまり残念ながら VS Code がポートフォワードしてくれるわけではないので、ここでは -p
オプションを付けてポートマッピングをしています。
これはホスト側の Port 8080 を app-container の Port 80 にマッピングしてるだけですので、Dev Container の中からはアクセスできません。残念。
VS Code が自動的にやってくれないのは残念ですが「ホスト側のブラウザ開いてテストする」っていう意味ではまあ同じかなと思いますです。
それに思い至るまで dev container の中から curl を打っては繋がらなくて泣きそうになってたのは秘密です
Visual Studio Code でディレクトリを開いたときに、そこに .devcontainer
というディレクトリがあると、ここは直接じゃなくてコンテナから開いた方がいいんだな、ということに気が付いてくれるわけです。
つまり以下のようなディレクトリ構成を作ってソースコードリポジトリで管理しておけば、あとはクローンして Visual Stduio Code で開くだけで「開発メンバー全員が同じ環境で開発作業ができる」ことが保障できるわけですね。