istio-proxy原理

前言

在上一篇文章介绍的kubernetes环境的示例中,istio以边车模式部署在pod中,它接管了业务容器所有出入站流量,从而灵活控制网络。本文关键讨论如何接管。

kubernetes Init Containers

在开始之前,先介绍kubernetes的 Init Containers。比如如下示例

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

initContainers有几个特点:

  1. 从开始到结束,只运行一次
  2. 每一个initContainer都必须成功执行后才会执行下一个
  3. 在容器启动之前执行
  4. 如果执行失败,pod会重启(restartPolicy为Never除外)

因为pod共享命名空间、网络和volumes,可以通过它在普通容器启动前初始化网络环境。

istio-proxy

一个普通nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.0-alpine
        ports:
        - containerPort: 80

使用istioctl部署如下

kubectl apply -f <(istioctl kube-inject -f nginx-deployment.yaml) 

我们关注istioctl kube-inject -f nginx-deployment.yaml 生成的内容。

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  strategy: {}
  template:
    metadata:
      annotations:
        sidecar.istio.io/status: '{"version":"c560152609b91ff8d7c47257873095fffb2bddad37acf20013cc9362e6b389de","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}'
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.0-alpine
        name: nginx
        ports:
        - containerPort: 80
        resources: {}
      - args:
        - proxy
        - sidecar
        - --configPath
        - /etc/istio/proxy
        - --binaryPath
        - /usr/local/bin/envoy
        - --serviceCluster
        - nginx
        - --drainDuration
        - 45s
        - --parentShutdownDuration
        - 1m0s
        - --discoveryAddress
        - istio-pilot.istio-system:15007
        - --discoveryRefreshDelay
        - 1s
        - --zipkinAddress
        - zipkin.istio-system:9411
        - --connectTimeout
        - 10s
        - --statsdUdpAddress
        - istio-statsd-prom-bridge.istio-system:9125
        - --proxyAdminPort
        - "15000"
        - --controlPlaneAuthPolicy
        - NONE
        env:
        - 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: ISTIO_META_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: ISTIO_META_INTERCEPTION_MODE
          value: REDIRECT
        image: registry.cn-hangzhou.aliyuncs.com/my-istio-release/proxyv2:1.0.2
        imagePullPolicy: IfNotPresent
        name: istio-proxy
        resources:
          requests:
            cpu: 10m
        securityContext:
          readOnlyRootFilesystem: true
          runAsUser: 1337
        volumeMounts:
        - mountPath: /etc/istio/proxy
          name: istio-envoy
        - mountPath: /etc/certs/
          name: istio-certs
          readOnly: true
      initContainers:
      - args:
        - -p
        - "15001"
        - -u
        - "1337"
        - -m
        - REDIRECT
        - -i
        - '*'
        - -x
        - ""
        - -b
        - 80,
        - -d
        - ""
        image: registry.cn-hangzhou.aliyuncs.com/my-istio-release/proxy_init:1.0.2
        imagePullPolicy: IfNotPresent
        name: istio-init
        resources: {}
        securityContext:
          capabilities:
            add:
            - NET_ADMIN
      volumes:
      - emptyDir:
          medium: Memory
        name: istio-envoy
      - name: istio-certs
        secret:
          optional: true
          secretName: istio.default
status: {}
  1. 在原理pod基础增加了容器istio-proxy
  2. 在原理pod基础增加了初始化容器istio-init

istio-init正是上文提到的k8s initContainers。使用proxy_init镜像,通过docker inspect命令可以查看此镜像的Entrypoint:

{
"Entrypoint": ["/usr/local/bin/istio-iptables.sh"]
}

这个初始化容器主要工作就是执行这样一个脚本istio-iptables.sh参数args是:

istio-iptables.sh -p "15001" -u "1337" -m REDIRECT -i '*' -x "" -b 80, -d ""

他的作用是把所有80端口的出入站流量重定向 UID为1337、端口为15001的istio_proxy(Envoy)。通过iptable完成了网络流量的拦截转发都到Envoy代理,这就是sidecar接管流量的关键。iptables的详细介绍见理解iptables

流量到envoy后,它的路由、服务发现等动态配置从哪里获取的?

istioctl kube-inject -f 生成的内容还有这样一段:

proxy
sidecar
--configPath
/etc/istio/proxy
--binaryPath
/usr/local/bin/envoy
--serviceCluster
nginx
--drainDuration
45s
--parentShutdownDuration
1m0s
--discoveryAddress
istio-pilot.istio-system:15007
--discoveryRefreshDelay
1s
--zipkinAddress
zipkin.istio-system:9411
--connectTimeout
10s
--statsdUdpAddress
istio-statsd-prom-bridge.istio-system:9125
--proxyAdminPort
"15000"
--controlPlaneAuthPolicy
NONE

这是proxy的初始化参数,告诉envoy,discoveryAddress地址istio-pilot.istio-system:15007

--discoveryAddress istio-pilot.istio-system:15007
--discoveryRefreshDelay 1s

总结

  1. 通过k8s初始化容器执行iptables指令,把流量交给proxy
  2. proxy通过pilot获取配置

下文我们讨论,pilot 和 envoy是怎么结合的。

CONTENTS