GKE Autopilot 触ってみました

Ryo Tozawa

2022.5.12

社内プロダクトではこんな感じで GKE Autopilot を使ってます

注意する箇所

Terraform

  • google provider のバージョンを一定以上に上げる必要がある
  • 公式の GCP Terraform Module では,enable_autopilot オプションは未サポート
    google_container_cluster リソースを使用する
  • 設定できないアドオンを記述している場合はエラーが出るので削除する

利用できるアドオン

  • HTTP ロードバランシング (NEG等)
  • VPA (垂直 Pod オートスケーリング)

デフォルト

  • CSI ドライバ
  • NodeLocal DNSCache

利用できないアドオン

  • DataPlane V2 (Cillium)

GKE Autopilot Terraform Sample

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "3.90.1"
    }
    google-beta = {
      source  = "hashicorp/google-beta"
      version = "3.90.1"
    }
  }
}

resource "google_container_cluster" "synthetics" {
  provider = google

  name    = "${local.name}-synthetics"
  project = var.project_id

  location         = var.region
  node_locations   = ["${var.region}-a", "${var.region}-b", "${var.region}-c"]
  network          = google_compute_network.this.id
  enable_autopilot = true

  release_channel {
    channel = "REGULAR"
  }

  private_cluster_config {
    enable_private_nodes    = true
    enable_private_endpoint = false
    master_ipv4_cidr_block  = "10.0.2.0/28"
  }

  subnetwork        = "projects/${var.project_id}/regions/${var.region}/subnetworks/${google_compute_subnetwork.tokyo.name}"

  min_master_version = local.gke_min_release_version

  logging_service    = "logging.googleapis.com/kubernetes"
  monitoring_service = "monitoring.googleapis.com/kubernetes"

  vertical_pod_autoscaling {
    enabled = true
  }

  master_authorized_networks_config {
    dynamic "cidr_blocks" {
      for_each = merge(
        local.master_authorized_networks_config,
        var.additional_master_authorized_networks_config
      )
      content {
        cidr_block   = cidr_blocks.value.cidr_block
        display_name = cidr_blocks.value.display_name
      }
    }
  }

  addons_config {
    http_load_balancing {
      disabled = false
    }

    horizontal_pod_autoscaling {
      disabled = false
    }
  }

  ip_allocation_policy {
    cluster_secondary_range_name  = local.pod_2_range_name
    services_secondary_range_name = local.service_2_range_name
  }

  maintenance_policy {
    daily_maintenance_window {
      start_time = "15:00"
    }
  }

  lifecycle {
    ignore_changes = [node_pool, initial_node_count, resource_labels["asmv"], resource_labels["mesh_id"]]
  }

  timeouts {
    create = "45m"
    update = "45m"
    delete = "45m"
  }
}

Spot Instance (≒ Preemptive / SpotVM)

  • GKE クラスタ構築時には特に設定の必要はなし
  • Pod の NodeSelector に cloud.google.com/gke-spot=true を指定する
  • Spot 含めて、課金は Pod が利用するリソース単位で kube-system の Pod は含まれない

スポットの場合

  • CPU = 1vCPU/H: $0.0133 = 1.6円
  • Memory = 1GB/H: $0.0014767 = 18円
  • Volume = 1GB/H: $0.0000548 = 0.007円

リソースの制限参考

  • デフォルト:CPU (500m) / Memory (2GiB) / Storage (1GiB)
  • 最低:CPU (250m) / Memory (512MiB) / Storage (10MiB)

※ クラスタの料金は別途かかります

運用

考えないこと

Cluster / Node のアップグレードの実施
→ 利用するリソースの API Version 互換性を追従しメンテナンスすることは必要

Node / Pod のスケール実施
→ 自動で実施してくれるため静観
※ Cluster Autoscaler がたまにエラーを吐いてスケールできないことがあるのでそこの監視は忘れずに

考えること

ロギング

実行が終了して空いたノードに関しては,縮退されるので Cloud Logging でみるのがいいかも(例 CronJob / Job など)

早めのスケールをしたい

Scheduling API の PriorityClass を低めに設定した バルーン Pod を設置する Adding spare capacity to GKE Autopilot with balloon pods

  • 余剰リソースを停止すれば,Pod がスケジュールできると判断すればそのように動く (数秒待機)
  • 余剰リソースを停止してもスケジュールできないと判断すればノードを増やす (数分待機)
  • Helm とか実行する時に admission 系の Pod が検査してから流すタイプのやつは待機時間があるので注意が必要

注意
余剰バルーンをどれくらい用意するかで「コスト」や「余剰リソースで差し替え可能か」が決まること

kube-system の制限

参照のみ許可されている
→ 地道に検索

注意

  • ツール や Helm Charts によっては default で kube-system にリソースを作成するものもある
    例: argocd cluster add ${KUBE_CONTEXT} -system-namespace kube-system
ResourceGETCREATEUPDATEDELETE
pods×××
sa×××
role
rolebinding
others×××

ノードの制限

参照のみ許可されている

  • kubectl api-resources --namespaced=false -o wide

注意

  • Node にラベル付与等はできない
  • ある Pod と ある Pod を同じノードで管理したくない場合は,PodAntiAffinity で頑張る

ノードに対する権限

ResourceGETCREATEUPDATEDELETE
nodes (NodeMetrics)×××

Affinity に利用できそうな topologyKey

Key Name
failure-domain.beta.kubernetes.io/region
failure-domain.beta.kubernetes.io/zone
kubernetes.io/hostname

Affinity のサンプル

  • 外形監視 Pod (k6-core / blackbox-exporter) 用

管理系のPodと同居させたくない場合

affinity:
  podAntiAffinity:
	  requiredDuringSchedulingIgnoredDuringExecution:
	    - labelSelector:
	        matchExpressions:
	          - key: app.kubernetes.io/instance
              operator: In
              values:
	              - external-dns
                - kube-prometheus-stack
                - kube-prometheus-stack-alertmanager
                - prometheus-statsd-exporter
                - victoria-metrics
                - kube-prometheus-stack-prometheus
        topologyKey: kubernetes.io/hostname
  • 管理系の Pod にも Affinity いれるとよい
    例: app.kubernetes.io/part-of=service / app.kubernetes.io/part-of=system
  • ここら辺のラベルを埋め込んでると楽かも

Pod Security Policy

利用できません.GKE Autopilot は GateKeeper になります

注意
helm で rbac.pspEnabled 系のパラメータは OFF にすること

PSP → Gatekeeper をどう扱うべきか
利用する場合は、Helm Chart が対応されるまで気合い ?

https://github.com/appvia/psp-migration
任意のポリシーエンジンに変換して出力してくれるツール

Helm template で特定のチャートファイルだけ出力して変換する例

helm template test prometheus-community/kube-prometheus-stack -s templates/prometheus-operator/psp.yaml --set global.rbac.pspEnabled=true | psp-migration --engine=gatekeeper | pbcopy

リソース名は変わらないので,更にいじるなら Kustomize … ?

kustomize helm plugin

  • base/kustomization.yaml
helmCharts:
- name: minecraft
  includeCRDs: false
  valuesInline:
    minecraftServer:
      eula: true
      difficulty: hard
      rcon:
        enabled: true
  releaseName: moria
  version: 3.1.3
  repo: https://itzg.github.io/minecraft-server-charts

# 利用できるパラメータ (https://github.com/kubernetes-sigs/kustomize/blob/2fe04496c285bc2fa7a7233a7a584ab96e21f88d/api/types/helmchartargs.go#L33)

コメント

  • helmfile とか使ってる方達は作業量ほぼ変わらなさそうなので意外と良い
  • kustomize に集約できるのはそれはそれで嬉しい
  • ksops + secret generator で helm value の復号ができるかは要調査

psp-migration

  • Before
# Source: kube-prometheus-stack/templates/prometheus-operator/psp.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: test-kube-prometheus-stack-operator
  labels:
    app: kube-prometheus-stack-operator

    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/instance: test
    app.kubernetes.io/version: "33.2.1"
    app.kubernetes.io/part-of: kube-prometheus-stack
    chart: kube-prometheus-stack-33.2.1
    release: "test"
    heritage: "Helm"
spec:
  privileged: false
  # Allow core volume types.
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    # Permits the container to run with root privileges as well.
    rule: 'RunAsAny'
  seLinux:
    # This policy assumes the nodes are using AppArmor rather than SELinux.
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'MustRunAs'
    ranges:
      # Allow adding the root group.
      - min: 0
        max: 65535
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      # Allow adding the root group.
      - min: 0
        max: 65535
  readOnlyRootFilesystem: false
  • After
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
  name: psp-k8spspprivilegedcontainer-219e3
spec:
  match:
    kinds:
      - apiGroups:
          - ""
        kinds:
          - Pod
  parameters: null

---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPHostNamespace
metadata:
  name: psp-k8spsphostnamespace-21bdb
spec:
  match:
    kinds:
      - apiGroups:
          - ""
        kinds:
          - Pod
  parameters: null

---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPHostNetworkingPorts
metadata:
  name: psp-k8spsphostnetworkingports-f1289
spec:
  match:
    kinds:
      - apiGroups:
          - ""
        kinds:
          - Pod
  parameters:
    hostNetwork: false

---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPVolumeTypes
metadata:
  name: psp-k8spspvolumetypes-b8f49
spec:
  match:
    kinds:
      - apiGroups:
          - ""
        kinds:
          - Pod
  parameters:
    volumes:
      - configMap
      - emptyDir
      - projected
      - secret
      - downwardAPI
      - persistentVolumeClaim

---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPAllowedUsers
metadata:
  name: psp-k8spspallowedusers-ec048
spec:
  match:
    kinds:
      - apiGroups:
          - ""
        kinds:
          - Pod
  parameters:
    supplementalGroups:
      rule: MustRunAs
      ranges:
        - min: 0
          max: 65535

---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPAllowedUsers
metadata:
  name: psp-k8spspallowedusers-de2b6
spec:
  match:
    kinds:
      - apiGroups:
          - ""
        kinds:
          - Pod
  parameters:
    fsGroup:
      rule: MustRunAs
      ranges:
        - min: 0
          max: 65535

まとめ

良い点

  • GateKeeper がこういう設定にしてねと怒ってくれるの体験がいい楽
  • kubectl を使える (Kubernetes をよく使う人にとってはインターフェースが変わらず嬉しい)
  • GKE のノードスケールよりも多分早い (1分ちょい)
  • Daemonset が使えます (EKS Fargate 比較)

悪い点

  • たまにクラスタ修復中ですになり、利用料金見るとちょっと値引きされてる
  • (人によっては) e2シリーズのインスタンスタイプのみ利用する (amd64)

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ