【GCP】 Pod からMemorystoreに接続できない場合の対応について

#この記事について
ip_alias が無効なkubernetesクラスタでMemorystoreを立ち上げた際に、podからMemorystoreに接続できずにハマったので接続する方法をまとめました。
コマンドライン上の日本語箇所は環境に合わせて適宜入れ替えてください。
後半はkubernetesのネットワークについて少し触れています。

## Memorystore 立ち上げ
以下の内容になるようにMemorystoreを立ち上げる。

```
Redis のバージョン: 3.x
ロケーション : 置きたいクラスタのロケーション
インスタンスの容量: 要件による
承認済みネットワーク : 接続させたいクラスタが所属しているネットワーク
インスタンス IP アドレス範囲: 10.0.0.0/29
alternative_location_id : 本番環境の場合は記載
```

GCPコンソールもしくはコマンドラインでインスタンスが立ち上がったか確認する。

 ```
# 例
 gcloud redis instances list --region asia-east1
```
## ConfigmapおよびDaemonSet作成と適用
このままだとpodからMemorystoreに接続できないので、Nodeにルーティングを入れる必要がある。  
google 紹介のインストールスクリプトを使用する。

```
gcloud config set project プロジェクトID 
gcloud container clusters get-credentials クラスタ名 --project プロジェクトID 
## 自分が何処にいるか確認
gcloud config list
kubectl get Node

# インストールスクリプトダウンロードと実行
cd /tmp/
git clone https://github.com/bowei/k8s-custom-iptables.git
cd k8s-custom-iptables/
TARGETS="10.0.0.0/29" ./install.sh
```

## 確認作業
### DaemonSet,iptables確認
```
# ルーティング用のDaemonSetが立ち上がっているか確認
kubectl get pods -o wide | grep iptables
# iptables が反映されたかNodeのどれかにログインして確認
kubectl get Node -o wide
ssh {ユーザー名}@IPアドレス -i {秘密鍵}
sudo iptables -t nat -L -n -v | grep 10.0.0.0/29
exit
```

### Memorystore接続確認
```
# podにログインして接続確認
kubectl get pod -o wide
kubectl exec pod名 -it sh
curl telnet://10.0.0.3:6379
# 以下のコマンドで確認。設定情報などがつらつら出るはず
 info
 quit
```


## 繋がる様になったけど...

これでMemorystoreとの疎通は取れる様になった。
が、ここで一つ疑問が。
DaemonSetのpodでのネットワーク設定変更が何故Nodeにも適用されるのだろうか。
ということでdockerのネットワーク設定を少し調べてみる。

|ドライバー|説明 |
|--------|:----|
|Host    |コンテナはホストのネットワーキングスタックを使用します。名前空間の分離はなく、ホスト上のすべてのインターフェイスはコンテナによって直接使用できます。
|Bridge  |Dockerによって管理されるホスト上にLinuxブリッジを作成します。デフォルトでは、ブリッジ上のコンテナは互いに通信できます。コンテナへの外部アクセスは、ブリッジドライバを使用して設定することもできます。|
|Overlay |複数のホストネットワークをサポートするオーバーレイネットワークを作成します。ローカルのLinuxブリッジとVXLANの組み合わせを使用して、物理ネットワークインフラストラクチャ上でコンテナ間の通信をオーバーレイします。|
|MACVLAN |MACVLANブリッジモードを使用して、コンテナインタフェースと親ホストインタフェース（またはサブインタフェース）間の接続を確立します。物理ネットワーク上でルーティング可能なコンテナにIPアドレスを提供するために使用できます。さらに、レイヤ2コンテナセグメンテーションを実施するために、VLANをmacvlanドライバにトランクすることができます。|
|None    |コンテナに独自のネットワーキングスタックとネームスペースを与えますが、コンテナ内にはインターフェイスを構成しません。追加の設定がなければ、コンテナはホストネットワーキングスタックから完全に分離されます。|
https://success.docker.com/article/networking より

上記の説明からいくとHostドライバーを使っている可能性が高い。
Memorystoreのiptables更新用DaemonSet  コンテナに関してはやはりNode側とネットワークインターフェース共有設定で立ち上がってる。

```
srvadm@gke-gke-xxxxxxxxxxxxx ~ $ docker network inspect host  |grep Name
        "Name": "host",
                "Name": "k8s_POD_k8s-custom-iptables-xxxxxxxxxxxxx",
                "Name": "k8s_POD_kube-proxy-gke-xxxxxxxxxxxxx",
```
k8s-custom-iptablesのyamlをみてみる。
以下の記載があり、hostnetworkを指定している。

```
"hostNetwork":true
```

DaemonSetのpodにも接続して確認してみる

```
$ kubectl get pods -o wide
NAME                                                              READY     STATUS    RESTARTS   AGE       IP            Node
k8s-custom-iptables-xxxx   

$ kubectl exec k8s-custom-iptables-xxxx -it sh

# ps auwxx
PID   USER     TIME   COMMAND
    1 root       0:00 sh -c /run.sh
    5 root       6:13 {run.sh} /bin/sh /run.sh
17890 root       0:00 sh
17968 root       0:00 sleep 10
17969 root       0:00 ps auwxx
```
run.shというスクリプトが動いている。
中身をみると10秒ごとにconfigmapで指定されたファイルを読み込んで変更があればiptablesを変更するものの様だ。
起動自体はDaemonSetのyamlに記載されている様に、pod立ち上がり時に起動指定されている。

```
[{"command":["sh","-c","/run.sh"]
# 一部抜粋
```

ネットワークインターフェースも見てみよう。
Node hostと同じものが見えている様だ。

```
# ifconfig | grep Ethernet | awk '{print $1}'
cbr0
docker0
eth0
veth116ef2c7
veth2c0b617e
veth7124acc7
vethaf9a1d7f
vethcc800297
vethd2c90de9
vethff581b13
```

## まとめ
絵にするとこんな感じだろうか。
#### 普段は...
![プレゼンテーション1.jpg](https://qiita-image-store.s3.amazonaws.com/0/311041/cf85ce5a-2865-c96c-6c2d-64f107875d05.jpeg)

#### Memorystoreの場合
![プレゼンテーション2.jpg](https://qiita-image-store.s3.amazonaws.com/0/311041/8de6e0e1-4d64-50a8-582b-1d964cc8005d.jpeg)

クラスタの各NodeにDaemonSetで配置されたk8s_POD_k8s-custom-iptablesが定期的にconfigmapに記載されたネットマスクを読み取り、Nodeのiptablesを更新しmemorysotreへのルーティングを追加している。
そのため、ルーティング追加後はpodがMemorystoreのネットワークに接続できる様になると言うことか。