【GKE】GKE × Terraformで基本的なk8s環境を構築する

Docker

どうも、株式会社KIYONOにてエンジニアをしております。

今回はGKE(Google Kubernetes Engine)を使用した基本的なKubernetes環境を構築する手順についてご紹介致します。今回はk8sクラスター上にnginxをデプロイし、ロードバランサーで外部公開するまでをゴールとします。

1.前提条

  • GCPアカウントをセットアップ済み
  • Terraformを使用できる状態
  • kubectlコマンドをインストール済み

2.主な使用技術

基本

名称 説明
GKE GCPのk8sマネージドサービス
kubectl kubernetesクラスターの操作(CLI)
Terraform 1.2.4 IaC(※バージョンによる影響にご注意下さい。)

Terraformモジュール

GCP上へのVPC、k8sクラスターの作成に下記の公式モジュール(Terraform Registry)を使用します。モジュールを使用することで煩雑な設定を意識するとこなく様々なリソースを作成できます。

名称 説明
terraform-google-modules/network
k8sクラスターを配置するVPCを設定、作成
terraform-google-modules/kubernetes-engine
k8sクラスターの設定、作成

3.準備

プロジェクト構成

今回作成するプロジェクト構成になります(※主要なファイルのみ記載)。まずはこの状態を目指します。

.
├── main.tf ...インフラ構成を記述
├── output.tf ...クラスター名を出力
├── kubeconfig-dev...kubeconfigファイル(apply後に生成)
├── secret.json ...サービスアカウントの鍵json
├── terraform.tfvars ...環境に依存する値や機密性の高い値を変数化(※.gitignore推奨)
└── variables.tf ...変数定義

サービスアカウントの作成

GCPコンソールからterraformで使用するGCPサービスアカウントを作成します。オーナー権限を付与して鍵(json)を取得しsecret.jsonとしてプロジェクト配下に設置してください。(※必ずGitの管理対象から外してください。また、強力な権限になりますので扱いには注意してください。)

tfファイルの作成

実際にterraformコードを記述していきます。

main.tf

インフラ構成を記述します。VPC、GKEの二本軸です。※変数部分は後述

# provider setting
terraform {
  required_version = "= 1.2.4"
    required_providers {
    google = {
      source  = "hashicorp/google"
      version = "4.31.0"
    }
  }
}

provider "google" {
  credentials = file("./secret.json")

  project = var.gcp_project_id
  region  = var.gcp_region
  zone    = "${var.gcp_region}-a"
}

# network
module "vpc" {
  source       = "terraform-google-modules/network/google"
  version      = "~> 4.0"
  project_id   = var.gcp_project_id
  network_name = "${var.network}-${var.env_name}"
  subnets = [
    {
      subnet_name   = "${var.subnetwork}-${var.env_name}"
      subnet_ip     = "10.10.0.0/16"
      subnet_region = var.gcp_region
    },
  ]
  secondary_ranges = {
    "${var.subnetwork}-${var.env_name}" = [
      {
        range_name    = var.ip_range_pods_name
        ip_cidr_range = "10.20.0.0/16"
      },
      {
        range_name    = var.ip_range_services_name
        ip_cidr_range = "10.30.0.0/16"
      },
    ]
  }
}

# gke
module "gke" {
  source                 = "terraform-google-modules/kubernetes-engine/google//modules/private-cluster"
  project_id             = var.gcp_project_id
  name                   = "${var.cluster_name}-${var.env_name}"
  regional               = true
  region                 = var.gcp_region
  network                = module.vpc.network_name
  subnetwork             = module.vpc.subnets_names[0]
  ip_range_pods          = var.ip_range_pods_name
  ip_range_services      = var.ip_range_services_name
  node_pools = [
    {
      name                      = "node-pool"
      machine_type              = "n2-standard-2"
      node_locations            = "${var.gcp_region}-a" # 複数のゾーンを指定できる
      min_count                 = 1
      max_count                 = 2
      disk_size_gb              = 30
    },
  ]
}

マルチゾーンクラスタによる冗長化構成にしたい場合は、node_locationsへ複数のゾーンを記載します。

続いて、kubetcltからクラスターへのアクセス認証に使用するkubeconfigファイルを作成するコードを記述します。

# gke auth
module "gke_auth" {
  source = "terraform-google-modules/kubernetes-engine/google//modules/auth"
  depends_on   = [module.gke]
  project_id   = var.gcp_project_id
  location     = module.gke.location
  cluster_name = module.gke.name
}

# kubeconfig
resource "local_file" "kubeconfig" {
  content  = module.gke_auth.kubeconfig_raw
  filename = "kubeconfig-${var.env_name}"
}

terraform applyによるリソース作成完了後に"kubeconfig-${var.env_name}"ファイル名の認証情報が記載されたファイルが生成されます。今回の場合はkubeconfig-devとなります。

terraform.tfvars

プロジェクト固有の値を設定します。ここに定義した値は後述するvariable変数に自動的に代入されます。

gcp_project_id="your-project-id"
gcp_region="your-region1"

variables.tf

main.tfから参照する変数の一式を定義します。

# from terraform.tfvars
variable gcp_project_id {
  description = "GCP project id"
}
variable gcp_region {
  description = "GCP region"
}

# others
variable "cluster_name" {
  description = "name of gke cluster"
  default     = "dev-cluster"
}
variable "env_name" {
  description = "name of environment"
  default     = "dev"
}
variable "network" {
  description = "name of VPC"
  default     = "dev-gek-network"
}
variable "subnetwork" {
  description = "name of subnetwork"
  default     = "dev-gke-subnet"
}
variable "ip_range_pods_name" {
  description = "name of secondary ip range used in pods"
  default     = "ip-range-pods"
}
variable "ip_range_services_name" {
  description = "name of secondary ip range used in services"
  default     = "ip-range-services"
}

output.tf

terraform apply後に作成されたクラスター名称がターミナルに出力されます。

output "cluster_name" {
  description = "cluster name"
  value       = module.gke.name
}

インフラの構築

terraformコマンドを使用して実際にコード定義したインフラをGCP上へ構築します。以下のコマンドを実施してください。

# モジュールのセットアップ
$ terraform init

# ドライランを実施し、作成されるリソースを確認する
$ terraform plan

# 実際にリソースを作成する
$ terraform apply

リソースが作成されるまで10分程かかります。

※また、リソースの作成後プロジェクト配下にkubeconfig-devファイルが作成されますので必ずバージョン管理から外してください。

クラスターへの接続と確認

kubectlで実際に作成されたクラスターを確認してみます。

# kubectlコマンドが対象のクラスターを見にいくように下記を実施します。
$ export KUBECONFIG="${PWD}/kubeconfig-dev"

# クラスターにアクセスしnodeを確認する
$ kubectl get nodes
NAME                                        STATUS ROLES  AGE   VERSION
gke-dev-cluster-dev-node-pool-a9c02cfb-tpf8 Ready  <none> 5m55s v1.24.3-gke.900

4.デプロイと公開

nginxのデプロイ

主にクラスター内部へのリソースの作成、アプリケーションのデプロイはymlで記述した「マニフェストファイル」を使用します。まずはデプロイ用のマニフェストファイルを作成します。

deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-nginx
spec:
  selector:
    matchLabels:
      name: hello-nginx
  template:
    metadata:
      labels:
        name: hello-nginx
    spec:
      containers:
        - name: app
          image: nginx:alpine # nginxイメージ
          ports:
            - containerPort: 80

詳細な説明は省きますが、上記ファイルにて特に重要なのはkindプロパティで、作成するk8sオブジェクトの種類を指定します。Deploymentオブジェクトは使用するイメージバージョンに変更がある際などの容易なローリングアップデートをサポートします。その他主要なプロパティは以下の通りです。

metadata … k8sオブジェクトの一意の識別名(オブジェクト種別単位)

spec.selector … 対象となるオブジェクト識別名

デプロイ

deployment.ymlを用意できたので実際にkubectlを使用してクラスターへnginxのデプロイと確認を行います。

# デプロイ
$ kubectl apply -f deployment.yml

# 確認
$ kubectl get pods
NAME                         READY STATUS  RESTARTS AGE
hello-nginx-5dd8f754fd-dbzgd 1/1   Running 0        4m39s

ポートフォワーディングで接続する

nginxのデプロイはできましたが、コンテナはpublicIPを持っておらずこの時点では外部公開されていません。ロードバランサー経由で公開する前にまずはポートフォワーディングを利用して8080ポートにマッピングしlocalhost:8080にアクセスしてみます。Welcomeページが表示されるはずです。

# 8080ポートへマッピング
$ kubectl port-forward hello-nginx-5dd8f754fd-dbzgd 8080:80

ロードバランサーの作成

ロードバランサー用のマニフェストを定義してnginxコンテナをネットワークに公開します。

loadbalancer.yml

apiVersion: v1
kind: Service
metadata:
  name: hello-nginx
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 80
  selector:
    name: hello-nginx

Serviceはネットワークに関わるオブジェクトになりspec以下で設定部分を記述しています。

作成と確認

先ほどと同じ要領でロードバランサーを設置します。

# ロードバランサーの作成
$ kubectl apply -f loadbalancer.yml

# 確認
$ kubectl get services
NAME        TYPE         CLUSTER-IP    EXTERNAL-IP  PORT(S)      AGE
hello-nginx LoadBalancer 10.30.195.150 34.85.90.129 80:31792/TCP 48s

EXTERNAL-IPに表示されたpublicIPにブラウザでアクセスし、nginxのウェルカムページが表示されたら成功です。

作成したGCPリソースを削除する場合は下記コマンド実行してください。

$ terraform destroy

5.まとめ

いかがでしたでしょうか、本記事ではterraformを使用した基本的なGKE構築からwebサーバー(nginx)の公開の手順をご紹介致しました。k8sは「コンテナオーケストレーション」と銘打っている通り同一クラスター内で様々なコンテナをマイクロサービス展開、構成管理できるのが強みの一つです。今回はnginxのデプロイまででしたので、そのような強みを生かした構成とは言えないのが正直なところです。より深掘りした内容は追々、ご紹介できればと思います。

コメント

タイトルとURLをコピーしました