Carpe Diem

備忘録

ECS + awsvpc + Consul でService Discovery

概要

ECS + awsvpc + Consul でService Discoveryができるようにします。
最終的にConsulにDNS問い合わせすることでECSのコンテナのIPを知ることができるようにします。

環境

  • Consul 1.1.0

Task Definition

結論を先にいうと以下のようになります。

[
  {
    "name": "consul-agent",
    "image": "consul:1.1.0",
    "memoryReservation": 128,
    "network_mode": "awsvpc",
    "environment": [
      {
        "name": "CONSUL_LOCAL_CONFIG",
        "value": "{\"service\":{\"name\":\"api\",\"tags\":[\"development\"]}}"
      },
      {
        "name": "CONSUL_BIND_INTERFACE",
        "value": "eth0"
      }
    ],
    "command": [
      "agent",
      "-retry-join",
      "consul-1a-01.example.io",
      "-retry-join",
      "consul-1c-01.example.io",
      "-retry-join",
      "consul-1d-01.example.io"
    ],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-region": "ap-northeast-1",
        "awslogs-group": "dev-consul"
      }
    }
  },
  {
    // APIコンテナ
  }
]

ポイントとしては以下で

  • network_modeをawsvpc
  • CONSUL_LOCAL_CONFIGでサービスのconfigを設定
  • CONSUL_BIND_INTERFACEでbindするNICeth0に指定

どうしてこういった設定になったかを下で説明します。

注意したところ・ハマったところ

Consulにconfigを渡すのにどうすればよいか

KubernetesであればConfigMapという仕組みがあるので、Podにconfigファイルをマウントする、といったことが簡単に行なえます。
しかしECSではあらかじめホストに置いておくなりしておかないといけないため、ファイルとしてconfigを渡すのが難しいです。

なのでConsulのconfigを極力コマンドライン引数や、CONSUL_LOCAL_CONFIGという環境変数で埋めるようにしました。

join対象を複数もたせる

configファイルではretry_joinフィールドは配列なので簡単に設定できますが、↑にあるようにコマンドライン引数の場合どうすればよいのかドキュメントを読んでも分かりませんでした。
いろいろ試した結果

$ consul agent -retry-join domainA -retry-join domainB -retry-join domainC

のように、-retry-joinを複数回書くことで実現することができました。
ログには

2018/05/31 06:58:20 [INFO] agent: (LAN) joining: [domainA domainB domainC]

のように表示されてjoin対象を複数ドメインにすることができてることが確認できます。

Multiple private IPv4 addresses found.

最初に起動したとき、

==> Multiple private IPv4 addresses found. Please configure one with 'bind' and/or 'advertise'.

というエラーが起きました。NICが複数あり、Private IPが複数あるとConsul agentは起動できないというエラーです。
どうやらawsvpcで起動したコンテナは内部でNICを複数持っているようです。
ECSのインスタンス上ではdocker0やらecs-bridgeが確認できるので、コンテナ側も通信のためそういったNICがあるのかもしれません。

しかしながらエラーメッセージのアドバイスどおりにbindでPrivate IPを固定すると言っても起動までそのIPを知ることはできません

そこでCONSUL_BIND_INTERFACEという環境変数でNICをeth0に指定することで対応しました。
対応後は以下のようにうまくいきました。

==> Found address '10.11.200.156' for interface 'eth0', setting bind option...
==> Starting Consul agent...
==> Consul agent running!

Consul ServerのDNS参照時のロードバランシングができない

AWSのLBはどれもUDPに対応していません
またRoute53はUDPに対応していますがヘルスチェッカーはVPC外なのでパブリックIPが必要です。
なので内部だけでConsul serverを立てている場合はNginxなどのProxyを自前で立てるしかありません。

ソース