もういつ用意したか覚えてませんが、簡易的なKubernetes環境としてWSL+kindを使えるようにしていました。昔はminikubeを使ってましたが軽いという噂を聞いてkindに切り替えた記憶があります。kindのデフォルト設定はシングルノードなので、ノードの理解がイマイチしにくいという欠点がありました。そこで今回はシングルノードのkindクラスタを一度削除し、マルチノード環境を作り直してみます。
マルチノードクラスタの構築
まずはシングルクラスタを削除します。
# 既存クラスタを削除
$ kind delete cluster
# マルチクラスタ用のyamlを作成して作成
$ vim k8s/kind-limited-config.yaml
# ------------------------------
# k8s/kind-limited-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
# ------------------------------
# マルチノードクラスタを作成
$ kind create cluster --config k8s/kind-limited-config.yaml
# 確認
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready control-plane 3m20s v1.32.2
kind-worker Ready <none> 3m8s v1.32.2
kind-worker2 Ready <none> 3m8s v1.32.2
これでマルチノードクラスタを作成できたので、Nginx Podで通信試験してみます。まずはDeploymentで3つのPodを作成し、各ノードに割り当てられたことを確認します。
$ vim k8s/deployment-nginx.yaml
# ------------------------------
# k8s/deployment-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
# ------------------------------
$ kubectl apply -f k8s/deployment-nginx.yaml
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-5656fbf665-gf2fb 0/1 ContainerCreating 0 5s
pod/nginx-deployment-5656fbf665-r2v7m 0/1 ContainerCreating 0 5s
pod/nginx-deployment-5656fbf665-xzl7x 0/1 ContainerCreating 0 5s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6m1s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 0/3 3 0 5s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5656fbf665 3 3 0 5s
各Podがどのノードに割り当てられたか確認します。想定通りworkerとworker2に分散されました。
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-5656fbf665-gf2fb 1/1 Running 0 35m 10.244.1.2 kind-worker <none> <none>
nginx-deployment-5656fbf665-r2v7m 1/1 Running 0 35m 10.244.2.3 kind-worker2 <none> <none>
nginx-deployment-5656fbf665-xzl7x 1/1 Running 0 35m 10.244.2.2 kind-worker2 <none> <none>
次にServiceを作成します。
$ vim k8s/service-nginx.yaml
# ------------------------------
# k8s/service-nginx.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
selector:
app: nginx
ports:
- port: 80 # Service自体のポート
targetPort: 80 # Pod内nginxのポート
nodePort: 30080 # Node上で公開されるポート
type: NodePort
# ------------------------------
yamlからServiceを作成。
$ kubectl apply -f k8s/service-nginx.yaml
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 43m
nginx-nodeport NodePort 10.96.30.16 <none> 80:30080/TCP 9s
これで外部から30080ポートでNginxにアクセスできるはず。
$ curl http://localhost:30080
curl: (7) Failed to connect to localhost port 30080 after 0 ms: Couldn't connect to server
出来ませんでした。
KindでNodePortを使うにはポートマッピング設定を追加する工夫が必要なためですね。公式ではkind – ConfigurationでextraPortMappings
を使ってDockerで永続的にポートマッピングをする例が提示されていました。今回は簡易試験なのでkubectlで一時的に解消してみようと思います。
まずはポート探し。8080が使われていないことを確認します。
$ netstat -tlnp | grep 8080
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
# nginx-nodeportの80番ポートを8080にフォーワード
$ kubectl port-forward service/nginx-nodeport 8080:80 &
# 8080がLISTENになったことを確認
$ netstat -tlnp | grep 8080
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN 61671/kubectl
tcp6 0 0 ::1:8080 :::* LISTEN 61671/kubectl
改めて通信試験してみましょう。
# 改めて接続試験
$ curl http://localhost:8080
Handling connection for 8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
無事成功しましたね。最後にjobをkillして検証完了です。
# ポートフォワード解除対応
$ jobs
[1]+ Running kubectl port-forward service/nginx-nodeport 8080:80 &
# フォーワードを解除
$ kill %1
補足: リソース制限の失敗
一般的な環境では複数のVMを用意し、それらを各ノードとするため利用可能なリソースはVMのリソースに依存します。 しかし、kindクラスタはDockerコンテナ上に構築されるため、コンテナが稼働するホストのリソースに依存します。
# 現在の設定値
$ docker stats --no-stream --format "table {.Name}\t{.CPUPerc}\t{.MemUsage}\t{.MemPerc}"
NAME CPU % MEM USAGE / LIMIT MEM %
kind-control-plane 11.39% 604.5MiB / 7.719GiB 7.65%
kind-worker2 0.78% 152.1MiB / 7.719GiB 1.92%
kind-worker 0.59% 148.1MiB / 7.719GiB 1.87%
とはいえ、無尽蔵にリソースを食われるわけにはいかないので、kindクラスタのリソース制限を試みました。
# コンテナにリソース制限
$ docker update --cpus="2.0" --memory="3g" --memory-swap="3g" kind-control-plane
$ docker update --cpus="3.0" --memory="2g" --memory-swap="2g" kind-worker
$ docker update --cpus="3.0" --memory="2g" --memory-swap="2g" kind-worker2
# Dockerコンテナの設定値が変更されたことを確認
$ docker stats --no-stream --format "table {.Name}\t{.CPUPerc}\t{.MemUsage}\t{.MemPerc}"
NAME CPU % MEM USAGE / LIMIT MEM %
kind-control-plane 7.32% 607.5MiB / 3GiB 19.78%
kind-worker2 0.72% 152.6MiB / 2GiB 7.45%
kind-worker 1.26% 150.8MiB / 2GiB 7.37%
# コンテナ再起動
$ docker restart kind-control-plane kind-worker kind-worker2
# クラスタのリソース制限を確認
$ kubectl describe node kind-control-plane | grep -A 5 "Capacity"
Capacity:
cpu: 8
ephemeral-storage: 1055762868Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 8093720Ki
結果、失敗。コンテナは制限できていますがkindクラスタ側ではホストのリソースをそのまま参照してしまっているようです。