ECS+NLBでgRPCする

先日AWSの新しいLoadBalancer NLBが出ましたね.

aws.amazon.com

おそらく便利なので,色んな場面で使えるかとは思いますが,「HTTP以外もECSの組み合わせで動的ポートマッピングできるのでは?」というのが気になったので,使ってみます.

今回はgRPCをECS上に立てて,それを動的ポートマッピングさせて,NLBでバランシングしてみます. 実はNLBが発表された直後からこれは試していましたが,リリース直後ECS絡みにバグが合ったのか,しばらくECSのサービスにNLBが登録できなくなっていました.

最近解消されたらしく,正常に動作するようになった模様.

NLBを作る

何はともあれ,NLBを作ってみる.

f:id:h3poteto:20171001230712p:plain

今回は手元から試すので internet-facing を選択したけど,あまりおすすめはしません. できれば internal にしたい.理由は後述.

f:id:h3poteto:20171001230729p:plain

Listenersは適当なポートにしておく.

初回なのでTarget groupの作成を促される. ここで,ALB時代に作成したtarget groupは選択肢に上がってこなかった.APIをちゃんと見てないので,確かなことは言えないけれど,おそらくALB用のtarget groupとNLB用のtarget groupは区別されている.

f:id:h3poteto:20171001230749p:plain

また,Healthcheckについては,traffic port 指定にしておく. 特定のポートを開けてもいいけれど,どうせ動的ポートマッピングしてしまうので,マッピングされたポートを見つけ出して欲しい.

ECRの準備

あまり本題ではないのだが,Docker imageはECRに登録しておく.

f:id:h3poteto:20171001230832p:plain

ECSの準備

ECSクラスタ作成

まず,ECSのクラスタを作成する.

f:id:h3poteto:20171001230906p:plain

TaskDefinitionの作成

次に,先ほど作ったECRのimageを動かすための,TaskDefinitionを作る.

f:id:h3poteto:20171001230930p:plain

ContainerDefinitionを追加する際に,注意すべきなのは,動的ポートマッピング用に,コンテナポートは動かすDocker imageに応じたポートを書くが,Host portは空にしておく. こうすることで動的ポートマッピングしてくれるようになる.

f:id:h3poteto:20171001230954p:plain

ECSサービスの作成

先ほど作成したECSクラスタの下にサービスを作る. 作成したTaskDefinitionもここで紐付ける.

f:id:h3poteto:20171001231024p:plain

ここでNLBとの紐付けを行う.

f:id:h3poteto:20171001231037p:plain

f:id:h3poteto:20171001231104p:plain

Add to ELBを押す.

f:id:h3poteto:20171001231141p:plain

すると,先ほど作成したtarget groupが選択できる.

これでサービスを作成すると, target groupの画面に,新たなインスタンスが登録されている.

f:id:h3poteto:20171001231215p:plain

Portを見ると,ちゃんと動的ポートマッピングされている模様. Status: initialhealthy になれば問題ない.そのへんはHealthcheckの設定あたりを見直すと良い.

このへんのは後述するSecurityGroupとの兼ね合いも大事になってくる.

SecurityGroupについて

ここが今までのLBとは少し違う点だ.

ELB(Classic)やALBとは違い,NLBはLB自身にSecurityGroupの設定はできない. そのため internet-facing なNLBはSecurityGroupを挟まずに,外部からの接続を受け付ける.

ではアクセス制限はどうやってやるかというと,NLBの下にぶら下がるEC2インスタンスのSecurityGroupでコントロールする. そして,紹介記事にあるように,NLBはアクセス元のIPを維持したまEC2インスタンスに投げる.

HealthCheckはどうするの?

HealthCheckは,NLBが存在するVPCからのアクセスとなる. そのため,先ほどの traffic port でのHealthCheckを許可するためには,以下のような設定が必要になる.

f:id:h3poteto:20171001231257p:plain

IPは隠しているが,CustomでVPCのIPを入れ,VPCからの接続を全て許可している.

外部からのアクセスはどうなるの?

先程も書いたように,NLBはアクセス元のIPを維持したまま接続してくる.

つまり,例えばEC2上の8080ポートでサービスを提供し,NLBで外部に公開する場合,8080ポートに,全てのIPからのアクセスを開放してやらなければならない.

更に言うなら,今回の例のように動的ポートマッピングしたサービスを公開するのであれば,EC2上のどのポートにマッピングされているかは,SecurityGroupからは知るすべもないので,使われそうなポートを全て開放する必要がある

大げさに言うとこうなる(厳密には,動的ポートマッピングは30000付近の使われていないポートを使っているため,若い番号のポートを開放する必要はない).

f:id:h3poteto:20171001231413p:plain

しかしこんな過激なSecurityGroupをinternet-facingにしておくことはできないので,やっぱりinternalで作るなり,SecurityGroupでかなり厳密にIP制限するなり,あとはLBの前段にちゃんと何か置くと良いかもしれない.

ちなみにgRPCはできた

こんな設定をしたところ,見事ECS上にgRPCサーバが立ち上がり,手元からアクセスすることができた. 今までgRPCしたかったけど,やっぱdockerで運用するならECSはきついかなーkubernetes使うかなーとか思ってた.

無事ECSだけでできるようになって,やったね!