逆引き GKE ネットワーク編


# はじめに

* Google Container Engine のネットワーク関連の How To についてあとで参照できるように逆引き形式でまとめる



# 検証環境

* Google Container Engine
* Google Cloud SDK 172.0.1
* Kubernetes 1.7.6



# GKE ネットワーク概要

* GKE クラスタは作成時に配備する `VPC ネットワーク` を選択する
* VPC ネットワークは GCP コンソールや gcloud コマンドで作成できる
* VPC ネットワークはリージョンごとにサブネットワークを持つ
* VPC ネットワークは設定値としてファイアーウォール設定やルーティング設定を持つ
* ファイアーウォール設定はデフォルトで全ての内向き通信を拒否し全ての外向き通信を許可する
* 詳細は[公式ドキュメント](https://cloud.google.com/compute/docs/networking?hl=ja)で



# 同一 Pod 内のコンテナ同士で通信する

* コンテナ設定の `containerPort` で設定しているポートであれば `hostname` コマンドで取得できるホスト宛に設定したポート番号でアクセスできる
* 以下の設定の Pod なら `busybox` コンテナから `wget http://$(hostname)` で `nginx` コンテナに通信できる

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    name: nginx
spec:
  hostname: nginx
  containers:
  - image: busybox
    name: busybox
    command:
      - sleep
      - "3600"
  - image: nginx
    name: nginx
    ports:
    - containerPort: 80
```



# 同一クラスタ内のコンテナ同士で通信する

* 同一クラスタ内であれば Pod の IP と公開しているコンテナのポートで直接アクセスできる
* クラスタ内の IP は可変なので静的な接続先が必要であれば NodePort サービスを使って内部 DNS で名前解決のできるドメイン名を使って通信できる
* 詳細は https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/ とか Headless Service とかで検索

## Service の作成

* selecter 要素で指定したラベルのついた Pod に対して `[POD_NAME].[SERVICE_NAME].[NAMESPACE].logging.svc.cluster.local` でアクセスできる

```yaml
apiVersion: v1
kind: Service
metadata:
  name: [SERVICE_NAME]
  namespace: [NAMESPACE]
spec:
  ports:
  - port: [SERVICE_PORT]
  # [podname].broker.logging.svc.cluster.local
  clusterIP: None
  selector:
    [KEY]: [VALUE]
```

## 設定例

* Pod `busybox1` の FQDN は `busybox-1.default-subdomain.my-namespace.svc.cluster.local` になる

```yaml
apiVersion: v1
kind: Service
metadata:
  name: default-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # Actually, no port is needed.
    port: 1234
    targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: default-subdomain
  containers:
  - image: busybox
    command:
      - sleep
      - "3600"
    name: busybox
```



# 同一プロジェクトの別クラスタのコンテナ同士で通信する

* 異なるクラスタのコンテナ同士で通信をしたい場合は VPC ネットワークを作成し、それぞれのクラスタの作成時に同じネットワークに割り当てる

## VPC ネットワークの作成

* GCP コンソールの「VPC ネットワーク > VPC ネットワーク」に移動
* 「VPC ネットワークを作成」で作成
  * 名前と説明は任意で
  * サブネット作成モードは「自動」なら全リージョン分サブネットを自動で作る、リソースを節約したい場合は「カスタム」で任意のリージョンにだけサブネットを作った方がいい
    * カスタムの場合は名前とリージョンは任意
	* IP アドレス範囲は多分「10.0.0.0/8」以下のクラス A のプライベート IP アドレスで適当に割り当てれば良さそう

### ネットワーク作成コマンド

* コンソールの作成フォームから実行コマンドを参照することができる
* my-project プロジェクトで my-network ネットワークに my-sub-networm サブネットワークを asia-northeast1 リージョンで 10.0.0.0/24 の範囲で作成した場合

```
gcloud compute --project=my-project networks create my-network --mode=custom
gcloud compute --project=my-project networks subnets create my-sub-network --network=my-network --region=asia-northeast1 --range=10.0.0.0/24
```

## コンテナの作成

* GCP コンソールから「Container Engine > コンテナクラスタ」で「クラスタを作成」で作成
* 同一ネットワークでサブネットワークを作成したリージョンで複数コンテナを作成する

## 疎通確認

* 複数のコンテナそれぞれで適当に Pod を作成
* `kubectl describe pods` で Pod の IP アドレスを確認
* 一方のコンテナで他方のコンテナに ping をする

```
kubectl exec -it [POD_NAME] -- ping [OTHER_POD_ID]
```

## 別コンテナの静的なエンドポイントを取得する

* Pod の IP での通信だと接続先が動的に変わるので扱いづらい
* コンテナが違うと内部 DNS による名前解決もできない
* そのため別コンテナで静的なエンドポイントを提供するために [Internal Load Balancing](https://cloud.google.com/container-engine/docs/internal-load-balancing) を使う

### Internal Load Balancing の作成

* Internal Load Balancing は内部向けのロードバランサで外部に公開する IP アドレスは持たない
* 同一ネットワークにあればデフォルトでルーティングしてくれるので別クラスタ同士でも固定された IP アドレスに接続しやすくなる
* 設定は以下の通り
  * `type: LoadBalancer` で記載のアノテーションが指定されていれば Internal Load Balancing 扱いになる
  * [IP-ADDRESS] にはサブネットワークに指定した範囲の IP が割り当てられる
    * とりあえずコメントにしておき割り当てられたら値を入れる

```yaml

apiVersion: v1
kind: Service
metadata:
  name: [SERVICE-NAME]
  annotations:
    cloud.google.com/load-balancer-type: "internal"
  labels:
    app: echo
spec:
  type: LoadBalancer
  loadBalancerIP: [IP-ADDRESS]
  ports:
  - port: 9000
    protocol: TCP
  selector:
    [KEY]: [VALUE]
```

### Internal Load Balancing の動作確認

* ステータスは普通に `kubectl describe service` で確認
* ping は通らないので公開したポートに telnet するとか実際にサービスを利用して確認する



# 別プロジェクトのコンテナ同士で通信する

* [VPC ネットワークピアリングを使った別プロジェクト同士での通信設定についての記事](https://qiita.com/nirasan/items/7bb1e0d96812c2dc92e6) を参照



# クラスタ外部に静的 IP アドレスで HTTP サービスを公開する

* NodePort サービスと IP アドレスを作成し Ingress と関連づけて公開する

## Service の作成

* NodePort タイプのサービスでクラスタ内にサービスを公開する

```yaml
apiVersion: v1
kind: Service
metadata:
  name: [SERVICE_NAME]
spec:
  selector:
    app: [APP_NAME]
  ports:
  - port: [APP_PORT]
  type: NodePort
```

## 静的 IP アドレスを作成する

```
# IP アドレスの作成
gcloud compute addresses create [IP_ADDRESS_NAME] --global

# IP アドレスの確認
gcloud compute addresses describe [IP_ADDRESS_NAME] --global
```

## Ingress の作成

* 作成した Service を公開する Ingress を作成する

```yaml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: [INGRESS_NAME]
  annotations:
    kubernetes.io/ingress.global-static-ip-name: [IP_ADDRESS_NAME]
spec:
  backend:
    serviceName: [SERVICE_NAME]
    servicePort: [APP_PORT]
```



# クラスタ外部に静的 IP アドレスで任意のサービスを公開する

* LoadBalancer サービスを作成し IP アドレスを静的に変更してサービス設定に IP アドレスを追加する

## Service を作成する

* 確認した IP アドレスを設定する

```yaml
apiVersion: v1
kind: Service
metadata:
  name: [SERVICE_NAME]
spec:
  type: LoadBalancer
  ports:
  - port: [APP_PORT]
  selector:
    app: [APP_NAME]
```

## IP アドレスを確認する

* 作成したサービスに割り当てられている動的 IP アドレスを確認する

```
kubectl describe service [SERVICE_NAME]
```

* 出力の以下の行が IP アドレス

```
LoadBalancer Ingress:   [IP_ADDRESS]
```

## 動的 IP アドレスを静的に変更する

* https://console.cloud.google.com/networking/addresses/list から対象の IP アドレスを探して、タイプを「静的」に変更する

## Serivce 設定に IP アドレスを追加する

* spce の要素に以下を追加

```
  loadBalancerIP: "[IP_ADDRESS]"
```