openshift 4.3 Istio的搭建

本文档覆盖了官方文档的Setup的所有章节

安装Istio

本次安装的Istio版本为1.6.0,环境为openshift 4.3

注:不建议使用openshift 1.11(即kubernetes 3.11)安装istio,可能会出现如下兼容性问题,参见此issue

must only have "properties", "required" or "description" at the root if the status subresource is enabled

openshift安装Istio

istio的安装涉及到两个文件:profile和manifest。前者用于控制组件的安装和组件的参数,profile配置文件所在的目录为install/kubernetes/operator/profiles;后者为安装所使用的yaml文件,如service,deployment等,会用到profile提供的参数,manifest配置文件所在的目录为install/kubernetes/operator/charts。因此可以通过两种方式安装istio,一种是通过profile进行安装,istio默认使用这种方式,如:

$ istioctl manifest apply --set profile=default

第二种是通过导出的manifest进行安装,如:

$ kubectl apply -f $HOME/generated-manifest.yaml

参考不同平台可以参考对应的SetUp。在openshift下面部署istio需要注意版本

首先创建istio-system命名空间

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: istio-system
labels:
istio-injection: disabled
EOF

允许istio的serviceaccount使用UID为0的用户,使用的命名空间为istio-system

$ oc adm policy add-scc-to-group anyuid system:serviceaccounts:istio-system

istio默认会注入一个名为istio-initinitContainer,用于将pod的网络流量导向istio的sidecar proxy,该initContainer需要用到完整的NET_ADMINNET_RAW capabilities来配置网络,由此可能造成安全问题,使用istio CNI插件可以替换istio-init,且无需提升kubernetes RBAC权限,区别见下:

# with istio-cni
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsGroup: 1337
runAsNonRoot: true
runAsUser: 1337 # without istio-cni
capabilities:
add:
- NET_ADMIN
- NET_RAW
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsGroup: 0
runAsNonRoot: false
runAsUser: 0

由于openshift 4.1以上版本不再使用iptables,转而使用nftables,因此需要安装istio CNI插件,否则在sidecar注入时会出现如下istio iptables-restore: unable to initialize table 'nat'的错误,即无法执行iptables-resotre命令。

执行如下命令安装istio-cni并使用default类型的profile(见下)安装istio,具体参数含义参见官方文档

cat <<'EOF' > cni-annotations.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
components:
cni:
enabled: true
namespace: kube-system
values:
cni:
excludeNamespaces:
- istio-system
- kube-system
chained: false
cniBinDir: /var/lib/cni/bin
cniConfDir: /etc/cni/multus/net.d
cniConfFileName: istio-cni.conf
sidecarInjectorWebhook:
injectedAnnotations:
"k8s.v1.cni.cncf.io/networks": istio-cni
EOF
$ istioctl manifest apply -f cni-annotations.yaml

安装结果如下:

# oc get pod
NAME READY STATUS RESTARTS AGE
istio-ingressgateway-64f6f9d5c6-lwcp8 1/1 Running 0 41m
istiod-5bb879d86c-6ttml 1/1 Running 0 42m
prometheus-77b9c64b9c-r2pld 2/2 Running 0 41m
# oc get pod -nkube-system
NAME READY STATUS RESTARTS AGE
istio-cni-node-2xzl8 2/2 Running 0 41m
istio-cni-node-4nb6k 2/2 Running 0 41m
istio-cni-node-7j5ck 2/2 Running 0 41m
istio-cni-node-f9bnf 2/2 Running 0 41m
istio-cni-node-lp7v6 2/2 Running 0 41m

为ingress gateway暴露router:

$ oc -n istio-system expose svc/istio-ingressgateway --port=http2

至此istio的基本组件已经安装完毕,可以使用如下方式导出本次安装的profile

$ istioctl profile dump -f cni-annotations.yaml > generated-profile.yaml

使用如下命令导出安装istio的manefest的内容

$ istioctl manifest generate -f cni-annotations.yaml  > generated-manifest.yaml

校验安装结果

$ istioctl verify-install -f generated-manifest.yaml

istio会使用UID为1337的用户将sidecar注入到应用中,openshift默认不允许使用该用户,执行如下命令进行授权。target-namespace为应用所在的命名空间。

$ oc adm policy add-scc-to-group privileged system:serviceaccounts:<target-namespace>
$ oc adm policy add-scc-to-group anyuid system:serviceaccounts:<target-namespace>

当清理应用pod后,需要删除添加的权限

$ oc adm policy remove-scc-from-group privileged system:serviceaccounts:<target-namespace>
$ oc adm policy remove-scc-from-group anyuid system:serviceaccounts:<target-namespace>

openshift下使用multus管理CNI,它需要在应用的命名空间中部署NetworkAttachmentDefinition来使用istio-cni插件,使用如下命令创建NetworkAttachmentDefinitiontarget-namespace替换为实际应用所在的命名空间。

$ cat <<EOF | oc -n <target-namespace> create -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: istio-cni
EOF

移除应用后,使用如下方式移除NetworkAttachmentDefinition

$ oc -n <target-namespace> delete network-attachment-definition istio-cni
更新istio配置

例如可以使用如下方式卸载已经安装的第三方工具Prometheus,注意必须带上文件cni-annotations.yaml,否则会使用默认的profile重新配置istio,这样会导致删除istio-cni。如果组件的配置没有改变,则不会重新该组件的pod

$ istioctl manifest apply -f cni-annotations.yaml --set addonComponents.prometheus.enabled=false
openshif卸载istio
$ istioctl manifest generate -f cni-annotations.yaml | kubectl delete -f -

标准安装istio

istioctl 使用内置的charts生成manifest,这些charts位于目录install/kubernetes/operator/charts

# ll
total 36
drwxr-xr-x. 4 root root 4096 Apr 21 06:51 base
drwxr-xr-x. 4 root root 4096 Apr 21 06:51 gateways
drwxr-xr-x. 3 root root 4096 Apr 21 06:51 istio-cni
drwxr-xr-x. 5 root root 4096 Apr 21 06:51 istio-control
drwxr-xr-x. 3 root root 4096 Apr 21 06:51 istiocoredns
drwxr-xr-x. 3 root root 4096 Apr 21 06:51 istio-operator
drwxr-xr-x. 3 root root 4096 Apr 21 06:51 istio-policy
drwxr-xr-x. 8 root root 4096 Apr 21 06:51 istio-telemetry
drwxr-xr-x. 4 root root 4096 Apr 21 06:51 security

直接执行如下命令即可安装官方默认配置的istio。

$ istioctl manifest apply --set profile=default

istio默认支持如下6种profile

# istioctl profile list
Istio configuration profiles:
default
demo
empty
minimal
preview
remote

安装的组件的区别如下:

Core components
istio-egressgatewayX
istio-ingressgatewayXX
istio-pilotXXX
Addons
grafanaX
istio-tracingX
kialiX
prometheusXXX

使用如下方式可以查看某个profile的配置信息,profile类型helm的values.yaml,用于给部署用的yaml提供配置参数。每个组件包含两部分内容:components下的组件以及组件的参数value

istio提倡使用IstioOperator API进行定制化配置。

$ istioctl profile dump default

使用如下方式可以查看某个组件的配置:

$ istioctl profile dump --config-path components.pilot default

在安装前可以使用如下方式导出需要安装的所有yaml信息 ,包括CRD,deployment,service等

$ istioctl manifest generate --set profile=default --set hub=docker-local.com/openshift4 > $HOME/generated-manifest.yaml

在进行确认或修改之后可以使用apply命令执行安装

$ kubectl apply -f $HOME/generated-manifest.yaml

使用如下方式校验安装结果

$ istioctl verify-install -f $HOME/generated-manifest.yaml

自定义配置:

  • 使用--set选项进行设置,如下面用于设置profile中的global.controlPlaneSecurityEnabled为true

    $ istioctl manifest apply --set values.global.controlPlaneSecurityEnabled=true
  • 如果修改的参数比较多,可以使用yaml文件统一进行配置,实际使用IstioOperator API进行profile的修改

    $ istioctl manifest apply -f samples/operator/pilot-k8s.yaml

istio的核心组件定义在 IstioOperator API的components下面:

base
pilot
proxy
sidecarInjector
telemetry
policy
citadel
nodeagent
galley
ingressGateways
egressGateways
cni

第三方插件可以在IstioOperator API 的addonComponents下指定,如:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
addonComponents:
grafana:
enabled: true

每个组件都有一个KubernetesResourceSpec,用于设置如下k8s属性

  1. Resources
  2. Readiness probes
  3. Replica count
  4. HorizontalPodAutoscaler
  5. PodDisruptionBudget
  6. Pod annotations
  7. Service annotations
  8. ImagePullPolicy
  9. Priority class name
  10. Node selector
  11. Affinity and anti-affinity
  12. Service
  13. Toleration
  14. Strategy
  15. Env
标准卸载istio
$ istioctl manifest generate <your original installation options> | kubectl delete -f -

更新Istio

金丝雀升级

revision安装方式支持同时部署多个版本的istio,在升级时可以将流量逐步转移到新版本的istio上。每个修订revision都是一个完整的istio控制面,具有独立的Deployment, Service等。

控制面升级

使用如下方式可以安装一个名为canary的istio修订版本。下面方式默认会使用default profile创建一套新的istio,如果新的istio和老的istio有组件重叠,则可能导致组件重建。从下面的AGE字段可以看出,新的Prometheus覆盖了老的Prometheusingressgateway也一样,因此最好的方式是指定文件,参见istio install

$ istioctl install --set revision=canary

命令执行成功后,可以看到存在2个控制面,每个控制面有各自的DeploymentService等。

$ oc get pod
NAME READY STATUS RESTARTS AGE
istio-ingressgateway-c9648ffbd-9pmwk 1/1 Running 0 113m
istiod-788cf6c878-cmn47 1/1 Running 0 139m
istiod-canary-79599d745b-ph7gj 1/1 Running 0 113m
prometheus-597596ffdd-v28hk 2/2 Running 0 113m $ oc get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
istio-ingressgateway 1/1 1 1 138m
istiod 1/1 1 1 139m
istiod-canary 1/1 1 1 113m
prometheus 1/1 1 1 138m

sidecar注入配置也有2套

$ oc get mutatingwebhookconfigurations
NAME CREATED AT
istio-sidecar-injector 2020-05-22T02:42:57Z
istio-sidecar-injector-canary 2020-05-22T03:08:28Z
数据面升级

创建新的istio修订版本并不会影响现有的代理。为了升级代理,需要将代理的配置指向新的控制面。通过命名空间标签istio.io/rev设置控制sidecar注入的控制面。

下面操作升级了test-ns命名空间,移除标签istio-injection,并增加标签istio.io/rev,指向新的istio canary。注意必须移除标签istio-injection,否则istio会优先处理istio-injection(原因是为了向后兼容)

$ kubectl label namespace test-ns istio-injection- istio.io/rev=canary

在升级命名空间后,需要重启pod来触发sidecar的注入,如下使用滚动升级

$ kubectl rollout restart deployment -n test-ns

通过如下方式可以查看使用canaryistio修订版的pod

$ kubectl get pods -n test-ns -l istio.io/rev=canary

为了验证test-ns命名空间中的新pod使用了istiod-canary服务,可以选择一个pod使用如下命令进行验证。从输出中可以看到其使用了istiod-canary控制面

$ istioctl proxy-config endpoints ${pod_name}.test-ns --cluster xds-grpc -ojson | grep hostname
"hostname": "istiod-canary.istio-system.svc"

可以通过如下方式导出canary修订版的配置信息

$ istioctl manifest generate --revision=canary >canary.yaml

替换升级

  • 首先下载最新的istio

  • 校验当前环境中支持的升级到的版本,如下命令会给出推荐的版本

    $ istioctl manifest versions
  • 确认需要升级的cluster是否正确

    $ kubectl config view
  • 通过如下命令执行升级,<your-custom-configuration-file>为当前版本的 IstioOperator API 配置 文件

    $ istioctl upgrade -f `<your-custom-configuration-file>`
  • 在更新完毕之后,需要手动重启带有istio sidecar的pod来更新istio数据面

    $ kubectl rollout restart deployment

sidecar注入

为了使用Istio的特性,pods必须运行在istio sidecar proxy的网格中。下面介绍两种注入istio sidecar的方式:手动注入和自动注入。

手动注入通过直接修改,如deployment的配置信息,将proxy配置注入到配置中;当应用所在的命名空间启用自动注入时,会在pod创建时通过mutating webhook admission controller 注入proxy配置。

sidecar的(手动或自动)注入会用到istio-sidecar-injector configmap。

  • 手动注入

    当前版本手动注入时有一个问题,就是使用istio CNI之后无法将annotation k8s.v1.cni.cncf.io/networks注入(或导出)到配置文件中,导致出现如下问题,参见该issue

    in new validator: 10.80.2.222
    Listening on 127.0.0.1:15001
    Listening on 127.0.0.1:15006
    Error connecting to 127.0.0.6:15002: dial tcp 127.0.0.1:0->127.0.0.6:15002: connect: connection refused
    Error connecting to 127.0.0.6:15002: dial tcp 127.0.0.1:0->127.0.0.6:15002: connect: connection refused
    Error connecting to 127.0.0.6:15002: dial tcp 127.0.0.1:0->127.0.0.6:15002: connect: connection refused
    Error connecting to 127.0.0.6:15002: dial tcp 127.0.0.1:0->127.0.0.6:15002: connect: connection refused
    Error connecting to 127.0.0.6:15002: dial tcp 127.0.0.1:0->127.0.0.6:15002: connect: connection refused
    • 手动注入需要满足一个条件,即在应用所在的命名空间中创建NetworkAttachmentDefinition

    • 一种是直接使用istio-sidecar-injector的默认配置直接注入sidecar

      $ istioctl kube-inject -f samples/sleep/sleep.yaml | kubectl apply -f -

      可以使用如下方式直接先导出注入sidecar的deployment,然后使用kubectl直接部署

      $ istioctl kube-inject -f samples/sleep/sleep.yaml -o sleep-injected.yaml --injectConfigMapName istio-sidecar-injector
      $ kubectl apply -f deployment-injected.yaml
    • 另一种是先导出istio-sidecar-injector的默认配置,可以修改后再手动注入sidecar

      导出配置:

      $ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml
      $ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.values}' > inject-values.yaml
      $ kubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yaml

      手动注入:

      $ istioctl kube-inject \
      --injectConfigFile inject-config.yaml \
      --meshConfigFile mesh-config.yaml \
      --valuesFile inject-values.yaml \
      --filename samples/sleep/sleep.yaml \
      | kubectl apply -f -
  • 自动注入

    自动注入时需要满足两个条件:

    • 给应用所在的命名空间打上标签istio-injection=enabled

      $ kubectl label namespace <app-namespace> istio-injection=enabled
    • 与手动注入相同,需要在应用所在的命名空间中创建NetworkAttachmentDefinition,然后在该命名空间下面正常创建(或删除并重建)pod即可自动注入sidecar。需要注意的是自动注入发生在pod层,并不体现在deployment上面,可以使用describe pod查看注入的sidecar。自动注入通过mutatingwebhookconfiguration定义了注入sidecar的规则,当前是istio-injection=enabled

      namespaceSelector:
      matchLabels:
      istio-injection: enabled

      可以通过如下命令修改注入的规则,修改后需要重启已经注入sidecar的pod,使规则生效

      $ kubectl edit mutatingwebhookconfiguration istio-sidecar-injector

      自动注入下,sidecar inject的webhook默认是打开的,如果要禁用该webhook,可以在上面cni-annotations.yaml文件中将sidecarInjectorWebhook.enabled置为false,这样就不会自动注入。

sidecar的注入控制

有两种方式可以控制sidecar的注入:

  • 第一种是修改pod template spec的sidecar.istio.io/inject annotation:当该annotation为true时会进行自动注入sidecar,为false则不会注入sidecar。默认为true。这种情况需要修改应用的deployment。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: ignored
    spec:
    template:
    metadata:
    annotations:
    sidecar.istio.io/inject: "false"
    spec:
    containers:
    - name: ignored
    image: tutum/curl
    command: ["/bin/sleep","infinity"]
  • 第二种是修改istio-sidecar-injector configmap,通过在neverInjectSelector数组中罗列出标签,并禁止对匹配这些标签的pod注入sidecar(各个匹配项的关系为OR)。如下内容中不会对具有标签openshift.io/build.nameopenshift.io/deployer-pod-for.name的pod注入sidecar。这种方式不需要修改应用的deployment。类似地,可以使用alwaysInjectSelector对某些具有特殊标签的pod注入sidecar

    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: istio-sidecar-injector
    data:
    config: |-
    policy: enabled
    neverInjectSelector:
    - matchExpressions:
    - {key: openshift.io/build.name, operator: Exists}
    - matchExpressions:
    - {key: openshift.io/deployer-pod-for.name, operator: Exists}
    template: |-
    initContainers:
    ...

更多istio CNI与sidecar注入和流量重定向相关的参数参见官方文档

卸载自动注入

使用如下方式可以卸载自动注入功能:

$ kubectl delete mutatingwebhookconfiguration istio-sidecar-injector
$ kubectl -n istio-system delete service istio-sidecar-injector
$ kubectl -n istio-system delete deployment istio-sidecar-injector
$ kubectl -n istio-system delete serviceaccount istio-sidecar-injector-service-account
$ kubectl delete clusterrole istio-sidecar-injector-istio-system
$ kubectl delete clusterrolebinding istio-sidecar-injector-admin-role-binding-istio-system

注意上面命令并不会卸载已经注入到pod的sidecar,需要通过滚动更新或直接删除pod来生效

或者使用如下方式对某个命名空间禁用自动注入(推荐):

$ kubectl label namespace <target-namespace> istio-injection-

Istio CNI的兼容

与init容器的兼容

当使用istio CNI的时候,kubelet会按照如下步骤启动注入sidecar的pod:

  • 使用istio CNI插件进行配置,将流量导入到pod中的istio sidecar proxy容器
  • 执行所有init容器,并成功运行结束
  • 启动pod中的istio sidecar proxy和其他容器

由于init容器会在sidecar proxy容器之前运行,因此可能导致应用本身的init容器的通信中断。为了避免发生这种情况,可以通过如下配置避免重定向应用的init容器的流量:

  • 设置traffic.sidecar.istio.io/excludeOutboundIPRanges annotation来禁用将流量重定向到与init容器通信的任何cidr。
  • 设置traffic.sidecar.istio.io/excludeOutboundPorts annotation来禁止将流量重定向到init容器使用的出站端口

与其他CNI插件的兼容

istio CNI插件作为CNI插件链中的一环,当创建或删除一个pod时,会按照顺序启动插件链上的每个插件,istio CNI插件仅仅(通过pod的网络命名空间中的iptables)将应用的pod流量重定向到注入的istio proxy sidecar容器。

istio CNI插件不会干涉配置pod网络的基本CNI插件。

更多细节参见CNI specification reference

TIPs:

  • 不同平台下使用istio CNI执行initContainer时可能会出现istio-validation无法启动的错误,这种情况下默认会导致kubelet删除并重建pod,为了定位问题,可以将repair.deletePods配置为false,这样就不会立即删除pod

    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    spec:
    components:
    cni:
    ...
    values:
    cni:
    repair:
    enabled: true
    deletePods: false
    ...
05-11 22:27