KubernetesのNodePortに接続できない時の原因と対策


# 現象

[Docker/Kubernetes 実践コンテナ開発入門](https://www.amazon.co.jp/o/ASIN/4297100339/gihyojp-22)で勉強していたところ、Chapter7で以下のエラーにぶち当たりました。

```
$ curl http://127.0.0.1:30060
curl: (7) Failed to connect to 127.0.0.1 port 30060: Connection refused
```

30060はNodePortでKubernetesクラスタ外部に公開しているポートです。
nginxが稼働しているServiceを事前にapplyしておいて、ポート番号30060でアクセスするという作業です。

この原因や対処法を探すのにだいぶ時間を取られたので記事として残しておきます。
わかってしまうと無知ゆえのミスという気もするので、だいぶ初心者用の情報になると思います。

# 原因

contextがGKE(Google Kubernetes Engine)になっていたためでした。
※　前の章までGKEを使用していました。
contextはkubectlコマンドの接続先です。
私はcontextをGKEに設定していたため、GKEにapplyしたServiceに対してlocalhostでアクセスしようとしていました。

当たり前の話ですが、GKE上のクラスタにapplyしたServiceにはlocalhostではアクセスできません。
そのためconnection refusedとなっていた訳です。

# 対策
対策は以下の2つです。

* contextを切り替えてlocalhostで稼働しているkubernetesクラスタにServiceを登録してアクセスする。
* GKEに登録しているServiceにアクセスする。

## contextを切り替える
contextをlocalhostに切り替えることでlocalhostを指定したアクセスができるようにします。
contextはコマンドライン上で簡単に切り替えることができます。
まず、現在登録されているcontextをチェックします。


```
$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER          AUTHINFO                                           NAMESPACE
          docker-for-desktop   docker-desktop   docker-desktop                                     
*         gke_******           gke_*******      gke_******
```

CURRENTがGKEになっていることが確認できます。
これをlocalhostで動作しているdocker-for-desktopに変更します。

```
$ kubectl config use-context docker-for-desktop
Switched to context "docker-for-desktop".
```

contextが変更されました。

```
$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER          AUTHINFO                                           NAMESPACE
*         docker-for-desktop   docker-desktop   docker-desktop                                     
          gke_******           gke_*******      gke_******
```

参考書通りのレスポンスが表示されました。
(参考書ではSecretリソースを使用したアクセスを説明しているため、パスワードを使用せずにアクセスするとAuthorization Requiredが出ます。)

```
$ curl http://127.0.0.1:30060
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.13.12</center>
</body>
</html>
```

## GKEに登録しているServiceにアクセスする
GKE上に既にServiceを登録済のため、そのServiceにアクセスを行います。

クラスタ内のノードに割り当てられたいずれかのノードの外部IPアドレスを、以下のコマンドで見つけます。

```
$ kubectl get nodes --output wide
```

複数出力されますが、その中の1つのEXTERNAL-IPを使用してアクセスを行います。

```
$ curl 35.xxx.xxx.xxx:30060
curl: (7) Failed to connect to 35.xxx.xxx.xxx port 30060: Operation timed out
```
この段階ではまだアクセスすることができません。エラーがtimed outに変わっています。

これはGCPのファイアウォールルールで許可されていないためです。
以下のコマンドで指定したポートを許可します。

```
$ gcloud compute firewall-rules create test-node-port --allow tcp:30060
Creating firewall...⠹Created [https://xxxx].                 
Creating firewall...done.   
```

これでポート番号30060を利用したアクセスが許可されました。
再びアクセスしてみます。

```
$ curl 35.xxx.xxx.xxx:30060
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.13.12</center>
</body>
</html>
```

正常にレスポンスが返ってきました。

# 最後に

私だけかもしれませんが、参考書通りやってうまく行かないことがあると原因探しに時間費やしてしまうのでそういう人が減れば良いかなと思います。
contextは知っている人からすれば当たり前かもしれませんが、Kubernetes初体験中だったので良い勉強になりました。

# 参考URL

[[Kubernetes入門] kubectlのアクセス先(コンテキスト)を切り替える方法](gskyarch.net/blog/?p=16631)
[NodePortタイプのServiceの作成](https://cloud.google.com/kubernetes-engine/docs/how-to/exposing-apps?hl=ja#creating_a_service_of_type_nodeport)