.NETは環境に依存しないけれど……
本番運用環境として、AWSやAzure、Google Cloudなどのクラウドが使われるようになったいま、サーバー環境がLinuxという場面も多いかと思います。
Linuxで動くネイティブなアプリを作るのも、1つの方法ですが、業務システムの開発では、過去の資産やライブラリの豊富さなどから、Linuxのネイティブで作り直すわけにもゆかず、WindowsでもLinuxでも動く、.NETの環境で開発している現場も多いはずです。
確かに.NETは、OSの依存がない環境ですが、「開発にWindowsを使い、本番でLinuxを使う」というような場合には、「ライブラリなどがインストールされているパスの違い」「システムに既存でインストールされているファイル(例えばフォント)の違い」などから、開発中は問題なかったけれども、いざ本番環境で動かそうとしたら動かないということも、よくあります。
パッケージ化して、そのままコピーできるDocker環境
ですから、本番環境でLinuxを使うのであれば、開発環境でもLinuxを使って同じ環境にそろえるのがベストです。それならばとLinuxの開発環境を作るのもよいですが、Windowsの開発者にとって、Linux環境を整えるのは少し煩雑です。
こうした煩雑さをなくすため、近年、使われているのが、「Docker」を使った開発手法です。Dockerは、コンテナと呼ばれる隔離された環境のなかでプログラムを実行できるLinuxのソフトウェアです。
「Debian」や「Ubuntu」などの基本的なLinuxディストリビューション、もしくはそこに「PHP」「.NET」など開発環境、もしくは、「Apache」「MySQL」などのソフトウェアが格納された「Dockerイメージ」というものが配布されていて、それを選ぶと、そのディストリビューションや言語、ソフトウェアがインストールされたDockerコンテナが起動します。
Dockerは、Linuxのソフトウェアですが、「Docker for Desktop」というソフトウェアを使うと、WindowsやmacOS上でも動きます(実行には仮想化環境、例えばWindowsの場合はWSL2などのシステムが使われています)。つまり、WindowsやmacOS上で、こうしたLinux環境を実行できます。Dockerコンテナは、イメージ化して別のコンピュータに持っていくことができます。
この性質を利用した、「Dockerを用いた.NET環境の開発」は、次のような流れで進めます。
- Linux + .NET SDKがインストールされたDockerコンテナを、あなたのWindowsマシン上に起動する
- 1.のコンテナで開発する
- 2.のコンテナをイメージ化して、本番サーバーに持っていく
つまり、あなたのWindowsマシンに、Linux版の.NET SDKがインストールされたDockerコンテナを起動する。それに対して開発して、イメージ化してそれごと本番サーバーに持っていくようにします。
必要なライブラリやファイルは、Dockerコンテナのなかに入れておきます。そうすれば、それらもイメージ化したときに、本番サーバーに持っていけます。つまり、まったく同じ環境で動くので、OSの違いや設定、ライブラリのバージョン違いなども解決するというわけです(図2)。
コンテナを使った開発の注意
本記事は、Dockerやコンテナについて詳しく解説するのが主ではないので、より詳しくは、別の記事や書籍等を参考にしてほしいのですが、コンテナを使った開発において、1つ大事なことがあります。それは、コンテナ内の「プログラムやデータを置く場所」についてです。
先ほど、コンテナは隔離されて実行される場所だと説明しました。コンテナは、いつでも作れますし破棄もできるのですが、破棄する場合、そのコンテナの中身、つまりコンテナに追加で置いたファイルや変更したファイルなどは、すべて失われてしまいます。
こうしたことがないように、コンテナ内の適当なディレクトリを、ホストとなるPCのディスクのどこかにマウントします。そうすれば、そのディレクトリ以下のファイルに対する修正は、そのまま残ります(図3)。
この性質から、コンテナを使った開発では、次のようにします。
(1)自分が作るファイル
特定のディレクトリをマウントする設定にし、そこにまとめて開発物を置きます。
(2)ライブラリのインストールやその他必要なファイル
マウントした場所以外のディレクトリに置いたものはコンテナを破棄すると消えてしまうので、次のいずれかの方法でインストールしたり配置したりします。
(a)コンテナを起動するときに、インストールスクリプトを自動実行する
インストールするスクリプトなどを用意しておき、コンテナを起動する際に、それを実行します。インストールに限らず、必要なファイルをコピーしたり、ダウンロードしたりするスクリプトを置くこともあります。
こうしたコマンドは、あとで説明するDockerfileに記述します。
(b)マウントするディレクトリ以下に置く
インストール先をマウントするディレクトリ以下にします。例えば自分が作るファイルを置くのと同じ場所に置いてもよいですし、ほかにもうひとつ(もしくはそれ以上)のマウント場所を用意して、そこにインストールします。
多くの場合、(a)の方法をとります。そうするとライブラリのアップデートがしやすいからです。コンテナを起動する際、都度、最新のライブラリをインストールするようにしておけば、コンテナを破棄して作り直すだけで最新版に置き換えることができます。また逆に、もしライブラリを最新に入れ替えたとき、それが動かなかったときも、(最新版でなく)前のバージョンをインストールするように修正すれば、すぐに戻せます。
本番運用では、(a)のインストールコマンドでは、動作検証したときのライブラリのバージョンに固定することが多いです。そうしないと、コンテナを作り直すときに勝手に最新版のライブラリがインストールされて動かないなどの問題が起きうるからです。
Visual Studio Codeを使ったDocker開発
このようにDockerを使うと、そのコンテナのなかで閉じた開発ができて、そのまま本番環境に持っていけるのですが、実際にDockerコンテナを起動したり、コンテナのなかでコマンドを入力したり、Dockerのなかのファイルを編集したりするのは、意外と煩雑です。
こうしたときに便利なのが、Visual Studio Code(以下、VS Code)と、その拡張機能を使った開発です。
Remote - Containers拡張
VS Codeには、「Remote - Containers」という拡張があり、この拡張を使うと、VS Codeの操作で、Dockerコンテナを起動したり、Dockerコンテナ内のファイルを編集したりできます。
Remote - Containers拡張の動作の仕組みは、図4の通りです。PC上のワークフォルダに、起動したいコンテナの情報を記述した「Dockerfileファイル」もしくは「docker-compose.ymlファイル」を置いておき、Remote - Containersで指定します。すると、そのコンテナが起動し、コンテナの/workspaceディレクトリ以下に、PCのワークフォルダがマウントされます。
この状態で、VS Codeはコンテナ内のワークフォルダのファイルを編集することができる、つまり、編集によって、/workspaceディレクトリ以下のファイルが書き換わるので、開発を進められるというわけです。ターミナルを起動すれば、コンテナのなかでターミナル(/bin/bash)が起動して、コンテナに対して、各種コマンドを実行できます。
すでに説明したように、コンテナ開発では、マウントしたディレクトリ以外を操作すると、コンテナを破棄したときに、それが失われます。すなわち、この環境では、/workspaceディレクトリ以下以外のディレクトリを変更したときは、その変更が失われるので注意してください。
Remote - Containers拡張をインストールする
以下では、この便利なRemote - Containers拡張を使って開発していきます。まず、この拡張をインストールしましょう。
Remote - Containers拡張だけをインストールしてもよいのですが、実は次の3つの拡張をパッケージにした「Remote Development」という拡張パックがありますから、ここでは、これを使って、まとめてインストールすることにします。
- Remote - Containers:いま説明したコンテナ開発をサポートするもの
- Remote - SSH:SSH通信越しにファイルを編集できるようにするもの
- Remote - WSL:WindowsでLinuxサブシステムであるWSLを使った開発をサポートするもの
VS Codeを起動し、[拡張]から「Remote Development」を探してインストールしてください(図5)。
Dockerのインストールと確認
本連載では、Dockerのインストールについては触れません。以下では、Dockerがインストールされていることを前提とします。
WindowsにDockerをインストールするには、Docker社のサイトから「Docker for Desktop」のWindows版をダウンロードしてDocker for Desktopインストールします。
インストールすると、タスクトレイに「クジラのアイコン」が現れるので、そのアイコンから、Dockerエンジンの起動・停止ができます。もし、Remote - Containersを使った各種操作がうまく動かないときは、まず、Docker自体が正しくインストールされているかどうかを確認してください。確認の方法は、主に2つあります。
(1)dockerコマンドのインストールを確認する
コマンドプロンプトから、次のように入力し、dockerコマンドのバージョンが表示されることを確認します。
docker --version
(2)Hello Worldの起動を確認する
dockerには、動作確認のための「Hello World」のコンテナがあり、次のようにすると起動します。「--rm」は、終了したときにコンテナを残さずに削除するオプションです。
docker run --rm hello-world
正しくインストールされていれば、次のように表示されます。
Hello from Docker! This message shows that your installation appears to be working correctly. …以下略…
Dockerを使って.NET開発を始める
少し前置きが長くなりましたが、準備が整ったら、Dockerを使って、.NET開発を進めていきましょう。Remote - Containersを使った開発方法はいくつかありますが、ここでは、新しく.NET SDKがインストールされたコンテナを起動して、それを対象に開発していく方法を説明します。
開発用のコンテナを起動する
【手順】Remote - Containersを使って.NET開発を始める
[1]適当なワークフォルダを作ってVS Codeで開く
ここでは、個人フォルダ以下に、example00というフォルダを作りました。このフォルダをVS Codeで開きます。
[2]Dockerfileを書く
このフォルダのなかに、コンテナを起動するためのDockerfileを書きます。ここでは、リスト1に示す1行だけの内容とします。
Dockerfileは、起動するコンテナの基となるイメージや最初に起動するときに実行するコマンド、環境設定などを記述するファイルです。リスト1に記述している「FROM」は、Dockerイメージを指定する命令で、最低限この命令は必須です。
ここで指定している「mcr.microsoft.com/dotnet/sdk:6.0」は、Debian GNU/Linux 11に.NETがインストールされたマイクロソフト社製のDockerイメージです。「.NET CLI」「.NETランタイム」「ASP.NET Core」が含まれます。「:6.0」はバージョン番号を示します。
ここでは1つしかコンテナを起動しないため、Dockerfileを使いますが、複数のコンテナを起動したいときは、docker-compose.ymlファイルを使います。docker-compose.ymlは複数のコンテナの設定を記述して、まとめて起動できる定義ファイルです。例えば開発のコンテナとは別に、「データベースサーバー(例えばMySQLコンテナなど)」を起動したいときなど、複数のコンテナを使いたいときは、docker-compose.ymlを使います。docker-compose.ymlを使う場合は、次の手順[3]の工程で、どのコンテナに対してVS Codeを有効にするのかが尋ねられます。
FROM mcr.microsoft.com/dotnet/sdk:6.0
[3]コンテナを起動する
[F1]キーを押してコマンドパレットを開き(もしくは右下に追加された[リモートウィンドウ]のアイコンをクリックし)、[Remote - Containers Reopen in Container]を選択します。そして、どこからコンテナを起動するのかが尋ねられたら、[From 'Dockerfile']を選択します(図6)。
ターミナルを起動する
これでDockerfileに記述したコンテナが起動しています。ターミナルを起動すれば、コンテナに接続できます。実際に[ターミナル]メニューから[新しいターミナル]を選択して、ターミナルを起動してみましょう。
すると、次のようなプロンプトが表示されると思います。この「983c…」の部分は、コンテナIDなので、環境によって異なります(図7)。
root@983c55fad1c8:/workspaces/example00#
このプロンプトは、「コンテナの内部のシェル」です。つまり、ここで入力したコマンドは、コンテナのなか――Linuxで.NETがインストールされた環境――で実行されます。
プロンプトには、カレントディレクトリが表示されていますが、「/workspaces/ワークフォルダ名」となっている点に注目してください。すでに説明したように、このディレクトリは、ホスト(Windowsマシン)のワークフォルダにマウントされています。ですから、コンテナを削除しても、このディレクトリ配下のファイルは残りますし、このディレクトリのなかで書き換えたファイルは、ワークフォルダにも反映されます。
Dockerに詳しい人ならわかりますが、「FROM mcr.microsoft.com/dotnet/sdk:6.0」とだけ書かれているDockerfileからコンテナを起動したとき、通常、workspacesのマウントの設定などはされません。こうしたマウントが設定されるのは、Remote - Containers拡張がDockerコンテナを起動する際、基のDockerfileをそのまま使うのではなく、書き換えて実行しているからです。
.devcontainerフォルダ
図7の画面のエクスプローラーの部分を見るとわかりますが、Dockerコンテナを起動すると、ワークフォルダに「.devcontainer」というフォルダが作られます。これは、Remote - Containers拡張の設定ファイルを置くフォルダです。このフォルダのなかにはdevcontainer.jsonファイルがあり、どのDockerfileで起動するか、コンテナの設定ファイル名、Visual Studio Codeの設定などが記載されています。
ここでは触れませんが、このファイルを変更すると、マウントの設定や起動時に実行するプログラム、最初にコンテナ内にコピーするファイルなどを指定できます。
.NETプロジェクトのひな形を作る
では、こうして作成したコンテナで、.NET開発を進めていきましょう。いくつかの方法がありますが、ここでは、.NETプロジェクトのひな形を作り、そこから始めましょう。
【手順】 .NETプロジェクトのひな形を作る
[1]プロジェクトのディレクトリを作る
まずは、プロジェクトのディレクトリを作りましょう。仮にsampleconsoleとします。そして、そのディレクトリをカレントディレクトリにします。
mkdir sampleconsole cd sampleconsole
[2]コンソールアプリのひな形を作る
ターミナルから、次のコマンドを入力します。
dotnet new console --framework net6.0
すると、.NETコンソールアプリのひな形がフォルダに作られます。具体的には、「sampleconsole.cprojファイル」と「Program.csファイル」です。Program.csファイルが、C#のソースファイルです。VS Codeで開いて確認するとわかりますが、次のように「Hello, world!」と表示するサンプルです。
// See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!");
[3]実行する
このサンプルを実行します。
dotnet run
するとビルドが始まり実行され、画面には、Hello, world!と表示されます。
Hello, World!
リモートエクスプローラーでDockerコンテナを操作する
Remote - Container拡張をインストールすると、左側に[リモートエクスプローラー]が追加されます。リモートエクスプローラーでは、Dockerコンテナの稼働状況を見たり、起動したり、停止したりするほか、接続しているDockerコンテナの、どのディレクトリがどのディレクトリにマウントされているかなどの情報を確認できます(図8)。
まとめ
ここまで、Docker+ .NETでアプリを作る方法を説明しました。内容をまとめると、
- ワークフォルダを作る
- Dockerfileを作る
- Remote - Containers拡張の[Reopen in Container]でDockerfileを選択して起動する
- ターミナルを起動して操作する
- dotnetコマンドを使って、.NET SDKの操作をしていく
ということです。あとは、VS Codeでワークフォルダ内のファイルを編集していく。そしてコンテナのなかで実行したいときは、ターミナルからdotnet runなどで実行するというように進めていきます。
このように、DockerとVS CodeのRemote - Containers拡張を使えば、コンテナを意識することなく、開発を進められます。
Docker+.NET開発の基本がわかったところで、次回はいよいよ本題である、グレープシティ社の「DioDocs」を使用して帳票アプリを作っていきましょう。