詳解GKE: はじめに
はじめに
SI/エンタープライズ分野においても、Kubernetesを用いた開発をすることが多くなってきました。詳解GKEの記事を書き始めようと思ったきっかけは、Kubernetesのアーキテクチャに言及した記事が少ないと感じたからです。いざインフラ領域の設計をしようと思っても、公式ドキュメントだけでは十分なインプットにならなかったり、トラブルシュートをしようとしてもアーキテクチャを十分理解していないため時間がかかってしまったりするなど、商用利用するには不安要素が多いのが現状です。この記事によって、読者のGKEへの理解が進み、商用環境への適用や安定稼働の助けになれば良いと考えています。
KubernetesはGCP、AWS、Azure、OpenShiftなどベンダによって実装が異なるため、今回は私が一番利用しているGKEのアーキテクチャについて掘り下げていきたいと思います。
執筆予定の内容
内容は執筆しながら考えていきます。Kubernetesの仕様について細かくは説明しません。アーキテクチャの説明をする上で一番重要となるネットワークの実装についての説明から初めて、kube-systemのPodを端から解説していこうと思います。アドベントカレンダー的に12月に毎日更新していきたいですが、忙しい日もあったりするので、数日かかる時もありそうです。
前提条件
GKEのアーキテクチャはGKEのバージョンやクラスタ作成時のオプションの選択によって少しずつ変わってきます。あまり商用で採用しない設定の構成を見ても意味がないので、12/1時点でRegularチャンネルのバージョンである1.17.12-gke.1504
をベースに、商用利用する上でよく採用されそうな設定を前提として解説をしてこうと思います。
本記事では、まず前提となる環境を作るところまでを目標とします。
環境準備
GCPのプロジェクトIDはgke-detail
、リージョンはus-west1
を前提として記載します。
default networkの削除
default VPCは邪魔なので削除します。
% gcloud compute networks delete default
このコマンドで削除が失敗する場合GUIで削除するといい感じに全部のリソースを削除してくれます。
GKE用 VPCの作成
# networkの作成
% gcloud compute networks create gke-network --bgp-routing-mode=global --subnet-mode=custom
# subnetの作成
% gcloud compute networks subnets create gke-subnet --network gke-network --range 10.128.0.0/20 --secondary-range gke-cluster-pods=100.64.0.0/14 --secondary-range gke-cluster-services=100.68.0.0/20
subnet作成時に指定している--seconary-range
はGKEのPodとService用に必要となるサブネットのセカンダリIPレンジです。GKE作成時に作成することも可能ですが、事前に作成したほうが良いでしょう。
GKEを動かすためには大量のIPアドレスが必要です。IP アドレス範囲の計画を参考にGKEノード、Pod、Serviceそれぞれのネットワークアドレスを設計しましょう。今回の作成したネットワークの場合、各種リソースの作成上限は以下の通りになります。
- 4,092 個のノード
- 112,640 個の Pod
- 4,096 個の Service
GCPで完結しているような自由にネットワークアドレスを確保可能なシステムであれば良いですが、オンプレミスと接続するようなシステムの場合、広大なネットワークアドレスを確保するのは困難です。今回の例では節約のために、GCPでサポートされているキャリアグレードNATの範囲からPodとService用のネットワークアドレスを払い出してみました。キャリアグレードNATを利用する際の注意点などについては後日記載する予定です。
GKE用KMSキーの作成
GKEで顧客管理の暗号鍵(CMEK)が利用できる箇所は、大きく分けてディスクと、Secret保管時のアプリケーション暗号化の2箇所です。ディスクはNodeのブートディスク[1]とPersistentVolume[2]でCMEKが利用できます。これらの詳細については後日説明しますがまずはCMEKを利用する前提でクラスタを作成ししていきますます。
% gcloud kms keyrings create keyrings-gke \
--location us-west1
% gcloud kms keys create key-gke-node \
--keyring keyring-gke \
--location us-west1 \
--purpose "encryption"
% gcloud kms keys create key-gke-secret \
--keyring keyring-gke \
--location us-west1 \
--purpose "encryption"
サービスアカウントに暗号化、復号化の権限を付与
Compute EngineとContainer Engineそれぞれに作成した鍵を使った暗号化、復号化権限を付与します。
% gcloud kms keys add-iam-policy-binding key-gke-node \
--location us-west1 \
--keyring keyring-gke \
--member serviceAccount:service-<プロジェクトID>@compute-system.iam.gserviceaccount.com \
--role roles/cloudkms.cryptoKeyEncrypterDecrypter
% gcloud kms keys add-iam-policy-binding key-gke-secret \
--location us-west1 \
--keyring keyring-gke \
--member serviceAccount:service-<プロジェクトID>@container-engine-robot.iam.gserviceaccount.com \
--role roles/cloudkms.cryptoKeyEncrypterDecrypter
Node用サービスアカウントの作成
Compute Engineのデフォルトサービスアカウントは権限が強すぎるため、GKEクラスタのセキュリティ強化に関するドキュメント[3]や、GKE CIS ベンチマーク[4]の方針に従い、最小限の権限を付与したサービスアカウントを作成します。
% gcloud iam service-accounts create gke-node \
--display-name=gke-node
% gcloud projects add-iam-policy-binding gke-detail \
--member "serviceAccount:gke-node@gke-detail.iam.gserviceaccount.com" \
--role roles/logging.logWriter
% gcloud projects add-iam-policy-binding gke-detail \
--member "serviceAccount:gke-node@gke-detail.iam.gserviceaccount.com" \
--role roles/monitoring.metricWriter
% gcloud projects add-iam-policy-binding gke-detail \
--member "serviceAccount:gke-node@gke-detail.iam.gserviceaccount.com" \
--role roles/monitoring.viewer
GKEクラスタの作成
デフォルト値から変更しているポイントのみ簡単に記載します。それぞれの詳細については後日触れます。
- GKE CIS ベンチマークへの準拠
- Network Policyの有効化
- Workload Identityの有効化
- シールドされたGKEノードの有効化
- Master Authorized Networksを有効化
- セキュアブートを有効化
- 検証のコスト削減のために・・・
- 安価なus-west1を利用
- 複数のZoneでノードを起動した構成で検証したいためリージョンクラスタを選択
- コスト削減のためNodeロケーションを2つのゾーンに限定
- コスト削減のためpreemtibleノードを選択
% gcloud beta container clusters create "cluster" \
--region "us-west1" \
--no-enable-basic-auth \
--cluster-version "1.17.12-gke.1504" \
--machine-type "e2-medium" \
--image-type "COS" \
--disk-type "pd-standard" \
--disk-size "100" \
--boot-disk-kms-key "projects/gke-detail/locations/us-west1/keyRings/keyring-gke/cryptoKeys/key-gke-node" \
--metadata disable-legacy-endpoints=true \
--service-account "gke-node@gke-detail.iam.gserviceaccount.com" \
--preemptible \
--num-nodes "1" \
--enable-stackdriver-kubernetes \
--enable-private-nodes \
--master-ipv4-cidr "172.16.0.0/28" \
--enable-ip-alias \
--network "projects/gke-detail/global/networks/gke-network" \
--subnetwork "projects/gke-detail/regions/us-west1/subnetworks/gke-subnet" \
--cluster-secondary-range-name=gke-cluster-pods \
--services-secondary-range-name=gke-cluster-services \
--default-max-pods-per-node "110" \
--enable-autoscaling --min-nodes "0" --max-nodes "2" \
--enable-network-policy \
--enable-master-authorized-networks \
--master-authorized-networks <自宅のグローバルIPアドレス> \
--addons HorizontalPodAutoscaling,HttpLoadBalancing,NodeLocalDNS \
--no-enable-autoupgrade \
--enable-autorepair \
--max-surge-upgrade 1 \
--max-unavailable-upgrade 0 \
--maintenance-window-start "2020-11-29T17:00:00Z" \
--maintenance-window-end "2020-11-29T23:00:00Z" \
--maintenance-window-recurrence "FREQ=WEEKLY;BYDAY=SA,SU" \
--database-encryption-key "projects/gke-detail/locations/us-west1/keyRings/keyring-gke/cryptoKeys/key-gke-secret" \
--workload-pool "gke-detail.svc.id.goog" \
--enable-shielded-nodes \
--shielded-secure-boot \
--tags "gke-node" \
--node-locations "us-west1-a","us-west1-b"
しばらく待てばクラスタが作成されます。シンプルなアプリケーションをデプロイして簡単に動作確認をします。
VPC リソースへの影響
VPC Firewall
GKEを作成すると、自動的にVPC Firewallの定義が追加されます。
% gcloud compute firewall-rules list --format="table(
name,
network,
direction,
priority,
sourceRanges.list():label=SRC_RANGES,
destinationRanges.list():label=DEST_RANGES,
allowed[].map().firewall_rule().list():label=ALLOW,
denied[].map().firewall_rule().list():label=DENY,
sourceTags.list():label=SRC_TAGS,
sourceServiceAccounts.list():label=SRC_SVC_ACCT,
targetTags.list():label=TARGET_TAGS,
targetServiceAccounts.list():label=TARGET_SVC_ACCT,
disabled
)"
NAME NETWORK DIRECTION PRIORITY SRC_RANGES DEST_RANGES ALLOW DENY SRC_TAGS SRC_SVC_ACCT TARGET_TAGS TARGET_SVC_ACCT DISABLED
gke-cluster-f0a2ee52-all gke-network INGRESS 1000 100.64.0.0/14 icmp,esp,ah,sctp,tcp,udp gke-cluster-f0a2ee52-node False
gke-cluster-f0a2ee52-master gke-network INGRESS 1000 172.16.0.0/28 tcp:10250,tcp:443 gke-cluster-f0a2ee52-node False
gke-cluster-f0a2ee52-vms gke-network INGRESS 1000 10.128.0.0/20 icmp,tcp:1-65535,udp:1-65535 gke-cluster-f0a2ee52-node False
それぞれのルールの意味はざっくり以下の通りです。
-
gke-cluster-f0a2ee52-all
PodからNode/Podに対する全ての通信を許可 -
gke-cluster-f0a2ee52-vms
NodeからNode/Podに対する全ての通信を許可 -
gke-cluster-f0a2ee52-master
マスターノードからNode、PodへのTCPポート443,10250の通信を許可
これらのルールがなぜ必要なのかは、後日詳細な解説をする予定です。
VPC ルート
GKEを作成すると、peering-route-adf862d07edcee9b
というルート情報が追加されます。
% gcloud compute routes list
NAME NETWORK DEST_RANGE NEXT_HOP PRIORITY
default-route-2c8e44c039515864 gke-network 100.64.0.0/14 gke-network 0
default-route-42843ca2303fece0 gke-network 0.0.0.0/0 default-internet-gateway 1000
default-route-cbdf5dc14d867476 gke-network 10.128.0.0/20 gke-network 0
default-route-fe873e0b8025b9d9 gke-network 100.68.0.0/20 gke-network 0
peering-route-adf862d07edcee9b gke-network 172.16.0.0/28 gke-nd805269a855557b2e09-2713-2ad4-peer 0
これは、GKEのマスターノード用のネットワークです。GCPマネージドなVPCにマスターノードが自動的に作成され、VPCピアリングをすることで、マスターノードとワーカーノードの相互通信を可能にしています。
動作確認
kubectl用クレデンシャルの設定
% gcloud container clusters get-credentials cluster --region us-west1
サンプルアプリケーションのデプロイ
% cat << _EOF_ | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-deployment
spec:
replicas: 2
selector:
matchLabels:
app: echo-server
template:
metadata:
labels:
app: echo-server
spec:
containers:
- name: echo-server
image: k8s.gcr.io/echoserver:1.4
ports:
- name: http-port
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: echo-service
spec:
ports:
- name: http-port
port: 80
targetPort: 8080
protocol: TCP
selector:
app: echo-server
_EOF_
% kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
echo-service ClusterIP 10.4.6.197 <none> 80/TCP 6m43s
kubernetes ClusterIP 10.4.0.1 <none> 443/TCP 102m
% kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
echo-deployment 2/2 2 2 6m49s
% kubectl get pods
NAME READY STATUS RESTARTS AGE
echo-deployment-75b8bcddf4-2qn2t 1/1 Running 0 5m8s
echo-deployment-75b8bcddf4-8tbt5 1/1 Running 0 5m15s
Serviceにアクセス
デプロイしたPod自身からServiceにアクセスしてみます。
% kubectl exec $(kubectl get pod --selector="app=echo-server" --output jsonpath='{.items[0].metadata.name}') -- curl echo-service -s
CLIENT VALUES:
client_address=10.138.0.2
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://echo-service:8080/
SERVER VALUES:
server_version=nginx: 1.10.0 - lua: 10001
HEADERS RECEIVED:
accept=*/*
host=echo-service
user-agent=curl/7.47.0
BODY:
-no body in request-
Serviceにアクセスできましたね。
次回は、まだアーキテクチャの詳解に入りません。GKEでデバッグをするための準備について紹介する予定です。
変更
- 2020/12/2: defaultネットワークを利用せず、新規にネットワークを作成手順に変更
- 2020/12/2: VPC リソースへの影響
Discussion