前回の続きです。
Istio でのサービス間通信
まあ、ただサービス間で通信するだけなら Istio は不要なわけだけれども、まずはここから。
httpbin をサービスとして deploy
httpbin.org のコンテナは Request Header をそのまま返してくれたりして便利なのでこれをサービスとして deploy します。
apiVersion: apps/v1 kind: Deployment metadata: name: httpbin-deployment labels: app: httpbin spec: replicas: 2 selector: matchLabels: app: httpbin template: metadata: labels: app: httpbin spec: containers: - name: httpbin image: kennethreitz/httpbin:latest imagePullPolicy: IfNotPresent ports: - containerPort: 80 readinessProbe: httpGet: path: /status/200 port: 80 initialDelaySeconds: 10 periodSeconds: 5 livenessProbe: httpGet: path: /status/200 port: 80 initialDelaySeconds: 15 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: httpbin-service spec: selector: app: httpbin ports: - name: http protocol: TCP port: 80 targetPort: 80
不要なんだけど、全部同じ名前にしちゃうとどれとどれが連動しているのかわかりにくくなるので -service とか -deployment とかを名前に入れています。これを httpbin.yaml
として保存します。
これに Istio の sidecar を inject するのが istioctl kube-inject
です。次のように実行すれば inject 済みの manifest が出力されます。
$ istioctl kube-inject -f httpbin.yaml
apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: httpbin name: httpbin-deployment spec: replicas: 2 selector: matchLabels: app: httpbin strategy: {} template: metadata: annotations: sidecar.istio.io/interceptionMode: REDIRECT sidecar.istio.io/status: '{"version":"1cdb312e0b39910b7401fa37c42113f6436e281598036cb126f9692adebf1545","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","podinfo","istiod-ca-cert"],"imagePullSecrets":null}' traffic.sidecar.istio.io/excludeInboundPorts: "15020" traffic.sidecar.istio.io/includeInboundPorts: "80" traffic.sidecar.istio.io/includeOutboundIPRanges: '*' creationTimestamp: null labels: app: httpbin security.istio.io/tlsMode: istio spec: containers: - image: kennethreitz/httpbin:latest imagePullPolicy: IfNotPresent livenessProbe: httpGet: path: /status/200 port: 80 initialDelaySeconds: 15 periodSeconds: 5 name: httpbin ports: - containerPort: 80 readinessProbe: httpGet: path: /status/200 port: 80 initialDelaySeconds: 10 periodSeconds: 5 resources: {} - args: - proxy - sidecar - --domain - $(POD_NAMESPACE).svc.cluster.local - --configPath - /etc/istio/proxy - --binaryPath - /usr/local/bin/envoy - --serviceCluster - httpbin.$(POD_NAMESPACE) - --drainDuration - 45s - --parentShutdownDuration - 1m0s - --discoveryAddress - istiod.istio-system.svc:15012 - --zipkinAddress - zipkin.istio-system:9411 - --proxyLogLevel=warning - --proxyComponentLogLevel=misc:error - --connectTimeout - 10s - --proxyAdminPort - "15000" - --concurrency - "2" - --controlPlaneAuthPolicy - NONE - --dnsRefreshRate - 300s - --statusPort - "15020" - --trust-domain=cluster.local - --controlPlaneBootstrap=false env: - name: JWT_POLICY value: first-party-jwt - name: PILOT_CERT_PROVIDER value: istiod - name: CA_ADDR value: istio-pilot.istio-system.svc:15012 - 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 - name: ISTIO_META_POD_PORTS value: |- [ {"containerPort":80} ] - name: ISTIO_META_CLUSTER_ID value: 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: REDIRECT - name: ISTIO_META_WORKLOAD_NAME value: httpbin-deployment - name: ISTIO_META_OWNER value: kubernetes://apis/apps/v1/namespaces/default/deployments/httpbin-deployment - name: ISTIO_META_MESH_ID value: cluster.local image: docker.io/istio/proxyv2:1.5.0 imagePullPolicy: IfNotPresent name: istio-proxy ports: - containerPort: 15090 name: http-envoy-prom protocol: TCP readinessProbe: failureThreshold: 30 httpGet: path: /healthz/ready port: 15020 initialDelaySeconds: 1 periodSeconds: 2 resources: limits: cpu: "2" memory: 1Gi requests: cpu: 100m memory: 128Mi securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL privileged: false readOnlyRootFilesystem: true runAsGroup: 1337 runAsNonRoot: true runAsUser: 1337 volumeMounts: - mountPath: /var/run/secrets/istio name: istiod-ca-cert - mountPath: /etc/istio/proxy name: istio-envoy - mountPath: /etc/istio/pod name: podinfo initContainers: - command: - istio-iptables - -p - "15001" - -z - "15006" - -u - "1337" - -m - REDIRECT - -i - '*' - -x - "" - -b - '*' - -d - 15090,15020 image: docker.io/istio/proxyv2:1.5.0 imagePullPolicy: IfNotPresent name: istio-init resources: limits: cpu: 100m memory: 50Mi requests: cpu: 10m memory: 10Mi securityContext: allowPrivilegeEscalation: false capabilities: add: - NET_ADMIN - NET_RAW drop: - ALL privileged: false readOnlyRootFilesystem: false runAsGroup: 0 runAsNonRoot: false runAsUser: 0 securityContext: fsGroup: 1337 volumes: - emptyDir: medium: Memory name: istio-envoy - downwardAPI: items: - fieldRef: fieldPath: metadata.labels path: labels - fieldRef: fieldPath: metadata.annotations path: annotations name: podinfo - configMap: name: istio-ca-root-cert name: istiod-ca-cert status: {} --- apiVersion: v1 kind: Service metadata: name: httpbin-service spec: selector: app: httpbin ports: - name: http protocol: TCP port: 80 targetPort: 80
よって、これを pipe で kubectl apply に食わせることで deploy できます。kubectl apply -f <(istioctl kube-inject -f ...)
でも良いし、もちろんファイルに一旦書き出しても大丈夫。
$ istioctl kube-inject -f httpbin.yaml | kubectl apply -f -
default namespace に deploy しました。
$ istioctl kube-inject -f httpbin.yaml | kubectl apply -f - deployment.apps/httpbin-deployment created service/httpbin-service created $ kubectl get pods,deployments,services NAME READY STATUS RESTARTS AGE pod/httpbin-deployment-9bfd96975-px5lv 2/2 Running 0 88s pod/httpbin-deployment-9bfd96975-v6mgg 2/2 Running 0 88s NAME READY UP-TO-DATE AVAILABLE AGE deployment.extensions/httpbin-deployment 2/2 2 2 88s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/httpbin-service ClusterIP 10.109.118.3180/TCP 88s service/kubernetes ClusterIP 10.96.0.1 443/TCP 4h24m
自動で inject されるようにする
毎回 Deployment を作る度に kube-inject をするのは面倒なので自動化することができる。自動 inject 対象としたい namespace に対して istio-injection=enabled
という label をつけるだけで良い。
$ kubectl label namespace default istio-injection=enabled
$ kubectl get ns default -o yaml apiVersion: v1 kind: Namespace metadata: creationTimestamp: "2020-03-07T06:44:22Z" labels: istio-injection: enabled name: default resourceVersion: "12273" selfLink: /api/v1/namespaces/default uid: 1c02a69b-5ab5-433a-90be-6c6e3b785867 spec: finalizers: - kubernetes status: phase: Active
先ほど deploy した httpbin に対してクラスタ内からアクセスするための ubuntu コンテナを Deployment として deploy します。
apiVersion: apps/v1 kind: Deployment metadata: name: ubuntu-deployment labels: app: ubuntu spec: replicas: 1 selector: matchLabels: app: ubuntu template: metadata: labels: app: ubuntu spec: containers: - name: ubuntu image: ubuntu:latest imagePullPolicy: IfNotPresent command: - sleep - infinity
この manifest を ubuntu.yaml として保存し、今度は istioctl kube-inject
を使わずにそのまま kubectl apply
します。それでも自動で inject されるはずです。
$ kubectl apply -f ubuntu.yaml deployment.apps/ubuntu-deployment created
Deployment の spec には ubuntu コンテナしか書いてなかったのにコンテナの数が2になっています。
$ kubectl get pods -l app=ubuntu NAME READY STATUS RESTARTS AGE ubuntu-deployment-cc86cc647-vsvbh 2/2 Running 0 21s
istio-proxy というコンテナが追加されています。長いので名前だけ表示。
$ kubectl get pods -l app=ubuntu -o json | jq '.items[].spec.containers[].name' "ubuntu" "istio-proxy"
curl でアクセスしてみる
ubuntu Pod から httpbin Service にアクセスしてみます。
$ kubectl exec -it ubuntu-deployment-cc86cc647-vsvbh -c ubuntu -- bash root@ubuntu-deployment-cc86cc647-vsvbh:/#
curl が入っていないのでインストールする。
root@ubuntu-deployment-cc86cc647-vsvbh# apt-get update && apt-get install -y curl
httpbin-service にアクセスしてみる。
root@ubuntu-deployment-cc86cc647-vsvbh:/# curl -sv http://httpbin-service/headers * Trying 10.109.118.31... * TCP_NODELAY set * Connected to httpbin-service (10.109.118.31) port 80 (#0) > GET /headers HTTP/1.1 > Host: httpbin-service > User-Agent: curl/7.58.0 > Accept: */* > < HTTP/1.1 200 OK < server: envoy < date: Sat, 07 Mar 2020 11:33:28 GMT < content-type: application/json < content-length: 521 < access-control-allow-origin: * < access-control-allow-credentials: true < x-envoy-upstream-service-time: 26 < { "headers": { "Accept": "*/*", "Content-Length": "0", "Host": "httpbin-service", "User-Agent": "curl/7.58.0", "X-B3-Parentspanid": "361390f32cd55bdc", "X-B3-Sampled": "0", "X-B3-Spanid": "f95fbc3875ab93e5", "X-B3-Traceid": "3a3e0cd03f1ded4e361390f32cd55bdc", "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/default;Hash=b87885c2542d1279071454ae1ce34cea21b7a265095bb23297ff44542009a304;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default" } } * Connection #0 to host httpbin-service left intact
できました。Response Header に server: envoy
や x-envoy-upstream-service-time: 26
が入っています。また、X-
プレフィックスのついた curl では送っていないヘッダーが httpbin 側に届いているようです。Envoy が間に入っているようです。X-Forwarded-Client-Cent
も送られているのでクライアント証明書も送られたっぽいですね。これは Istio 1.5 からデフォルトになったのだろうか。1.4.6 の時は追加の設定が必要だったのだが。
$ kubectl get -n istio-system cm istio -o yaml | grep -v '{' | grep enableAutoMtls: enableAutoMtls: true
true
になってる。前回の記事に戻って default プロファイルの値を確認してみよう。
$ istioctl manifest generate --set profile=default | grep enableAutoMtls enableAutoMtls: false
あれ?どこで変わったんだ? 🤔 後で確認してみよう。
# If true, automatically configure client side mTLS settings to match the corresponding service's # server side mTLS authentication policy, when destination rule for that service does not specify # TLS settings. enableAutoMtls: true
っていう値。
Envoy のログを確認してみる
前回の istio のインストール時に Envoy のログを有効にしておいたので istio-proxy の出力を確認してみます。
まずは送信元の ubuntu 側。 grep を pipe に繋げると buffering されて全然 jq まで渡ってこないので --line-buffered
をつけています。
$ kubectl logs -l app=ubuntu -c istio-proxy -f --tail 0 | grep --line-buffered '^{' | jq .
{ "authority": "httpbin-service", "bytes_received": "0", "bytes_sent": "521", "downstream_local_address": "10.109.118.31:80", "downstream_remote_address": "172.17.0.9:52178", "duration": "2", "istio_policy_status": "-", "method": "GET", "path": "/headers", "protocol": "HTTP/1.1", "request_id": "a93e2afa-cc27-4bb7-897c-5282c754d382", "requested_server_name": "-", "response_code": "200", "response_flags": "-", "route_name": "default", "start_time": "2020-03-07T12:00:58.425Z", "upstream_cluster": "outbound|80||httpbin-service.default.svc.cluster.local", "upstream_host": "172.17.0.8:80", "upstream_local_address": "172.17.0.9:43422", "upstream_service_time": "2", "upstream_transport_failure_reason": "-", "user_agent": "curl/7.58.0", "x_forwarded_for": "-" }
172.17.0.9 は ubuntu Pod の IP アドレス。10.109.118.31 は httpbin-service Serivce の IP アドレス。172.17.0.8 は httpbin Pod の IP アドレス。
istio-proxy が 172.17.0.9:43422 --> 172.17.0.8:80 でリクエストを投げて、10.109.118.31:80 から 172.17.0.9:52178 に返したことにしてるってことなのか??
次に httpbin 側のログ
kubectl logs -l app=httpbin -c istio-proxy -f --tail 0 | grep --line-buffered '^{' | grep --line-buffered -v kube-probe | jq .
{ "authority": "httpbin-service", "bytes_received": "0", "bytes_sent": "521", "downstream_local_address": "172.17.0.8:80", "downstream_remote_address": "172.17.0.9:43422", "duration": "1", "istio_policy_status": "-", "method": "GET", "path": "/headers", "protocol": "HTTP/1.1", "request_id": "a93e2afa-cc27-4bb7-897c-5282c754d382", "requested_server_name": "outbound_.80_._.httpbin-service.default.svc.cluster.local", "response_code": "200", "response_flags": "-", "route_name": "default", "start_time": "2020-03-07T12:00:58.425Z", "upstream_cluster": "inbound|80|http|httpbin-service.default.svc.cluster.local", "upstream_host": "127.0.0.1:80", "upstream_local_address": "127.0.0.1:55232", "upstream_service_time": "1", "upstream_transport_failure_reason": "-", "user_agent": "curl/7.58.0", "x_forwarded_for": "-" }
172.17.0.9:43422 (ubuntu:istio-proxy) --> 172.17.0.8:80 (httpbin:istio-proxy) 127.0.0.1:55232 --> 127.0.0.1:80 (httpbin:httpbin)
各 Pod と Serivce の IP アドレスは次の通り。
$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpbin-deployment-9bfd96975-px5lv 2/2 Running 0 60m 172.17.0.8 m01httpbin-deployment-9bfd96975-v6mgg 2/2 Running 0 60m 172.17.0.7 m01 ubuntu-deployment-cc86cc647-vsvbh 2/2 Running 0 45m 172.17.0.9 m01 $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpbin-service ClusterIP 10.109.118.31 80/TCP 60m kubernetes ClusterIP 10.96.0.1 443/TCP 5h22m
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 編