TerraformとAnsibleを使ってGCPでConsulクラスタと構築したときの知見

Posted by

はじめまして!インフラチームでインターンをしていました 松田です。
TerraformとAnsibleを用いてGCP上でConsulクラスタの集約環境構築を行いましたので、利用した構成や学んだことについて触れていきます。

経緯

AnsibleやTerraformを使ってインフラ環境を自動化してるところで実践的な技術を学びたい!と思いインターンに応募し、Consulクラスタの集約環境構築の自動化をやらせていただきました。
これまでは、複数の環境でそれぞれConsulクラスタを稼働させており、情報を確認するためにはそれぞれのサーバから取得する必要があり、確認作業が冗長になっていました。
しかし、Consulでは別環境の情報も取得できるためこの機能を利用して、複数環境の情報を集約することにしました。

構成

今回の環境はGCP上で構築します。構成図は下図のようになります。
同様の役割を担うConsul統合ノードを3台設置し、外部からのアクセスはGCPのロードバランサを用いて分散します。別環境のConsul ServerとしてDatacenterを変えたものを1台設置しました。

Terraformで利用する各リソースのイメージは以下のようになります。
Consul ServerはプライベートIPのみ割り当てるので、NAT用のインスタンスをgoogle_compute_routeで設置します。

Ansible Galaxyを意識したrole構成

実装する上でAnsible Galaxyなどで読み込んで別のリポジトリでも使いまわしやすい様にroleを設計しました。
geerlingguy.nginxのようにtemplatesを親リポジトリに設置してroleに読み込ませる形にしたかったのですが、playbookをplaybook/に配置すると同じ階層のtemplates/を探しに行ってくれないのでDatadog.datadogにならい、変数から読み込むようにしました。
例えば、Consulのroleでは以下のようにcopyモジュールで直接変数から書き込むようにし、defaults/main.ymlには最低限の設定だけ記載するようにしました。

- name: Copy global setting file
  copy:
  content: |
    {{ consul_config | to_nice_json }}
  dest: /etc/consul/consul.json
  notify: Reload consul

Consulはjson形式で記述できるのでto_nice_jsonでサクッといけるんですが、NginxだとそうもいかないのでKey/Value形式で書き込むようにしました。
これについてはgeerlingguy.nginxと同じですね。

{% for key, value in nginx_base_config.items() -%}
{{ key }} {{ value }};
{% endfor %}

Consulのセキュリティポリシーについて

ACLのtokenを持っていないユーザの操作はすべて受け付けないようにしたかったんですが、ACLで制限がかけられないAPIが5つあったので諦めました。
制限できないAPIは以下になります。(ACL HTTP API)

  • /acl/bootstrap acl_master_tokenを設定していない場合に発行する
  • /acl/info/:uuid uuidのacl token情報を返す
  • /acl/replication ACL replication processの状態を返す
  • /catalog/datacenters 把握しているdatacenterリストを返す
  • /cordinate/datacenters WANに所属しているConsulサーバをdatacenter毎に出す

/acl/bootstrapはacl_master_tokenを設定しておけば問題無いんですが、他に関しては攻撃できそうな情報が漏れてしまうので、ACLとは別にNginxでIP制限をかけることにしました。

WebUIに関してもユーザ認証機能を持たないのでなんでだろうなと調べたところ、以下の2つのissueにそれっぽい議論がありました。
https://github.com/hashicorp/consul/issues/1076
https://github.com/hashicorp/consul/issues/1720
ローカル環境で動かすことを前提にしているので、ユーザ認証機能を追加する予定はないとのことで、やりたければNginxとかoauth2_proxyを使ってねということでした。

google_compute_instance_groupだと複数zoneが使えない

複数zoneにインスタンスを配置して冗長化したかったのですが、google_compute_instance_groupだと単一zoneしか設定できませんでした。
なのでTerraformを用いてLBを構築して複数zone化する場合は、zoneの数だけインスタンスグループを設定してバックエンドサービスに追加することになりそうです。

また、複数リージョンにまたがったインスタンスグループもTerraformでは実装されていませんでしたが、2017/09/06に該当機能がマージされていたので、次のリリース(v0.1.4?)から組み込まれるようです。
https://github.com/terraform-providers/terraform-provider-google/issues/45

interpolation syntaxについて

interpolation syntaxをしっかり使うと必要なリソースが作られていなくてこけるといった問題を減らせます。
例えば、google_compute_instanceでsubnetwork = "${lookup(var.instance_info, "subnetwork")}"という感じでサブネットワークを設定するとサブネットワークが先に作られていなくてもう一回実行する必要がある場合があります。
interpolation syntaxを使い、subnetwork = "${google_compute_subnetwork.test_subnet.name}"のようにリソースから名前を引くと、事前にサブネットワークが作成されます。

まとめ

TerraformとAnsibleを使って、GCP上でConsulクラスタを構築した際の知見について紹介しました。
Consul単体だと認証がかけられないのがネックですが、Nginxなどの他のツールを組み合わせれば容易に解決できます。
TerraformでGCP環境を構築すると複雑な部分もありますが、どちらもドキュメントに説明や例がしっかり記載されています。

今回、再利用性の高い構成・コードを書くように特に意識しました。
ゲームのインフラ側の構成は、似たような構成別のゲームでも使っていることは多々あります。
その際、ゲーム毎にコードを書くのではなく、基本の部分は別リポジトリで管理できるようにしました。
これにより、メンテナンス量を減らすことができ、今後のコスト削減につながると思います。

インターンを検討している人に

期間中にはアドテクやメディアなどのCyberAgentの別部署の方とのランチも設定していただき、色々な方のお話を聞くことができました。
また、私は香川から渋谷に来て参加しましたが、交通費をすべて出していただき、1ヶ月間の宿泊費もすべて出していただけたので遠方の方もおすすめです!
エンジニアJOBはほぼ年間募集しており、週の勤務日数も相談できるため、近隣の方は学業の合間に参加するのもありかもしれません。
遠方の方はリモート参加はできないため、1ヶ月ほど予定を空けてからまとめて参加することになると思います。
CyberAgentがどんなところか知りたい方、集中して取り組みたい方おすすめです。