はじめに
参画案件で新しいアプリケーションの開発をするにあたり、AWSによるインフラ構築を実施しています。
インフラ要件として、アプリケーションをホストするサービスはFargateタイプのECS(Elastic Container Service)を使用することにしました。なるべく保守の手間を減らしたいのと、EC2と比較したらこっちのほうが料金も安く済むのではないか、という観点からになります。
さて、ECSを使うということはECR(Elastic Container Registory)、ひいてはECRにプッシュするためのDockerイメージ、つまるところDockerに関する知識が必要になります。
今回は、アプリケーションをコンテナ化するためDockerイメージを作成するにあたり詰まった個所について、備忘録も兼ねてまとめてみることにしました。
詰まった個所
①そもそもDockerとはなんなのか
まず詰まったのがココです。
「Docker?何それ?美味しいの?」状態から始まったので、まずはDockerとは何たるかを知るところから始めました。
AWSによる説明↓
Docker は、アプリケーションをすばやく構築、テスト、デプロイできるソフトウェアプラットフォームです。Docker は、コンテナと呼ばれる標準化されたユニットにソフトウェアをパッケージ化します。コンテナには、ライブラリ、システムツール、コード、ランタイムなど、ソフトウェアの実行に必要なすべてのものが含まれています。Docker を使用すると、どのような環境にもアプリケーションをすばやくデプロイおよびスケールでき、コードを実行することができます。
Docker を AWS で運用することで、開発者や管理者は、どのような規模の分散アプリケーションでも、高信頼性と低価格を実現した方法ですばやくビルド、出荷、実行できます。
今でこそ意味は理解できますが、初めてこれを読んだときは「何言ってるかわかんなくて草」状態でした。
要約すると…
「アプリケーションを実行するにあたり必要なものをコンテナという単位でまとめて、どんな環境でも問題なく動かせるようにするもの」になります。
なんでこんなことをするのかというと…
- 開発環境と本番環境など、環境が違うと片方のみ動かないケースがあった
- OSやライブラリのバージョンの違いで動かないケースがあった
- 環境構築が大変
といった背景があったかららしいです。
なので、Dockerを使ってアプリケーションと必要なもの(ライブラリ)を1つのパッケージにまとめることで、どの環境においても、パッケージひとつで問題なく動かせるようになりました。
ここでいうパッケージのことをコンテナと言います。
なぜECSを使うにあたりDockerが必要なのか
ECSのCはContainerのCです。
ECSは「コンテナ」を使ってアプリケーションを実行・管理するサービスなので、コンテナを作成するための技術であるDockerの知識が必要になります。
Dockerイメージとは
簡単に言うと、コンテナの設計図です。
ローカルにアプリケーションのソースコードディレクトリがあったとして、これをコンテナ化して実行するには、まずコンテナ化するための設計図が必要になります。このとき、ソースコードディレクトリ内にあるDockerfileなるものを使って、CLIでコマンドを叩くことでDockerイメージを作ることができます。
まとめると、おおざっぱな流れとしては↓
①アプリケーションのソースコード内にあるDockerfileを使い、Dockerイメージを作る
↓
②ECRにDockerイメージをプッシュする
↓
③プッシュされたDockerイメージをもとに、ECSでアプリケーションがコンテナ化され、実行される
といった具合になります。
②Dockerfileがある場所から他の場所にあるファイルを参照できない
Dockerイメージを作成するには…
①cdでDockerfileがある位置に移動する
↓
②移動したらdocker buildコマンドを叩き、ビルドを実施する
docker build -t my-image
これだけです。
たったこれだけですが、ここから私はいろんなところで躓きました。
まず、Dockerの仕様として、docker buildを実施する際は、現在移動しているディレクトリ内のファイルにしかアクセスできないという仕様があるそうです。
たとえば以下のような構成だった場合、
.
├── Project <-- ここから
└── Project.csproj
│ └── Dockerfile
└── .nuget<-- ここを参照したい
└── Nuget.Config
cdでProjectの配下に移動すると、Projectディレクトリ内のファイルしか参照できないことになります。
別のディレクトリに参照したいファイルがあったとして、相対パスで指定してあげても「指定されたソースが見つかりません」というエラーを吐いてきます。
# 誤りの書き方
COPY Project /src/Project.csproj
COPY ../.nuget/NuGet.Config /src/NuGet.Config
なので、docker buildをする際はルートに移動して、ルートから見たビルドに必要な各種ファイルの位置を相対パスで指定してあげるのが正解となります。
# 正しい書き方
COPY Project/Project.csproj /src/Project.csproj
COPY .nuget/NuGet.Config /src/NuGet.Config
また、docker buildを叩く際のコマンド書き方もちょっと変わります。
cdでDockerfileがある場所に移動して叩くのであれば、冒頭に示したdocker buildコマンドでよいのですが、ルートに移動して叩く場合は以下のような書き方になります。
docker build -t my-image -f Project/Dockerfile .
私のケースでは、ソースディレクトリ内のいろんなファイルを参照する必要があったので、ルートからみてすべてのディレクトリおよびファイルを作業ディレクトリにコピーする形で書きました。
WORKDIR /src
COPY . /src
③ローカルソースは改めて指定してあげないといけない
たとえばNuGetパッケージファイルが格納されているディレクトリが、ソースプロジェクトの中にあるとします。
COPYで全部取り込んで、さあいざdotnet publishだ!
…と息巻いてコマンドを叩くと、現れるのが「NuGetパッケージとして指定されているソースが存在するディレクトリが見つかりません」という趣旨のエラー。
「なんでや!全部作業ディレクトリにコピーしたやろがい!ちゃんと探さんかい!」と突っ込みたくなる気持ちを抑えて原因を探ったところ、どうもCOPYしただけではNuGetの検索対象にならないらしいということがわかりました。
今回のケースでは、dotnet restore時にNuGet.Configに記述された相対パスを基にローカルソースを探しに行く、という手段を取っていますが、この際明示的に「ここにローカルソースがあるよ!」という指示をしてあげないと、検索されないという仕様があるそうです。
なので、以下コマンドを追加してみたところ、ローカルソースが検索されて、正しくdotnet publishが実行されました。
RUN dotnet nuget add source /src/localsource -n local-source
ファイルが存在することと、それがNuGetの検索対象になっていることは別物なのですね。
おわりに
上記困難を乗り越えて、無事にDockerイメージを作成することができました。
調べに調べつくしてようやく完成…といった感じですが、正直Docker、ひいてはコンテナという領域はなかなか抽象的d理解するのが難しく、今現在もノリと雰囲気でしか理解できていない、というのが正直な感想です。
しかし、アプリケーション開発においてコンテナの知識があれば絶対に役立つと今回の件で強く感じたため、詰まった個所をまとめておいてよかったなと心から思います。