社内プロダクトではこんな感じで 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
Resource | GET | CREATE | UPDATE | DELETE |
pods | ○ | × | × | × |
sa | ○ | × | × | × |
role | ○ | ○ | ○ | ○ |
rolebinding | ○ | ○ | ○ | ○ |
others | ○ | × | × | × |
ノードの制限
参照のみ許可されている
kubectl api-resources --namespaced=false -o wide
注意
- Node にラベル付与等はできない
- ある Pod と ある Pod を同じノードで管理したくない場合は,
PodAntiAffinity
で頑張る
ノードに対する権限
Resource | GET | CREATE | UPDATE | DELETE |
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)