LoginSignup
5
0

More than 1 year has passed since last update.

Cloud Load BalancingとAnthos Service Meshを利用してgRPC通信環境を構築

Last updated at Posted at 2021-12-21

グレンジ Advent Calendar 2021の22日目担当の石川です.

この記事では, Cloud Load BalancingAnthos Service Mesh を利用して gRPC で通信できる環境の構築を紹介します.
本記事で解説している内容をまとめたコードは,GitHubにて公開しています.

この記事はエッジからメッシュへ: GKE Ingress を介したサービス メッシュ アプリケーションの公開を参考にしています.
参考記事では,同じような構成でOnline Boutiqueというアプリケーションを作成してます.
今回はgRPCを利用した環境を作成したいため一部内容が異なりますが,概念などを丁寧に解説してくれていますので,本記事を読む前に見ていただければより理解しやすいかと思います.

目的

今回は, headerenv というキーで対象の環境を判断できるようにし, gRPC を利用して通信するアプリケーションの開発環境を想定して環境を構築していきます.
今回作成するものを図にすると次のようになります.

network.drawio.png

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.nameenv1などになります.
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で指定したhostportにリクエストを転送するようになっています.
ここで,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 で起動したアプリケーションが起動していることを確認できます.

構成図

今回作成したものを図にすると次のようになります.

AnthosServiceMesh.drawio (1).png

クリーンアップ

クラスタを削除します.

$ 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)から大きく変わった印象です.
マネージドを利用することで今後より恩恵を受けられるようになっていくことを期待しています.

参考

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0