kind 製 Kubernetes クラスタ内サービスへのアクセス (for macOS)
はじめに
kind を使うと複数 Node のクラスタが簡単に作成できて便利ですが、ホスト (macOS) 側から kind 上で提供されているサービスにどうアクセスするのか少し悩みました。
kind では docker コンテナ1つを1 Nodeとして Kubernetes クラスタを構築できますが、 Docker Desktop for Mac の制約 によりそもそもホストからコンテナへ IP で到達することができません。そのため kind で作成されたクラスタに type: NodePort
の Service をデプロイしたとてホスト側からその Service にアクセスできないのです。
kind の Issue にも関連がありそうな話題が。
やってみよう
kind で作ったクラスタ内のサービスにアクセスするいくつかの方法を試します。
環境は以下の通り。
- macOS Mojave 10.14.6
- Docker Desktop for Mac 2.1.0.7
- Kubernetes v1.15.3
- kind v0.5.1
まずは kind でクラスタを作成します。
❯ cat <<EOF | kind create cluster --config - kind: Cluster apiVersion: kind.sigs.k8s.io/v1alpha3 nodes: - role: control-plane - role: worker - role: worker - role: worker EOF ❯ export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" ❯ kubectl get nodes NAME STATUS ROLES AGE VERSION kind-control-plane Ready master 84s v1.15.3 kind-worker Ready <none> 50s v1.15.3 kind-worker2 Ready <none> 50s v1.15.3 kind-worker3 Ready <none> 50s v1.15.3
テスト用のアプリケーションをデプロイ
❯ cat <<EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 EOF ❯ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-68c7f5464c-8b58k 1/1 Running 0 2m16s 10.244.2.6 kind-worker2 <none> <none> nginx-68c7f5464c-m6tz5 1/1 Running 0 2m16s 10.244.1.5 kind-worker3 <none> <none> nginx-68c7f5464c-tcnhc 1/1 Running 0 2m16s 10.244.3.7 kind-worker <none> <none> ❯ for PODNAME in `kubectl get pods -o jsonpath='{.items[*].metadata.name}'`; do kubectl exec -it ${PODNAME} -- cp /etc/hostname /usr/share/nginx/html/index.html; done
HTTP でアクセスすると Pod 名が表示されます。
ホスト (macOS) から如何にしてクラスタ内で展開されるサービスを見ることができるでしょうか。思いついたものを試してみます。
kubectl port-forward
デバッグでよく使うやつ。ただし Pod に直接接続するため今回のようなケースではロードバランスされません。
❯ kubectl port-forward deploy/nginx 8080:80 ## 別ターミナルにて ❯ curl 127.0.0.1:8080 nginx-68c7f5464c-8b58k ❯ curl 127.0.0.1:8080 nginx-68c7f5464c-8b58k ❯ curl 127.0.0.1:8080 nginx-68c7f5464c-8b58k
kind の extraPortMappings
kindの公式ドキュメント にある extraPortMappings
を使うと hostPort
に割当が可能なようです。
❯ cat <<EOF | kind create cluster --config - kind: Cluster apiVersion: kind.sigs.k8s.io/v1alpha3 nodes: - role: control-plane - role: worker - role: worker - role: worker extraPortMappings: - containerPort: 30000 hostPort: 8080 EOF ## Deployment 作成とアプリの設定は前述したものと同様のコマンドを実行 ❯ cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Service metadata: name: nginx spec: type: NodePort ports: - name: "http-port" protocol: TCP port: 80 targetPort: 80 nodePort: 30000 selector: app: nginx EOF ❯ curl 0.0.0.0:8080 nginx-68c7f5464c-w4h7v ❯ curl 0.0.0.0:8080 nginx-68c7f5464c-rd6hs ❯ curl 0.0.0.0:8080 nginx-68c7f5464c-vl2r6
kind クラスタの config として3つ目の Node に extraPortMappings
を設定し立ち上げました。
ホストのポート (extraPortMappings[*].hostPort: 8080
) へのアクセスが Node のコンテナのポート (extraPortMappings[*].containerPort: 30000
) に転送されます。そのため nodePort: 30000
で指定した NodePort タイプの Service を用意することでロードバランスも可能です。
❯ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 81b82a6df489 kindest/node:v1.15.3 "/usr/local/bin/entr…" 8 minutes ago Up 8 minutes 53372/tcp, 127.0.0.1:53372->6443/tcp kind-control-plane 6f9bf2c0662b kindest/node:v1.15.3 "/usr/local/bin/entr…" 8 minutes ago Up 8 minutes kind-worker 0d5c32d65b11 kindest/node:v1.15.3 "/usr/local/bin/entr…" 8 minutes ago Up 8 minutes kind-worker2 8b955ef61737 kindest/node:v1.15.3 "/usr/local/bin/entr…" 8 minutes ago Up 8 minutes 0.0.0.0:8080->30000/tcp kind-worker3
実際に docker コンテナの状態を確認すると kind-worker3
の PORTS
の項目にて 0.0.0.0:8080->30000/tcp
されていることが分かります。
そのため以下のような記述をしても、1つ目の Node をデプロイした時点でホスト側の 8080 ポートは使用されてしまい、複数の Node に同じ extraPortMappings[*].hostPort
を割り当てることはできません。
❯ cat <<EOF | kind create cluster --config - kind: Cluster apiVersion: kind.sigs.k8s.io/v1alpha3 nodes: - role: control-plane - role: worker extraPortMappings: - containerPort: 30000 hostPort: 8080 - role: worker extraPortMappings: - containerPort: 30000 hostPort: 8080 - role: worker extraPortMappings: - containerPort: 30000 hostPort: 8080 EOF ## エラーが発生 ERRO[00:46:47] docker: Error response from daemon: driver failed programming external connectivity on endpoint kind-worker2 (17e1142d52d7f985f716cf49a2fc77a2af235bb8eca3918218b1d900f64831aa): Bind for 0.0.0.0:8080 failed: port is already allocated.
つまりは NodePort と言いつつも実際にユーザからのトラフィックを受け取るのは1つの Node のみということになります。
kubectl-open-svc-plugin
@superbrothers さんが作成された kubectl-open-svc-plugin というプラグインを試します。
kubectl port-forward
の ClusterIP 版だと理解しています。
これは、クラスタ外からアクセスできない ClusterIP タイプの Service にクラスタの外から簡単にアクセスするためのプラグインです。
kubectl plugin マネージャである krew
を使えば簡単に導入可能でした。
❯ kubectl krew install open-svc ❯ cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Service metadata: name: nginx spec: type: ClusterIP ports: - name: "http-port" protocol: "TCP" port: 8080 targetPort: 80 selector: app: nginx EOF ❯ curl http://127.0.0.1:8080/api/v1/namespaces/default/services/nginx:http-port/proxy/ nginx-68c7f5464c-m6tz5 ❯ curl http://127.0.0.1:8080/api/v1/namespaces/default/services/nginx:http-port/proxy/ nginx-68c7f5464c-tcnhc ❯ curl http://127.0.0.1:8080/api/v1/namespaces/default/services/nginx:http-port/proxy/ nginx-68c7f5464c-tcnhc ❯ curl http://127.0.0.1:8080/api/v1/namespaces/default/services/nginx:http-port/proxy/ nginx-68c7f5464c-8b58k
生成された URL にアクセスすると ClusterIP によるロードバランスが確認できます。
なんて便利!色々と使い所がありそうです。
おわり
kind や minikube など手元で試せる Kubernetes にデプロイしたサービスにアクセスする方法はどれもそのツールの実装方法に依存しており、なかなかに直感的でないように感じています。今回の話だと macOS ではなく Linux を使えばシンプルに解決するとも言えますが。
kubectl-open-svc-plugin
のような補助ツールがあることでマルチクラスタにおけるロードバランスを手軽に試すことができるのは非常に有り難いと感じました。
Go 力養成のため自分も何かプラグインを作ってみたいと思いました。