はじめに
前回は、ソースコードをレポジトリへCommitし、CodeBuilidで検知してBuildし、出来上がったコンテナイメージをECRへPushするまでを行いました。
今回はそれを用いてECSへのコンテナ立ち上げと、全体のパイプラインを整備していきたいと思います。
コンテナ環境におけるパイプライン処理
やることは前回の作ったやつらをCodePipelineでつなげていきましょう。というくらいです。
- ソースコードがレポジトリへCommitされたことを検知する。
- CodePipelineが起動してCodeBuildを走らせる。
- コンテナイメージの生成
- コンテナイメージのテスト
- レジストリへのプッシュ。
- ECSへのコンテナデプロイ
ECSクラスタの作成とイメージのデプロイ
ECSは「サービス」と「タスク」の2つの設定によってコンテナをデプロイします。
サービスとタスク
タスクはKubernetesでいうところのPodです。複数のコンテナによって構成され、コンテナの集合によって一つの機能を提供するものです。nginxコンテナとphp(-fpm)コンテナによってWebサービスを提供するフロントエンド、などという感じです。
サービスはKubernetesで言うところのSvcやReplicaSetなどが混ざったような機能です。ALBのターゲットグループを管理したり、Autoscaleしてくれたり様々な役割があります。
クラスタ作成
まずは前回レジストリへプッシュしたコンテナイメージをECSにデプロイしてみます。
コンソールからECSのページへ移動し、「クラスター作成」ボタンを押しましょう。
- fargateを利用したほうが手っ取り早いので、クラスタテンプレートは「ネットワーキングのみ」を利用してECSクラスタを構築して下さい。
- VPCは新規作成にして空いているCIDR/Subnetを割り当てて上げて下さい。
それだけ設定するとあとは勝手にクラスタが構築されていきます。
しばらく待ちましょう。
※今回は入門用に適当なネットワークでやりますが、プロダクションでやるときはしっかり設計して作りましょう。可能ならばCloudFormationでクラスタ全体を管理できると尚良いかと思います。
タスク定義
ECSクラスタが出来上がったら、実行するコンテナに関する情報をタスク定義にて設定していきます。
- 起動タイプ:fargate
- コンテナ名:適当に
- ネットワークモード:awsvpc
- タスクの実行IAMロール:自動生成の「ecsTaskExecutionRole」でOK。
- タスクサイズ:適当に指定。
- コンテナの定義
- コンテナ名は適当
- イメージは 「[registry-url]/[namespace]/[image]:[tag]」 の書式で書くこと。
- ポートマッピングは必要に応じて書く。コンテナとノードのマッピングなので、外部との通信部分はサービスで設定することとなる。
細かいオプション類はここでは割愛します。ログ出力部分はやりやすい形にして、ulimitのnofileあたりはアプリケーションによっては変更しておいたほうがいいかもですね。
※参考
AWS ECS service: The ulimit settings to pass to the container.
サービスの設定
タスクを定義したら、それを用いてサービスの設定をしていきます。
- 起動タイプ: fargate
- タスク定義: さっき設定した定義を指定
- クラスタ:どのクラスタにデプロイするか
- サービス名:任意
- サービスタイプ:Replica
- Replica:指定された数のタスクがクラスタ内の各ホストで動作
- Daemon:クラスタに所属する全ノードで動作(Fargateだとどうなるのかはしらない
- タスクの数:コンテナの同時実行数(Webサーバの並列度みたいな
- 最小/最大ヘルス率:50/200のまま。デプロイ時に減らしてから増やすか、増やしてから減らすかなどの関係性。
- タスクの配置:クラスタ内のノードにどのようにタスクを配置するのかというルール。AZがつくものはAZを跨いでタスク分散するように努める。
- pinback: 最小限に抑える
- random: ランダム配置
- spread: 均等配置
続いてネットワーク構成です。
事前にALBを作成しておいて下さい。(ターゲットの種類は「ip」で。ターゲットは後で追加するのでカラでOK。作成先のVPCに注意!
- ネットワーク構成
- VPC/Subnet:デプロイ先のVPCと利用可能なサブネットを選択します。
- セキュリティグループ:デフォルトだと80番ポートが空いたものになる。
- パブリックIPの自動割当:ALBとか使うなら不要。
- ロードバランサー
- ALBを選択し、事前に作成しておいたELB名を選択。
- コンテナの選択:
- 該当のコンテナをプルダウンメニューから選んで「ELBへの追加」ボタンを押下。
- ターゲットグループ名を選ぶとある程度ルールに沿って設定してくれます。 or パスパターンの設定などしたい場合は新たに足しても良いです。
AutoScalingの設定はここでは省きます。
これが済むと自動的にサービスが作成されていき、完了後にタスクが起動するとALB経由でコンテナにアクセス出来ます。
タスクの実行
サービスを追加するとタスクは自動的に生成されてきます。ので実際はやることないです。
しばらく待って、生成されたタスクが正常にRunningになっていればブラウザから該当のELBのURLへアクセスしてみましょう。
もししばらく待ってタスクタブを開いてもタスクが見当たらないときは生成に失敗してSTOPPEDステータスになってしまっているかもしれません。ずっとPENDINGのように見えて、実はSTOPPED担ったタスクが裏でいっぱい眠っているなんてこともあります。
そんなときはSTOPPEDとなってしまったタスクの画面へ遷移してLogsタブを開くと該当のコンテナで出力されたログ情報が得られます。ログを見ながらトラブルシューティングして、ちゃんとRUNNINGになってブラウザからページが表示できればOKでしょう。
(セキュリティグループの設定間違っていると、ECS動いてても表示できないので気をつけて下さい。
CodePipeline
さて、ようやく今回の主役、CodePipelineです。
毎度のことながら細かい説明は公式に譲りますが、所謂リリースプロセスの自動化ツール、CI/CDツール、みたいなものです。
今回の例で言えば、ソースコードのコミットをトリガーとして
- Build : ソースコードコンパイル、Dockerfileを利用したコンテナイメージの生成とプッシュ
- Staging : コンテナイメージを各ステージへ配布
こんなようなプロセスを自動的に踏んでくれます。最初の方にも書きましたけどね。
背後で動くビルド処理はCodeBuildを利用したり、Jenkinsなどの外部ツールと連携させることも可能です。
パイプラインの構築
ここから実際にパイプライン作っていきます。
とはいえ全部AWS上なら今までの準備が済んでいれば本当に選択するだけで終わります。
- パイプライン名: 任意
- ソースプロバイダ: CodeCommit
- リポジトリ名: 選択
- ブランチ名: 選択
- ビルドプロバイダ: CodeBuild
- プロジェクト名: 選択
- デプロイプロバイダ: ECS
- クラスタ名: 選択
- サービス名: 選択
- イメージのファイル名: buildspec.ymlで一番したに書いたjsonファイル名(前回記事だとartifacts.json)
以上、CodeCommitとCodeBuild、ECSクラスタとそのタスク・サービスを事前に作成してあれば選択するだけで終わりです。
これでCodeCommitにコードをPushすると、それを検知してCodebuildが動き、ECSへコンテナが配布されるまで勝手に動きます。
ちょくちょくエラー起こすところ
- buildspecのコンテナ名と、ECSで定義するタスクのコンテナ名が異なる。
- CodeCommitのリポジトリ名や、ECRのレジストリ名画間違ってる。
- buildspecのアーティファクト名とCodepipelineで設定したアーティファクト名が異なる。
- CodeBuilidで設定した環境変数にスペースが混ざってる。
おわりに
とりあえずまずは使ってみて感覚を掴むというところから。ECSへコンテナをデプロイするまでのパイプライン構築方法でした。
Commitすると自動的にコンテナが新しくなってるのはちょっとおもしろいですね。
プロダクションに乗せるまでにはまだまだ詳細練らないといけませんが載せられそうな物があればまた後日。