Istio シリーズ 第12回です。
Istio は各 Pod に sidecar として Envoy コンテナを差し込み、通信の受信も送信も Envoy を経由します。アプリの更新時などに旧バージョンの Pod の停止する時、先に Envoy コンテナが停止してしまうとアプリのコンテナが通信できなくなり処理が完了できなくなったりします。開始時にもコンテナの起動順序は不定であるため起動スクリプトの調整や LivnessProbe, ReadinessProbe は重要です。
preStop フック
そこで、sidecar である Envoy が先に終了してしまわないようにするために Pod の preStop hook を活用することができます。
Envoy sidecar は istio によって deploy 時に inject されますが、どんな設定を inject するかは istio-system ネームスペースにある istio-sidecar-injector という ConfigMap に定義されています。
$ kubectl get configmap -n istio-system NAME DATA AGE istio 3 21d istio-ca-root-cert 1 21d istio-leader 0 21d istio-security 1 21d istio-sidecar-injector 2 21d pilot-envoy-config 1 21d prometheus 1 21d
config にテンプレートなどと、そのテンプレートに渡す values という変数が入っています。
istio-sidecar-injector ConfigMap の config
$ kubectl get configmap -n istio-system istio-sidecar-injector -o jsonpath='{.data.config}'
policy: enabled alwaysInjectSelector: [] neverInjectSelector: [] injectedAnnotations: # Configmap optimized for Istiod. Please DO NOT MERGE all changes from istio - in particular those dependent on # Values.yaml, which should not be used by istiod. # Istiod only uses SDS based config ( files will mapped/handled by SDS). template: | rewriteAppHTTPProbe: {{ valueOrDefault .Values.sidecarInjectorWebhook.rewriteAppHTTPProbe false }} initContainers: {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} {{ if .Values.istio_cni.enabled -}} - name: istio-validation {{ else -}} - name: istio-init {{ end -}} {{- if contains "/" .Values.global.proxy_init.image }} image: "{{ .Values.global.proxy_init.image }}" {{- else }} image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" {{- end }} command: - istio-iptables - "-p" - 15001 - "-z" - "15006" - "-u" - 1337 - "-m" - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - "-i" - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - "-x" - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - "-b" - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` `*` }}" - "-d" - "15090,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - "-o" - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" {{ end -}} {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - "-k" - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" {{ end -}} {{ if .Values.istio_cni.enabled -}} - "--run-validation" - "--skip-rule-apply" {{ end -}} imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" {{- if .Values.global.proxy_init.resources }} resources: {{ toYaml .Values.global.proxy_init.resources | indent 4 }} {{- else }} resources: {} {{- end }} securityContext: allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} privileged: {{ .Values.global.proxy.privileged }} capabilities: {{- if not .Values.istio_cni.enabled }} add: - NET_ADMIN - NET_RAW {{- end }} drop: - ALL readOnlyRootFilesystem: false {{- if not .Values.istio_cni.enabled }} runAsGroup: 0 runAsNonRoot: false runAsUser: 0 {{- else }} runAsGroup: 1337 runAsUser: 1337 runAsNonRoot: true {{- end }} restartPolicy: Always {{ end -}} {{- if eq .Values.global.proxy.enableCoreDump true }} - name: enable-core-dump args: - -c - sysctl -w kernel.core_pattern=/var/lib/istio/core.proxy && ulimit -c unlimited command: - /bin/sh {{- if contains "/" .Values.global.proxy_init.image }} image: "{{ .Values.global.proxy_init.image }}" {{- else }} image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" {{- end }} imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" resources: {} securityContext: allowPrivilegeEscalation: true capabilities: add: - SYS_ADMIN drop: - ALL privileged: true readOnlyRootFilesystem: false runAsGroup: 0 runAsNonRoot: false runAsUser: 0 {{ end }} containers: - name: istio-proxy {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" {{- else }} image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }}" {{- end }} ports: - containerPort: 15090 protocol: TCP name: http-envoy-prom args: - proxy - sidecar - --domain - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - --configPath - "/etc/istio/proxy" - --binaryPath - "/usr/local/bin/envoy" - --serviceCluster {{ if ne "" (index .ObjectMeta.Labels "app") -}} - "{{ index .ObjectMeta.Labels `app` }}.$(POD_NAMESPACE)" {{ else -}} - "{{ valueOrDefault .DeploymentMeta.Name `istio-proxy` }}.{{ valueOrDefault .DeploymentMeta.Namespace `default` }}" {{ end -}} - --drainDuration - "{{ formatDuration .ProxyConfig.DrainDuration }}" - --parentShutdownDuration - "{{ formatDuration .ProxyConfig.ParentShutdownDuration }}" - --discoveryAddress - "{{ annotation .ObjectMeta `sidecar.istio.io/discoveryAddress` .ProxyConfig.DiscoveryAddress }}" {{- if eq .Values.global.proxy.tracer "lightstep" }} - --lightstepAddress - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAddress }}" - --lightstepAccessToken - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAccessToken }}" - --lightstepSecure={{ .ProxyConfig.GetTracing.GetLightstep.GetSecure }} - --lightstepCacertPath - "{{ .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }}" {{- else if eq .Values.global.proxy.tracer "zipkin" }} - --zipkinAddress - "{{ .ProxyConfig.GetTracing.GetZipkin.GetAddress }}" {{- else if eq .Values.global.proxy.tracer "datadog" }} - --datadogAgentAddress - "{{ .ProxyConfig.GetTracing.GetDatadog.GetAddress }}" {{- end }} - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel}} - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel}} - --connectTimeout - "{{ formatDuration .ProxyConfig.ConnectTimeout }}" {{- if .Values.global.proxy.envoyStatsd.enabled }} - --statsdUdpAddress - "{{ .ProxyConfig.StatsdUdpAddress }}" {{- end }} {{- if .Values.global.proxy.envoyMetricsService.enabled }} - --envoyMetricsService - '{{ protoToJSON .ProxyConfig.EnvoyMetricsService }}' {{- end }} {{- if .Values.global.proxy.envoyAccessLogService.enabled }} - --envoyAccessLogService - '{{ protoToJSON .ProxyConfig.EnvoyAccessLogService }}' {{- end }} - --proxyAdminPort - "{{ .ProxyConfig.ProxyAdminPort }}" {{ if gt .ProxyConfig.Concurrency 0 -}} - --concurrency - "{{ .ProxyConfig.Concurrency }}" {{ end -}} {{- if .Values.global.istiod.enabled }} - --controlPlaneAuthPolicy - NONE {{- else if .Values.global.controlPlaneSecurityEnabled }} - --controlPlaneAuthPolicy - MUTUAL_TLS {{- else }} - --controlPlaneAuthPolicy - NONE {{- end }} - --dnsRefreshRate - {{ valueOrDefault .Values.global.proxy.dnsRefreshRate "300s" }} {{- if (ne (annotation .ObjectMeta "status.sidecar.istio.io/port" .Values.global.proxy.statusPort) "0") }} - --statusPort - "{{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }}" {{- end }} {{- if .Values.global.sts.servicePort }} - --stsPort={{ .Values.global.sts.servicePort }} {{- end }} {{- if .Values.global.trustDomain }} - --trust-domain={{ .Values.global.trustDomain }} {{- end }} {{- if .Values.global.logAsJson }} - --log_as_json {{- end }} - --controlPlaneBootstrap=false {{- if .Values.global.proxy.lifecycle }} lifecycle: {{ toYaml .Values.global.proxy.lifecycle | indent 4 }} {{- end }} env: - name: JWT_POLICY value: {{ .Values.global.jwtPolicy }} - name: PILOT_CERT_PROVIDER value: {{ .Values.global.pilotCertProvider }} # Temp, pending PR to make it default or based on the istiodAddr env - name: CA_ADDR {{- if .Values.global.configNamespace }} value: istio-pilot.{{ .Values.global.configNamespace }}.svc:15012 {{- else }} value: istio-pilot.istio-system.svc:15012 {{- end }} - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: INSTANCE_IP valueFrom: fieldRef: fieldPath: status.podIP - name: SERVICE_ACCOUNT valueFrom: fieldRef: fieldPath: spec.serviceAccountName - name: HOST_IP valueFrom: fieldRef: fieldPath: status.hostIP {{- if eq .Values.global.proxy.tracer "datadog" }} {{- if isset .ObjectMeta.Annotations `apm.datadoghq.com/env` }} {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - name: {{ $key }} value: "{{ $value }}" {{- end }} {{- end }} {{- end }} - name: ISTIO_META_POD_PORTS value: |- [ {{- $first := true }} {{- range $index1, $c := .Spec.Containers }} {{- range $index2, $p := $c.Ports }} {{- if (structToJSON $p) }} {{if not $first}},{{end}}{{ structToJSON $p }} {{- $first = false }} {{- end }} {{- end}} {{- end}} ] - name: ISTIO_META_CLUSTER_ID value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - name: ISTIO_META_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: ISTIO_META_CONFIG_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: ISTIO_META_INTERCEPTION_MODE value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" {{- if .Values.global.network }} - name: ISTIO_META_NETWORK value: "{{ .Values.global.network }}" {{- end }} {{ if .ObjectMeta.Annotations }} - name: ISTIO_METAJSON_ANNOTATIONS value: | {{ toJSON .ObjectMeta.Annotations }} {{ end }} {{- if .DeploymentMeta.Name }} - name: ISTIO_META_WORKLOAD_NAME value: {{ .DeploymentMeta.Name }} {{ end }} {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - name: ISTIO_META_OWNER value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} {{- end}} {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - name: ISTIO_BOOTSTRAP_OVERRIDE value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" {{- end }} {{- if .Values.global.meshID }} - name: ISTIO_META_MESH_ID value: "{{ .Values.global.meshID }}" {{- else if .Values.global.trustDomain }} - name: ISTIO_META_MESH_ID value: "{{ .Values.global.trustDomain }}" {{- end }} {{- if eq .Values.global.proxy.tracer "stackdriver" }} - name: STACKDRIVER_TRACING_ENABLED value: "true" - name: STACKDRIVER_TRACING_DEBUG value: "{{ .ProxyConfig.GetTracing.GetStackdriver.GetDebug }}" - name: STACKDRIVER_TRACING_MAX_NUMBER_OF_ANNOTATIONS value: "{{ .ProxyConfig.GetTracing.GetStackdriver.GetMaxNumberOfAnnotations.Value }}" - name: STACKDRIVER_TRACING_MAX_NUMBER_OF_ATTRIBUTES value: "{{ .ProxyConfig.GetTracing.GetStackdriver.GetMaxNumberOfAttributes.Value }}" - name: STACKDRIVER_TRACING_MAX_NUMBER_OF_MESSAGE_EVENTS value: "{{ .ProxyConfig.GetTracing.GetStackdriver.GetMaxNumberOfMessageEvents.Value }}" {{- end }} {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - name: {{ $key }} value: "{{ $value }}" {{- end }} {{- end }} {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - name: {{ $key }} value: "{{ $value }}" {{- end }} imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} readinessProbe: httpGet: path: /healthz/ready port: {{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }} initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} {{ end -}} securityContext: allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} capabilities: {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} add: {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - NET_ADMIN {{- end }} {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - NET_BIND_SERVICE {{- end }} {{- end }} drop: - ALL privileged: {{ .Values.global.proxy.privileged }} readOnlyRootFilesystem: {{ not .Values.global.proxy.enableCoreDump }} runAsGroup: 1337 fsGroup: 1337 {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} runAsNonRoot: false runAsUser: 0 {{- else -}} runAsNonRoot: true runAsUser: 1337 {{- end }} resources: {{ if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} requests: {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" {{ end}} {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" {{ end }} {{ else -}} {{- if .Values.global.proxy.resources }} {{ toYaml .Values.global.proxy.resources | indent 4 }} {{- end }} {{ end -}} volumeMounts: {{- if eq .Values.global.pilotCertProvider "istiod" }} - mountPath: /var/run/secrets/istio name: istiod-ca-cert {{- end }} {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - mountPath: /etc/istio/custom-bootstrap name: custom-bootstrap-volume {{- end }} # SDS channel between istioagent and Envoy - mountPath: /etc/istio/proxy name: istio-envoy {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - mountPath: /var/run/secrets/tokens name: istio-token {{- end }} {{- if .Values.global.mountMtlsCerts }} # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - mountPath: /etc/certs/ name: istio-certs readOnly: true {{- end }} - name: podinfo mountPath: /etc/istio/pod {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - mountPath: {{ directory .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }} name: lightstep-certs readOnly: true {{- end }} {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - name: "{{ $index }}" {{ toYaml $value | indent 4 }} {{ end }} {{- end }} volumes: {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - name: custom-bootstrap-volume configMap: name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} {{- end }} # SDS channel between istioagent and Envoy - emptyDir: medium: Memory name: istio-envoy - name: podinfo downwardAPI: items: - path: "labels" fieldRef: fieldPath: metadata.labels - path: "annotations" fieldRef: fieldPath: metadata.annotations {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - name: istio-token projected: sources: - serviceAccountToken: path: istio-token expirationSeconds: 43200 audience: {{ .Values.global.sds.token.aud }} {{- end }} {{- if eq .Values.global.pilotCertProvider "istiod" }} - name: istiod-ca-cert configMap: name: istio-ca-root-cert {{- end }} {{- if .Values.global.mountMtlsCerts }} # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - name: istio-certs secret: optional: true {{ if eq .Spec.ServiceAccountName "" }} secretName: istio.default {{ else -}} secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} {{ end -}} {{- end }} {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - name: "{{ $index }}" {{ toYaml $value | indent 2 }} {{ end }} {{ end }} {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - name: lightstep-certs secret: optional: true secretName: lightstep.cacert {{- end }} {{- if .Values.global.podDNSSearchNamespaces }} dnsConfig: searches: {{- range .Values.global.podDNSSearchNamespaces }} - {{ render . }} {{- end }} {{- end }} podRedirectAnnot: sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" traffic.sidecar.istio.io/includeOutboundIPRanges: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" traffic.sidecar.istio.io/includeInboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` (includeInboundPorts .Spec.Containers) }}" traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" {{- end }} traffic.sidecar.istio.io/kubevirtInterfaces: "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}"
istio-sidecar-injector ConfigMap の values
$ kubectl get configmap -n istio-system istio-sidecar-injector -o jsonpath='{.data.values}'
{ "global": { "arch": { "amd64": 2, "ppc64le": 2, "s390x": 2 }, "certificates": [], "configNamespace": "istio-system", "configValidation": true, "controlPlaneSecurityEnabled": true, "defaultNodeSelector": {}, "defaultPodDisruptionBudget": { "enabled": true }, "defaultResources": { "requests": { "cpu": "10m" } }, "disablePolicyChecks": true, "enableHelmTest": false, "enableTracing": true, "enabled": true, "hub": "docker.io/istio", "imagePullPolicy": "IfNotPresent", "imagePullSecrets": [], "istioNamespace": "istio-system", "istiod": { "enabled": true }, "jwtPolicy": "first-party-jwt", "k8sIngress": { "enableHttps": false, "enabled": false, "gatewayName": "ingressgateway" }, "localityLbSetting": { "enabled": true }, "logAsJson": false, "logging": { "level": "default:info" }, "meshExpansion": { "enabled": false, "useILB": false }, "meshNetworks": {}, "mountMtlsCerts": false, "mtls": { "auto": true, "enabled": false }, "multiCluster": { "clusterName": "", "enabled": false }, "namespace": "istio-system", "network": "", "omitSidecarInjectorConfigMap": false, "oneNamespace": false, "operatorManageWebhooks": false, "outboundTrafficPolicy": { "mode": "ALLOW_ANY" }, "pilotCertProvider": "istiod", "policyCheckFailOpen": false, "policyNamespace": "istio-system", "priorityClassName": "", "prometheusNamespace": "istio-system", "proxy": { "accessLogEncoding": "JSON", "accessLogFile": "/dev/stdout", "accessLogFormat": "", "autoInject": "enabled", "clusterDomain": "cluster.local", "componentLogLevel": "misc:error", "concurrency": 2, "dnsRefreshRate": "300s", "enableCoreDump": false, "envoyAccessLogService": { "enabled": false }, "envoyMetricsService": { "enabled": false, "tcpKeepalive": { "interval": "10s", "probes": 3, "time": "10s" }, "tlsSettings": { "mode": "DISABLE", "subjectAltNames": [] } }, "envoyStatsd": { "enabled": false }, "excludeIPRanges": "", "excludeInboundPorts": "", "excludeOutboundPorts": "", "image": "proxyv2", "includeIPRanges": "*", "includeInboundPorts": "*", "kubevirtInterfaces": "", "logLevel": "warning", "privileged": false, "protocolDetectionTimeout": "100ms", "readinessFailureThreshold": 30, "readinessInitialDelaySeconds": 1, "readinessPeriodSeconds": 2, "resources": { "limits": { "cpu": "2000m", "memory": "1024Mi" }, "requests": { "cpu": "100m", "memory": "128Mi" } }, "statusPort": 15020, "tracer": "zipkin" }, "proxy_init": { "image": "proxyv2", "resources": { "limits": { "cpu": "100m", "memory": "50Mi" }, "requests": { "cpu": "10m", "memory": "10Mi" } } }, "sds": { "enabled": false, "token": { "aud": "istio-ca" }, "udsPath": "" }, "securityNamespace": "istio-system", "sts": { "servicePort": 0 }, "tag": "1.5.0", "telemetryNamespace": "istio-system", "tracer": { "datadog": { "address": "$(HOST_IP):8126" }, "lightstep": { "accessToken": "", "address": "", "cacertPath": "", "secure": true }, "stackdriver": { "debug": false, "maxNumberOfAnnotations": 200, "maxNumberOfAttributes": 200, "maxNumberOfMessageEvents": 200 }, "zipkin": { "address": "" } }, "trustDomain": "cluster.local", "useMCP": false }, "istio_cni": { "enabled": false }, "sidecarInjectorWebhook": { "alwaysInjectSelector": [], "enableNamespacesByDefault": false, "enabled": false, "image": "sidecar_injector", "injectLabel": "istio-injection", "injectedAnnotations": {}, "namespace": "istio-system", "neverInjectSelector": [], "objectSelector": { "autoInject": true, "enabled": false }, "rewriteAppHTTPProbe": false, "selfSigned": false } }
preStop hook のカスタマイズ
template の中に次のような箇所があります。よって、values でこの global.proxy.lifecycle
を定義してやれば preStop hook を入れることが可能になります。
{{- if .Values.global.proxy.lifecycle }} lifecycle: {{ toYaml .Values.global.proxy.lifecycle | indent 4 }} {{- end }}
preStop hook にどんなコマンドを入れるかですが「本番環境のマルチテナント Kubernetes クラスタへの Istio 導入」で Istio の issue #7136 に投稿されているものが紹介されています。
lifecycle: preStop: exec: command: - "/bin/sh" - "-c" - "while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0 ]; do sleep 1; done"
netstat で envoy 以外に port を listen しているプロセスが存在するかどうかを確認して、存在しなくなるまで sleep 1 して再チェックを繰り返し、存在しなくなったら終了します。その後コンテナのプロセスに対して SIGTERM が送られます。
試しに Istio 1.5 環境で netstat -plunt を実行してみると次のようになりました。pilot-agent も istio-proxy コンテナ内で実行されているため、このコマンドのままでは terminationGracePeriodSeconds (デフォルト30秒) を待っても終了せず、その2秒後に SIGTERM が送られることになります。
$ netstat -plunt Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 18/envoy tcp 0 0 127.0.0.1:15000 0.0.0.0:* LISTEN 18/envoy tcp 0 0 0.0.0.0:15001 0.0.0.0:* LISTEN 18/envoy tcp 0 0 0.0.0.0:15006 0.0.0.0:* LISTEN 18/envoy tcp6 0 0 :::15020 :::* LISTEN 1/pilot-agent
ところで、netstat に -u
がついてるけど grep tcp
してるから意味ないな。あと、xargs
って何の意味があるんだろうか??
ということで global.proxy.lifecycle
に設定するのは次のようになる。kubectl edit cm istio-sidecar-injector -n istio-system
で編集します。
$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.values}' | jq .global.proxy.lifecycle { "preStop": { "exec": { "command": [ "/bin/sh", "-c", "while [ $(netstat -plnt | grep tcp | egrep -v 'envoy|pilot-agent' | wc -l) -ne 0 ]; do sleep 1; done" ] } } }
Deployment の Pod を削除すれば新しい Pod が作成される時に新しい設定で istio-proxy が Inject されるため、kubectl get pod
で確認できる。
$ kubectl get pod httpbin-deployment-v2-ccd49cc9c-5z9mt -o jsonpath='{.spec.containers[*].lifecycle.preStop}' map[exec:map[command:[/bin/sh -c while [ $(netstat -plnt | grep tcp | egrep -v 'envoy|pilot-agent' | wc -l) -ne 0 ]; do sleep 1; done]]]
Istio 導入への道シリーズ
- Istio 導入への道 (1) – インストール編
- Istio 導入への道 (2) – サービス間通信編
- Istio 導入への道 (3) – VirtualService 編
- Istio 導入への道 (4) – Fault Injection 編
- Istio 導入への道 (5) – OutlierDetection と Retry 編
- Istio 導入への道 (6) – Ingress Gatway 編
- Istio 導入への道 (7) – 外部へのアクセス / ServiceEntry 編
- Istio 導入への道 (8) – 外部へのアクセスでも Fault Injection 編
- Istio 導入への道 (9) – gRPC でも Fault Injection 編
- Istio 導入への道 (10) – 図解
- Istio 導入への道 (11) – Ingress Gateway で TLS Termination 編
- Istio 導入への道 (12) – sidecar の調整編