Gunosy Tech Blog

Gunosy Tech Blogは株式会社Gunosyのエンジニアが知見を共有する技術ブログです。

Istioの構成管理の話

この記事は Gunosy Advent Calendar 2020 の 24日目の記事です。Gunosy Adsチームのリードエンジニアの会田です。Gunosy Adsチームでは昨年から配信システムのEKS移行などのシステムのインフラ改善に取り組んおり、この一年はEKSとKubernetes漬けの一年でした。

至近では、クラスタ内のトラフィックの管理のためにサービスメッシュとしてIstioを導入したので、Istioの導入における構成管理の話をしたいと思います。

Istio OperatorとTerraform

Istioのインストールは公式から、istioctl による手動インストールと、Istio Operator によるインストールの2つの方法が提供されています。istioctlはIstioのユーティリティコマンドで、Istioのインストールだけでなく、コンフィグの確認などで、Istioを使っていると何かとお世話になります。一方、Istio Operatorは手動インストールをする代わりに、Istioの構成管理を行ってくれるKubernetes Operatorです。Istio Operatorを使用するとKuberntesのCustom Resourceで、Istioのコンポーネントの構成管理ができます。

Gunosyではインフラの構成管理をTerraformで行っていて、Istioのインストールも可能な限りコードで管理したいので、istioctlによる手動インストールは避けて、Istio OperatorのインストールをTerraformで行うようにしました。

※作業時点では1.7.xが最新でした。現在の最新は1.8.xです。

公式のドキュメントのコマンドを参考に、次のようにistio-operatorのmanifestを生成して、k2tfでtfファイルに変換しました。

istio.io

$ cd istio-1.7.3/
$ helm template manifests/charts/istio-operator \ 
    --set hub=docker.io/istio \ 
    --set tag=1.7.3 \ 
    --set operatorNamespace=istio-operator \ 
    --set watchedNamespaces=istio-system \ 
    --set operator.resources.limits.cpu=64m \ 
    --set operator.resources.limits.memory=256Mi \ 
    --set operator.resources.requests.cpu=64m \ 
    --set operator.resources.requests.memory=256Mi | k2tf > istio.tf

上記のコマンドを実行すると下のようなメッセージが出るので該当箇所については修正の必要があります。

8:43AM Warn | excluding attribute [kubernetes_deployment.spec.template.spec.container.security_context.capability] not found in Te
rraform schema  field=Deployment.Spec.Template.Spec.Containers.SecurityContext.Capabilities name=istio_operator type=kubernetes_de
ployment

helm template で生成したmanifestにはCRDが含まれていないので、上記のmanifestとは別にapplyする必要があります。

CRDとIstio Operatorをインストールすると、次のようなIstioOperatorのmanifestをapplyするとIstioの導入ができます。今回の導入ではIstio Gatewayを使用しないのでprofileを minimal を指定しました。各profileでインストールされるコンポーネントはこちらから確認ができます。

istio.io

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
  name: default-istiocontrolplane
spec:
  profile: minimal

kubernetes-provider で管理できないmanifestの管理に kubenetes-provider-alpha の仕様も検討しましたが、残念ながら次のようなエラーが terraform plan で出てしまうので断念しました。おそらく kubernets-provider-alpha がplan時にCustom Resourceのバリデーションのために、KubernetesのAPIを叩くので、planの前にCRDの追加する必要があるためです。これは depends_on で依存関係を指定しても解決しませんでした。

Error: rpc error: code = Unknown desc = no matches for install.istio.io/v1alpha1, Resource=IstioOperator

そこで kubernetes-provider で管理できないmanifestは、次のように null_resourcelocal-exec を使用することにしました。 Istio Operatorのmanifestは環境毎に変更したい設定があるのでテンプレートを使用しています。

data "local_file" "istio_operator_crd" {
  filename = "${path.module}/manifests/istio-operator-crd.yaml"
}

resource "null_resource" "istio_operator_crd" {
  triggers = {
    content_sha1 = filesha1(data.local_file.istio_operator_crd.filename)
  }

  provisioner "local-exec" {
    command = "kubectl apply -f ${data.local_file.istio_operator_crd.filename}"
  }
}

data "template_file" "default_istio_operator" {
  template = "${file("${path.module}/manifests/istio-operator.yaml.tpl")}"

  vars = {
    cpu    = local.istio_pilot_cpu_size[terraform.workspace]
    memory = local.istio_pilot_memory_size[terraform.workspace]
  }
}

resource "null_resource" "default_istio_operator" {
  triggers = {
    content_sha1 = sha1(data.template_file.default_istio_operator.rendered)
  }

  provisioner "local-exec" {
    command = <<CMD
cat << EOF | kubectl apply -f -
${data.template_file.default_istio_operator.rendered}
EOF
CMD
  }

  depends_on = [
    null_resource.istio_operator_crd,
    kubernetes_namespace.istio_operator,
    kubernetes_deployment.istio_operator,
  ]
}

namespaceのPodへのsidecar injectionの許可

Istioでトラフィックの管理を行いたい場合、対象のPodに istio-proxy をサイドカーとして追加するように Sidecar Injection をする必要があります。インジェクションには手動と自動の2つの方法があり、namespaceに対して istio-injection=enable というラベルを追加すると、そのnamespaceのPodに自動でistio-proxyが追加されるようになります。

resource "kubernetes_namespace" "ads_api" {
  metadata {
    name = "ads-api"
    labels = {
      "istio-injection" = "enabled"
    }
}

Istio Operatorの設定

上記の手順でIstio OperatorによるIstioの導入はできましたが、これらに加えていくつかの設定を追加しているので説明したいと思います。各設定の内容はこちらのドキュメントから確認ができます。

istio.io

datadogをtracerに指定する

Gunosy AdsではAPMにDatadogを使用しているので、次のように修正してtracerの設定を追加しました。

  profile: minimal
  meshConfig:
    enableTracing: true
  values:
        tracer: datadog
    pilot:
      traceSampling: 100.0

Istioのコンポーネントのリソース調整

ステージングやプロダクションなどの環境毎にistioのコンポーネントが使用するリソースの調整を行うために、次のような設定を追加しました。

  components:
    pilot:
      k8s:
        resources:
          requests:
            cpu: 100m
            memory: 256Mi
          limits:
            cpu: 100m
            memory: 256Mi

istio-proxy のlifecycleの変更

istio-proxyがSidecar InjectionされたPodでは、外部との通信がistio-proxyを経由するようになるので、MySQLなどPodの外のデータソースなどと通信するには istio-proxy が起動している必要があります。アプリケーション側でistio-proxyの起動を確認するのは避けたいので、istio-proxyが起動してから他のコンテナが起動するように変更しました。 また、Podの停止時にデフォルトの挙動ではistio-proxyは他のコンテナを待たずに停止してしまうので、これはアプリケーションがgracefulに停止するために preStopに sleep などを仕掛けている場合に都合が悪いので、istio-proxyが最後に停止するように変更しました。 上の2つの設定を反映したものが下の設定です。

  values:
    global:
      proxy:
        holdApplicationUntilProxyStarts: true
        lifecycle:
          preStop:
            exec:
              command:
                - "/bin/sh"
                - "-c"
                - "while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0 ]; do sleep 1; done"
          postStart:
            exec:
              command:
                - pilot-agent
                - wait

この設定にはこちらのサイトを参考にさせていただきました。

blog.1q77.com

blog.1q77.com

まとめ

この記事では、TerraformとIstio OpeartorでKubernetesクラスタ上のIstioのコンポーネントの構成管理をする方法について紹介しました。

今回は導入したてのため、アプリケーションへの導入の実例がご紹介できませんでしたが、知見が溜まったら、またご紹介させていただきたいと思います。