Kubernetes Service まとめ

# Kubernetes Service とは

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

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

```yaml:namespace.yml
apiVersion: v1
kind: Namespace
metadata:
  name: qiita-sample
```

```yaml: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 の種類

```yaml
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 アドレスが割り当てられる。

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

`clusterip.yml` を適用。

```sh
$ 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
```

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

```sh
$ 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 アドレスを使用する。

```yaml: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` を適用。

```sh
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
```

アクセスしてみる。

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

外部からアクセス可能。

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

```yaml: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` を適用する。

```sh
$ 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
```

アクセスしてみる。

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

外部からアクセス可能。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204925/190be0f4-ba46-8d7d-3189-b41d7c2db8e5.png)

すべての 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](https://metallb.universe.tf/) を使用する。これは BGP モードか L2 モードがある。今回は L2 モードで使用する。

```yaml: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
```

```sh
$ kubectl apply -f https://raw.githubusercontent.com/danderson/metallb/main/manifests/metallb.yaml
$ kubectl apply -f metallb.yml
```

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

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

これを適用する。

```sh
$ 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
```

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

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

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

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

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

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