Kubernetes の Service の ExternalIP/NodePort を試す

作業メモ。

[Kubernetes完全ガイド impress top gearシリーズ](https://www.amazon.co.jp/dp/B07HFS7TDT/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)を読みながら手元で確認した時のメモ。

ただし、ExternalIP についてはうまくいかず。

# 環境

```bash
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.3", GitCommit:"2bba0127d85d5a46ab4b778548be28623b32d0b0", GitTreeState:"clean", BuildDate:"2018-05-28T20:03:09Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.5-eks-6bad6d", GitCommit:"6bad6d9c768dc0864dab48a11653aa53b5a47043", GitTreeState:"clean", BuildDate:"2018-12-06T23:13:14Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
```

Kubernetes 環境としては EKS を利用。

# ExternalIP を試す

[External IPs](https://kubernetes.io/docs/concepts/services-networking/service/#external-ips)

>If there are external IPs that route to one or more cluster nodes, Kubernetes services can be exposed on those externalIPs. Traffic that ingresses into the cluster with the external IP (as destination IP), on the service port, will be routed to one of the service endpoints. externalIPs are not managed by Kubernetes and are the responsibility of the cluster administrator.

Node に振られたグローバル IP アドレスを使って Pod へのルーティングを行う方法。

## Node の IP を調べる

ExternalIP を使う場合、マニュフェストファイルに利用する Node のグローバル IP アドレスを記載する必要があるため、事前に確認する。
`kubectl describe node`から確認可能。

```bash
$kubectl describe node |grep ExternalIP
  ExternalIP:   13.113.212.75
  ExternalIP:   13.113.172.56
  ExternalIP:   54.168.61.99
```

上記例の場合、Node が3つあり、それぞれ外部から接続可能な IP は上記。
今回の例では「13.113.212.75」を使う。

## マニフェストファイルを作成する

以下とほぼ同じものをつかう。

[Kubernetes の Service(ClusterIP) を検証する](https://qiita.com/toshihirock/items/d8bce02269c64478649f)


+ sample-deployment.yaml
+ sample-clusterip.yaml

今回は ExternalIP を使うため、sample-clusterip.yaml は少し変更して sample-externalip.yaml とした。

```bash
$diff sample-clusterip.yaml sample-externalip.yaml
4c4
<   name: sample-clusterip
---
>   name: sample-externalip
6a7,8
>   externalIPs:
>     - 13.113.212.75
10c12
<       port: 8080
---
>       port: 80
```

+ Service 名を sample-externalip に変更
+ externalIPs　に Node の IP を追加
+ ExternalIP で受け付けるポートを 80 に変更

```bash
# deployment を作成
$kubectl apply -f sample-deployment.yaml
deployment.apps "sample-deployment" created

# Service を作成
$kubectl apply -f sample-externalip.yaml
service "sample-externalip" created

# ExternalIP  が設定されている
$kubectl get service sample-externalip
NAME                TYPE        CLUSTER-IP       EXTERNAL-IP     PORT(S)   AGE
sample-externalip   ClusterIP   10.100.143.185   13.113.212.75   80/TCP    30s

# 接続できない...
$curl http://13.113.212.75
curl: (7) Failed to connect to 13.113.212.75 port 80: Connection refused
```

なぜか接続に失敗する。

```bash
# クラスター内の IP アドレスからの名前解決は可能(返却されるのは ClusterIP
$kubectl run testpod --image=centos:6 --restart=Never -i --rm -- dig +short sample-externalip.default.svc.cluster.local
10.100.143.185

# クラスター内からのリクエストは可能
$ kubectl run testpod --image=centos:6 --restart=Never -i --rm -- curl -s http://sample-externalip
sample-deployment-86d576464c-wkhjd
```

こうなると Node 側の問題?

```bash
# Node に SSH 接続する
$ssh -i ~/.ssh/xxxx.pem ec2-user@13.113.212.75

# 何も出ない
$ss -napt |grep 80
```

そもそもリッスンされていない。
切り分けのためにポートを80->8080 に変更して `ss -napt |grep 8080`してみたがうまくいかず。
別途これは調査する

# NodePort

[Type NodePort](https://kubernetes.io/docs/concepts/services-networking/service/#nodeport)

ExternalIP はマニュフェストファイルに指定した Node の グローバルIP:Port で受け付けたトラフィックを転送していた。
NodePort ではすべての Node の グローバルIP:Port で受付を行い、同様にトラフィックを転送する。

```sample-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport
spec:
  type: NodePort
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
      nodePort: 30080
  selector:
    app: sample-app
```

ExternalIP では 受付をおこなう Node の IP を指定していたが、NodePort の場合にはその記載は不要。
代わりに type では **NodePort** を設定する。
また、 **nodePort** を指定することで Node でリッスンするポートを指定する。
なお、ExternalIP の場合、ports.port に書いたポート番号で ClusterIP(クラスタ内)及び ExternalIP(外部からの疎通）でリッスンしていたが、NodePort の場合、ClusterIP で受け付けるポートが ports.port の番号に該当し、Node で受け付けるポートは ports.nodePort となっているので注意。
上記マニュフェストファイルの場合以下を意味する

+ ClusterIP で受け付けるポート:8080
+ 各 Node への IP アドレスで受け付けるポート:30080
+ コンテナで受け付けるポート:80

```bash
# service を作成
$kubectl apply -f sample-nodeport.yaml
service "sample-nodeport" created

# type が NodePort となっている.CLUSTER-IP がクラスタ内からアクセスする際の仮想 IP
$kubectl get service sample-nodeport
NAME              TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
sample-nodeport   NodePort   10.100.241.100   <none>        8080:30080/TCP   34s

# Node に SSH 接続する
$ssh -i ~/.ssh/xxxx.pem ec2-user@13.113.212.75

# nodePort で記載した番号 30080 でリッスンしている
$ss -napt |grep 30080
LISTEN0      128                         *:30080                        *:*

# 一つの Node の IP アドレスへリクエストするとレスポンスが返ってくる
$curl http://13.113.212.75:30080/
sample-deployment-86d576464c-x75hx

# 別の Node へのリクエストも同様
$curl http://13.113.172.56:30080/
sample-deployment-86d576464c-x75hx

# クラスター内から ClusterIP を使ってアクセスする場合には 30080 ではなく、8080 ポート
$kubectl run testpod --image=centos:6 --restart=Never -i --rm -- curl -s http://sample-nodeport:8080
sample-deployment-86d576464c-hnkm7
```