最強のマネージドNATサービス Cloud NATとGKEのセットアップ手順が公開されたので翻訳がてら動かしてみました

Cloud NATが発表されて以降、心待ちにしていたGKEでのセットアップ方法がしれっと公式ドキュメントに追加されていました！！
（いつから公開されていたんでしょうね？自分が気付いてなかっただけとかいうオチもありえますが・・笑）

> [Example GKE Setup  \|  Cloud NAT  \|  Google Cloud](https://cloud.google.com/nat/docs/gke-example)

という訳で、このエントリーではドキュメントを翻訳しながら、実際にKubernetesクラスタを作成していきたいと思います！！ちなみに**Cloud NATがいかに最強か？**という事は下記のエントリーが分かりやすくまとまっているので、一読されることをオススメします。

[マネージドNATサービス GCP Cloud NAT がリリースされました \- 本日も乙](https://blog.jicoman.info/2018/10/gcp-cloud-nat/)


# イントロダクション
このページはCloud NATの設定方法を説明します。Cloud NATをセットアップする前に[Cloud NAT Overview](https://cloud.google.com/nat/docs/overview)も読んでおきましょう。


# 事前準備
## 1. IAMパーミッション
*[roles/compute.networkAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute_engine_roles)* ロールは下記の権限を得るために必要です。

- Cloud Router上へのNATゲートウェイの作成
- NAT IPの予約・割り当て
- NATゲートウェイによるIPアドレス変換を適用すべきトラフィックが流れるNATサブネットを指定

## 2. GCPのセットアップ
1. GCPプロジェクトを選択、または作成
2. プロジェクトに対して課金が有効になっていることを確認
3. Cloud SDKをインストールし、初期化する

今回のチュートリアルのために`k8s-sample-app-20181220`というプロジェクトを作りました。リージョンは`asia-northeast1`（東京リージョン）を使います。

```sh
# set environment variables
$ PRJ=k8s-sample-app-20181220
$ REGION=asia-northeast1

$ gcloud config set project $PRJ

$ gcloud config list --format='text(core.project)'
core.project: k8s-sample-app-20181220
```


# GKEのセットアップ
## Step 1: VPCネットワークとサブネットを作成
既にVPCネットワークとサブネットがあるなら、次のステップに進みましょう。

まずはカスタムモードで新しいVPCネットワークを作成します。

```sh
$ NW=custom-network1

$ gcloud compute networks create $NW --subnet-mode custom

NAME             SUBNET_MODE  BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
custom-network1  CUSTOM       REGIONAL
```
**注意：** これはVPCネットワークなので、ネットワークレベルのIPv4アドレスのレンジやゲートウェイIPはありません。

次に、リージョン情報を付けたサブネットを作成しましょう。この例では`asia-northeast1`（東京リージョン）に`192.168.1.0/24`のIPアドレスレンジを割り当てます。

```sh
$ SUBNW=subnet-asia-northeast1-192

$ gcloud compute networks subnets create $SUBNW \
  --network $NW \
  --region $REGION \
  --range 192.168.1.0/24

NAME                        REGION           NETWORK          RANGE
subnet-asia-northeast1-192  asia-northeast1  custom-network1  192.168.1.0/24
```

## Step 2: テスト用に踏み台サーバーを作成
Cloud NATをテストするために、外部IPアドレスを持たないテスト用のVMインスタンスを用意する必要があります。そのインスタンスは外部IPアドレスを持たないため、直接SSHで接続することができません。そこで、まずは外部IPアドレスを持つ踏み台サーバーに接続し、そこから内部IPアドレスを使って目標のインスタンスに接続します。

このステップでは[踏み台サーバー](https://cloud.google.com/solutions/connecting-securely#bastion)用のVMを作成します。（後のステップでテストインスタンスへ接続するために、この踏み台を使います。）
今回はインスタンスのゾーンは`asia-northeast1-a`を指定します。

```sh
$ ZONE=asia-northeast1-a

$ gcloud compute instances create bastion-1 \
  --image-family debian-9 \
  --image-project debian-cloud \
  --network $NW \
  --subnet $SUBNW \
  --zone asia-northeast1-a

NAME       ZONE               MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
bastion-1  asia-northeast1-a  n1-standard-1               192.168.1.2  35.221.81.168  RUNNING
```

## Step 3: プライベートk8sクラスターを作成
`enable-private-nodes`を指定することでk8sクラスターをプライベートにできます。

```sh
$ gcloud container --project $PRJ clusters create "nat-test-cluster" \
  --zone $ZONE \
  --username admin \
  --cluster-version "1.11.2-gke.26" \
  --num-nodes "3" \
  --machine-type n1-standard-1 \
  --image-type COS \
  --disk-type pd-standard \
  --disk-size 100 \
  --scopes "https://www.googleapis.com/auth/compute","https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append" \
  --enable-cloud-logging \
  --enable-cloud-monitoring \
  --enable-private-nodes \
  --enable-private-endpoint \
  --master-ipv4-cidr 172.16.0.0/28 \
  --enable-ip-alias \
  --network projects/$PRJ/global/networks/$NW \
  --subnetwork projects/$PRJ/regions/$REGION/subnetworks/$SUBNW \
  --max-nodes-per-pool "110" \
  --enable-master-authorized-networks \
  --addons HorizontalPodAutoscaling,HttpLoadBalancing,KubernetesDashboard \
  --enable-autoupgrade \
  --enable-autorepair

NAME              LOCATION           MASTER_VERSION  MASTER_IP   MACHINE_TYPE   NODE_VERSION   NUM_NODES  STATUS
nat-test-cluster  asia-northeast1-a  1.11.2-gke.26   172.16.0.2  n1-standard-1  1.11.2-gke.26  3          RUNNING
```

## Step 4: SSH接続を許可するためのファイアウォールを作成

```sh
$ gcloud compute firewall-rules create allow-ssh \
  --network $NW \
  --allow tcp:22

NAME       NETWORK          DIRECTION  PRIORITY  ALLOW   DENY  DISABLED
allow-ssh  custom-network1  INGRESS    1000      tcp:22        False
```

## Step 5: ノードへログインし、外部ネットワークから隔離されていることを確認
いずれかのクラスターノード名を取得しましょう。ノード名は`gke-nat-test-cluster-default-pool-7d3a845e-c2qf`などです。

```sh
$ gcloud compute instances list

NAME                                             ZONE               MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
bastion-1                                        asia-northeast1-a  n1-standard-1               192.168.1.2  35.221.81.168  RUNNING
gke-nat-test-cluster-default-pool-7d3a845e-c2qf  asia-northeast1-a  n1-standard-1               192.168.1.4                 RUNNING
gke-nat-test-cluster-default-pool-7d3a845e-jkt6  asia-northeast1-a  n1-standard-1               192.168.1.3                 RUNNING
gke-nat-test-cluster-default-pool-7d3a845e-nm15  asia-northeast1-a  n1-standard-1               192.168.1.5                 RUNNING
```

Compute EngineのSSHキーをローカルマシンに追加します。

```sh
$ ssh-add ~/.ssh/google_compute_engine
```

踏み台サーバー`bastion-1`に接続します。
インスタンスへの初回アクセスの場合はGCPはSSHキーを生成します。

```sh
$ gcloud compute ssh bastion-1 --zone $ZONE -- -A
```

踏み台サーバーからk8sクラスターのいずれかのノード名を指定してSSHで接続します。（**この手順だけ公式ドキュメントの"GCLOUD"版で記載漏れがありましたのでご注意を**）

```sh
ssh gke-nat-test-cluster-default-pool-7d3a845e-c2qf -A
```

`curl`コマンドが入っていればどのノードでも良いのですが、今回はプロンプトからStackdriverエージェント用のコンテナに接続します。

```sh
$ docker exec -it $(docker container list | grep stackdriver | awk '{print $1}') /bin/bash
```

ちなみに、**公式ドキュメントでは`kube-dns`コンテナに入る手順が示されていますが、今回のチュートリアルで使用しているk8sエンジンのバージョン`1.11.2-gke.26`では`kube-dns`のコンテナは見つかりませんでした**
kube-dnsは`CoreDNS`に統合されるらしいので、それが関係していそうです。

> CoreDNS is currently a Beta feature in Kubernetes and on course to being graduated to General Availability (GA) for Kubernetes 1.11
[Migration from kube\-dns to CoreDNS](https://coredns.io/2018/05/21/migration-from-kube-dns-to-coredns/)

コンテナ内に入ったら、外部のサイトにアクセスしてみましょう。クラスターがプライベートになっていれば、結果が返って返って来ないはずです。

```sh
$ curl example.com
```

## Step 6: Cloud Routerを使ってNATを設定する
次に、Cloud NATを使うインスタンスと同じリージョンにCloud Routerを作成する必要があります。Cloud NATはVM上にNAT情報を保存するためだけに使われます。実際のNATゲートウェイの一部としては使われません。

この設定により、リージョンに属する全てのインスタンスが、プライマリおよびエイリアスIP範囲のためにCloud NATを使うことができるようになります。また、NATゲートウェイのための外部IPアドレスを自動で割り当てます。その他のオプションは[gcloud command-line interface](https://cloud.google.com/sdk/gcloud/reference/beta/compute/routers/)のドキュメントを参照してください。

**注記:** Cloud NATはNATの設定情報をグルーピングするためだけにCloud Routerを使います。（コントロール側）Cloud NATはCloud RouterにBGP（Border Gateway Protocol）の利用や、ルーティング追加の命令は出しません。また、NATのトラフィックはCloud Routerを通過しません。（データ側）

Cloud Routerを作成します。

```sh
$ gcloud compute routers create nat-router \
  --network $NW \
  --region $REGION

NAME        REGION           NETWORK
nat-router  asia-northeast1  custom-network1
```

routerに設定を追加します。

```sh
$ gcloud compute routers nats create nat-config \
  --router-region $REGION \
  --router nat-router \
  --nat-all-subnet-ip-ranges \
  --auto-allocate-nat-external-ips
```

## Step 7: 再度インターネットアクセスを試行
NAT設定が反映されるのに長いと3分程度かかることもあるため、再度インターネットアクセスを試みる前に少し待つ必要があります。

もし、先程ログインしたコンテナに入ったままなら、再接続を行い、curlコマンドを実行しましょう。

```sh
$ curl example.com

<!doctype html>
<html>
<head>
    <title>Example Domain</title>
...
...
</body>
</html>
```

今度はHTMLがレスポンスとして返って来たはずです。
これでプライベートクラスターからNATゲートウェイを経由して外部ネットワークに接続できたことが確認できましたね！！


最後にチュートリアルのプロジェクトを削除しておきましょう。

```sh
$ gcloud projects delete $PRJ
```

以上でチュートリアルは終了です。


# まとめ
動作確認のための手順を除くと、非常に少ない設定でGKEにCloud NATを組み込むことができましたね！
Cloud NATが登場するまでは、NATゲートウェイ用のGCEインスタンスを自前で用意して、ルーティング設定を行う必要がありました。この構成では、NATゲートウェイのインスタンスが単一障害点になったり、帯域のボトルネックになったりしていました。しかし、マネージドなCloud NATを使うことで、NATインスタンス自体が無くなり、レイテンシやスループットが安定するようになります。これらの要因で、既存のオンプレや他のパブリッククラウドと組み合わせることに踏み切れなかった方も一度、導入を検討してみてはいかがでしょうか！？

それではCloud NATで快適なクラウドライフを〜！！