Kubernetes Service まとめ


Kubernetes Service とは

Kubernetes で動かしているサービスを外部公開するものである(たぶん合ってる)

今回はサンプル YAML と実際の動きを追っていく。
動きを追うときに使う Namespace Deployment はこちら。

namespace.yml
apiVersion: v1
kind: Namespace
metadata:
  name: qiita-sample
deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample
  namespace: qiita-sample
spec:
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
        - name: sample
          image: jwilder/whoami
          resources:
            limits:
              memory: "128Mi"
              cpu: "100m"
          ports:
            - containerPort: 8000

Service の種類

apiVersion: v1
kind: Service
metadata:
  name: sample
spec:
  type: xxxxx # <- こいつの指定
  selector:
    app: sample
  ports:
    - port: 80
      targetPort: 80

Cluster IP

type: ClusterIP もしくは type を記述しなかった場合に適用される。
Kubernetes クラスタ内のみアクセス可能な IP アドレスが割り当てられる。

clusterip.yml
apiVersion: v1
kind: Service
metadata:
  name: sample
spec:
  type: ClusterIP
  selector:
    app: sample
  ports:
    - port: 80
      targetPort: 8000

clusterip.yml を適用。

$ kubectl get svc -o wide -n qiita-sample
NAME     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
sample   ClusterIP   10.103.31.220   <none>        80/TCP    37s   app=sample

実際にアクセスしてみる。

$ kubectl run --image centos --restart Never --rm -i centos -- curl -s 10.103.31.220
I'm sample-57cdd86fd5-b4hsl

内部からはアクセスできた。
これはクラスタ内部からしかアクセスできないため、クラスタ外部からはタイムアウトしてしまう。

External IP

type: CluterIP だが、 externalIPs: [] が存在する場合に適用される。
externalIPs: [] には Kubernetes Nodes の IP アドレスを使用する。

externalip.yml
apiVersion: v1
kind: Service
metadata:
  name: sample
  namespace: qiita-sample
spec:
  type: ClusterIP
  externalIPs:
    - 10.0.0.234
  selector:
    app: sample
  ports:
    - port: 80
      targetPort: 8000

externalip.yml を適用。

kubectl get svc -o wide -n qiita-sample
NAME     TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE    SELECTOR
sample   ClusterIP   10.97.67.86   10.0.0.234    80/TCP    112s   app=sample

アクセスしてみる。

$ curl 10.0.0.234
I'm sample-57cdd86fd5-b4hsl

外部からアクセス可能。

NodePort

type: NodePort で適用される。
Kubernetes Nodes の NIC に直接バインドさせるもの。 0.0.0.0:<NodePort> にバインドされるため NIC 単位の指定はできない(たぶん?)。
割り当てられるポートは 30000-32767

nodeport.yml
apiVersion: v1
kind: Service
metadata:
  name: sample
  namespace: qiita-sample
spec:
  type: NodePort
  selector:
    app: sample
  ports:
    - port: 80
      targetPort: 8000
      nodePort: 30080

nodeport.yml を適用する。

$ kubectl get svc -o wide -n qiita-sample
NAME     TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE   SELECTOR
sample   NodePort   10.96.225.63   <none>        80:30080/TCP   37m   app=sample

アクセスしてみる。

$ curl <NodesIP>:30080
I'm sample-57cdd86fd5-b4hsl

外部からアクセス可能。

すべての Node が *:30080 で LISTEN していることが確認できる。
だがこのままだと Node をまたいで Pod にアクセスすることがある。
例えば Node1 の IP アドレスにアクセスしたからと言って必ず Node1 で動いている Pod に回されるわけではなく、別の Node の Pod に回される場合がある。
これは spec.externalTrafficPolicy を使うとコントロールできる。
externalTrafficPolicy: Cluster にすると別の Node に回せるようになる。
externalTrafficPolicy: Local にするとアクセスした Node 内で動く Pod で処理をする。だがアクセスしたい Pod が動いていない Node だった場合、そのアクセスが失敗してしまうのであまり使わないほうがよい。

Load Balancer

外部からトラフィックを受ける際に一番実用的で使いやすいためプロダクション環境で利用されることが多い。
外部の LoadBalancer を利用するため、 Node が SPoF (Single Point of Failure 単一障害点) となることがなく、外部公開に対する障害耐性が高くなる。
また、万一 Node に障害が発生した場合は自動でその Node にパケットを転送しないようにする。

パブリッククラウドでは GCP(Google Cloud Platform) の GKE(Google Kubernetes Engine) では GCLB(Google Cloud Load Balancing) を、 Microsoft Azure の AKS(Azure Kubernetes Service) では ALB(Azure Load Balancer) を、 AWS(Amazon Web Services) の EKS(Elastic Kubernetes Service) では ELB(Elastic Load Balancer) などが使われることが多い。
プライベートクラウドの OpenStack では Octavia か、外部の LoadBalancer が使われる。

今回は Baremetal のため MetalLB を使用する。これは BGP モードか L2 モードがある。今回は L2 モードで使用する。

metallb.yml
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 10.0.0.140-10.0.0.190
$ kubectl apply -f https://raw.githubusercontent.com/danderson/metallb/main/manifests/metallb.yaml
$ kubectl apply -f metallb.yml

これで MetalLB のデプロイは完了。

loadbalancer.yml
apiVersion: v1
kind: Service
metadata:
  name: smaple
  namespace: qiita-sample
spec:
  type: LoadBalancer
  selector:
    app: sample
  ports:
    - port: 80
      targetPort: 8000

これを適用する。

$ kubectl get svc -o wide -n qiita-sample
NAME     TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE   SELECTOR
smaple   LoadBalancer   10.98.27.34   10.0.0.140    80:31854/TCP   62s   app=sample

実際にアクセスしてみる。

$ curl 10.0.0.140
I'm sample-57cdd86fd5-b4hsl

外部からアクセス可能。
また、これは NodePort と同じ方法でもアクセスができる。

$ curl <NodesIP>:31854
I'm sample-57cdd86fd5-b4hsl

type: LoadBalancer は他にもオプションがいくつかあるが今回は省略する。

Ingress

長くなるため別の記事で。