グレンジ Advent Calendar 2021の22日目担当の石川です.
この記事では, Cloud Load Balancing と Anthos Service Mesh を利用して gRPC で通信できる環境の構築を紹介します.
本記事で解説している内容をまとめたコードは,GitHubにて公開しています.
この記事はエッジからメッシュへ: GKE Ingress を介したサービス メッシュ アプリケーションの公開を参考にしています.
参考記事では,同じような構成でOnline Boutiqueというアプリケーションを作成してます.
今回はgRPCを利用した環境を作成したいため一部内容が異なりますが,概念などを丁寧に解説してくれていますので,本記事を読む前に見ていただければより理解しやすいかと思います.
目的
今回は, header に env というキーで対象の環境を判断できるようにし, gRPC を利用して通信するアプリケーションの開発環境を想定して環境を構築していきます.
今回作成するものを図にすると次のようになります.
Google Cloud プロジェクトの環境変数を定義します.
$ export PROJECT=$(gcloud info --format='value(config.project)')
クラスタの作成
GKEクラスタを作成します.
GKE クラスタの環境変数を定義します.
$ export CLUSTER_NAME=asm-advent2021
$ export CLUSTER_LOCATION=asia-northeast1-b
Google Kubernetes Engine API を有効にします.
$ gcloud services enable container.googleapis.com
GKE クラスタを作成します.
$ gcloud container clusters create ${CLUSTER_NAME} \
--project=${PROJECT} \
--zone=${CLUSTER_LOCATION} \
--machine-type=e2-standard-4 \
--num-nodes=2 \
--enable-ip-alias \
--workload-pool=${PROJECT}.svc.id.goog
クラスタが実行されていることを確認します.
$ gcloud container clusters list
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
asm-advent2021 asia-northeast1-b 1.21.5-gke.1302 104.198.90.75 e2-standard-4 1.21.5-gke.1302 2 RUNNING
クラスタに接続します.
$ gcloud container clusters get-credentials ${CLUSTER_NAME} \
--project=${PROJECT} \
--zone=${CLUSTER_LOCATION}
kubectl の現在のコンテキストをクラスタに設定します.
$ kubectl config set-context ${CLUSTER_NAME}
Anthos Service Meshのインストール
前に作成したClusterに対し Anthos Service Mesh をインストールしていきます.
asmcliをダウンロードします.
$ mkdir -p build/.toolchain/bin/
$ curl https://storage.googleapis.com/csm-artifacts/asm/asmcli_1.11 > build/.toolchain/bin/asmcli
asmcliに実行権限を付与します.
$ chmod +x build/.toolchain/bin/asmcli
Anthos Service Mesh をインストールします.
$ ./build/.toolchain/bin/asmcli install \
--project_id ${PROJECT} \
--cluster_name ${CLUSTER_NAME} \
--cluster_location ${CLUSTER_LOCATION} \
--enable_all \
--ca mesh_ca \
--custom_overlay $(pwd)/deployments/asm/ingress-backendconfig-operator.yaml
デプロイが稼働していることを確認します.
$ kubectl wait --for=condition=available --timeout=600s deployment --all -n istio-system
deployment.apps/istiod-asm-1115-3 condition met
$ kubectl wait --for=condition=available --timeout=600s deployment --all -n asm-system
deployment.apps/canonical-service-controller-manager condition met
$ kubectl get deployment -n istio-system
NAME READY UP-TO-DATE AVAILABLE AGE
istio-ingressgateway 2/2 2 2 3h12m
istiod-asm-1115-3 2/2 2 2 3h12m
--custom_overlay
で指定しているyamlは次のようになっています.
IstioOperator を利用して backend-config
を指定しています.
cloud.google.com/neg: '{"ingress": true}'
で Network Endpoint Groupを作成しています.
cloud.google.com/app-protocols: '{"https":"HTTP2"}'
では Ingress と istio-ingressgateway 間の通信を構成しています.
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
meshConfig:
accessLogFile: "/dev/stdout"
components:
ingressGateways:
- name: istio-ingressgateway
enabled: true
k8s:
service:
type: ClusterIP
serviceAnnotations:
cloud.google.com/backend-config: '{"default": "ingress-backendconfig"}'
cloud.google.com/neg: '{"ingress": true}'
cloud.google.com/app-protocols: '{"https":"HTTP2"}'
この後作成する Load Balancer ではサービスポートとして 443 ポート を利用します.
また,istio-ingressgatewayでは, 15021 ポートでステータスが確認できるようになっています.
そこでサービスポートがヘルスチェックのステータスポートが異なるので, BackendConfig でヘルスチェックのポートを変更します.
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
name: ingress-backendconfig
namespace: istio-system
spec:
timeoutSec: 3600
healthCheck:
requestPath: /healthz/ready
port: 15021
type: HTTP
ingress-backendconfig.yamlを適用します.
$ kubectl apply -f deployments/asm/ingress-backendconfig.yaml
IP アドレス指定と DNS の設定
今回アクセスするアドレスを作成します.
静的IPアドレスを作成し,作成した静的IPを利用した Cloud Endpoints(DNS) を作成します.
静的IPを作成します.
$ gcloud compute addresses create asm-advent2021-ingress-ip --global
静的IPアドレスを取得します.
$ export GCLB_IP=$(gcloud compute addresses describe asm-advent2021-ingress-ip --global --format=json | jq -r '.address')
$ echo ${GCLB_IP}
Cloud Endpointsを作成するためのyamlファイルを作成します.
$ cat <<EOF > deployments/asm/dns-spec.yaml
swagger: "2.0"
info:
description: "ASM Advent2021 Cloud Endpoints DNS"
title: "ASM Advent2021 Cloud Endpoints DNS"
version: "1.0.0"
paths: {}
host: "asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog"
x-google-endpoints:
- name: "asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog"
target: "${GCLB_IP}"
EOF
Cloud プロジェクトに dns-spec.yaml ファイルをデプロイします.
$ gcloud endpoints services deploy deployments/asm/dns-spec.yaml
TLS 証明書をプロビジョニングする
この後作成する Load Balancer は Ingress を利用して作成します.
そこで,Load Balancer(Ingress) で利用するTLS証明書を ManagedCertificate を利用して作成します.
ManagedCertificate マニフェストを managed-cert.yaml として作成します.
$ cat <<EOF > deployments/asm/managed-cert.yaml
apiVersion: networking.gke.io/v1beta2
kind: ManagedCertificate
metadata:
name: gke-ingress-cert
namespace: istio-system
spec:
domains:
- "asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog"
EOF
GKE クラスタに managed-cert.yaml ファイルをデプロイします.
$ kubectl apply -f deployments/asm/managed-cert.yaml
ManagedCertificate リソースを調べて、証明書の生成の進行状況を確認します.
$ kubectl describe managedcertificate gke-ingress-cert -n istio-system
Name: gke-ingress-cert
Namespace: istio-system
Labels: <none>
Annotations: <none>
API Version: networking.gke.io/v1
Kind: ManagedCertificate
Metadata:
Creation Timestamp: 2021-12-16T02:36:38Z
Generation: 2
Managed Fields:
API Version: networking.gke.io/v1beta2
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
.:
f:domains:
Manager: kubectl-client-side-apply
Operation: Update
Time: 2021-12-16T02:36:38Z
API Version: networking.gke.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
.:
f:certificateName:
f:certificateStatus:
f:domainStatus:
Manager: managed-certificate-controller
Operation: Update
Time: 2021-12-16T02:36:41Z
Resource Version: 27606
UID: 8fe6f938-276b-45d8-a8ef-fccb48a25600
Spec:
Domains:
asm-advent2021-frontend.endpoints.YOUR_POJECT.cloud.goog
Status:
Certificate Name: mcrt-849158c7-5054-42d7-aa7d-aefaae17c1b1
Certificate Status: Provisioning
Domain Status:
Domain: asm-advent2021-frontend.endpoints.YOUR_POJECT.cloud.goog
Status: Provisioning
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Create 87s managed-certificate-controller Create SslCertificate mcrt-849158c7-5054-42d7-aa7d-aefaae17c1b1
証明書の準備ができると、Certificate Status は Active になります.
自己署名 Ingress ゲートウェイ証明書をインストールする
Google Front End がサービス メッシュの Ingress ゲートウェイへの TLS 接続を確立できるようにする証明書を(Kubernetes secret リソースとして)生成し、インストールします.
openssl を使用して秘密鍵と証明書を作成します.
$ openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
-subj "/CN=asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog/O=Edge2Mesh Inc" \
-keyout deployments/asm/cert/asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog.key \
-out deployments/asm/cert/asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog.crt
TLS証明書secret マニフェストを edge2mesh-credential.yaml として作成します.
$ cat <<EOF > deployments/asm/edge2mesh-credential.yaml
apiVersion: v1
kind: Secret
metadata:
name: edge2mesh-credential
namespace: istio-system
type: kubernetes.io/tls
data:
tls.crt: |
$(base64 deployments/asm/cert/asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog.crt)
tls.key: |
$(base64 deployments/asm/cert/asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog.key)
EOF
GKE クラスタに edge2mesh-credential.yaml ファイルをデプロイします.
$ kubectl apply -f deployments/asm/edge2mesh-credential.yaml
Gatewayリソースを使用して,Gatewayで開くポートとそれらのポートを許可する仮想ホストを指定します.
また,先程作成したTLS証明書をhttpsのtlsとして設定しています.
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: common-istio-gateway
namespace: default
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 8080
name: http
protocol: HTTP
hosts:
- "*"
- port:
number: 8443
name: https
protocol: HTTPS
hosts:
- "*"
tls:
mode: SIMPLE
credentialName: edge2mesh-credential
Gatewayリソースをデプロイします.
$ kubectl apply -f deployments/asm/app-gateway.yaml
Ingressを利用してLoad Balancerを作成します.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: istio-system
annotations:
kubernetes.io/ingress.global-static-ip-name: "asm-advent2021-ingress-ip"
networking.gke.io/managed-certificates: "gke-ingress-cert"
kubernetes.io/ingress.class: "gce"
spec:
defaultBackend:
service:
name: istio-ingressgateway
port:
number: 443
kubernetes.io/ingress.global-static-ip-name: "asm-advent2021-ingress-ip"
では作成した静的IPを利用するようにしています.
networking.gke.io/managed-certificates: "gke-ingress-cert"
も同じく前で作成した ManagedCertificate を利用しています.
また,backendとして istio-ingressgateway
を指定しています.
$ kubectl apply -f deployments/asm/ingress.yaml
アプリケーションをデプロイする
helm をインストールする.
$ make install-helm
ENV=env1 でアプリケーションを起動する.
$ build/.toolchain/bin/helm upgrade app-env1 deployments/helm/app \
--set=namespace.name=env1,namespace.labels."istio\.io/rev"=$(kubectl -n istio-system get pods -l app=istiod -o=jsonpath='{.items[0].metadata.labels.istio\.io/rev}') \
--install
ENV=env2 でアプリケーションを起動する.
$ build/.toolchain/bin/helm upgrade app-env2 deployments/helm/app \
--set=namespace.name=env2,namespace.labels."istio\.io/rev"=$(kubectl -n istio-system get pods -l app=istiod -o=jsonpath='{.items[0].metadata.labels.istio\.io/rev}') \
--install
このアプリケーションは次の4つのマニフェストからできています.
- Namespace
- Deployment
- Service
- VirtualService
Namespace
今回作成したアプリケーションをデプロイするためのNamespaceです.
metadata.name
はenv1
などになります.
metadata.labels
で重要なのは,istio.io/rev=asm-1115-3
という値です.
アプリケーションを起動した際に,kubectl -n istio-system get pods -l app=istiod -o=jsonpath='{.items[0].metadata.labels.istio\.io/rev}'
の実行結果(asm-1115-3)を設定しています.
これにより,この後作成するアプリケーションをデプロイした際に Envoy サイドカー プロキシ が自動挿入されるようになります.
apiVersion: v1
kind: Namespace
metadata:
name: {{ .Values.namespace.name }}
labels:
{{- range $key, $value := .Values.namespace.labels }}
{{ $key }}: {{ $value }}
{{- end }}
Deployment
Deploymentの内容は次のようになっています.
ここで注目したいのはspec.template.spec.env
です.
アプリケーション起動時に,Namespaceと同じ値(env1)を渡しています.
このアプリケーションでは,アクセスがあった際にログを出力していますがその際にこのEnvの情報を出力するようにしています.
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: {{ .Values.namespace.name }}
spec:
selector:
matchLabels:
app: app
replicas: 1
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: "{{ .Values.app.image.repository }}:{{ .Values.app.image.tag }}"
workingDir: /app
command:
- ./echo-server
env:
- name: ENV
value: {{ .Values.namespace.name }}
Service
Serviceは,作成したDeploymentに対応する設定になっています.
apiVersion: v1
kind: Service
metadata:
name: app-service
labels:
app: app
namespace: {{ .Values.namespace.name }}
spec:
type: NodePort
ports:
{{- range $key, $value := .Values.app.ports }}
- name: {{ $key }}
port: {{ $value.port }}
targetPort: {{ $value.targetPort }}
protocol: {{ $value.protocol }}
{{- end }}
selector:
app: app
VirtualService
VirtualServiceでは,作成したcommon-istio-gateway
を利用しルーティングする設定を記載しています.
spec.http.match.headers
では,headerのenvキーがNamespaceの名前(env1)とマッチするかを条件にしています.
マッチした際には,spec.http.match.route
で指定したhost
とport
にリクエストを転送するようになっています.
ここで,app-service.{{ .Values.namespace.name }}.svc.cluster.local
ですが,先に作成したアプリケーションのService
を指定しています.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: app-virtual-service-{{ .Values.namespace.name }}
namespace: default
spec:
hosts:
- "*"
gateways:
- common-istio-gateway
http:
- match:
- headers:
env:
exact: {{ .Values.namespace.name }}
route:
- destination:
host:
app-service.{{ .Values.namespace.name }}.svc.cluster.local
port:
number: {{ .Values.app.ports.grpc.port }}
gRPCurlで確認する
gRPCurlを利用して動作を確認します.
gRPCurlをインストールします.
$ make install-grpcurl
headerに env=env1
を設定してアクセスする.
$ ./build/.toolchain/bin/grpcurl -proto api/echo.proto -rpc-header 'env: env1' asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog:443 echo.EchoService/Call
{
"message": "Hello World! ServerEnv:env1, ClientEnv:env1"
}
ログを確認する.
$ kubectl logs -f deployment/app -n env1
2021/12/16 05:05:59 start server. port:50051, env:env1
2021/12/16 05:47:20 client request env:env1
同じようにheaderに env=env2
を設定すると ENV=env2
で起動したアプリケーションが起動していることを確認できます.
構成図
今回作成したものを図にすると次のようになります.
クリーンアップ
クラスタを削除します.
$ gcloud container clusters delete ${CLUSTER_NAME} \
--project=${PROJECT} \
--zone=${CLUSTER_LOCATION}
Anthosマネージドクラスタのリストを取得します.
$ gcloud container hub memberships list
NAME EXTERNAL_ID
asm-advent2021 3a1d4344-82f6-4b3b-a6df-75f72478be53
今回作成したクラスタを削除します.
$ gcloud container hub memberships delete asm-advent2021
静的IPを削除します.
$ gcloud compute addresses delete asm-advent2021-ingress-ip --global
Cloud Endpointsを削除します.
$ gcloud endpoints services delete asm-advent2021-frontend.endpoints.${PROJECT}.cloud.goog
まとめ
Cloud Load Balancing と Anthos Service Mesh を利用して gRPC で通信する環境を作りました.
かなり手数が多い感じがしますが,terraformなどを利用することで構成を楽にすることができます.
Anthos Service Mesh はまだ大きな仕様変更などがある印象です.(インストールのscriptなども1.10)から大きく変わった印象です.
マネージドを利用することで今後より恩恵を受けられるようになっていくことを期待しています.
参考
- Anthos Service Mesh
- Google Kubernetes Engine
- Network Endpoint Group
- Cloud Endpoints
- Tools