こんにちは。@jedipunkz です。
今回は Hashicorp の Consul クラスタを Kubernetes 上で稼働させる方法について調べてみました。
Hashicorp Consul はサービスディスカバリが行えるソフトウェアで、私も以前居た職場で利用していました。アプリケーション間で互いに接続先を確認し合う事が出来ます。以前構築した Consul クラスタはインスタンス上に直に起動していたのですが最近だとどうやってデプロイするのか興味を持ち Kubernetes 上にデプロイする方法を調べた所 Helm を使って簡単にデプロイ出来る事が分かりました。
また今回は minikube を使って複数のレプリカを起動するようにしていますが、Helm Chart を用いると Kubernetes のノード毎に Consul Pod が1つずつ起動するようになっていて、ノードの障害を考慮した可用性という点でも優れているなぁと感じました。また Kubernetes の Pod ですのでプロセスが落ちた際に即座に再起動が行われるという点でも優れています。勿論 Consul クラスタの Leader が落ちた場合には Leader Election (リーダ昇格のための選挙) が行われ、直ちに隣接した Kubernetes ノード上の Consul Pod がリーダーに昇格します。といった意味でも Kubernetes 上に Consul をデプロイするという考えは優れているのではないでしょうか。
Requirements
下記のソフトウェアが事前に必要です。この手順では予めこれらがインストールされていることとして記していきます。
- minikube
- kubectl
- helm
Consul クラスタ起動までの手順
早速ですが手順を記していきます。
Hashicorp の Github にて Consul の Helm Chart が公開されています。helm search
しても出てきますが、今回は Github のものを用いました。
git clone https://github.com/hashicorp/consul-helm.git
cd consul-helm
git checkout v0.7.0
次にコンフィギュレーションを記した yaml ファイルを生成 (修正) します。本来、Kubernetes のノード毎に1つずつの Pod が起動するようになっていて、逆に言うと1ノードに複数の Consul Pod は起動しません。今回は手元も端末の minikube でお手軽に試せるようレポジトリ上のファイル value.yml を下記のように修正加えました。この手順は Github の Issue https://github.com/hashicorp/consul-helm/issues/13 で記されています。
もちろん、minikube ではなく Kubernetes 環境を利用できる方はこの手順は飛ばして構いません。
# 下記はコメントアウト
# affinity: |
# podAntiAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# - labelSelector:
# matchLabels:
# app: {{ template "consul.name" . }}
# release: "{{ .Release.Name }}"
# component: server
# topologyKey: kubernetes.io/hostname
# minikube 用に下記を有効にする
affinity: |
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchExpressions:
- key: component
operator: In
values:
- "{{ .Release.Name }}-{{ .Values.Component }}"
kubectl, helm コマンドが minikube を向くように下記のようにコマンドを実行します。
kubectl config use-context minikube
helm init
Consul クラスタを helm を用いてデプロイします。下記のコマンドでデプロイが一気に完了します。
helm install --name consul .
暫くすると下記の通り Pods, Services の状態が確認出来ると思います。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
consul-q5s62 1/1 Running 0 21m
consul-server-0 1/1 Running 0 21m
consul-server-1 1/1 Running 0 21m
consul-server-2 1/1 Running 0 21m
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
consul-dns ClusterIP 10.108.171.87 <none> 53/TCP,53/UDP 22m
consul-server ClusterIP None <none> 8500/TCP,8301/TCP,8301/UDP,8302/TCP,8302/UDP,8300/TCP,8600/TCP,8600/UDP 22m
consul-ui ClusterIP 10.107.40.197 <none> 80/TCP 22m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 48m
起動した Consul クラスタの状態を確認してみます。consul members
コマンドを各 Pod 上で実行すると、クラスタに Join しているノード (この場合 Consul Pod) の状態を一覧表示出来ます。minikube ノードにも consul-agent が起動していることが確認出来ます。
$ for i in {0..2}; do kubectl exec consul-server-$i -- sh -c 'consul members'; done
Node Address Status Type Build Protocol DC Segment
consul-server-0 172.17.0.6:8301 alive server 1.4.4 2 dc1 <all>
consul-server-1 172.17.0.7:8301 alive server 1.4.4 2 dc1 <all>
consul-server-2 172.17.0.8:8301 alive server 1.4.4 2 dc1 <all>
minikube 172.17.0.5:8301 alive client 1.4.4 2 dc1 <default>
Node Address Status Type Build Protocol DC Segment
consul-server-0 172.17.0.6:8301 alive server 1.4.4 2 dc1 <all>
consul-server-1 172.17.0.7:8301 alive server 1.4.4 2 dc1 <all>
consul-server-2 172.17.0.8:8301 alive server 1.4.4 2 dc1 <all>
minikube 172.17.0.5:8301 alive client 1.4.4 2 dc1 <default>
Node Address Status Type Build Protocol DC Segment
consul-server-0 172.17.0.6:8301 alive server 1.4.4 2 dc1 <all>
consul-server-1 172.17.0.7:8301 alive server 1.4.4 2 dc1 <all>
consul-server-2 172.17.0.8:8301 alive server 1.4.4 2 dc1 <all>
minikube 172.17.0.5:8301 alive client 1.4.4 2 dc1 <default>
次に Consul のリーダーがどの Pod なのかを確認してみます。下記の結果から consule-server-2 という Pod がリーダーだと分かります。
$ for i in {0..2}; do kubectl exec consul-server-$i -- sh -c 'consul info | grep leader'; done
leader = false
leader_addr = 172.17.0.8:8300
leader = false
leader_addr = 172.17.0.8:8300
leader = true
leader_addr = 172.17.0.8:8300
この状態で consule-server-2 Pod を削除してみます。削除しても Kubernetes Pod なので即座に再作成・起動がされるのですが、Consul 的には「リーダーが落ちた」という判断が下り、リーダー選出のための選挙が consule-server-0, consul-server-1 の 2 Pods で行われます。
$ kubectl delete pod consul-server-2
下記が consul-server-0 で確認した Consul プロセスのログになります。“consul: New leader elected: consul-server-1” と表示され新たに consul-server-1 Pod がリーダーに選出されたことが確認出来ます。
$ kubectl logs consul-server-0
<snip>
2019/04/26 07:31:22 [INFO] serf: EventMemberLeave: consul-server-2.dc1 172.17.0.8
2019/04/26 07:31:22 [INFO] consul: Handled member-leave event for server "consul-server-2.dc1" in area "wan"
2019/04/26 07:31:26 [INFO] serf: EventMemberLeave: consul-server-2 172.17.0.8
2019/04/26 07:31:26 [INFO] consul: Removing LAN server consul-server-2 (Addr: tcp/172.17.0.8:8300) (DC: dc1)
2019/04/26 07:31:29 [WARN] raft: Rejecting vote request from 172.17.0.7:8300 since we have a leader: 172.17.0.8:8300
2019/04/26 07:31:33 [WARN] raft: Heartbeat timeout from "172.17.0.8:8300" reached, starting election
2019/04/26 07:31:33 [INFO] raft: Node at 172.17.0.6:8300 [Candidate] entering Candidate state in term 3
2019/04/26 07:31:36 [ERR] agent: Coordinate update error: No cluster leader
2019/04/26 07:31:36 [INFO] serf: EventMemberJoin: consul-server-2 172.17.0.8
2019/04/26 07:31:36 [INFO] consul: Adding LAN server consul-server-2 (Addr: tcp/172.17.0.8:8300) (DC: dc1)
2019/04/26 07:31:37 [INFO] serf: EventMemberJoin: consul-server-2.dc1 172.17.0.8
2019/04/26 07:31:37 [INFO] consul: Handled member-join event for server "consul-server-2.dc1" in area "wan"
2019/04/26 07:31:37 [INFO] raft: Node at 172.17.0.6:8300 [Follower] entering Follower state (Leader: "")
2019/04/26 07:31:37 [INFO] consul: New leader elected: consul-server-1
<snip>
先程と同様にリーダーの確認を下記の通り行います。
$ for i in {0..2}; do kubectl exec consul-server-$i -- sh -c 'consul info | grep leader'; done
leader = false
leader_addr = 172.17.0.7:8300
leader = true
leader_addr = 172.17.0.7:8300
leader = false
leader_addr = 172.17.0.7:8300
コンフィギュレーションの例
今回レポジトリ上にある value.yaml を一部修正しただけでしたが、yaml 内にある各設定値を変更することで構成を変更することが可能です。各設定値については下記のサイトに詳細が記されています。公式のドキュメントになります。
https://www.consul.io/docs/platform/k8s/helm.html
設定値の例をあげると…
- server - replicas
replicas はレプリカ数。Consul クラスタの Pod 数になります。但し冒頭で述べたとおり Consul Helm Chat は各 Kubernetes ノードに対して 1 Pod の原則で起動しますので、その点認識しておく必要があります。
- server - bootstrapExpect
何台の Consul クラスタ Pod が起動していればリーダー選出を行うか?の設定値です。
- server - storageClass
デフォルトはローカルディスクです。Ceph 等を選択することも可能です。
- client - grpc
agent が gRPC リスナを持つかどうかです。true に設定すると gRPC リスナが 8502 番ポートで起動します。
まとめ
Kubernetes 上で Helm を使って簡単に Consul クラスタをデプロイ出来る事が分かりました。また運用を考慮された設計になっていることも確認出来ました。ロードバランサを用いずとも、アプリケーション間、API 間で互いに正常に可動している先をディスカバリし接続し合えるという点で Consul はとても有用なので Kubernetes を用いたアーキテクチャにも適しているのではないでしょうか。