🕸️

GKEクラスタへのAnthos Service Meshの導入

2022/05/10に公開

TL;DR

前の記事でTerraformでGKEクラスタを作成しましたが、さらにGKEについて深く学習しようと思い立ち、サンプルアプリケーションのデプロイとAnthos Service Meshの導入まで実施しましたので、その過程についてまとめてみました。

実施した方針について

GKEクラスタ

前回までに作成したTerraformコードを活用し、以下の変更ポイントを加えてみました。

  • GKEプライベートクラスタを作成し、GKEノードはVPCネットワーク内に作成したCloud NAT経由でインターネットにアクセス可能な構成
  • Cloud Shellからkubectlコマンドを実行したいので、コントロールプレーンのパブリックエンドポイントを有効化し、Cloud Shell(パブリックIPアドレス)からのアクセスを有効化
  • Anthos Service Meshの導入まで実施するので、ノードのマシンタイプはAnthos Service Meshの導入における最低要件である「e2-standard-4」を指定

サンプルアプリケーション

Google Cloudが公開しているマイクロサービス用デモアプリケーションである「Online Boutique」を使用させて頂きました。

https://github.com/GoogleCloudPlatform/microservices-demo

Anthos Service Mesh

マイクロサービス用デモアプリケーションのデプロイと合わせてサービスメッシュの機能についても把握できるように、名前と特長だけ知っていたAnthos Service Mesh(以降、ASMと記載します)の導入まで体験してみようと思い、Webサービスの本番環境での導入を想定して、ASMをIngressと組み合わせてみました。

初めてで色々参考サイトを拝見して調査したところ、ASMをIngressと組み合わせる場合、外部からのトラフィックは、Ingressで作成したロードバランサがまず最初に受けてから連携しているASM Ingress ゲートウェイ(Service、Pod)に流れて、その後VirtualServiceリソース(サンプルアプリケーションのService、Pod)に到達することで自分なりに解釈しました。

実施した手順について

GKEクラスタ、関連リソースの作成

今回の変更ポイントを取込み、以下のコードでStandardモードのGKEクラスタを作成しました。
合わせて、関連するリソースであるVPCネットワーク、サブネット、Cloud NATも同時に作成しました。

  • Terraformコード
# VPC
resource "google_compute_network" "vpc_network" {
  name                    = "btc4043-vpc"
  auto_create_subnetworks = false
  routing_mode            = "REGIONAL"
}

# Subnet1(asia-northeast1)
resource "google_compute_subnetwork" "an1_private1" {
  name          = "btc4043-an1-private1"
  ip_cidr_range = "192.168.1.0/24"
  region        = "asia-northeast1"
  network       = google_compute_network.vpc_network.id
}

# Cloud Router (for asia-northeast1)
resource "google_compute_router" "an1_router" {
  name    = "btc4043-an1-router"
  region  = google_compute_subnetwork.an1_private1.region
  network = google_compute_network.vpc_network.id
}

# External ip for Cloud NAT
resource "google_compute_address" "nat_external_ipaddress" {
  name = "btc4043-nat-external-ip"
}

# Cloud NAT
resource "google_compute_router_nat" "an1_nat" {
  name                               = "btc4043-an1-nat"
  router                             = google_compute_router.an1_router.name
  region                             = google_compute_router.an1_router.region
  nat_ip_allocate_option             = "MANUAL_ONLY"
  nat_ips                            = google_compute_address.nat_external_ipaddress.*.self_link
  source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"

  subnetwork {
    name                    = google_compute_subnetwork.an1_private1.id
    source_ip_ranges_to_nat = ["ALL_IP_RANGES"]
  }
}

# Service account for GKE Cluster node
resource "google_service_account" "sc_gke1" {
  account_id   = "btc4043-sc-gck1"
  display_name = "Service Account for GkE1"
}

# GKE cluster(Standard)
resource "google_container_cluster" "gke1" {
  name     = "btc4043-gke1-cluster"
  location = "asia-northeast1"

  remove_default_node_pool = true
  initial_node_count       = 1

  release_channel {
    channel = "STABLE"
  }

  min_master_version = "1.21.10-gke.2000"

  networking_mode = "VPC_NATIVE"
  ip_allocation_policy {
    cluster_ipv4_cidr_block  = "10.10.0.0/16"
    services_ipv4_cidr_block = "10.20.0.0/16"
  }

  network    = google_compute_network.vpc_network.self_link
  subnetwork = google_compute_subnetwork.an1_private1.self_link

  private_cluster_config {
    enable_private_nodes = true
    enable_private_endpoint = false
    master_ipv4_cidr_block = "192.168.100.0/28"

    master_global_access_config {
    enabled = true
    }
  }

  master_authorized_networks_config {
  }

  logging_config {
    enable_components = [
        "SYSTEM_COMPONENTS",
        "WORKLOADS"
    ]
  }

  monitoring_config {
    enable_components = [
        "SYSTEM_COMPONENTS",
    ]
  }

  addons_config {
    http_load_balancing {
      disabled = false
    }
  }

  workload_identity_config {
    workload_pool = "btc4043.svc.id.goog"
  }

  maintenance_policy {
    recurring_window {
      start_time = "2022-05-07T17:00:00Z"
      end_time   = "2022-05-07T21:00:00Z"
      recurrence = "FREQ=WEEKLY;BYDAY=FR,SA,SU"
    }
  }

}

# GKE nodepool
resource "google_container_node_pool" "gke1_nodes" {
  name       = "btc4043-node-pool"
  location   = "asia-northeast1"
  cluster    = google_container_cluster.gke1.name
  node_count = 1

  autoscaling {
    min_node_count = 1
    max_node_count = 2
  }

  upgrade_settings {
    max_surge       = 1   
    max_unavailable = 0
  }

  management {
    auto_repair  = true
    auto_upgrade = true
  }

  node_config {
    machine_type = "e2-standard-4"

    service_account = google_service_account.sc_gke1.email
    oauth_scopes    = [
      "https://www.googleapis.com/auth/cloud-platform"
    ]
  }
}
  • terraform applyを実行して、リソースの作成を実施
terraform apply

アプリケーションデプロイの事前準備

GKEクラスタに対して、サンプルアプリケーションのデプロイとASMのインストールを実施するための事前準備を行います。
これ以降は、対象のプロジェクトでCloud Shellを起動し、コマンドを発行して進めて行きます。

GKE追加設定とクラスタ認証情報の取得

# 環境変数の定義
$ export CLUSTER_NAME="btc4043-gke1-cluster"
$ export CLUSTER_LOCATION="asia-northeast1"

# Cloud Shell(インスタンス)のグローバルIPアドレスを取得
$ curl http://ifconfig.me/

# Cloud Shell(インスタンス)のグローバルIPアドレスをコントロールプレーンの承認済みネットワークアドレスに追加(${Cloud_Shell_IP}部分は、先のコマンドで取得した値に/32を付与して置換)
$ gcloud container clusters update ${CLUSTER_NAME} --region ${CLUSTER_LOCATION} --enable-master-authorized-networks --master-authorized-networks "${Cloud_Shell_IP}"

# クラスタ認証情報を取得
$ gcloud container clusters get-credentials ${CLUSTER_NAME} --region ${CLUSTER_LOCATION}

GKEプライベートクラスタ用のファイアウォールルールの追加設定

# GKEプライベートクラスタを作成したときに自動的に作成されるファイアウォールルール名を確認
$ gcloud compute firewall-rules list --filter="name~gke-${CLUSTER_NAME}-[0-9a-z]*-master"

# 自動サイドカー挿入(Auto injection)と「istioctl version」及び「istioctl version」コマンドが実行できるように上記ファイアウォールルールにポート開放設定を追加 (${FIREWALL_RULE_NAME}部分は、先のコマンドで取得したファイアウォールルール名の値に置換)
$ gcloud compute firewall-rules update ${FIREWALL_RULE_NAME} --allow tcp:10250,tcp:443,tcp:15017,tcp:15014,tcp:8080

ASMの導入

事前準備とASMインストール用スクリプトのダウンロード

作業用ディレクトの作成「work-asm」とASMインストール用スクリプト「asmcli」のダウンロードを実施します。

# 作業ディレクトリの作成
$ mkdir work-asm

# asmcliのダウンロードと実行権限付与
$ curl https://storage.googleapis.com/csm-artifacts/asm/asmcli_1.13 > asmcli
$ chmod +x asmcli

ASMのインストール

インストールオプションで、特に悩んだ点は以下の3つで、ドキュメントを読んで方針を決めてみました。

  • プロジェクトには、今回のテスト用のクラスタが1つしかないので、フリートホストプロジェクトは指定しない。
  • 「--enable_all」オプションを付与することで必要なIAM権限、Google Cloud APIを有効にする。
  • Anthos Service Mesh 認証局(Mesh CA)を有効にする。
# インストールスクリプトの実行 (${PROJECT_ID}部分は、GKEクラスタが作成されているプロジェクト名の値に置換)
$ ./asmcli install --project_id ${PROJECT_ID} --cluster_name ${CLUSTER_NAME} --cluster_location ${CLUSTER_LOCATION} --output_dir ~/work-asm --enable_all --ca mesh_ca

ASM Ingress ゲートウェイのデプロイ

事前準備

ASM Ingress ゲートウェイのnamespace「asm-ingress」の作成、自動サイドカー挿入(Auto injection)の有効化を行います。

# ASM Ingressgatewayのnamespaceの作成
$ kubectl create namespace asm-ingress

# istiodのリビジョンラベルの確認
$ kubectl get deploy -n istio-system -l app=istiod -o jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'

# 自動サイドカー挿入(Auto injection)の有効化(${REVISION}は、先のコマンドで取得したistiodのリビジョンラベルの値に置換)
$ kubectl label namespace asm-ingress istio.io/rev=${REVISION} --overwrite

ちなみに、検証初期に以下のエラーに遭遇して上手くいきませんでした。
調べたところ、ASM Ingress ゲートウェイのマニフェストではistio-proxyのコンテナイメージが「image: auto」というプレースホルダーになっており、自動サイドカー挿入を有効化した状態でないと、istiod-proxyの最新コンテナイメージのpullが上手くいかないようでした。

Failed to pull image "auto": rpc error: code = Unknown desc = failed to pull and unpack image "docker.io/library/auto:latest": failed to resolve reference "docker.io/library/auto:latest": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed

https://github.com/istio/istio/issues/35789

ASM Ingress ゲートウェイのデプロイ

https://cloud.google.com/architecture/exposing-service-mesh-apps-through-gke-ingress#deploying_gke_ingress

上記の公式チュートリアルに沿って、各リソース(Deployment、Service、Backend、Ingress、Ingress ゲートウェイ)のマニフェスト作成しました。
但し、今回はシンプルにIngress ゲートウェイまで構築できるように、各マニフェストにて以下設定について調整させて頂きました。

  1. 外部公開ポートはHTTPに設定(TLS証明書の定義なし)
  2. セキュリティポリシーは定義しない
  3. DNS設定、静的グローバルIPアドレスの取得はしない
  • Deploymentの作成(マニフェスト:ingress-deployment.yaml)
$ kubectl apply -f ingress-deployment.yaml
  • Serviceの作成(マニフェスト:ingress-service.yaml)
$ kubectl apply -f ingress-service.yaml
  • BackendConfigの作成(マニフェスト:ingress-backendconfig.yaml)
$ kubectl apply -f ingress-backendconfig.yaml
  • Ingressの作成(マニフェスト:ingress.yaml)
$ kubectl apply -f ingress.yaml
  • Ingress ゲートウェイの作成(マニフェスト:ingress-gateway.yaml)
$ kubectl apply -f ingress-gateway.yaml

Online Boutiqueサンプルアプリケーションのデプロイ

事前準備

Online Boutiqueサンプルアプリケーションのnamespace「onlineboutique」作成、自動サイドカー挿入(Auto injection)の有効を行います。

# Online Boutiqueのnamespaceの作成
$ kubectl create namespace onlineboutique

# istiodのリビジョンラベルの確認
$ kubectl get deploy -n istio-system -l app=istiod -o jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'

# 自動サイドカー挿入(Auto injection)の有効化 (${REVISION}は、先のコマンドで取得したistiodのリビジョンラベルの値に置換)
$ kubectl label namespace onlineboutique istio.io/rev=${REVISION} --overwrite

Online Boutique サンプルアプリケーションのデプロイと確認

# サンプルアプリケーション用マニフェストのダウンロード
$ curl -LO https://raw.githubusercontent.com/GoogleCloudPlatform/microservices-demo/main/release/kubernetes-manifests.yaml

# デプロイ
$ kubectl apply -f kubernetes-manifests.yaml -n onlineboutique

# Pod、サービスのデプロイ確認
$ kubectl get pods -n onlineboutique
$ kubectl get services -n onlineboutique

VirtualServiceマニフェスト作成

VirtualServiceのマニフェストを「frontend-virtualservice.yaml」として作成

$ cat <<EOF > frontend-virtualservice.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: frontend-ingress
  namespace: onlineboutique
spec:
  hosts:
  - "*"
  gateways:
  - asm-ingress/asm-ingressgateway
  http:
  - route:
    - destination:
        host: frontend
        port:
          number: 80
EOF

VirtualServiceをデプロイ

$ kubectl apply -f frontend-virtualservice.yaml

動作確認

IngressのグローバルIPアドレスを確認

先述した通り、外部からのトラフィックは、まず最初にIngressで受け取るので、このグローバルIPアドレスを調べます。

$ kubectl get ingress gke-ingress -n asm-ingress

ブラウザからのアクセスを試行

ブラウザを立ち上げて、http://[IngressのグローバルIPアドレス] にアクセスを試行してみたところ、想定通りOnline Boutique サンプルアプリケーションのトップ画面が表示されました!

コンソール画面で確認

Ingress ゲートウェイのService

Ingress

Anthos サービスメッシュトポロジ

まとめ

Terraformで作成したGKE環境(Standardモード)にサンプルアプリケーションのデプロイとAnthos Service Meshの導入を実施してみました。
初めての試みで時間がかけてしまいましたが、目標にしていた所まで実際に検証して記事にすることができました。
引続き、ASMのより詳細な機能について学習していこうと思います。

参考

Anthos Service Mesh をインストールする

Anthos Service Mesh を導入、移行、そして使いこなしてみよう

Discussion