openshift 4.3 Istio的搭建(istio 系列一)

openshift 4.3 Istio的搭建

本文檔覆蓋了官方文檔的Setup的所有章節

安裝Istio

本次安裝的Istio版本為1.5.2,環境為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需要注意版本

OpenShift 4.1 and above use nftables, which is incompatible with the Istio proxy-init container. Make sure to use CNI instead.

首先創建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:
    empty
    minimal
    remote
    separate
    default
    demo

安裝的組件的區別如下:

default demo minimal remote
Core components
istio-egressgateway X
istio-ingressgateway X X
istio-pilot X X X
Addons
grafana X
istio-tracing X
kiali X
prometheus X X X

使用如下方式可以查看某個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下面:

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

升級過程中可能會造成流量中斷,為了最小化影響,需要確保istio中的各個組件(除Citadel)至少有兩個副本正在運行,此外需要通過PodDistruptionBudgets保證至少有一個可用的pod

  • 首先下載最新的istio

  • 校驗當前環境中支援的升級到的版本,如下命令會給出推薦的版本

    $ istioctl manifest versions
    
  • 確認需要升級的cluster是否正確

    $ kubectl config view
    
  • 通過如下命令執行升級,<your-custom-configuration-file>為當前版本的 IstioOperator API 配置 文件

    $ istioctl upgrade -f `<your-custom-configuration-file>`
    

    istioctl upgrade不支援--set命令

  • 在更新完畢之後,需要手動重啟帶有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定義了注入的命名空間的規則,當前是istio-injection=enabled label

        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容器通訊的CIDRs
  • 設置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
            ...
    
Tags: