.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)のインストールコマンドでは、動作検証したときのライブラリのバージョンに固定することが多いです。そうしないと、コンテナを作り直すときに勝手に最新版のライブラリがインストールされて動かないなどの問題が起きうるからです。