概要 #
GKEプライベートクラスタと踏み台の構成は、デフォルトのデータプレーン(kube-proxy、iptablesベース)を使っていました。同じネームスペースに複数チームのアプリが同居する場合、Pod間の通信を意図したものだけに制限したい場面があります。
GKE Dataplane V2(Cilium/eBPFベース)は、NetworkPolicyの強制がビルトインされています。追加のCNI(Calico等)を用意しなくても、ラベルベースでPod間通信を制限できます。
「Dataplane V2を有効化した」ことと「NetworkPolicyが実際に意図通り機能する」ことは別です。許可したラベルのPodからは通り、許可していないPodからは遮断されることを、実際にPodを起動して確認します。
Terraformの基本操作と、前々回のGKEプライベートクラスタ+踏み台構成は前提としています。
Filestore CSI、GCS FUSE CSI、Secret Manager CSI、NodeLocal DNSCache、詳細なLogging/Monitoring設定は扱いません。Dataplane V2とNetworkPolicyの検証に範囲を絞ります。
検証すること #
datapath_provider = "ADVANCED_DATAPATH"でDataplane V2を有効化できること- 許可したラベルのPodからは、NetworkPolicyで保護されたPodへ通信できること
- 許可していないPodからは、実際に通信が遮断されること(apply成功とは別の確認)
前提環境 #
- Google Cloud CLI、Terraform、Docker
- Billingが有効な検証用Google Cloud Project
- GKE、Compute Engineを操作できるGoogleアカウント
- 検証時のバージョン:Terraform 1.14.3、google 7.46.0
使用するTerraformコード #
通信がどこで遮断されるか #
flowchart LR
subgraph NODE["GKE ノード"]
A["Pod A
label: role=client"]
EBPF["Dataplane V2
eBPF による適用点"]
B["Pod B
label: role=server"]
end
NP["NetworkPolicy
podSelector: role=server
ingress: role=client のみ"]
NP -.->|"ルールを配る"| EBPF
A -->|"許可"| EBPF --> B
C["Pod C
label なし"] -->|"遮断"| EBPF
NetworkPolicy はルールを宣言するだけで、実際に落とすのは Dataplane V2 側です。
Dataplane V2とNetworkPolicyの関係(一次情報での裏取り) #
Dataplane V2を有効にする際、Google Cloudの公式ドキュメントには次のように明記されています。
GKE Dataplane V2 comes with Kubernetes network policy enforcement built-in. This means that you don’t need to enable network policy in clusters that use GKE Dataplane V2.
別途Calicoベースのnetwork_policyアドオンを有効にする必要はなく、むしろ設定してはいけません。 両方を有効にすると、次のエラーでapplyが失敗します。
Enabling NetworkPolicy for clusters with DatapathProvider=ADVANCED_DATAPATH is not allowed.
GKEクラスタの設定 #
resource "google_container_cluster" "primary" {
# ...
datapath_provider = "ADVANCED_DATAPATH"
# network_policy ブロックは設定しない(Dataplane V2にビルトイン済み)
}
サーバPod・Service・NetworkPolicy #
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: netpol-server-allow-labeled-only
namespace: default
spec:
podSelector:
matchLabels:
app: netpol-server
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: netpol-allowed
ports:
- protocol: TCP
port: 8080
podSelectorで保護対象のPod(app: netpol-server)を指定し、ingress.from.podSelectorで許可する送信元(role: netpol-allowed)を指定します。この2つのラベルが一致しない限り、通信はブロックされます。
設定と実行 #
cd Advanced-Examples/16-gke-dataplane-v2-networkpolicy
cp terraform.tfvars.example terraform.tfvars
project_id = "YOUR_PROJECT_ID"
iap_member = "user:YOUR_ACCOUNT"
terraform init
terraform fmt -check
terraform validate
terraform plan
terraform apply
Apply complete! Resources: 25 added, 0 changed, 0 destroyed.
Dataplane V2が実際に有効化されたかは、コンソールの表示ではなくgcloudの出力で確認します。
gcloud container clusters describe "$(terraform output -raw cluster_name)" \
--zone="$(terraform output -raw zone)" --format="value(networkConfig.datapathProvider)"
ADVANCED_DATAPATH
サーバとNetworkPolicyをデプロイする #
gcloud compute scp k8s/netpol-demo.yaml \
"$(terraform output -raw bastion_name):/tmp/netpol-demo.yaml" \
--zone="$(terraform output -raw zone)" --tunnel-through-iap \
--project="$(terraform output -raw project_id)"
# 踏み台の中で実行
gcloud container clusters get-credentials "$(terraform output -raw cluster_name)" \
--zone="$(terraform output -raw zone)" --project="$(terraform output -raw project_id)"
kubectl apply -f /tmp/netpol-demo.yaml
kubectl wait --for=condition=ready pod -l app=netpol-server --timeout=90s
許可したラベルのPodからは通信できることを確認する #
role=netpol-allowedラベルを付けたPodから接続します。kubectl run --rm -itは踏み台への非対話SSH実行では接続が不安定だったため、sleepで待機させたPodへexecする方式にしています。
kubectl run netpol-debug --image=busybox:1.36 --restart=Never \
--labels="role=netpol-allowed" -- sleep 3600
kubectl wait --for=condition=ready pod/netpol-debug --timeout=60s
kubectl exec netpol-debug -- wget -qO- --timeout=5 http://netpol-server:8080
authorized-reached
サーバが返す固定文字列がそのまま取得できました。
許可していないPodからは遮断されることを確認する #
同じServiceに、ラベルなしのPodから接続を試みます。
kubectl run netpol-debug-denied --image=busybox:1.36 --restart=Never -- sleep 3600
kubectl wait --for=condition=ready pod/netpol-debug-denied --timeout=60s
kubectl exec netpol-debug-denied -- wget -qO- --timeout=5 http://netpol-server:8080
wget: download timed out
command terminated with exit code 1
タイムアウトして失敗しました。同じService・同じPodへの接続でも、送信元のラベルによって結果が変わることを確認できました。
後片付け #
kubectl delete pod netpol-debug netpol-debug-denied --ignore-not-found=true
terraform destroy
Destroy complete! Resources: 25 destroyed.
GKE Clusterと踏み台VMは利用中に料金が発生します。検証後は必ずdestroyします。
まとめ #
- Dataplane V2は
datapath_provider = "ADVANCED_DATAPATH"で有効化する。別途Calicoベースのnetwork_policyアドオンを設定するとapplyが失敗する - NetworkPolicyは
podSelector(保護対象)とingress.from.podSelector(許可する送信元)のラベルの一致で通信を制御する - 許可したラベルのPodからは通り、許可していないPodからは実際にタイムアウトして遮断される。同じServiceへの接続でも送信元のラベルで結果が変わることを、実際に確認できた
参考資料 #
- Google Cloud: GKE Dataplane V2
- Kubernetes: Network Policies
- Terraform Registry: google_container_cluster