ECS+ALBの動的ポートマッピングでダウンタイムのないデプロイを試してみた

AWS
Docker

はじめに

少し前に個人で作成しているWebサービスのインフラにDockerを使い始めました。 複数台のサーバーは1つに集約してその中で複数のサービスを動かしています。

Dockerを使ったインフラ構築は前よりもだいぶ楽になった気がしますが、デプロイ方法はまだまだ確立されておらず、docker-compose build とdocker-compose up -d コマンドで配置をしていますがこの方法だと同じサーバー上で新しいバージョンを配置する際にポートがかぶるという問題から一度旧サービスを停止しなければいけません。

数十秒程度かつユーザーも少ないのでこの方法で現状は満足していますが、実際のサービスに使うには適していません。 今回はダウンタイムなしで新しいバージョンのサービスを配置する方法をECS+ALBを使って試してみました。

 

デプロイのイメージ

現状の配置方法はポートかぶりの問題から旧コンテナの停止→新コンテナの起動をとらなければいけないためダウンタイムが発生してしまいます。

このポートかぶり問題を解決し、新コンテナ起動→新コンテナに切り替え→旧コンテナの停止という手順が取れればダウンタイムがなくなります。

ポートかぶり問題を解決するための1つの手段としては新しいインスタンスを立ち上げ、そちらで新サービスをデプロイ、ロードバランサーで向き先を旧から新に切り替える方法が考えらます。

 

AWS Networking

 

 

ただ、この方法は1インスタンス1サービスで動かしているので、あまりDocker を使っている感じがしません。

可能であれば、同じインスタンス内でポートかぶり問題がなんとかできるとベストです。

そこでALB(Application Load Balancer)の動的ポートマッピングという機能に注目しました。

これはdocker-compose.yml内で

ports:
- 10080:80

といったようにホストの10080番ポートにクライアントの80番をわり当てる部分の10080番ポート部分が動的に割りあてられるイメージです。確かにこの方法であればポートかぶりが起こらないずに1インスタンス内に複数のコンテナを立ち上げることが可能です。

さてこの方法を使って今回はダウンタイムのないデプロイ方法を試してみます。

最終イメージは次の通りです。

AWS

 

 

ECS の準備 リポジトリ作成

まずはECS でDockerイメージを格納するためのリポジトリを作成します。

スクリーンショット_2017-08-15_19_51_34

スクリーンショット_2017-08-15_19_50_44

あらかじめログイン情報はとっておく必要があるのでドキュメントページ等を参考に1 番目のコマンドが実行できるようにする必要があります。特にハマったことはなかったので調べればすぐ出てきます。

基本的にはローカル等で作ったイメージに対して、ECS上で扱えるようにタグ付けをしてプッシュするだけです。

 

タスク定義

次にタスクの定義です。

今回実験に使ったアプリでは1Nginx、1アプリ、Nginxとアプリでデータを共有するためのデータボリュームコンテナ1つです。なおDBはRDSを使っているためここでは出てきません。

コンテナを追加していきます。
スクリーンショット_2017-08-15_20_20_04

 

特記すべき点はホスト側のポートの設定に0を指定することです。
説明書きにも書いてありますがこうすることでホスト側の任意のポートが80番に割り振られるようになります。
スクリーンショット_2017-08-15_20_20_30

 

 

また、個人的にハマった点がデータストレージコンテナは起動しておく必要がないのですが、タスク実行時にデータストレージコンテナが起動してないということでコンテナの作成を繰り返していました。
これを防ぐためにはコンテナ詳細の基本のチェックを外しておくことで、起動していなくてもOKな状態になります。

 

ALB の作成

クラスタ、サービス定義の前にALBを作成しておきます。

一旦EC2の画面に戻って定義していきます。

スクリーンショット_2017-08-15_20_54_33
スクリーンショット 2017-08-15 20.54.49

詳細は設定は省きますが、個人的にハマった点はALBからNginxを動かすインスタンスに対してポートを解放するのを忘れていて、繋がらないぞ となった点です。
動的ポートなのでALBからのアクセスは全ポートアクセス解放しています。

 

クラスタの作成

さて、ECSの画面に戻ってクラスタ、サービスの定義をしていきます。

クラスタを作成します。

スクリーンショット 2017-08-15 20.42.16

 

実験なのでクラスタにスポットインスタンスを使ってみたのですが、t2シリーズは選択できないものの、m3mediumで1時間あたり0.0125ドルととても良心的な価格ですね。
あんまり大きなサイズでなければ3ヶ月の間に料金の変動もないので、いつかスポットインスタンスを使って安くサービスが稼働できそうな気がしてきました。
スクリーンショット 2017-08-15 20.45.47

次のサービスの作成です。

 

スクリーンショット_2017-08-15_21_17_14

 

スクリーンショット 2017-08-15 21.17.44

 

ELB名のところに先ほど作成したALBを指定、負荷分散用のコンテナにnginxを設定し、ELBへの追加をします。

タスク数を1に設定し、しばらく待つとコンテナが作成され、サービスにアクセスできるようになりました。

ロードバランサーのターゲットグループを見るとホスト側の32783ポートがNginxの80番ポートに自動的に追加されていることが見ることができます。素晴らしいですね!

 

スクリーンショット 2017-08-15 21.25.41

 

デプロイを試す

さて、新しいバージョンのリリースを試してみます。
わかりやすいように旧バージョンには左上にOLDと表示させておきます。
スクリーンショット 2017-08-15 21.31.31

現在は古いバージョンが動いている状態で新たにサービスを追加します。

 

スクリーンショット 2017-08-15 22.06.27

スクリーンショット 2017-08-15 22.07.31

 

このまましばらくすると新しいバージョンのコンテナが作成されます。
ロードバランサーでヘルスチェックが通らないと新しい方には割り振られないため、新バージョンが立ち上がりステータス200が帰ってくるまでの間に新しい方に割り振られることはありません。

スクリーンショット 2017-08-15 22.09.13

しばらくしてから何度かリロードすると新しい方に割り振られていることがわかります。

スクリーンショット 2017-08-15 22.11.42

最後に古いコンテナのタスク数を 0にして、古いタスクを止めれば作業完了です。

今回は手動にて試しましたが、コマンドでも実行できるはずなのでこの作業が自動化出来れば実際のサービスにも使えそうですね。

コメントはまだありません

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

スクリーンショット 2016-01-09 20.02.08
Ruby on Rails
Capistrano3を利用してBitbucketプライベートリポジトリにあるRailsアプリをデプロイしてみた

Railsアプリを配置する際、毎回手作業で頑張って配置してきたが、そろそろ自動デプロイを・・・ とい …

MarketTreeViewのイメージ
Ruby on Rails
[Eve Online 3rd party app] Market Tree Viewの実装 [EOPES制作記]

Eve Online 3rd party app のEOPES を作り始めてから約3ヶ月が経ちました …

-2015-03-22-19.51.35-e1427022407466
Ruby on Rails
EveOnline Authenticated Crestを使ったEOPES をリリースしました!!

前回の投稿から時間が立ってしまった。 というのもここ数ヶ月 Crestを作ったEve Online …