Kubernetes を手元で検証しようとすると CNI (Container Network Interface) プラグインの機能が障壁になることがある。 たとえば kind を使う場合はデフォルトで kindnetd という CNI プラグインがインストールされる。 しかし、この CNI プラグインは動作する上で最低限の機能しか有していない。 そのため、Ingress リソースや NetworkPolicy リソースはデフォルトでは利用できない。 もちろん、別途 Calico などの CNI プラグインをインストールすることも考えられるが、その分の手間はかかる。
そこで、今回は k3s という IoT 向けの軽量な Kubernetes ディストリビューションを k3d というツールでインストールして試してみる。 k3d (k3s) ではデフォルトで Flannel が CNI プラグインとしてインストールされる。 そのため Ingress リソースを扱うことができる。 また、Flannel 自体は NetworkPolicy リソースをサポートしていないが k3s は kube-router の機能を使ってサポートしているらしい。
使った環境は次のとおり。
$ sw_vers ProductName: macOS ProductVersion: 12.6.3 BuildVersion: 21G419 $ uname -srm Darwin 21.6.0 x86_64 $ k3d version k3d version v5.4.7 k3s version v1.25.6-k3s1 (default) $ docker version Client: Cloud integration: v1.0.29 Version: 20.10.22 API version: 1.41 Go version: go1.18.9 Git commit: 3a2c30b Built: Thu Dec 15 22:28:41 2022 OS/Arch: darwin/amd64 Context: default Experimental: true Server: Docker Desktop 4.16.2 (95914) Engine: Version: 20.10.22 API version: 1.41 (minimum version 1.12) Go version: go1.18.9 Git commit: 42c8b31 Built: Thu Dec 15 22:26:14 2022 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.14 GitCommit: 9ba4b250366a5ddde94bb7c9d1def331423aa323 runc: Version: 1.1.4 GitCommit: v1.1.4-0-g5fd4c4d docker-init: Version: 0.19.0 GitCommit: de40ad0
もくじ
下準備
ホストには、あらかじめ Docker と Homebrew がインストールされた状態を仮定する。
そして Homebrew で k3d をインストールする。
$ brew install k3d
インストールすると k3d コマンドが使えるようになる。
$ k3d version k3d version v5.4.7 k3s version v1.25.6-k3s1 (default)
これで下準備が整った。
Ingress リソースを試す
まずは Ingress リソースから試してみよう。
その前に k3d cluster create サブコマンドを使ってクラスタを作成する。
このとき Ingress リソースを試すときは -p
オプションを付ける必要がある。
これはワーカーノードの前段に入るプロキシの機能を提供するホストの特定のポートをローカルホストのポートに対応付けて扱うため。
今回は Nginx の Pod を使って HTTP を公開する Ingress リソースを作って試してみる。
そこで、プロキシ機能を提供するホストの 80 ポートをローカルホストの 8080 ポートに対応付ける。
これには -p
オプションに "8080:80@loadbalancer"
という引数を渡す。
$ k3d cluster create helloworld \ -p "8080:80@loadbalancer"
上記を実行するとクラスタが作成される。 作成されたクラスタは k3d cluster list で状態を確認できる。
$ k3d cluster list NAME SERVERS AGENTS LOADBALANCER helloworld 1/1 0/0 true
作成されたクラスタを操作するための kubeconfig は k3d kubeconfig write サブコマンドで生成できる。
環境変数の KUBECONFIG
にセットしよう。
$ export KUBECONFIG=$(k3d kubeconfig write helloworld)
このコマンドは kubeconfig を生成しつつ、そのパスを標準出力に返す。
$ k3d kubeconfig write helloworld /Users/amedama/.k3d/kubeconfig-helloworld.yaml
kubectl config current-context サブコマンドで k3d で作ったクラスタが操作対象になっていることを確認する。
$ kubectl config current-context k3d-helloworld
作成した直後はシステムの作成するリソースの準備が整っていないことがある。 そこで kubectl get all サブコマンドなどを使ってシステムのリソースが稼働していることを確認する。
$ kubectl get all -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system pod/local-path-provisioner-79f67d76f8-hpcs9 1/1 Running 0 64s kube-system pod/coredns-597584b69b-n268h 1/1 Running 0 64s kube-system pod/metrics-server-5f9f776df5-qhzz2 1/1 Running 0 64s kube-system pod/helm-install-traefik-crd-7m896 0/1 Completed 0 64s kube-system pod/helm-install-traefik-mnlxz 0/1 Completed 1 64s kube-system pod/svclb-traefik-07031ac5-j8cx2 2/2 Running 0 36s kube-system pod/traefik-66c46d954f-qgfh7 1/1 Running 0 36s NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 79s kube-system service/kube-dns ClusterIP 10.43.0.10 <none> 53/UDP,53/TCP,9153/TCP 76s kube-system service/metrics-server ClusterIP 10.43.76.82 <none> 443/TCP 75s kube-system service/traefik LoadBalancer 10.43.82.71 172.18.0.3 80:31992/TCP,443:32477/TCP 36s NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE kube-system daemonset.apps/svclb-traefik-07031ac5 1 1 1 1 1 <none> 36s NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE kube-system deployment.apps/local-path-provisioner 1/1 1 1 76s kube-system deployment.apps/coredns 1/1 1 1 76s kube-system deployment.apps/metrics-server 1/1 1 1 75s kube-system deployment.apps/traefik 1/1 1 1 36s NAMESPACE NAME DESIRED CURRENT READY AGE kube-system replicaset.apps/local-path-provisioner-79f67d76f8 1 1 1 65s kube-system replicaset.apps/coredns-597584b69b 1 1 1 65s kube-system replicaset.apps/metrics-server-5f9f776df5 1 1 1 65s kube-system replicaset.apps/traefik-66c46d954f 1 1 1 36s NAMESPACE NAME COMPLETIONS DURATION AGE kube-system job.batch/helm-install-traefik-crd 1/1 32s 74s kube-system job.batch/helm-install-traefik 1/1 34s 74s
準備が整っていることを確認したらリソースを作成していく。 まずは Pod を作る。
$ kubectl run nginx-pod \ --image=nginx \ --labels="app=web" pod/nginx-pod created $ kubectl get pod NAME READY STATUS RESTARTS AGE nginx-pod 1/1 Running 0 11s
続いて Pod に対応する Service を作成する。 Service では nginx-pod の TCP/80 ポートを公開する。 Pod に対応する Service を作るには kubectl expose pod サブコマンドを使うと手っ取り早い。
$ kubectl expose pod nginx-pod \ --port 80 \ --protocol TCP \ --name nginx-service service/nginx-service exposed $ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 9m50s nginx-service ClusterIP 10.43.138.181 <none> 80/TCP 22s
最後に Service に対応する Ingress を作る。
$ kubectl apply -f - << EOF apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-ingress annotations: ingress.kubernetes.io/ssl-redirect: "false" spec: rules: - http: paths: - path: / pathType: Prefix backend: service: name: nginx-service port: number: 80 EOF ingress.networking.k8s.io/nginx-ingress created
Ingress リソースが作成されたことを確認する。
$ kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE nginx-ingress traefik * 172.18.0.3 80 18s
これでローカルホストの TCP/8080 ポート経由で Ingress リソースにアクセスできる。 実際に curl(1) を使ってアクセスしてみよう。
$ curl -sL http://localhost:8080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
ちゃんと Nginx がデフォルトで提供するウェルカムページの内容が得られた。
確認が終わったら作成したリソースを一通り削除して掃除する。
$ kubectl delete ingress nginx-ingress ingress.networking.k8s.io "nginx-ingress" deleted $ kubectl delete service nginx-service service "nginx-service" deleted $ kubectl delete pod nginx-pod pod "nginx-pod" deleted
NetworkPolicy リソースを試す
続いては NetworkPolicy リソースを試す。
まずは次のように Pod を 3 つ作成する。
名前やラベルは pod[123]
として付けておく。
$ kubectl apply -f - << EOF apiVersion: v1 kind: Pod metadata: name: pod1 labels: app: pod1 spec: containers: - name: pod1 image: nginx --- apiVersion: v1 kind: Pod metadata: name: pod2 labels: app: pod2 spec: containers: - name: pod2 image: nginx --- apiVersion: v1 kind: Pod metadata: name: pod3 labels: app: pod3 spec: containers: - name: pod3 image: nginx EOF pod/pod1 created pod/pod2 created pod/pod3 created
次のように Pod に IP アドレスが割り当てられて、ちゃんと起動したことを確認する。
$ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod2 1/1 Running 0 5m5s 10.42.0.13 k3d-helloworld-server-0 <none> <none> pod3 1/1 Running 0 5m5s 10.42.0.14 k3d-helloworld-server-0 <none> <none> pod1 1/1 Running 0 5m5s 10.42.0.12 k3d-helloworld-server-0 <none> <none>
NetworkPolicy のないデフォルトの状態では、同じネームスペース内で自由に通信できる。
たとえば pod2
と pod3
から pod1
に HTTP GET してみよう。
$ kubectl exec -it pod2 -- curl 10.42.0.12 | head -n 5 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> $ kubectl exec -it pod3 -- curl 10.42.0.12 | head -n 5 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style>
いずれのリクエストにも、ちゃんと Nginx のウェルカムページの内容が返ってきている。
では、ここで pod1
を対象とする NetworkPolicy を作成してみよう。
以下の NetworkPolicy は pod1
に送信元が pod2
からの通信だけを許可する。
$ kubectl apply -f - << EOF apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: pod1-nwpolicy spec: podSelector: matchLabels: app: pod1 policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: pod2 EOF networkpolicy.networking.k8s.io/pod1-nwpolicy created
NetworkPolicy を作成した状態で pod2
から pod1
に HTTP GET してみよう。
$ kubectl exec -it pod2 -- curl 10.42.0.12 | head -n 5 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style>
これは明示的に NetworkPolicy で通信を許可しているのでうまくいく。
では pod3
から pod1
に HTTP GET した場合はどうだろうか。
$ kubectl exec -it pod3 -- curl 10.42.0.12 | head -n 5 curl: (7) Failed to connect to 10.42.0.12 port 80: Connection refused command terminated with exit code 7
こちらは NetworkPolicy で許可されていないため Connection refused となった。 どうやら、ちゃんと動作しているようだ。
まとめ
今回は k3d を使って k3s のクラスタを作成し、Ingress リソースと NetworkPolicy リソースが動作することを確かめた。