詳解GKE: はじめに

公開:2020/12/01
更新:2020/12/02
12 min読了の目安(約11100字TECH技術記事

はじめに

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でデバッグをするための準備について紹介する予定です。

変更

  1. 2020/12/2: defaultネットワークを利用せず、新規にネットワークを作成手順に変更
  2. 2020/12/2: VPC リソースへの影響
脚注
  1. 顧客管理の暗号鍵(CMEK)の使用 ↩︎

  2. アプリケーション レイヤでのシークレットの暗号化 ↩︎

  3. クラスタのセキュリティの強化 ↩︎

  4. CIS GKE Benchmark ↩︎