なにかの技術メモ置き場

なにかの技術メモ置き場

@インフラエンジニア

Kubernetes環境でアプリケーションの外部公開をしてみた

全編目次

none06.hatenadiary.org

概要

  • Kubernetes環境でアプリケーションを作成する(Podを作成する)。
  • アプリケーションを外部に公開する(Serviceを作成する)。

環境

構成

ホスト名 IPアドレス OS 用途 備考
k8sctrpln01 172.16.0.11/24 CentOS 7.9.2009 コントロールプレーンノード QEMU+KVM仮想ゲスト
k8sworker01 172.16.0.12/24 CentOS 7.9.2009 ワーカノード QEMU+KVM仮想ゲスト
k8sworker02 172.16.0.13/24 CentOS 7.9.2009 ワーカノード QEMU+KVM仮想ゲスト
- 172.16.0.21/24 MetalLB ソフトウェアロードバランサ address pool: 172.16.0.21-172.16.0.25/24
client01 172.16.0.100/24 Windows 10 クライアント CLI: Git Bash
Web UI: Google Chrome

前提

  • 原則として公式ドキュメントに沿って進めていく。
  • マニフェストの作成やkubectlコマンドの実行は、クライアントマシン(Windows 10)上で行う。Windowsへのkubectlコマンドのインストールは以下を参照。 kubernetes.io

STEP1 - Podの作成

ソース

kubernetes.io

概要図

概要

nginxコンテナ1つで構成されるPodを2つ作成する。Podの数は任意。

実践

Deploymentを作成する。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      run: nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment created

Podに割り当てられたIPアドレスを確認する。

$ kubectl get pods -o wide
NAME                               READY   STATUS    RESTARTS   AGE   IP            NODE    
      NOMINATED NODE   READINESS GATES
nginx-deployment-89b88cd64-6dbwl   1/1     Running   0          14s   10.244.1.21   k8sworker01   <none>           <none>
nginx-deployment-89b88cd64-r6mhd   1/1     Running   0          14s   10.244.2.18   k8sworker02   <none>           <none>

このIPアドレスKubernetesクラスタ内部のものなので、クラスタ外部からのアクセスは通らない。以下はノードと同一ネットワークに所属するWindowsマシンからcurlを実行した例。
※当環境のプロキシが邪魔をしたので--noproxyオプションを使用。

$ curl.exe --noproxy "*" -o /dev/null -w '%{http_code}\n' -s http://10.244.1.21:80
000

クラスタ内部からのアクセスは通る。以下はPodnginx-deployment-89b88cd64-6dbwlから10.244.1.2110.244.2.18curlを実行した例。

$ kubectl exec nginx-deployment-89b88cd64-6dbwl -- curl -o /dev/null -w '%{http_code}\n' -s http://10.244.1.21:80
200
$ kubectl exec nginx-deployment-89b88cd64-6dbwl -- curl -o /dev/null -w '%{http_code}\n' -s http://10.244.2.18:80
200

STEP2 - Serviceの作成

CASE1 - ClusterIPタイプのServiceを使用した構成

ソース

kubernetes.io

概要図

概要

先に結論だが、この構成ではクラスタ外部にアプリケーションを公開できない。ClusterIPはクラスタ内部からのみアクセス可能。

実践

ClusterIPタイプのServiceを作成する。

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    run: nginx
spec:
  type: ClusterIP # 公式ドキュメントでは省略している(省略時はClusterIPになる)
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: nginx
$ kubectl apply -f nginx-service-clusterip.yaml 
service/nginx-service created

クラスタ内部からアクセス可能なClusterIPが割り当てられた。

$ kubectl get services nginx-service
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-service   ClusterIP   10.109.120.89   <none>        80/TCP    68s

Endpointsが各PodのIPアドレスとなっている。Serviceにアクセスが来るとkube-proxyによりEndpointsに分散される。

$ kubectl describe services nginx-service
Name:              nginx-service
Namespace:         default
Labels:            run=nginx
Annotations:       <none>
Selector:          run=nginx
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.109.120.89
IPs:               10.109.120.89
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.21:80,10.244.2.18:80
Session Affinity:  None
Events:            <none>

念のためクラスタ外部からはアクセスできないことを確認。

$ curl.exe --noproxy "*" -o /dev/null -w '%{http_code}\n' -s http://10.109.120.89:80
000

クラスタ内部からはアクセスできる。

$ kubectl exec nginx-deployment-89b88cd64-6dbwl -- curl -o /dev/null -w '%{http_code}\n' -s http://10.109.120.89:80
200

CASE2 - NodePortタイプのServiceを使用した構成

未稿

CASE3 - LoadBalancerタイプのServiceを使用した構成

ソース

kubernetes.io metallb.universe.tf

概要図

概要

クラウド環境においてLoadBalancerタイプのServiceをデプロイすると、クラウド環境上にロードバランサを作成し、クラスタ外部のIPアドレスを割り当ててくれるらしい。
オンプレ環境ではロードバランサの作成処理が進まないため、ソフトウェアロードバランサであるMetalLBをインストールすることで代替することができる。

実践

MetalLBの作成

MetalLBのインストール準備をする。

$ kubectl describe configmap -n kube-system kube-proxy | grep strictARP
  strictARP: false
$ kubectl get configmap kube-proxy -n kube-system -o yaml | sed -e "s/strictARP: false/strictARP: true/" | kubectl diff -f - -n kube-system
diff -u -N "C:\\Users\\root\\AppData\\Local\\Temp\\LIVE-1938845299/v1.ConfigMap.kube-system.kube-proxy" "C:\\Users\\root\\AppData\\Local\\Temp\\MERGED-1255996808/v1.ConfigMap.kube-system.kube-proxy"
--- "C:\\Users\\root\\AppData\\Local\\Temp\\LIVE-1938845299/v1.ConfigMap.kube-system.kube-proxy"    2022-06-04 01:33:37.477912900 +0900
+++ "C:\\Users\\root\\AppData\\Local\\Temp\\MERGED-1255996808/v1.ConfigMap.kube-system.kube-proxy"  2022-06-04 01:33:37.479487200 +0900
@@ -33,7 +33,7 @@
       excludeCIDRs: null
       minSyncPeriod: 0s
       scheduler: ""
-      strictARP: false
+      strictARP: true
       syncPeriod: 0s
       tcpFinTimeout: 0s
       tcpTimeout: 0s
@@ -100,9 +100,9 @@
     fieldsV1:
       f:data:
         f:config.conf: {}
-    manager: kubectl-edit
+    manager: kubectl-client-side-apply
     operation: Update
-    time: "2022-06-03T12:03:26Z"
+    time: "2022-06-03T16:33:36Z"
   name: kube-proxy
   namespace: kube-system
   resourceVersion: "667909"
$ kubectl get configmap kube-proxy -n kube-system -o yaml | sed -e "s/strictARP: false/strictARP: true/" | kubectl apply -f - -n kube-system
Warning: resource configmaps/kube-proxy is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
configmap/kube-proxy configured
$ kubectl describe configmap -n kube-system kube-proxy | grep strictARP
  strictARP: true

MetalLBをインストールする。

$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml
namespace/metallb-system created
$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/controller created
podsecuritypolicy.policy/speaker created
serviceaccount/controller created
serviceaccount/speaker created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
role.rbac.authorization.k8s.io/config-watcher created
role.rbac.authorization.k8s.io/pod-lister created
role.rbac.authorization.k8s.io/controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/config-watcher created
rolebinding.rbac.authorization.k8s.io/pod-lister created
rolebinding.rbac.authorization.k8s.io/controller created
daemonset.apps/speaker created
deployment.apps/controller created
$ kubectl -n metallb-system get pods
NAME                          READY   STATUS                       RESTARTS      AGE
controller-7476b58756-dp7hd   0/1     CrashLoopBackOff             1 (12s ago)   18s
speaker-427rt                 0/1     CreateContainerConfigError   0             18s
speaker-mkbcr                 0/1     CreateContainerConfigError   0             18s

なにやらつまづいている様子。

$ kubectl -n metallb-system logs controller-7476b58756-dp7hd
{"branch":"HEAD","caller":"level.go:63","commit":"v0.12.1","goversion":"gc / go1.16.14 / amd64","level":"info","msg":"MetalLB controller starting version 0.12.1 (commit v0.12.1, branch HEAD)","ts":"2022-06-04T13:35:16.862838665Z","version":"0.12.1"}
{"caller":"level.go:63","error":"Get \"https://10.96.0.1:443/api/v1/namespaces/metallb-system/secrets?fieldSelector=metadata.name%3Dmemberlist\": dial tcp 10.96.0.1:443: connect: no route to host","level":"error","msg":"failed to create memberlist secret","op":"startup","ts":"2022-06-04T13:35:17.865843229Z"}

dial tcp 10.96.0.1:443: connect: no route to hostとのことで、以下のServiceへの疎通ができていないように見える。

$ kubectl get services kubernetes
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   6d3h

暫定対処として、

の順に試したが状況変わらず。最終的に

ことで解消した。追って必要なポートを調査せねば。

$ ssh k8sctrpln03 systemctl stop firewalld
$ ssh k8sworker01 systemctl stop firewalld
$ ssh k8sworker02 systemctl stop firewalld
$ kubectl -n metallb-system get pods
NAME                          READY   STATUS    RESTARTS        AGE
controller-7476b58756-dp7hd   1/1     Running   8 (6m50s ago)   17m
speaker-427rt                 1/1     Running   0               17m
speaker-mkbcr                 1/1     Running   0               17m

MetalLBをLayer2モードで設定する。

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 172.16.0.21-172.16.0.25
$ kubectl apply -f metallb-config.yaml 
configmap/config created
$ kubectl -n metallb-system get configmap config
NAME     DATA   AGE
config   1      6s

Serviceの作成

LoadBalancerタイプのServiceを作成する。

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    run: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: nginx
$ kubectl delete services nginx-service
service "nginx-service" deleted
$ kubectl apply -f nginx-service-loadbalancer.yaml
service/nginx-service created

EXTERNAL-IPが割り当てられている。

$ kubectl get services nginx-service
NAME            TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx-service   LoadBalancer   10.96.239.88   172.16.0.21   80:32191/TCP   8s
$ kubectl describe services nginx-service
Name:                     nginx-service
Namespace:                default
Labels:                   run=nginx
Annotations:              <none>
Selector:                 run=nginx
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.96.239.88
IPs:                      10.96.239.88
LoadBalancer Ingress:     172.16.0.21
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  32191/TCP
Endpoints:                10.244.1.30:80,10.244.2.28:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason        Age   From                Message
  ----    ------        ----  ----                -------
  Normal  IPAllocated   22s   metallb-controller  Assigned IP ["172.16.0.21"]
  Normal  nodeAssigned  22s   metallb-speaker     announcing from node "k8sworker02"

クラスタ外部のIPにアクセスできた。

$ curl.exe --noproxy "*" -o /dev/null -w '%{http_code}\n' -s http://172.16.0.21:80
200

※アクセスできないこともあったが、Serviceを再作成したらうまくいった。しかしそれは正常とは言えないので、原因を調査する必要がある。

CASE4 - IngressとClusterIPタイプのServiceを使用した構成

未稿

CASE5 - ExternalIPsとClusterIPタイプのServiceを使用した構成

未稿

あとがき

LoadBalancerかIngressが実用性がありそう。

参考記事

kubernetes.io metallb.universe.tf blog.framinal.life blog.yumenomatayume.net