Dockerコンテナは、いくつかのクラウドプラットフォームのうちの1つを使用してデプロイできます。Amazon Elastic Container Service (ECS) もその1つです。 ECSはFargate起動タイプを提供します。これは、EC2インスタンスではなくDockerコンテナ上でコンテナサービスを実行するためのサーバーレスプラットフォームです。
問題
Dockerコンテナのデプロイメントは、Dockerイメージのソースコードまたはコードビルドの変更により、更新または変更が必要になる場合があります。Dockerイメージのソースコードを変更した場合は、Dockerイメージを再構築してDockerサービスを再デプロイする必要があります。AWS ECSデプロイの実行中にソースコードを統合、構築、配信、デプロイするメカニズムがないと、ECSサービスタスクを停止し、その結果ECSサービスのダウンタイムが発生します。ECSサービスの高可用性が優先され、アプリケーションを再デプロイするためにタスクを停止することは適切な選択肢ではありません。
解決策
AWS CodePipelineは、デプロイプラットフォームとしてAmazon ECSやFargateなど、さまざまなAWSプラットフォームでホストされるアプリケーションの継続的インテグレーション、継続的デリバリ、継続的デプロイのためのDevOpsサービスです。ECSサービスは、ECSサービスタスクを停止することなく更新または修正することができます。AWS CodePipelineは、Dockerイメージのソースコードの変更が一般的である動的環境でECSサービスの高可用性を提供します。CodePipelineは、図1に示すように、ソースコードの統合、ソースコードの構築、デプロイの3つのフェーズで構成されています。
図1. CodePipelineのフェーズ
ソースコードにはGithubレポジトリを使用します。ソースコードの構築にはAWS CodeBuildプロジェクトを使用します。デプロイには、起動タイプがFargateのECSサービスを使用します。
CodePipelineアプリケーションを作成してECS Fargateにデプロイするには、次の手順に従います。
- ECS Fargateタスク定義とサービスの作成
- Task Subnet/sで接続設定
- CodePipelineビルドステージから出力アーティファクトを出力するためのS3バケットの作成または構成
- ECS FargateにDockerプラットフォームアプリケーション(イメージ)をデプロイするためのコードパイプラインの作成
- ステージアーティファクトのための入出力設定の変更
- CodePipelineの実行
- CodePipelineを再実行するためのソースコードの修正
環境設定
唯一の前提条件はAWSアカウントです。ECS FargateのCodePipelineによってデプロイされたアプリケーションはDockerアプリケーションです。ソースコードのレポジトリを持っているDockerイメージであればどんなものでも使うことができ、私たちはDockerイメージdvohra/node-server
を使いました。Dockerイメージdvohra/node-server.
のGitHubソースコードリポジトリはこちらです。
GitHubコードレポジトリの作成
新しいGitHubソースコードリポジトリを使用する場合は、そこにDockerイメージを構築するためのDockerfileを含める必要があります。dvohra/node-server
イメージのDockerfileは、Dockerイメージnode:4.6
をベースにしています。Dockerfileの手順に従って、server.js
ファイルをコピーします。それによって、現在のディレクトリにNodeサーバーが作成され、Nodeサーバが待機するポート8080が公開され、server.jsスクリプトでnodeコマンドが実行されます。server.jsファイルは、Nodeサーバを作成し、HTTPリクエスト/レスポンスを処理します。
CodeBuildプロジェクト向けのビルドスペックの追加
ビルドスペックは、ビルドを実行するためにCodeBuildプロジェクトで使用されるビルドコマンドと設定を含むYAML構文ファイルです。ビルドスペックファイルは「buildspec.yml」という名前である必要があり、ソースコードリポジトリのルートにコピーする必要があります。buildspec.ymlファイルはビルドのさまざまなフェーズを記述するためのキー・バリューのペアで構成されます。 ビルドphases
はフェーズ順で表され、buildspec.yml
で必須のマッピングです。version
はbuildspec.yml
で必須のマッピングです。buildspec.ymlファイルはGitHubリポジトリに記載されています。
イメージ定義ファイルの追加
ECSにデプロイされるような、コンテナベースのアプリケーションをデプロイするには、AWS CodePipelineにはJSON形式のイメージ定義ファイルが必要です。イメージ定義ファイルはデフォルトではimagedefinitions.json
という名前ですが、別の名前を付けることもできます。イメージ定義ファイルにはコンテナアプリケーションを記述し、nameとimageURI
の2つの属性で構成されます。name
はDockerコンテナ名を指定し、コンテナはCodePipelineを実行する前に実行されていなければなりません。 imageURI
は、Dockerコンテナで実行されるDockerイメージを指定します。Dockerイメージは通常、すでにECSコンテナで実行されているDockerイメージと同じです。イメージは異なる場合があり、バリエーションは通常、イメージタグのバリエーションとなります。ECSにデプロイされたNodeサーバアプリケーションに使われるimagedefinitions.jsonはGitHubに表示されています。
タスク定義の作成
ECSアプリケーションのタスク定義は、ECSデプロイでcontainer/sを記述します。このセクションでは、ECS FargateにデプロイされるNodeサーバコンテナのタスク定義を作成します。このURLを開き、まだログインしていない場合はログインします。 Get startedをクリックしてECSコンソールにアクセスします。ナビゲーションマージンのTask Definitionsをクリックします。 Task DefinitionsでCreate new Task Definitionをクリックします。Create new Task Definitionで、Fargateとして起動タイプの互換性を選択します。Next stepをクリックします。次に、タスクとコンテナの定義を設定します。Add containerダイアログで、Container name(ノードサーバ)を指定し、Imageをdvohra/node-serverと指定します。タスク定義を図2に示します。
図2. タスク定義
タスクサブネットでの接続の設定
サービスを作成する前に、サービスを設定するときに使用するサブネットでインターネットへの接続を設定する必要があります。Route Tableにルートが一覧表示されます。 デフォルトのインターネットゲートウェイと共にルートを追加する必要があります。IPアドレスが0.0.0.0/0のDestinationを持つルートを追加します。internet gateway.としてTargetを選択します。
コンテナサービスの作成とテスト
次に、図3に示すように、デフォルトのクラスタにECSコンテナサービスを作成します。
図3. 1サービスのクラスタ
Fargate起動タイプでは、各タスクに対してElastic Network Interface(ENI)がプロビジョニングされます。NetworkセクションのタスクのDetailsページまたはENIコンソールから、Elastic Network InterfaceのパブリックIPと同じタスクのパブリックIPをコピーします。ブラウザでURL<Public IP of Task>:8080を開き、Nodeサービスアプリケーションを起動します。Nodeサーバは、図4に示すようにメッセージを返します。
図4. Nodeサーバレスポンス
S3バケットの作成または設定
CodePipelineでNodeサーバアプリケーションのソースコードをビルドし、DockerイメージをECSサービスにデプロイするために、CodeBuildプロジェクトで「出力アーティファクト」を生成する必要があります。出力アーティファクトは、CodePipelineを作成するときに選択されるS3バケットに保存されます。S3コンソールで新しいS3バケットを作成するか、またはCodePipelineの事前実行で作成されるS3バケットを選択します。
CodeBuildプロジェクトの作成
次に、ソースコードをDockerイメージにビルドするために使用されるCodeBuildプロジェクトを作成します。Dockerイメージのソースコードをビルドするために使用されるDockerイメージdvohra/node-server
およびbuildspec.yml
ファイルのソースコードについては既に説明しました。Dockerイメージがビルドされた後、それはCodeBuildによってDockerハブにアップロードされます。CodeBuildプロジェクトを作成するには、ウェブブラウザでこのURLを開いてください。 図5に示すように、Build projectsを選択してCreate projectをクリックします。
図5. Build projects>Create project
Create projectウィザードが起動します。Configure projectで、プロジェクト名(ノードサーバ)を指定します。 Source>Source ProviderでGitHubを選択します。 RepositoryでUse a repository in my accountを選択し、dvohra/docker-node-server リポジトリとしてリポジトリを選択します。Git clone depthのデフォルト設定を1のままにします。
Webhook、Insecure SSL、Build Badgeのオプションを選択します。Webhookを選択すると、GitHubリポジトリでコードが更新されるたびにコードが再構築されます。Insecure SSLオプションを選択すると、プロジェクトソースに接続するときにコードビルドSSL警告が生成されます。Build Badgeオプションを選択すると、プロジェクトのステータスが表示され、埋め込み可能になります。Environment: How to buildでEnvironment imageとしてUse an image managed by AWS CodeBuildを選択します。Operating Systemとして、Ubuntuを選択します。ランタイム向けに、図6に示すようにDockerを選択します。
図6. ランタイムとしてDockerを選択
ランタイムバージョンについては、aws:codebuild/docker:17.09.0を選択します。これはDockerバージョン17.9を表します。新しいバージョンが利用可能な場合は、新しいバージョンを選択してください。Dockerイメージを構築するのに必要なので、Dockerランタイムに対してPrivilegedオプションが自動的に選択されます。ビルド仕様では、図7に示すようにUse the buildspec.yml in the source code root directoryを選択します。ビルドスペック名はデフォルトでbuildspec.ymlです。
図7. Environment: How to buildの設定
CertificateではDo not install any certificateを選択します。証明書はCodePipelineには必要ありませんが、より安全なCodeBuildのために自己署名証明書をS3からインストールすることができます。次に、Artifactsで出力アーティファクトを設定します。出力アーティファクトはCodePipelineに必要です。TypeでAmazon S3を選択します。使用するS3バケットフォルダ名を指定してください。Pathを「/」に設定すると、バケットルートにフォルダが作成されます。Namespace typeでNodeを選択します。 Bucket nameで図8に示すように、以前に設定したバケットを選択します。CacheでNo cacheを選択します。
図8. アーティファクトのためのS3バケットの設定
CodeBuildプロジェクトを初めて作成する場合は、Service roleでCreate a service role in your account オプションを選択します。CodeBuildプロジェクトを以前に作成した場合は、Choose an existing service role from your accountを選択します。Allow AWS CodeBuild to modify this service roleオプションを選択すると、図9に示すようにこのビルドプロジェクトで使われます。VPCでNo VPCを選択します。 Continueをクリックします。
図9. サービスロールとVPCの設定
Reviewで、SourceおよびBuild環境を確認します。下にスクロールしてCreateをクリックし、CodeBuildプロジェクトを作成します。図10に示すように、CodeBuildプロジェクトが作成され、Build projectsに表示されます。
図10. CodeBuildプロジェクト
CodeBuildによってデフォルトで作成されたサービスロールには、必要な権限の一部が含まれていません。 権限s3:GetObject
とs3:PutObject
を追加するインラインポリシーを追加して、サービスロールを変更する必要があります。追加するインラインポリシーは次のとおりです。
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":["s3:PutObject","s3:GetObject"],
"Resource":"arn:aws:s3:::*"
}
]
}
CodeBuildプロジェクトのテスト
CodePipelineでCodeBuildを作成および設定する前にCodeBuildプロジェクトをテストして、エラーがある場合は修正します。図11に示すように、Start buildをクリックしてビルドを開始します。
図11. Start build
Start new buildウィザードが起動します。Start buildをクリックします。CodeBuildプロジェクトが開始され、コードがビルドされ始めます。CodeBuildプロジェクトが完了すると、Phase detailsは図12のようになります。
図12. フェーズ詳細とビルドログは、CodeBuildが完了したことを示す
CodeBuildプロジェクトによって、Docker Hubに生成され、更新されたDockerイメージdvohra/node-serverを図13に示します。
図13. CodeBuildによって生成され、Docker HubにアップロードされたDockerイメージ
コードパイプラインの作成
Having created projects for each of the CodePipeline stages; GitHub code repository for Source, CodeBuild for Build and ECS Fargate service for Staging, next we shall create a CodePipeline. Open CodePipeline Console with URL and click on Get started as shown in Figure 14.
各CodePipelineステージ用にプロジェクトを作成します。 ステージは、ソース用のGitHubコードリポジトリ、ビルド用のCodeBuild、ステージング用のECS Fargateサービスです。次にCodePipelineを作成します。URLでCodePipelineコンソールを開き、図14に示すようにGet startedをクリックします。
図14. CodePipeline>Get started
図15に示すように、Create pipelineウィザードが起動します。最初にPipeline name(node-server-fargate)を指定して、Next stepをクリックします。
図15. Pipeline Nameの指定
次に、ソースの場所を設定します。図16に示すようにSource providerとしてGitHubを選択します。
図16. Source providerの選択
次に、図17に示すように、Connect to GitHubでGitHubに接続します。
図17. GitHubへの接続
図18のようにGitHubリポジトリを選択します。
図18. Selecting GitHub Repo
図19に示すように、レポジトリBranchを選択します。
図19. Source location>Next step
次に、ビルドを設定します。図20に示すように、Build providerでAWS CodeBuildを選択します。
図20. ビルドプロバイダとしてAWS CodeBuildを選択
表示される後続のセクションは、選択したビルドプロバイダに基づきます。AWS CodeBuildの場合、CodeBuildに関する詳細を提供するセクションが表示されます。図21に示すようにConfigure your projectでSelect an existing projectを選択します。Project nameで、以前に作成したCodeBuildプロジェクトnode-serverを選択します。
図21. CodeBuild Projectを選択
Next stepをクリックしてください。次に、図22に示すように、CodePipelineのDeploy ステージを設定します。Deployment providerでAmazon ECSを選択します。
図22. Deployment providerでAmazon ECSを選択
その後に表示されるセクションは、図23のAmazon ECSセクションに示されているように、選択されたデプロイメントプロバイダに基づきます。Amazon ECSセクションで、Cluster nameで、デプロイされるECSサービスが作成されるクラスタを選択します。defaultを選択します。
図23. ECS Clusterを選択
図24に示すように、Service nameとしてデプロイ先のECSサービスnode-server-serviceを選択します。
図24. ECS Serviceを選択
図25に示すように、Image filenameとしてimagedefinitions.json
を指定します。省略した場合、デフォルトのイメージファイル名はimagedefinitions.json
になります。ファイルはソースコードGitHubリポジトリで利用可能であるべきです。Next stepをクリックしてください。
図25. イメージファイル名を指定
次に、図26に示すようにService Role nameを選択します。新しいサービスロールは、CodePipelineが最初に作成されたときに、自動的にIAMに作成されます。その後、サービスロールが選択できるように一覧表示されます。
図26. サービスロールを選択
Next stepをクリックしてください。CodePipelineを確認して、図27に示すようにCreate pipelineをクリックします。
図27. Create pipeline
図28に示すように、CodePipelineが作成されます。作成後、CodePipelineは自動的に実行され、SourceステージのIn Progressステータスで示されます。
図28. CodePipelineのSourceステージ実行中
Sourceステージが完了すると、図29に示すように、そのステータスはSucceededになります。そして、Buildステージが実行を開始し、In Progressで示される。
図29. Sourceステージが完了し、Buildステージが実行中
図30のSucceededステータスに示されているように、Buildステージも完了しました。Stagingステージが実行を開始します。
図30. Buildステータスが完了し、Stagingステージが実行中
入力/出力アーティファクトの修正
すべてのCodePieplineステージの設定が済んでいます。入力/出力アーティファクトの設定を変更する必要があるのはなぜでしょうか。CodePipelineによってデプロイされたECSアプリケーションの例では、デフォルトの入出力アーティファクト設定はCodePipelineを実行するのに必要なものではありません。デフォルトでは、ステージングステージへの入力アーティファクトは、Buildステージからの出力アーティファクトです。Dockerイメージを作成してDockerイメージをDocker HubまたはAmazon ECRにアップロードするだけのCodeBuildの場合、CodeBuildステージでは出力アーティファクトは生成されません。Stagingステージへの入力アーティファクトは、Sourceステージからの出力アーティファクトに設定する必要があります。入出力アーティファクトの設定を変更しないと、Stagingステージは失敗します。作成後に自動的に開始されたCodePipelineは失敗します。入力/出力アーティファクトを変更するには、図31に示すようにEditをクリックします。
図31. CodePipeline>Edit
入力/出力アーティファクトを変更したら、図32に示すようにSave pipeline changesをクリックします。
図32. Save pipeline changes
CodePipelineの実行
変更後にCodePipelineを実行するには、図33に示すようにRelease changeをクリックします。
図33. Release change
Release change確認ダイアログで、Releaseをクリックします。図34のSourceステージのIn Progressステータスに示されているように、CodePipelineに対する変更が適用され、CodePipelineが最初から実行され始めます。BuildおよびStagingステージのステータスは前回の実行のものであり、現在のステージのステータスを示すものではありません。
図34. 実行中のSourceステージ
図35に示すように、CodePipelineのすべてのステージはSucceededのステータスで完了します。
図35. CodePipelineのすべてのステージが成功で完了
前のサービスタスクが停止し、図36のRUNNINGタスクステータスに示されているように、修正されたタスク定義に基づくタスクが実行を開始します。
図36. 新しいタスクの実行
新しいタスクを呼び出すには、そのタスクのElastic Networkインターフェイスから、以前と同様にそのパブリックIPを見つけます。タスクを呼び出すには、ウェブブラウザでURL<Public IP>:8080を開きます。Nodeサーバアプリケーションのレスポンスを図37に示します。
図37. Nodeサーバアプリケーションのレスポンス
CodePipelineを再実行するためのソースコードの修正
ECSサービスのレスポンスがCodePipelineなしでサービスを直接呼び出す場合と同じである場合、CodePipelineを実行する利点は何でしょうか。通常、本番環境にデプロイされたECSのソースコードは定期的に更新する必要があります。これは、Dockerイメージを再構築する必要があることを意味します。ECSサービスタスクにデプロイされたDockerイメージも更新する必要があります。Dockerイメージは、ECSベースのサービスを中断することなく更新する必要があります。CodePipelineでは、ソースコードの修正が行われるたびにCodePipelineの新しい実行が自動的に開始されます。ユーザの介入なしに、ソースコードが変更されたときにECSのデプロイが更新されます。
説明するために、server.js
のHelloメッセージを変更するなど、GitHubリポジトリのソースコードをわずかに変更します。レポジトリのCommit changesをクリックします。図38でSourceステージが正常に完了し、Buildステージが進行中であることが示されているように、CodePipelineが自動的に再実行されます。
図38. CodePipelineが自動的に再実行
Buildステージが正常に完了すると、図39のステータスメッセージで示されるように、Stagingの実行を開始します。
図39. Buildステージが完了し、Stagingステージが開始
Stagingも正常に完了しました。新しいタスクが開始されます。新しいタスクのパブリックIPを使用して、ウェブブラウザでURL<Public IP>:8080を開きます。新しいタスクが呼び出され、ノードサーバのレスポンスが図40のように表示されます。Nodeサーバのレスポンスは、server.jsから変更されたレスポンスです。
図40. 新しいタスクからの、修正後のNodeサーバのレスポンス
まとめ
この記事では、DockerコンテナアプリケーションをECS Fargateにデプロイする方法について説明しました。AWS CodePipelineを使用して、Dockerイメージのソースコードを更新し、コンテナサービスを停止せずに実行中のコンテナサービスに新しいDockerイメージをデプロイする方法を示しました。
著者について
Deepak Vohra氏は、Sun認定のJavaプログラマーおよびSun認定のWebコンポーネント開発者です。Vohra氏は、WebLogic Developer's Journal、XML Journal、ONJava、java.net、 IBM developerWorks、Java Developer’s Journal、Oracle Magazine、devxでJavaおよびJava EEに関連する技術記事を公開してきました。Vohra氏はDockerに関する5冊の本を出版しており、Docker Mentorです。