TerraformでGKEを作ってVPC上に乗せる

## やりたいこと

terraformを使って、vpcとgkeを作成して、gkeをvpc上に乗せます。
以下の記事を参考にさせていただきましたので、詳細は下記をご確認ください！

- https://qiita.com/takasp/items/8104c175232e74217cc3
- https://qiita.com/kawakawaryuryu/items/c74a7a90a8b4e7e01c1f
- https://qiita.com/MahoTakara/items/85096f8b2632c802ab22

ついでにこちらの本も参考にさせていただきました！  

- Kubernetes完全ガイド (impress top gear)   青山 真也
  - https://www.amazon.co.jp/dp/4295004804/ref=cm_sw_r_tw_dp_U_x_mn4yDbB750WEK

## 手順

- vpcとgkeを作成
- gke上にnginxのサンプルコンテナを配置

## ソースコード

- githubで公開しています
  - https://github.com/fnaoto/sample-terraform/tree/master/gcp_kubernetes

## terraform

### terraformでGCPを使うときの事前設定

- サービスアカウントとjsonファイルの作成
  - https://cloud.google.com/iam/docs/creating-managing-service-account-keys
- terraformのbackend用にgcsバケットの作成
  - https://cloud.google.com/storage/docs/creating-buckets
- google compute engineのAPIを有効化
  - https://console.developers.google.com/apis/library/compute.googleapis.com
- gcloudコマンドのインストール
  - https://cloud.google.com/sdk/downloads

### ディレクトリ構成

```sh
→ tree
.
├── README.md
├── _provider.tf
├── _variable.tf
├── compute.tf
├── gcp_key.json
├── gke.tf
├── terraform.sh
└── tfvars
    └── sample.tfvars
```

### provider

- `credentials`にはサービスアカウントのjsonファイルのパスを指定しています
- `project`や`region`はtfvarsファイルで切り替えられるようにしています
- `backend`はあえて設定を省略して、コンソール上から入力できるようにしています
  - githubに上げるためやっているので、ハードコードでOKです！

```HCL:_provider.tf
provider "google" {
  credentials = "${file("${var.gcp_key_path}")}"
  project     = "${var.project_id}"
  region      = "${var.region}"
  zone        = "${var.zone}"
}

terraform {
  backend "gcs" {}
}
```

### tfvarsとvariables

- サンプル用に作ったので、よしなに変えていただければOKです！
- `project_name`にはGCPのプロジェク名を指定してください
- `project_id`にはGCPのプロジェクトIDをapply時に入力するようにしています
  - githubに公開するため、tfvarsには入れていません
- `gke_cidr_block`は下記を参考に作っていただけるとよいかと思います
  - https://tools.ietf.org/html/rfc1918#section-3
  - https://cloud.google.com/vpc/docs/configure-private-services-access
- `min_master_version`と`node_version`はできるだけ最新にしたほうが無難です
  - https://cloud.google.com/kubernetes-engine/docs/release-notes

```:sample.tfvars
project_name = "sample"
gcp_key_path = "./gcp_key.json"
region = "asia-northeast1"
zone = "asia-northeast1-a"
network = "default"
initial_node_count = "3"
description = "cluster"
min_master_version = "1.13.7-gke.8"
node_version = "1.13.7-gke.8"
machine_type = "g1-small"
disk_size = "10"
gke_cidr_block = "192.168.0.0/16"
```

```HCL:_variable.tf
# all

variable "project_id" {}
variable "project_name" {}
variable "gcp_key_path" {}

# gke

variable "region" {}
variable "zone" {}
variable "network" {}
variable "initial_node_count" {}
variable "description" {}
variable "min_master_version" {}
variable "node_version" {}
variable "machine_type" {}
variable "disk_size" {}
variable "gke_cidr_block" {}

# local

locals {
  ws = "${terraform.workspace}"
}
```

### vpcとglobal ip

- `google_compute_global_address`はkubernetesのingressで使う固定IPアドレスを生成しています
- `auto_create_subnetworks`をtrueにすると自動でサブネットを作ってくれます
  - ちなみに以前、自分でサブネットを作ってみましたがうまく動きませんでした・・・

```HCL:compute.tf
resource "google_compute_global_address" "gke" {
  name = "${var.project_name}-global-ip"
}

resource "google_compute_network" "gke" {
  name                            = "${var.project_name}-gke-vpc"
  routing_mode                    = "REGIONAL"
  auto_create_subnetworks         = "true"
  delete_default_routes_on_create = "false"
}
```

### gkeとnode pool

- gkeの`network`にはvpcを指定しています
- `remove_default_node_pool`はtrueにして、デフォルトで作られるnode poolを削除しています
  - node poolは`google_container_node_pool`で追加するのでデフォルトは必要ないです
- `cluster_ipv4_cidr_block`と`services_ipv4_cidr_block`は`/20`で分けています
  - `cidrsubnet(var.gke_cidr_block, 8, 1)`にして`/24`で分けてもいいです！
    - コンテナ数やクラスタ数の上限などはサービス毎に違うので、適宜考えていただけるといいかと思います
    - cidrsubnetの計算は`terraform console`を使えば簡単に確認できます！
- `preemptible`はfalseにしています
- `oauth_scopes`もサービス毎に使う権限が違いますので、考慮いただけるといいかと思います
- `autoscaling`でオートスケールするnode数を決めれます！
  - 今回は1〜3ノードでスケールします

```HCL:gke.tf
resource "google_container_cluster" "gke" {
  name                     = "${var.project_name}"
  location                 = "${var.zone}"
  network                  = "${google_compute_network.gke.self_link}"
  initial_node_count       = "${var.initial_node_count}"
  remove_default_node_pool = true
  description              = "${var.description}"

  min_master_version = "${var.min_master_version}"
  node_version       = "${var.node_version}"

  ip_allocation_policy {
    use_ip_aliases           = true
    cluster_ipv4_cidr_block  = "${cidrsubnet(var.gke_cidr_block, 4, 1)}"
    services_ipv4_cidr_block = "${cidrsubnet(var.gke_cidr_block, 4, 2)}"
  }
}

resource "google_container_node_pool" "gke" {
  name       = "${var.project_name}-node-pool"
  location   = "${var.zone}"
  cluster    = "${google_container_cluster.gke.name}"
  node_count = "${var.initial_node_count}"

  autoscaling {
    max_node_count = 3
    min_node_count = 1
  }

  node_config {
    machine_type = "${var.machine_type}"
    disk_size_gb = "${var.disk_size}"
    preemptible  = false

    oauth_scopes = [
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring",
      "https://www.googleapis.com/auth/compute",
    ]
  }
}
```

## Kubernetes

kubectlコマンドを使って適用していきます。
詳細は省略いたしますが、以下をご確認いただけるといいかと思います。

- https://qiita.com/suzukihi724/items/241f7241d297a2d4a55c
- https://qiita.com/ntoreg/items/74aa6de2f8f29b4a3b79

### deployment

- サンプルでnginxを動かしています
- `namespace`は事前に`kubectl create namespace`で作成してください
- `readinessProbe`でヘルスチェックしています
  - `/`で200のレスポンスを返さないコンテナの場合、`readinessProbe`は必須ですのでお気をつけ下さい

```yaml:deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
  namespace: sample-ns
  labels:
    app: sample
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
        - name: sample-container
          image: nginx:latest
          ports:
            - containerPort: 80
          readinessProbe:
            httpGet:
              path: /
              port: 80
          resources:
            limits:
              cpu: 200m
              memory: 10Mi
```

### ingress

- `kubernetes.io/ingress.global-static-ip-name`には`google_compute_global_address`で作ったIPアドレス名を指定します
- httpsを使う場合は`annotations -> kubernetes.io/ingress.allow-http`と`spec -> tls -> secretName`が必要です
  - kubernetesにsecretsとして証明書の登録も必要になります
- `serviceName`と`servicePort`には`service.yml`で設定したリソース名を指定します

```yaml:ingress.yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: sample-ingress
  namespace: sample-ns
  annotations:
    kubernetes.io/ingress.global-static-ip-name: sample-global-ip
spec:
  backend:
    serviceName: sample-service
    servicePort: service-port
```

### service

- `ports`に`name`をつけて置くと`ingress`の`servicePort`に名前で指定できます

```yaml:service.yml
apiVersion: v1
kind: Service
metadata:
  name: sample-service
  namespace: sample-ns
  labels:
    app: sample
spec:
  type: NodePort
  ports:
  - name: service-port
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: sample
```

## 確認

- GCPで作った固定のIPアドレスにブラウザでアクセスして確認します

<img width="573" alt="スクリーンショット 2019-08-27 1.24.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259386/40c2fd66-6d2e-6ce4-2391-c65f60e4806b.png">