├── README.md ├── authors ├── _index.md └── admin │ ├── _index.md │ └── avatar.jpg ├── basics ├── _index.md ├── install-istioctl │ └── index.md └── install-with-operator │ └── index.md ├── best-practice ├── _index.md ├── graceful-termination │ ├── 1.jpg │ └── index.md ├── optimize-performance │ └── index.md ├── set-default-route │ └── index.md ├── specify-protocol │ └── index.md └── tracing │ └── index.md ├── faq ├── 404-status-code │ └── index.md ├── 426-status-code │ └── index.md ├── 431-status-code │ ├── 1.png │ └── index.md ├── _index.md ├── circuit-breaking-not-work │ └── index.md ├── grpc-config-stream-closed │ ├── 1.png │ └── index.md ├── headless-svc │ └── index.md ├── http-match │ └── index.md ├── isito-init-crash │ └── index.md ├── kubeflow-on-istio │ └── index.md ├── listen-any │ └── index.md ├── locality-lb │ └── index.md ├── multicluster │ └── index.md ├── retries-for-non-idempotent-services │ └── index.md ├── sidecar-shutdown │ └── index.md ├── sidecar-startup-order │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── index.md ├── sidecar │ ├── 1.png │ └── index.md ├── smart-dns │ └── index.md ├── the-case-of-http-header │ └── index.md ├── uppercase-header-causes-sticky-sessions-to-not-work │ └── index.md ├── virtualservice-not-working │ ├── 1.png │ └── index.md └── why-is-tracing-incomplete │ └── index.md ├── ha ├── _index.md └── locality-loadbalancing │ └── index.md ├── home ├── hero.md └── index.md ├── ref ├── _index.md ├── bug │ └── index.md ├── envoy-config │ └── index.md ├── envoy-log │ └── index.md ├── link │ └── index.md ├── shell │ └── index.md └── yaml │ └── index.md ├── source ├── _index.md ├── code-index │ └── index.md ├── istiod │ └── index.md ├── project-structure │ └── index.md └── use-clion-read-envoy-source-code │ ├── 1.png │ ├── 2.png │ ├── 3.gif │ ├── 4.png │ ├── 5.gif │ ├── 6.png │ └── index.md ├── tcm ├── _index.md ├── best-practice │ ├── _index.md │ └── upgrade-mesh │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ └── index.md └── faq │ ├── _index.md │ ├── cannot-inject-sidecar │ ├── 1.png │ └── index.md │ ├── cluster-auth │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── index.md │ └── public-ingressgateway-connect-failed │ ├── 1.png │ └── index.md ├── trick ├── _index.md ├── check │ └── index.md ├── customize-proxy-loglevel │ └── index.md ├── debug │ └── index.md ├── header-authorization │ └── index.md ├── hide-server-header │ └── index.md ├── multi-version-test-service-with-prism │ └── index.md ├── partially-enable-accesslog │ └── index.md ├── restrict-sidecar-namespace │ └── index.md ├── set-max-body-size │ └── index.md └── troubleshooting-networking │ ├── 1.png │ └── index.md ├── troubleshooting ├── _index.md ├── apollo-on-istio │ ├── 1.jpg │ └── index.md ├── cannot-connect-pod-without-sidecar │ └── index.md ├── grpc-not-loadbalancing │ └── index.md ├── grpc-without-status-code │ └── index.md ├── istio-token-setup-failed-for-volume-istio-token │ ├── 1.png │ └── index.md ├── traffic-policy-does-not-take-effect │ └── index.md └── using-istio-reserved-port-causes-pod-start-failed │ ├── 1.png │ ├── 2.jpg │ └── index.md └── usage ├── _index.md ├── accesslogs └── index.md ├── cors └── index.md ├── iphash └── index.md ├── oidc ├── 1.png ├── 2.png ├── 3.png └── index.md └── websocket └── index.md /README.md: -------------------------------------------------------------------------------- 1 | # istio 学习笔记 2 | 3 | https://imroc.cc/istio -------------------------------------------------------------------------------- /authors/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # To publish author profile pages, remove all of the `_build` and `cascade` settings below. 3 | _build: 4 | render: never 5 | cascade: 6 | _build: 7 | render: never 8 | list: always 9 | --- 10 | -------------------------------------------------------------------------------- /authors/admin/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # Display name 3 | title: roc 4 | 5 | # Is this the primary user of the site? 6 | superuser: true 7 | 8 | # Role/position/tagline 9 | role: 云原生架构师 10 | 11 | # Organizations/Affiliations to show in About widget 12 | organizations: 13 | - name: Tencent 14 | url: https://www.tencent.com/ 15 | 16 | # Short bio (displayed in user profile at end of posts) 17 | bio: 授人以鱼不如授人以渔 18 | 19 | # Social/Academic Networking 20 | # For available icons, see: https://sourcethemes.com/academic/docs/page-builder/#icons 21 | # For an email link, use "fas" icon pack, "envelope" icon, and a link in the 22 | # form "mailto:your-email@example.com" or "/#contact" for contact widget. 23 | social: 24 | - icon: github 25 | icon_pack: fab 26 | link: https://github.com/imroc 27 | - icon: envelope 28 | icon_pack: fas 29 | link: "mailto:roc@imroc.cc" 30 | - icon: twitter 31 | icon_pack: fab 32 | link: https://twitter.com/imrocchan 33 | # - icon: bilibili 34 | # icon_pack: custom 35 | # link: https://space.bilibili.com/473883733 36 | - icon: linkedin 37 | icon_pack: fab 38 | link: https://www.linkedin.com/in/%E9%B9%8F-%E9%99%88-b51646113 39 | - icon: youtube 40 | icon_pack: fab 41 | link: https://www.youtube.com/channel/UCEtWQhtiNi1vvDmoC6wydNQ 42 | 43 | # Link to a PDF of your resume/CV. 44 | # To use: copy your resume to `static/media/resume.pdf`, enable `ai` icons in `params.toml`, 45 | # and uncomment the lines below. 46 | # - icon: cv 47 | # icon_pack: ai 48 | # link: media/resume.pdf 49 | 50 | # Enter email to display Gravatar (if Gravatar enabled in Config) 51 | email: "roc@imroc.cc" 52 | 53 | # Highlight the author in author lists? (true/false) 54 | highlight_name: true 55 | --- 56 | 57 | Hi,我是 roc,专注云原生技术领域,不定期分享容器、Kubernetes、Service Mesh 等相关干货。 58 | 59 | (欢迎使用 [RSS订阅](/index.xml) 本站内容更新) -------------------------------------------------------------------------------- /authors/admin/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/authors/admin/avatar.jpg -------------------------------------------------------------------------------- /basics/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | linktitle: Istio 基础 3 | title: Istio 基础 4 | date: "2021-01-26" 5 | type: book # Do not modify. 6 | --- 7 | 8 | 先占位,后续陆续补充笔记 -------------------------------------------------------------------------------- /basics/install-istioctl/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 安装 istioctl 3 | type: book 4 | date: "2021-01-26" 5 | weight: 10 6 | --- 7 | 8 | 一键下载: 9 | ``` bash 10 | $ curl -sL https://istio.io/downloadIstioctl | sh - 11 | Downloading istioctl-1.8.2 from https://github.com/istio/istio/releases/download/1.8.2/istioctl-1.8.2-osx.tar.gz ... 12 | istioctl-1.8.2-osx.tar.gz download complete! 13 | 14 | Add the istioctl to your path with: 15 | export PATH=$PATH:$HOME/.istioctl/bin 16 | 17 | Begin the Istio pre-installation check by running: 18 | istioctl x precheck 19 | 20 | Need more information? Visit https://istio.io/docs/reference/commands/istioctl/ 21 | ``` 22 | 23 | 二进制会被下载到 `~/.istioctl/bin/istioctl`,在我们的 shell 启动脚本里 (`~/.bashrc` 或 `~/.zshrc`) 加入: 24 | ``` bash 25 | export PATH=$PATH:$HOME/.istioctl/bin 26 | ``` 27 | 28 | 新开终端,检测是否安装成功: 29 | ``` bash 30 | $ istioctl version 31 | no running Istio pods in "istio-system" 32 | 1.8.2 33 | ``` -------------------------------------------------------------------------------- /basics/install-with-operator/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 istio-operator 安装 istio 3 | type: book 4 | date: "2021-01-26" 5 | weight: 20 6 | --- 7 | 8 | ## 安装 istioctl 9 | 10 | 需要先安装 istioctl,参考 [安装 istioctl](install-istioctl.html) 11 | 12 | ## 使用 istioctl 安装 istio-operator 13 | 14 | 一键安装: 15 | 16 | ```bash 17 | $ istioctl operator init 18 | Installing operator controller in namespace: istio-operator using image: docker.io/istio/operator:1.8.2 19 | Operator controller will watch namespaces: istio-system 20 | ✔ Istio operator installed 21 | ✔ Installation complete 22 | ``` 23 | 24 | 检查 operator 是否 running: 25 | 26 | ```bash 27 | $ kubectl -n istio-operator get pods 28 | NAME READY STATUS RESTARTS AGE 29 | istio-operator-675b8ff647-n4bvc 1/1 Running 0 75s 30 | ``` 31 | 32 | ## 安装 istio 33 | 34 | 准备 `istio.yaml`: 35 | 36 | ```yaml 37 | apiVersion: install.istio.io/v1alpha1 38 | kind: IstioOperator 39 | metadata: 40 | namespace: istio-system 41 | name: example-istiocontrolplane 42 | spec: 43 | profile: demo 44 | ``` 45 | 46 | 安装: 47 | 48 | ```bash 49 | kubectl apply -f istio.yaml 50 | ``` 51 | 52 | 查看是否安装成功: 53 | 54 | ```bash 55 | $ kubectl -n istio-system get pods 56 | NAME READY STATUS RESTARTS AGE 57 | istio-egressgateway-69d75b5f96-jrsd9 1/1 Running 0 3m9s 58 | istio-ingressgateway-674d7d9bb5-cthhs 1/1 Running 0 3m9s 59 | istiod-57799dfcf9-wftrt 1/1 Running 0 3m35s 60 | ``` 61 | 62 | 查看 ingressgateway 的 LB 是否正常创建: 63 | 64 | ```bash 65 | $ kubectl -n istio-system get svc 66 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 67 | istio-egressgateway ClusterIP 172.21.254.245 80/TCP,443/TCP,15443/TCP 5m41s 68 | istio-ingressgateway LoadBalancer 172.21.255.248 120.53.204.198 15021:31590/TCP,80:30547/TCP,443:30701/TCP,31400:30329/TCP,15443:31790/TCP 5m41s 69 | istiod ClusterIP 172.21.254.57 15010/TCP,15012/TCP,443/TCP,15014/TCP 6m8s 70 | ``` 71 | 72 | 拿到流量入口(LB) 的 IP 地址 `120.53.204.198`,若要通过域名访问,配置域名解析到该 IP 地址 (如果使用 80/443 访问,国内部署需要备案)。 73 | 74 | ## 自定义安装配置 75 | 76 | 上述安装使用了名为 `demo` 的内置 profile (安装配置),资源占用较少,适合学习和演示用。通过以下命令可查看有哪些内置的 profile: 77 | 78 | ```bash 79 | $ istioctl profile list 80 | Istio configuration profiles: 81 | default 82 | demo 83 | empty 84 | minimal 85 | openshift 86 | preview 87 | remote 88 | ``` 89 | 90 | 具体每种 profile 的解释可参考 [官方文档](https://istio.io/latest/docs/setup/additional-setup/config-profiles/),若需要自定义安装配置,建议基于内置的 profile 进行修改,通过以下命令导出配置: 91 | ``` bash 92 | istioctl profile dump demo > istio.yaml 93 | ``` 94 | 95 | 导出的配置是不包含 `metadata` 的,不能直接 `apply`,可以在 `kind` 下方加入 `metadata` 来指定 istio 需要安装的 namespace 和 CR (Custom Resource) 名称: 96 | 97 | ```yaml 98 | metadata: 99 | namespace: istio-system 100 | name: example-istiocontrolplane 101 | ``` 102 | 103 | 导出的 `istio.yaml` 将所有字段都列出来了,我们可以根据需求进行自定义,具体字段含义可参考 [API 文档](https://istio.io/latest/docs/reference/config/istio.operator.v1alpha1/)。 104 | 105 | 修改完后可通过 `apply` 来安装或更新: 106 | 107 | ```bash 108 | kubectl apply -f istio.yaml 109 | ``` -------------------------------------------------------------------------------- /best-practice/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 最佳实践 3 | type: book # Do not modify. 4 | --- 5 | 6 | 先占位,后续陆续补充笔记 -------------------------------------------------------------------------------- /best-practice/graceful-termination/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/best-practice/graceful-termination/1.jpg -------------------------------------------------------------------------------- /best-practice/graceful-termination/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "优雅终止" 3 | type: book 4 | date: "2021-05-29" 5 | --- 6 | 7 | ## 概述 8 | 9 | 本文介绍在 istio 场景下实现优雅终止时需要重点关注的点,一些容器场景通用的关注点请参考 [Kubenretes 最佳实践: 优雅终止](https://imroc.cc/k8s/best-practice/graceful-shutdown/) 。 10 | 11 | ## envoy 被强杀导致流量异常 12 | 13 | 当业务上了 istio 之后,流量被 sidecar 劫持,进程之间不会直接建立连接,而是经过了 sidecar 这一层代理: 14 | 15 | ![](1.jpg) 16 | 17 | 当 Pod 开始停止时,它将从服务的 endpoints 中摘除掉,不再转发流量给它,同时 Sidecar 也会收到 `SIGTERM` 信号,立刻不再接受 inbound 新连接,但会保持存量 inbound 连接继续处理,outbound 方向流量仍然可以正常发起。 18 | 19 | 不过有个值得注意的细节,若 Pod 没有很快退出,istio 默认是会在停止开始的 5s 后强制杀死 envoy,当 envoy 进程不在了也就无法转发任何流量(不管是 inbound 还是 outbound 方向),所以就可能存在一些问题: 20 | 21 | 1. 若被停止的服务提供的接口耗时本身较长(比如文本转语音),存量 inbound 请求可能无法被处理完就断开了。 22 | 2. 若停止的过程需要调用其它服务(比如通知其它服务进行清理),outbound 请求可能会调用失败。 23 | 24 | ## 自定义 terminationDrainDuration 25 | 26 | istio 提供了 `terminationDrainDuration` 这个连接优雅终止时长的自定义配置,表示 sleep 多长时间之后才强制杀死 envoy,默认是 5s,可以使用 `proxy.istio.io/config` 这个 [Resource Annotation](https://istio.io/latest/docs/reference/config/annotations/) 来对需要自定义连接优雅终止时长的服务配置 `terminationDrainDuration`,用法示例: 27 | 28 | ```yaml 29 | apiVersion: apps/v1 30 | kind: Deployment 31 | metadata: 32 | name: nginx 33 | spec: 34 | replicas: 1 35 | selector: 36 | matchLabels: 37 | app: nginx 38 | template: 39 | metadata: 40 | annotations: 41 | proxy.istio.io/config: | 42 | terminationDrainDuration: 60s # 这里自定义 Envoy 优雅终止时长 43 | labels: 44 | app: nginx 45 | spec: 46 | terminationGracePeriodSeconds: 60 # 若 terminationDrainDuration 超时 30s 则显式指定 terminationGracePeriodSeconds 47 | containers: 48 | - name: nginx 49 | image: "nginx" 50 | ``` 51 | 52 | * 如果 `terminationDrainDuration` 大于 30s,需要显式给 Pod 指定 `terminationGracePeriodSeconds`,因为这个值默认为 30s,即 30s 之后容器内进程还未退出就发 SIGKILL 信号将其强制杀死。所以要确保 `terminationGracePeriodSeconds` 大于等于 `terminationDrainDuration` 才好让优雅终止时长完全生效。 53 | * `terminationDrainDuration` 设置的越大,同时也意味着 Pod 会停止得越慢,所以建议根据业务场景进行自定义,只给需要的服务进行合理自定义,其它情况可以使用默认值。 54 | 55 | ### 使用 preStop 56 | 57 | 如果业务停止需要的时长不太固定,不好使用固定的 `terminationDrainDuration` 去控制 sidecar 停止时间,其实也可以通过给 sidecar 加一个 preStop 脚本,在脚本里通过判断是否还要连接来间接判断应用是否已经退出,等应用退出了之后 envoy 才真正开始退出 (默认再等 5s)。 58 | 59 | 添加 preStop 可以通过修改 sidecar injector 的全局 configmap 来实现: 60 | 61 | ```bash 62 | kubectl -n istio-system edit configmap istio-sidecar-injector 63 | ``` 64 | 65 | > 如果使用 TCM,托管网格添加 preStop 需要提工单后台操作,独立网格可以自行修改主集群里的 configmap,但 configmap 名称和这里不一样,会带上版本后缀。 66 | 67 | 在 values 里面的 `global.proxy` 加入以下 lifecycle 字段: 68 | 69 | ```json 70 | "lifecycle": { 71 | "preStop": { 72 | "exec": { 73 | "command": ["/bin/sh", "-c", "while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0 ]; do sleep 1; done"] 74 | }, 75 | }, 76 | }, 77 | ``` 78 | 79 | ## 参考资料 80 | 81 | * [istio 常见问题: Sidecar 停止问题](https://imroc.cc/istio/faq/sidecar-shutdown/) -------------------------------------------------------------------------------- /best-practice/optimize-performance/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 性能优化 3 | type: book 4 | date: "2021-01-27" 5 | weight: 3 6 | draft: true 7 | --- 8 | 9 | ## istiod 负载均衡 10 | envoy 定时重连 11 | 12 | ## istiod HPA 13 | istiod 无状态,可水平扩展 14 | 15 | ## xDS 按需下发 16 | 17 | * lazy loading 18 | * [Delta xDS](https://docs.google.com/document/d/1hwC81_jS8qARBDcDE6VTxx6fA31In96xAZWqfwnKhpQ/edit#heading=h.xw1gqgyqs5b) 19 | 20 | ## batch 推送间隔优化 21 | istiod推送流控规则有合并推送策略,目前这个时间间隔默认值为100ms。可配,一般很少用户会关心这个,在 mesh 全局配置中可以改: PILOT_DEBOUNCE_AFTER 和 PILOT_DEBOUNCE_MAX。 主要取决于:用户期望流控规则更新的实时性,以及 istiod 稳定性的权衡,如果期望实时性高,则把防抖动时间设置短些,如果mesh规模大,希望istiod提高稳定性,则把防抖动时间设置长些。 22 | 23 | 24 | ## 关闭不必要的遥测 25 | 26 | TODO 27 | 28 | ## 关闭 mtls 29 | 30 | 如果认为集群内是安全的,可以关掉 mtls 以提升性能 31 | 32 | ## istio 版本 33 | 34 | * istio 1.8: 资源消耗上,envoy 大概有 30% 的降低 -------------------------------------------------------------------------------- /best-practice/set-default-route/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 为服务设置默认路由 3 | type: book 4 | date: "2021-03-17" 5 | weight: 1000 6 | --- 7 | 8 | 很多时候一开始我们的服务没有多个版本,也没配置 vs,只有一个 deployment 和一个 svc,如果我们要为业务配置默认流量策略,可以直接创建 dr,给对应 host 设置 trafficPolicy,示例: 9 | 10 | ```yaml 11 | apiVersion: networking.istio.io/v1alpha3 12 | kind: DestinationRule 13 | metadata: 14 | name: reviews 15 | spec: 16 | host: reviews 17 | trafficPolicy: 18 | connectionPool: 19 | tcp: 20 | maxConnections: 100 21 | subsets: 22 | - name: v1 23 | labels: 24 | version: v1 25 | ``` 26 | 27 | 需要注意的是,虽然 subsets 下也可以设置 trafficPolicy,但 subsets 下设置的不是默认策略,而且在没有 vs 明确指定路由到对应 subset 时,即便我们的服务只有一个版本,istio 也不会使用 subset 下指定的 trafficPolicy,错误示例: 28 | 29 | ```yaml 30 | apiVersion: networking.istio.io/v1alpha3 31 | kind: DestinationRule 32 | metadata: 33 | name: reviews 34 | spec: 35 | host: reviews 36 | subsets: 37 | - name: v1 38 | labels: 39 | version: v1 40 | trafficPolicy: 41 | connectionPool: 42 | tcp: 43 | maxConnections: 100 44 | ``` 45 | 46 | 想要做的更好,可以定义下 vs,明确路由到指定版本(后续就可以针对不同版本指定不同的流量策略): 47 | 48 | ```yaml 49 | apiVersion: networking.istio.io/v1alpha3 50 | kind: VirtualService 51 | metadata: 52 | name: reviews 53 | spec: 54 | hosts: 55 | - reviews 56 | http: 57 | - route: 58 | - destination: 59 | host: reviews 60 | subset: v1 61 | ``` -------------------------------------------------------------------------------- /best-practice/specify-protocol/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 为服务显式指定协议 3 | type: book 4 | date: "2021-03-30" 5 | weight: 50 6 | --- 7 | 8 | ## 背景 9 | 10 | istio 需要知道服务提供什么七层协议,从而来为其配置相应协议的 filter chain,通常最好是显式声明协议,如果没有声明,istio 会自动探测,这个探测能力比较有限,有些时候可能会匹配协议错误(比如使用非标端口),导致无法正常工作。 11 | 12 | 本文将列出显示声明协议的方法。 13 | 14 | ## 集群内: 指定 Service 端口的协议 15 | 16 | 给集群内 Service 指定 port name 时加上相应的前缀或指定 `appProtocol` 字段可以显示声明协议,如: 17 | 18 | ```yaml 19 | kind: Service 20 | metadata: 21 | name: myservice 22 | spec: 23 | ports: 24 | - number: 8080 25 | name: rpc 26 | appProtocol: grpc # 指定该端口提供 grpc 协议的服务 27 | - number: 80 28 | name: http-web # 指定该端口提供 http 协议的服务 29 | ``` 30 | 31 | 更多详细信息请参考 [Explicit protocol selection](https://istio.io/latest/docs/ops/configuration/traffic-management/protocol-selection/#explicit-protocol-selection) 。 32 | 33 | ## 集群外: 手动创建 Service + Endpoint 34 | 35 | 如果服务在集群外部 (比如 mysql),我们可以为其手动创建一组 Service + Endpoint,且 Service 端口指定协议(跟上面一样),这样就可以在集群内通过 Service 访问外部服务,且正确识别协议。示例: 36 | 37 | ```yaml 38 | apiVersion: v1 39 | kind: Service 40 | metadata: 41 | name: mysql 42 | namespace: default 43 | spec: 44 | ports: 45 | - port: 4000 46 | name: mysql 47 | protocol: TCP 48 | 49 | --- 50 | apiVersion: v1 51 | kind: Endpoints 52 | metadata: 53 | name: mysql 54 | namespace: default 55 | subsets: 56 | - addresses: 57 | - ip: 190.64.31.232 # 替换外部服务的 IP 地址 58 | ports: 59 | - port: 4000 60 | name: mysql 61 | protocol: TCP 62 | ``` 63 | 64 | 创建好之后就可以通过 svc 去访问外部服务了,本例中服务地址为: `mysql.default.svc.cluster.local:4000`。 65 | 66 | ## 集群外: 使用 ServiceEntry 指定协议 67 | 68 | 如果外部服务可以被 DNS 解析,可以定义 ServiceEntry 来指定协议: 69 | 70 | ```yaml 71 | apiVersion: networking.istio.io/v1beta1 72 | kind: ServiceEntry 73 | metadata: 74 | name: external-mysql 75 | spec: 76 | hosts: 77 | - mysql.example.com 78 | location: MESH_EXTERNAL 79 | ports: 80 | - number: 4000 81 | name: mysql 82 | protocol: mysql 83 | resolution: DNS 84 | ``` 85 | 86 | 创建好之后就可以通过域名去访问外部服务了,本例中服务地址为: `mysql.example.com:4000`。 -------------------------------------------------------------------------------- /best-practice/tracing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 链路追踪 3 | type: book 4 | date: "2021-04-01" 5 | weight: 50 6 | draft: true 7 | --- -------------------------------------------------------------------------------- /faq/404-status-code/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "状态码: 404 Not Found" 3 | type: book 4 | date: "2021-02-23" 5 | weight: 71 6 | --- 7 | 8 | ## 访问 StatefulSet Pod IP 返回 404 9 | * 问题描述:在 istio 中业务容器访问同集群一 Pod IP 返回 404,在 istio-proxy 中访问却正常 10 | * 原因: Pod 属于 StatefulSet,使用 headless svc,在 istio 中对 headless svc 的支持跟普通 svc 不太一样,如果 pod 用的普通 svc,对应的 listener 有兜底的 passthrough,即转发到报文对应的真实目的IP+Port,但 headless svc 的就没有,我们理解是因为 headless svc 没有 vip,它的路由是确定的,只指向后端固定的 pod,如果路由匹配不上就肯定出了问题,如果也用 passthrough 兜底路由,只会掩盖问题,所以就没有为 headless svc 创建 passthrough 兜底路由。同样的业务,上了 istio 才会有这个问题,也算是 istio 的设计或实现问题。 11 | * 示例场景: 使用了自己的服务发现,业务直接使用 Pod IP 调用 StatefulSet 的 Pod IP 12 | * 解决方案: 同集群访问 statefulset pod ip 带上 host,以匹配上 headless svc 路由,避免匹配不到就 4 -------------------------------------------------------------------------------- /faq/426-status-code/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "状态码: 426 Upgrade Required" 3 | type: book 4 | date: "2021-03-26" 5 | weight: 70 6 | --- 7 | 8 | ## 背景 9 | 10 | Istio 使用 Envoy 作为数据面转发 HTTP 请求,而 Envoy 默认要求使用 HTTP/1.1 或 HTTP/2,当客户端使用 HTTP/1.0 时就会返回 `426 Upgrade Required`。 11 | 12 | ## 常见的 nginx 场景 13 | 14 | 如果用 nginx 进行 `proxy_pass` 反向代理,默认会用 HTTP/1.0,你可以显示指定 [proxy_http_version](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version) 为 `1.1`: 15 | 16 | ```nginx 17 | upstream http_backend { 18 | server 127.0.0.1:8080; 19 | 20 | keepalive 16; 21 | } 22 | 23 | server { 24 | ... 25 | 26 | location /http/ { 27 | proxy_pass http://http_backend; 28 | proxy_http_version 1.1; 29 | proxy_set_header Connection ""; 30 | ... 31 | } 32 | } 33 | ``` 34 | 35 | ## 压测场景 36 | 37 | ab 压测时会发送 HTTP/1.0 的请求,Envoy 固定返回 426 Upgrade Required,根本不会进行转发,所以压测的结果也不会准确。可以换成其它压测工具,如 [wrk](https://github.com/wg/wrk) 。 38 | 39 | ## 让 istio 支持 HTTP/1.0 40 | 41 | 有些 SDK 或框架可能会使用 HTTP/1.0 协议,比如使用 HTTP/1.0 去资源中心/配置中心 拉取配置信息,在不想改动代码的情况下让服务跑在 istio 上,也可以修改 istiod 配置,加上 `PILOT_HTTP10: 1` 的环境变量来启用 HTTP/1.0。 42 | 43 | ## 参考资料 44 | 45 | * [Envoy won’t connect to my HTTP/1.0 service](https://istio.io/latest/docs/ops/common-problems/network-issues/#envoy-won-t-connect-to-my-http-1-0-service) -------------------------------------------------------------------------------- /faq/431-status-code/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/faq/431-status-code/1.png -------------------------------------------------------------------------------- /faq/431-status-code/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "状态码: 431 Request Header Fields Too Large" 3 | type: book 4 | date: "2021-02-23" 5 | weight: 72 6 | --- 7 | 8 | ## 问题描述 9 | 10 | istio 中 http 请求,envoy 返回 431 异常状态码: 11 | 12 | ``` txt 13 | HTTP/1.1 431 Request Header Fields Too Large 14 | ``` 15 | 16 | ## 原因分析 17 | 18 | 此状态码说明 http 请求 header 大小超限了,默认限制为 60 KiB,由 `HttpConnectionManager` 配置的 `max_request_headers_kb` 字段决定,最大可调整到 96 KiB: 19 | 20 | ![](1.png) 21 | 22 | ## 解决方案 23 | 24 | 可以通过 EnvoyFilter 调整 `max_request_headers_kb` 字段来提升 header 大小限制。 25 | 26 | EnvoyFilter 示例 (istio 1.6 验证通过): 27 | ``` yaml 28 | apiVersion: networking.istio.io/v1alpha3 29 | kind: EnvoyFilter 30 | metadata: 31 | name: max-header 32 | namespace: istio-system 33 | spec: 34 | configPatches: 35 | - applyTo: NETWORK_FILTER 36 | match: 37 | context: ANY 38 | listener: 39 | filterChain: 40 | filter: 41 | name: "envoy.http_connection_manager" 42 | patch: 43 | operation: MERGE 44 | value: 45 | typed_config: 46 | "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager" 47 | max_request_headers_kb: 96 48 | ``` 49 | 50 | 高版本兼容上面的 v2 配置,但建议用 v3 的 配置 (istio 1.8 验证通过): 51 | ``` yaml 52 | apiVersion: networking.istio.io/v1alpha3 53 | kind: EnvoyFilter 54 | metadata: 55 | name: max-header 56 | namespace: istio-system 57 | spec: 58 | configPatches: 59 | - applyTo: NETWORK_FILTER 60 | match: 61 | context: ANY 62 | listener: 63 | filterChain: 64 | filter: 65 | name: "envoy.http_connection_manager" 66 | patch: 67 | operation: MERGE 68 | value: 69 | typed_config: 70 | "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager" 71 | max_request_headers_kb: 96 72 | ``` 73 | 74 | 若 header 大小超过 96 KiB,这种情况本身也很不正常,建议将这部分数据放到 body。 -------------------------------------------------------------------------------- /faq/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 常见问题 3 | type: book # Do not modify. 4 | --- 5 | 6 | 先占位,后续陆续补充笔记 -------------------------------------------------------------------------------- /faq/circuit-breaking-not-work/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 熔断不生效 3 | type: book 4 | date: "2021-05-12" 5 | weight: 47 6 | --- 7 | 8 | ## 未定义 http1MaxPendingRequests 9 | 10 | 我们给 DR 配置了 `maxConnections`: 11 | 12 | ```yaml 13 | apiVersion: networking.istio.io/v1beta1 14 | kind: DestinationRule 15 | metadata: 16 | name: nginx 17 | spec: 18 | host: nginx 19 | trafficPolicy: 20 | connectionPool: 21 | tcp: 22 | maxConnections: 1 23 | ``` 24 | 25 | 但测试当并发超过这里定义的最大连接数时,并没有触发熔断,只是 QPS 很低。通常是因为没有配置 `http1MaxPendingRequests`,不配置默认为 `2^32-1`,非常大,表示如果超过最大连接数,请求就先等待(不直接返回503),当连接数低于最大值时再继续转发。 26 | 27 | 如果希望连接达到上限或超过上限一定量后或直接熔断(响应503),那么就需要显式指定一下 `http1MaxPendingRequests`: 28 | 29 | ```yaml 30 | apiVersion: networking.istio.io/v1beta1 31 | kind: DestinationRule 32 | metadata: 33 | name: nginx 34 | spec: 35 | host: nginx 36 | trafficPolicy: 37 | connectionPool: 38 | tcp: 39 | maxConnections: 1 40 | http: 41 | http1MaxPendingRequests: 1 42 | ``` -------------------------------------------------------------------------------- /faq/grpc-config-stream-closed/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/faq/grpc-config-stream-closed/1.png -------------------------------------------------------------------------------- /faq/grpc-config-stream-closed/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Envoy 报错: gRPC config stream closed' 3 | type: book 4 | date: "2021-02-03" 5 | weight: 110 6 | --- 7 | 8 | ## gRPC config stream closed: 13 9 | 10 | ![](1.png) 11 | 12 | 这通常是正常的,因为控制面(istiod)默认每 30 分钟强制断开 xDS 连接,然后数据面(proxy)再自动重连。 13 | 14 | ## gRPC config stream closed: 14 15 | 16 | 如果只出现一次,通常是在 envoy 启动或重启时报这个错,没什么问题;但如果反复报这个错,可能是数据面(proxy)连接控制面(istiod)有问题,需要排查下。 17 | 18 | ## 参考资料 19 | 20 | * [Istio Common Issues](https://github.com/istio/istio/wiki/Troubleshooting-Istio#common-issues) -------------------------------------------------------------------------------- /faq/headless-svc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "headless service 问题汇总" 3 | type: book 4 | date: "2021-05-12" 5 | weight: 10 6 | --- 7 | 8 | ## 服务间通过注册中心调用响应 404 9 | 10 | * 现象: 传统服务 (比如 Srping Cloud) 迁移到 istio 后,服务间调用返回 404。 11 | * 原因: 没走 Kubernetes 的服务发现,而是通过注册中心获取服务 IP 地址,然后服务间调用不经过域名解析,直接向获取到的目的 IP 发起调用。由于 istio 的 LDS 会拦截 headless service 中包含的 PodIP+Port 的请求,然后匹配请求 hosts,如果没有 hosts 或者 hosts 中没有这个 PodIP+Port 的 service 域名 (比如直接是 Pod IP),就会匹配失败,最后返回 404。 12 | * 解决方案: 13 | 1. 注册中心不直接注册 Pod IP 地址,注册 service 域名。 14 | 2. 或者客户端请求时带上 hosts (需要改代码)。 15 | 16 | ## 负载均衡策略不生效 17 | 18 | 由于 istio 默认对 headless service 进行 passthrougth,使用 `ORIGINAL_DST` 转发,即直接转发到原始的目的 IP,不做任何的负载均衡,所以 `Destinationrule` 中配置的 `trafficPolicy.loadBalancer` 都不会生效,影响的功能包括: 19 | * 会话保持 (consistentHash) 20 | * 地域感知 (localityLbSetting) 21 | 22 | 解决方案: 单独再创建一个 service (非 headless) 23 | 24 | ## 访问不带 sidecar 的 headless service 失败 25 | 26 | * 现象: client (有sidecar) 通过 headless service 访问 server (无sidecar),访问失败,access log 中可以看到 response_flags 为 `UF,URX`。 27 | * 原因: istio 1.5/1.6 对 headless service 支持有个 bug,不管 endpoint 有没有 sidecar,都固定启用 mtls,导致没有 sidecar 的 headless 服务(如 redis) 访问被拒绝 (详见 [#21964](https://github.com/istio/istio/issues/21964)) ,更多细节可参考 [Istio 运维实战系列(2):让人头大的『无头服务』-上](https://zhaohuabing.com/post/2020-09-11-headless-mtls/)。 28 | 29 | **解决方案一:** 配置 `DestinationRule` 禁用 mtls 30 | 31 | ```yaml 32 | kind: DestinationRule 33 | metadata: 34 | name: redis-disable-mtls 35 | spec: 36 | host: redis.default.svc.cluster.local 37 | trafficPolicy: 38 | tls: 39 | mode: DISABLE 40 | ``` 41 | 42 | **解决方案二:** 升级 istio 到 1.7 及其以上的版本。 43 | 44 | ## pod 重建后访问失败 45 | 46 | * 现象: client 通过 headless service 访问 server,当 server 的 pod 发生重建后,client 访问 server 失败,access log 中可以看到 response_flags 为 `UF,URX`。 47 | * 原因: istio 1.5 对 headless service 支持的 bug。 48 | * client 通过 dns 解析 headless service,返回其中一个 Pod IP,然后发起请求。 49 | * envoy 检测到是 headless service,使用 `ORIGINAL_DST` 转发,即不做负载均衡,直接转发到原始的目的 IP。 50 | * 当 headless service 的 pod 发生重建,由于 client 与它的 sidecar (envoy) 是长连接,所以 client 侧的连接并没有断开。 51 | * 又由于是长连接,client 继续发请求并不会重新解析 dns,而是仍然发请求给之前解析到的旧 Pod IP。 52 | * 由于旧 Pod 已经销毁,Envoy 会返回错误 (503)。 53 | * 客户端并不会因为服务端返回错误而断开连接,后续请求继续发给旧的 Pod IP,如此循环,一直失败。 54 | * 更多详情参考 [Istio 运维实战系列(3):让人头大的『无头服务』-下](https://zhaohuabing.com/post/2020-09-19-headless-mtls/) 。 55 | 56 | **解决方案:** 升级 istio 到 1.6 及其以上的版本,Envoy 在 Upstream 链接断开后会主动断开和 Downstream 的长链接。 -------------------------------------------------------------------------------- /faq/http-match/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: VirtualService 路由匹配顺序问题 3 | type: book 4 | date: "2021-05-01" 5 | weight: 45 6 | --- 7 | 8 | ## 背景 9 | 10 | 在写 VirtualService 路由规则时,通常会 match 各种不同路径转发到不同的后端服务,有时候不小心命名冲突了,导致始终只匹配到前面的服务,比如: 11 | 12 | ```yaml 13 | apiVersion: networking.istio.io/v1beta1 14 | kind: VirtualService 15 | metadata: 16 | name: test 17 | spec: 18 | gateways: 19 | - default/example-gw 20 | hosts: 21 | - 'test.example.com' 22 | http: 23 | - match: 24 | - uri: 25 | prefix: /usrv 26 | rewrite: 27 | uri: / 28 | route: 29 | - destination: 30 | host: usrv.default.svc.cluster.local 31 | port: 32 | number: 80 33 | - match: 34 | - uri: 35 | prefix: /usrv-expand 36 | rewrite: 37 | uri: / 38 | route: 39 | - destination: 40 | host: usrv-expand.default.svc.cluster.local 41 | port: 42 | number: 80 43 | ``` 44 | 45 | istio 匹配是按顺序匹配,不像 nginx 那样使用最长前缀匹配。这里使用 prefix 进行匹配,第一个是 `/usrv`,表示只要访问路径前缀含 `/usrv` 就会转发到第一个服务,由于第二个匹配路径 `/usrv-expand` 本身也属于带 `/usrv` 的前缀,所以永远不会转发到第二个匹配路径的服务。 46 | 47 | ## 解决方案 48 | 49 | 这种情况可以调整下匹配顺序,如果前缀有包含的冲突关系,越长的放在越前面: 50 | 51 | ```yaml 52 | apiVersion: networking.istio.io/v1beta1 53 | kind: VirtualService 54 | metadata: 55 | name: test 56 | spec: 57 | gateways: 58 | - default/example-gw 59 | hosts: 60 | - 'test.example.com' 61 | http: 62 | - match: 63 | - uri: 64 | prefix: /usrv-expand 65 | rewrite: 66 | uri: / 67 | route: 68 | - destination: 69 | host: usrv-expand.default.svc.cluster.local 70 | port: 71 | number: 80 72 | - match: 73 | - uri: 74 | prefix: /usrv 75 | rewrite: 76 | uri: / 77 | route: 78 | - destination: 79 | host: usrv.default.svc.cluster.local 80 | port: 81 | number: 80 82 | ``` 83 | 84 | 也可以用正则匹配: 85 | 86 | ```yaml 87 | apiVersion: networking.istio.io/v1beta1 88 | kind: VirtualService 89 | metadata: 90 | name: test 91 | spec: 92 | gateways: 93 | - default/gateway 94 | hosts: 95 | - 'test.example.com' 96 | http: 97 | - match: 98 | - uri: 99 | regex: "/usrv(/.*)?" 100 | rewrite: 101 | uri: / 102 | route: 103 | - destination: 104 | host: nginx.default.svc.cluster.local 105 | port: 106 | number: 80 107 | subset: v1 108 | - match: 109 | - uri: 110 | regex: "/usrv-expand(/.*)?" 111 | rewrite: 112 | uri: / 113 | route: 114 | - destination: 115 | host: nginx.default.svc.cluster.local 116 | port: 117 | number: 80 118 | subset: v2 119 | ``` 120 | -------------------------------------------------------------------------------- /faq/isito-init-crash/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: istio-init crash 3 | type: book 4 | date: "2021-04-13" 5 | weight: 100 6 | --- 7 | 8 | ## 问题描述 9 | 10 | 在 istio 环境下有 pod 处于 `Init:CrashLoopBackOff` 状态: 11 | ```txt 12 | wk-sys-acl-v1-0-5-7cf7f79d6c-d9qcr 0/2 Init:CrashLoopBackOff 283 64d 172.16.9.229 10.1.128.6 13 | ``` 14 | 15 | 查得 istio-init 的日志: 16 | 17 | ```txt 18 | Environment: 19 | ------------ 20 | ENVOY_PORT= 21 | INBOUND_CAPTURE_PORT= 22 | ISTIO_INBOUND_INTERCEPTION_MODE= 23 | ISTIO_INBOUND_TPROXY_MARK= 24 | ISTIO_INBOUND_TPROXY_ROUTE_TABLE= 25 | ISTIO_INBOUND_PORTS= 26 | ISTIO_LOCAL_EXCLUDE_PORTS= 27 | ISTIO_SERVICE_CIDR= 28 | ISTIO_SERVICE_EXCLUDE_CIDR= 29 | 30 | Variables: 31 | ---------- 32 | PROXY_PORT=15001 33 | PROXY_INBOUND_CAPTURE_PORT=15006 34 | PROXY_UID=1337 35 | PROXY_GID=1337 36 | INBOUND_INTERCEPTION_MODE=REDIRECT 37 | INBOUND_TPROXY_MARK=1337 38 | INBOUND_TPROXY_ROUTE_TABLE=133 39 | INBOUND_PORTS_INCLUDE=* 40 | INBOUND_PORTS_EXCLUDE=15090,15021,15020 41 | OUTBOUND_IP_RANGES_INCLUDE=* 42 | OUTBOUND_IP_RANGES_EXCLUDE= 43 | OUTBOUND_PORTS_EXCLUDE= 44 | KUBEVIRT_INTERFACES= 45 | ENABLE_INBOUND_IPV6=false 46 | 47 | Writing following contents to rules file: /tmp/iptables-rules-1618279687646418248.txt617375845 48 | * nat 49 | -N ISTIO_REDIRECT 50 | -N ISTIO_IN_REDIRECT 51 | -N ISTIO_INBOUND 52 | -N ISTIO_OUTPUT 53 | -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001 54 | -A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006 55 | -A PREROUTING -p tcp -j ISTIO_INBOUND 56 | -A ISTIO_INBOUND -p tcp --dport 22 -j RETURN 57 | -A ISTIO_INBOUND -p tcp --dport 15090 -j RETURN 58 | -A ISTIO_INBOUND -p tcp --dport 15021 -j RETURN 59 | -A ISTIO_INBOUND -p tcp --dport 15020 -j RETURN 60 | -A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT 61 | -A OUTPUT -p tcp -j ISTIO_OUTPUT 62 | -A ISTIO_OUTPUT -o lo -s 127.0.0.6/32 -j RETURN 63 | -A ISTIO_OUTPUT -o lo ! -d 127.0.0.1/32 -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT 64 | -A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 1337 -j RETURN 65 | -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN 66 | -A ISTIO_OUTPUT -o lo ! -d 127.0.0.1/32 -m owner --gid-owner 1337 -j ISTIO_IN_REDIRECT 67 | -A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 1337 -j RETURN 68 | -A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN 69 | -A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN 70 | -A ISTIO_OUTPUT -j ISTIO_REDIRECT 71 | COMMIT 72 | 73 | iptables-restore --noflush /tmp/iptables-rules-1618279687646418248.txt617375845 74 | iptables-restore: line 2 failed 75 | iptables-save 76 | # Generated by iptables-save v1.6.1 on Tue Apr 13 02:08:07 2021 77 | *nat 78 | :PREROUTING ACCEPT [5214353:312861180] 79 | :INPUT ACCEPT [5214353:312861180] 80 | :OUTPUT ACCEPT [6203044:504329953] 81 | :POSTROUTING ACCEPT [6203087:504332485] 82 | :ISTIO_INBOUND - [0:0] 83 | :ISTIO_IN_REDIRECT - [0:0] 84 | :ISTIO_OUTPUT - [0:0] 85 | :ISTIO_REDIRECT - [0:0] 86 | -A PREROUTING -p tcp -j ISTIO_INBOUND 87 | -A OUTPUT -p tcp -j ISTIO_OUTPUT 88 | -A ISTIO_INBOUND -p tcp -m tcp --dport 22 -j RETURN 89 | -A ISTIO_INBOUND -p tcp -m tcp --dport 15090 -j RETURN 90 | -A ISTIO_INBOUND -p tcp -m tcp --dport 15021 -j RETURN 91 | -A ISTIO_INBOUND -p tcp -m tcp --dport 15020 -j RETURN 92 | -A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT 93 | -A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006 94 | -A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN 95 | -A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT 96 | -A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 1337 -j RETURN 97 | -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN 98 | -A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --gid-owner 1337 -j ISTIO_IN_REDIRECT 99 | -A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 1337 -j RETURN 100 | -A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN 101 | -A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN 102 | -A ISTIO_OUTPUT -j ISTIO_REDIRECT 103 | -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001 104 | COMMIT 105 | # Completed on Tue Apr 13 02:08:07 2021 106 | panic: exit status 1 107 | 108 | goroutine 1 [running]: 109 | istio.io/istio/tools/istio-iptables/pkg/dependencies.(*RealDependencies).RunOrFail(0x3bb0090, 0x22cfd22, 0x10, 0xc0006849c0, 0x2, 0x2) 110 | istio.io/istio/tools/istio-iptables/pkg/dependencies/implementation.go:44 +0x96 111 | istio.io/istio/tools/istio-iptables/pkg/cmd.(*IptablesConfigurator).executeIptablesRestoreCommand(0xc0009dfd68, 0x22c5a01, 0x0, 0x0) 112 | istio.io/istio/tools/istio-iptables/pkg/cmd/run.go:493 +0x387 113 | istio.io/istio/tools/istio-iptables/pkg/cmd.(*IptablesConfigurator).executeCommands(0xc0009dfd68) 114 | istio.io/istio/tools/istio-iptables/pkg/cmd/run.go:500 +0x45 115 | istio.io/istio/tools/istio-iptables/pkg/cmd.(*IptablesConfigurator).run(0xc0009dfd68) 116 | istio.io/istio/tools/istio-iptables/pkg/cmd/run.go:447 +0x2625 117 | istio.io/istio/tools/istio-iptables/pkg/cmd.glob..func1(0x3b5d680, 0xc0004cce00, 0x0, 0x10) 118 | istio.io/istio/tools/istio-iptables/pkg/cmd/root.go:64 +0x148 119 | github.com/spf13/cobra.(*Command).execute(0x3b5d680, 0xc0004ccd00, 0x10, 0x10, 0x3b5d680, 0xc0004ccd00) 120 | github.com/spf13/cobra@v1.0.0/command.go:846 +0x29d 121 | github.com/spf13/cobra.(*Command).ExecuteC(0x3b5d920, 0x0, 0x0, 0x0) 122 | github.com/spf13/cobra@v1.0.0/command.go:950 +0x349 123 | github.com/spf13/cobra.(*Command).Execute(...) 124 | github.com/spf13/cobra@v1.0.0/command.go:887 125 | main.main() 126 | istio.io/istio/pilot/cmd/pilot-agent/main.go:505 +0x2d 127 | ``` 128 | 129 | ## 原因与解决方案 130 | 131 | 跟这个 issue 基本一致 https://github.com/istio/istio/issues/24148 132 | 133 | 直接原因: 这种情况应该通常是清理了已退出的 istio-init 容器,导致 k8s 检测到 pod 关联的容器不在了,然后会重新拉起被删除的容器,而 istio-init 的执行不可重入,因为之前已创建了 iptables 规则,导致后拉起的 istio-init 执行 iptables 失败而 crash。 134 | 135 | 根因与解决方案: 清理的动作通常是执行了 `docker container rm` 或 `docker container prune` 或 `docker system prune`。 一般是 crontab 定时脚本里定时清理了容器导致,需要停止清理。 136 | -------------------------------------------------------------------------------- /faq/kubeflow-on-istio/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: kubeflow on istio 相关问题 3 | type: book 4 | date: "2021-02-02" 5 | weight: 120 6 | --- 7 | 8 | ## 版本最高支持到 istio 1.5 9 | 10 | 目前 kubeflow 最新版对 istio 的支持最高只到 1.5,issue: https://github.com/kubeflow/kubeflow/issues/5313 -------------------------------------------------------------------------------- /faq/listen-any/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 应用未监听 0.0.0.0 导致连接异常 3 | type: book 4 | date: "2021-03-25" 5 | weight: 60 6 | --- 7 | 8 | ## 背景 9 | 10 | istio 要求应用提供服务时监听 `0.0.0.0`,因为 127 和 Pod IP 地址都被 envoy 占用了。有些应用启动时没有监听 `0.0.0.0` 或 `::` 的地址,就会导致无法正常通信,参考 [Application Bind Address](https://istio.io/latest/docs/ops/deployment/requirements/#application-bind-address) 。 11 | 12 | ## 案例: zookeeper 13 | 14 | 当 zookeeper 部署到集群中时,默认监听的 Pod IP,会导致 zookeeper 各个实例之间无法正常通信。 15 | 16 | 解决方案: 在 zk 的配置文件中键入 [quorumListenOnAllIPs=true](https://zookeeper.apache.org/doc/r3.5.7/zookeeperAdmin.html) 的配置 ( 参考 [istio官方文档](https://istio.io/v1.8/faq/applications/#zookeeper) ) 17 | 18 | ## istio 1.10 19 | 20 | 在 istio 1.10 及其以上的版本,应用将无需对端口监听进行特殊处理,即如果应用只监听 eth0 (pod ip) 也能正常使用,详细参考官方博客 [Upcoming networking changes in Istio 1.10](https://istio.io/latest/blog/2021/upcoming-networking-changes/) 。 -------------------------------------------------------------------------------- /faq/locality-lb/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 地域感知不生效 3 | type: book 4 | date: "2021-03-25" 5 | weight: 33 6 | --- 7 | 8 | ## 概述 9 | 10 | 使用 istio 地域感知能力时,测试发现没生效,本文介绍几个常见原因。 11 | 12 | ## DestinationRule 未配置 outlierDetection 13 | 14 | 地域感知默认开启,但还需要配置 DestinationRule,且指定 `outlierDetection` 才可以生效,指定这个配置的作用主要是让 istio 感知 endpoints 是否异常,当前 locality 的 endpoints 发生异常时会 failover 到其它地方的 endpoints。 15 | 16 | 配置示例: 17 | 18 | ```yaml 19 | apiVersion: networking.istio.io/v1beta1 20 | kind: DestinationRule 21 | metadata: 22 | name: nginx 23 | spec: 24 | host: nginx 25 | trafficPolicy: 26 | outlierDetection: 27 | consecutive5xxErrors: 3 28 | interval: 30s 29 | baseEjectionTime: 30s 30 | ``` 31 | 32 | ## client 没配置 service 33 | 34 | istio 控制面会为每个数据面单独下发 EDS,不同数据面实例(Envoy)的locality可能不一样,生成的 EDS 也就可能不一样。istio会获取数据面的locality信息,获取方式主要是找到数据面对应的 endpoint 上保存的 region、zone 等信息,如果 client 没有任何 service,也就不会有 endpoint,控制面也就无法获取 client 的 locality 信息,也就无法实现地域感知。 35 | 36 | 解决方案: 为 client 配置 service,selector 选中 client 的 label。如果 client 本身不对外提供服务,service 的 ports 也可以随便定义一个。 37 | 38 | ## 使用了 headless service 39 | 40 | 如果是访问 headless service,本身是不支持地域感知的,因为 istio 会对 headless service 请求直接 passthrough,不做负载均衡,客户端会直接访问到 dns 解析出来的 pod ip。 41 | 42 | 解决方案: 单独再创建一个 service (非 headless) -------------------------------------------------------------------------------- /faq/multicluster/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "多集群相关问题" 3 | type: book 4 | date: "2021-05-20" 5 | weight: 130 6 | draft: true 7 | --- 8 | 9 | ## 概述 10 | 11 | 本文介绍 istio 多集群下需要注意的问题。 12 | 13 | ## 跨集群访问 service 14 | 15 | 同一网格的多个集群之间通过 service 调用,可能调用失败,报错 dns 解析失败。原因是 -------------------------------------------------------------------------------- /faq/retries-for-non-idempotent-services/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 默认的重试策略导致非幂等服务异常 3 | type: book 4 | date: "2021-03-29" 5 | weight: 50 6 | --- 7 | 8 | ## 背景 9 | 10 | Istio 为 Envoy 设置了缺省的重试策略,会在 connect-failure,refused-stream, unavailable, cancelled, retriable-status-codes 等情况下缺省重试两次。出现错误时,可能已经触发了服务器逻辑,在操作不是幂等的情况下,可能会导致错误。 11 | 12 | 13 | ## 解决方案 14 | 15 | 可以通过配置 VS 关闭重试: 16 | 17 | ```yaml 18 | apiVersion: networking.istio.io/v1alpha3 19 | kind: VirtualService 20 | metadata: 21 | name: ratings 22 | spec: 23 | hosts: 24 | - ratings 25 | http: 26 | - retries: 27 | attempts: 0 28 | ``` 29 | -------------------------------------------------------------------------------- /faq/sidecar-shutdown/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sidecar 停止问题 3 | type: book 4 | date: "2021-04-09" 5 | weight: 2 6 | --- 7 | 8 | ## 背景 9 | 10 | Istio 在 1.1 版本之前有个问题: Pod 销毁时,如果进程在退出过程中继续调用其它服务 (比如通知另外的服务进行清理),会调用失败。 11 | 12 | 更多详细信息请参考 issue [#7136: Envoy shutting down before the thing it's wrapping can cause failed requests ](https://github.com/istio/istio/issues/7136) 。 13 | 14 | ## 原因 15 | 16 | Kubernetes 在销毁 Pod 的过程中,会同时给所有容器发送 SIGTERM 信号,所以 Envoy 跟业务容器同时开始停止,Envoy 停止过程中不接受 inbound 新连接,默认在 5s 内会接收 outbound 新连接,5s 后 envoy 被强制杀死。又由于 istio 会进行流量劫持,所有 outbound 流量都会经过 Envoy 进行转发,如果 Envoy 被杀死,outbound 流量无法被转发,就会导致业务调用其它服务失败。 17 | 18 | ## 社区解决方案 19 | 20 | 如果 Kubernetes 自身支持容器依赖管理,那这个问题自然就可以解决掉。社区也提出了 [Sidecar Container](https://github.com/kubernetes/enhancements/issues/753) 的特性,只可惜最终还是被废弃了,新的方案还未落地,详细可参考 [这篇笔记](https://imroc.cc/k8s/kep/sidecar-containers.html) 。 21 | 22 | 后来随着 istio 社区的推进,针对优雅终止场景进行了一些优化: 23 | 24 | * 2019-02: Liam White 提交 PR [Envoy Graceful Shutdown](https://github.com/istio/istio/pull/11485) ,让 Pod 在停止过程中 Envoy 能够实现优雅停止 (保持存量连接继续处理,但拒绝所有新连接),等待 `terminationDrainDuration` 时长后再停掉 envoy 实例。该 PR 最终被合入 istio 1.1。 25 | * 2019-11: Rama Chavali 提交 PR [move to drain listeners admin endpoint](https://github.com/istio/istio/pull/18581) ,将 Envoy 优雅停止的方式从热重启改成调用 Envoy 后来自身提供的 admin 接口 ([/drain_listeners?inboundonly](https://www.envoyproxy.io/docs/envoy/latest/operations/admin#post--drain_listeners?inboundonly)) ,重点在于带上了 `inboundonly` 参数,即仅仅拒绝 inbound 方向的新连接,outbound 的新连接仍然可以正常发起,这也使得 Pod 在停止过程中业务进程继续调用其它服务得以实现。该 PR 最终被合入 istio 1.5。 26 | 27 | 所以在 istio 1.5 及其以上的版本,在 Pod 停止期间的一小段时间内 (默认 5s),业务进程仍然可以对其它服务发请求。 28 | 29 | ## 如何解决 ? 30 | 31 | 考虑从自定义 `terminationDrainDuration` 或加 preStop 判断连接处理完两种方式之一,详细请参考 [istio 最佳实践: 优雅终止](https://imroc.cc/istio/best-practice/graceful-termination/) 。 -------------------------------------------------------------------------------- /faq/sidecar-startup-order/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/faq/sidecar-startup-order/1.png -------------------------------------------------------------------------------- /faq/sidecar-startup-order/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/faq/sidecar-startup-order/2.png -------------------------------------------------------------------------------- /faq/sidecar-startup-order/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/faq/sidecar-startup-order/3.png -------------------------------------------------------------------------------- /faq/sidecar-startup-order/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/faq/sidecar-startup-order/4.png -------------------------------------------------------------------------------- /faq/sidecar-startup-order/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sidecar 启动顺序问题 3 | type: book 4 | date: "2021-04-09" 5 | weight: 1 6 | --- 7 | 8 | ## 背景 9 | 10 | 一些服务在往 istio 上迁移过渡的过程中,有时可能会遇到 Pod 启动失败,然后一直重启,排查原因是业务启动时需要调用其它服务(比如从配置中心拉取配置),如果失败就退出,没有重试逻辑。调用失败的原因是 envoy 还没就绪(envoy也需要从控制面拉取配置,需要一点时间),导致业务发出的流量无法被处理,从而调用失败(参考 k8s issue [#65502](https://github.com/kubernetes/kubernetes/issues/65502) )。 11 | 12 | ## 最佳实践 13 | 14 | 目前这类问题的最佳实践是让应用更加健壮一点,增加一下重试逻辑,不要一上来调用失败就立马退出,如果嫌改动麻烦,也可以在启动命令前加下 sleep,等待几秒 (可能不太优雅)。 15 | 16 | 如果不想对应用做任何改动,也可以参考下面的规避方案。 17 | 18 | ## 规避方案: 调整 sidecar 注入顺序 19 | 20 | 在 istio 1.7,社区通过给 istio-injector 注入逻辑增加一个叫 `HoldApplicationUntilProxyStarts` 的开关来解决了该问题,开关打开后,proxy 将会注入到第一个 container。 21 | 22 | ![](1.png) 23 | 24 | ![](2.png) 25 | 26 | 查看 istio-injector 自动注入使用的 template,可以知道如果打开了 `HoldApplicationUntilProxyStarts` 就会为 sidecar 添加一个 postStart hook: 27 | 28 | ![](3.png) 29 | 30 | 它的目的是为了阻塞后面的业务容器启动,要等到 sidecar 完全启动了才开始启动后面的业务容器。 31 | 32 | 这个开关配置分为全局和局部两种,以下是启用方法。 33 | 34 | **全局配置:** 35 | 36 | 修改 istio 的 configmap 全局配置: 37 | 38 | ``` bash 39 | kubectl -n istio-system edit cm istio 40 | ``` 41 | 42 | 在 `defaultConfig` 下加入 `holdApplicationUntilProxyStarts: true` 43 | 44 | ``` yaml 45 | apiVersion: v1 46 | data: 47 | mesh: |- 48 | defaultConfig: 49 | holdApplicationUntilProxyStarts: true 50 | meshNetworks: 'networks: {}' 51 | kind: ConfigMap 52 | ``` 53 | 54 | 若使用 IstioOperator,defaultConfig 修改 CR 字段 `meshConfig`: 55 | 56 | ``` yaml 57 | apiVersion: install.istio.io/v1alpha1 58 | kind: IstioOperator 59 | metadata: 60 | namespace: istio-system 61 | name: example-istiocontrolplane 62 | spec: 63 | meshConfig: 64 | defaultConfig: 65 | holdApplicationUntilProxyStarts: true 66 | ``` 67 | 68 | 如果你使用了 TCM (Tecnet Cloud Mesh),已经产品化了该能力,直接开启 `Sidecar 就绪保障` 即可: 69 | 70 | ![](4.png) 71 | 72 | **局部配置:** 73 | 74 | 如果使用 istio 1.8 及其以上的版本,可以为需要打开此开关的 Pod 加上 `proxy.istio.io/config` 注解,将 `holdApplicationUntilProxyStarts` 置为 `true`,示例: 75 | 76 | ```yaml 77 | apiVersion: apps/v1 78 | kind: Deployment 79 | metadata: 80 | name: nginx 81 | spec: 82 | replicas: 1 83 | selector: 84 | matchLabels: 85 | app: nginx 86 | template: 87 | metadata: 88 | annotations: 89 | proxy.istio.io/config: | 90 | holdApplicationUntilProxyStarts: true 91 | labels: 92 | app: nginx 93 | spec: 94 | containers: 95 | - name: nginx 96 | image: "nginx" 97 | ``` 98 | 99 | 需要注意的是,打开这个开关后,意味着业务容器需要等 sidecar 完全 ready 后才能启动,会让 Pod 启动速度变慢一些。在需要快速扩容应对突发流量场景可能会显得吃力,所以建议是自行评估业务场景,利用局部配置的方法,只给需要的业务打开此开关。 100 | 101 | ## 完美方案: K8S 支持容器依赖 102 | 103 | 最完美的方案还是 Kubernetes 自身支持容器依赖,社区也提出了 [Sidecar Container](https://github.com/kubernetes/enhancements/issues/753) 的特性,只可惜最终还是被废弃了,新的方案还未落地,详细可参考 [这篇笔记](https://imroc.cc/k8s/kep/sidecar-containers/) 。 104 | 105 | 106 | ## 参考资料 107 | 108 | * [Istio 运维实战系列(1):应用容器对 Envoy Sidecar 的启动依赖问题](https://zhaohuabing.com/post/2020-09-05-istio-sidecar-dependency/#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88) 109 | * [PR: Allow users to delay application start until proxy is ready](https://github.com/istio/istio/pull/24737) 110 | * [Kubernetes Sidecar Containers 特性调研笔记](https://imroc.cc/k8s/kep/sidecar-containers/) -------------------------------------------------------------------------------- /faq/sidecar/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/faq/sidecar/1.png -------------------------------------------------------------------------------- /faq/sidecar/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: sidecar 注入相关问题 3 | type: book 4 | date: "2021-03-26" 5 | weight: 1000000 6 | --- 7 | 8 | ## 可以只在一端注入 sidecar 吗? 9 | 10 | * Q: 只在客户端和服务端其中一方注入 sidecar,是否能够正常工作呢? 11 | * A: 一般是建议都注入。有些功能在 outbound 和 inbound 端都需要,有些只在其中一端需要,下面一张图可以一目了然: 12 | 13 | ![](1.png) 14 | 15 | -------------------------------------------------------------------------------- /faq/smart-dns/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 启用 Smart DNS 后解析失败 3 | type: book 4 | date: "2021-04-26" 5 | weight: 96 6 | --- 7 | 8 | ## 问题描述 9 | 10 | 在启用了 istio 的 Smart DNS (智能 DNS) 后,我们发现有些情况下 DNS 解析失败,比如: 11 | 12 | * 基于 alpine 镜像的容器内解析 dns 失败。 13 | * grpc 服务解析 dns 失败。 14 | 15 | ## 原因 16 | 17 | Smart DNS 初期实现存在一些问题,响应的 DNS 数据包格式跟普通 DNS 有些差别,走底层库 glibc 解析没问题,但使用其它 dns 客户端可能就会失败: 18 | * alpine 镜像底层库使用 musl libc,解析行为跟 glibc 有些不一样,musl libc 在这种这种数据包格式异常的情况会导致解析失败,而大多应用走底层库解析,导致大部分应用解析失败。 19 | * 基于 c/c++ 的 grpc 框架的服务,dns 解析默认使用 c-ares 库,没有走系统调用让底层库解析,c-ares 在这种数据包异常情况,部分场景会解析失败。 20 | 21 | ## 修复 22 | 23 | 在 istio 1.9.2 的时候修复了这个问题,参考关键 PR [#31251](https://github.com/istio/istio/pull/31251) 以及其中一个 [issue](https://github.com/istio/istio/issues/31295) 。 24 | 25 | ## 规避 26 | 27 | 如果暂时无法升级 istio 到 1.9.2 以上,可以通过以下方式来规避: 28 | 29 | * 基础镜像从 alpine 镜像到其它镜像 (其它基础镜像底层库基本都是 glibc)。 30 | * c/c++ 的 grpc 服务,指定 `GRPC_DNS_RESOLVER` 环境变量为 `native`,表示走底层库解析,不走默认的 c-ares 库。环境变量解释参考 [GRPC 官方文档](https://github.com/grpc/grpc/blob/master/doc/environment_variables.md) 。 -------------------------------------------------------------------------------- /faq/the-case-of-http-header/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: HTTP Header 大小写问题 3 | type: book 4 | date: "2021-03-29" 5 | weight: 60 6 | --- 7 | 8 | ## Envoy 默认会将 Header 转换为小写 9 | 10 | Envoy 缺省会把 http header 的 key 转换为小写,例如有一个 http header `Test-Upper-Case-Header: some-value`,经过 envoy 代理后会变成 `test-upper-case-header: some-value`。这个在正常情况下没问题,[RFC 2616](https://www.ietf.org/rfc/rfc2616.txt) 规范也说明了处理 HTTP Header 应该是大小写不敏感的。 11 | 12 | ## 可能依赖大小写的场景 13 | 14 | 通常 header 转换为小写不会有问题(符合规范),有些情况对 header 大小写敏感可能就会有问题,如: 15 | * 业务解析 header 依赖大小写。 16 | * 使用的 SDK 对 Header 大小写敏感,如读取 `Content-Length` 来判断 response 长度时依赖首字母大写。 17 | 18 | ## Envoy 所支持的规则 19 | 20 | Envoy 只支持两种规则: 21 | * 全小写 (默认使用的规则) 22 | * 首字母大写 (默认没有启用) 23 | 24 | 如果应用的 http header 的大小写完全没有规律,就没有办法兼容了。 25 | 26 | 这两种是可以的: 27 | * test-upper-case-header: some-value 28 | * Test-Upper-Case-Header: some-value 29 | 30 | 类似这种就没有办法兼容了: 31 | * Test-UPPER-CASE-Header: some-value 32 | 33 | ## 规避方案: 强制指定为 TCP 协议 34 | 35 | 我们可以将服务声明为 TCP 协议,不让 istio 进行七层处理,这样就不会更改 http header 大小写了,但需要注意的是同时也会丧失 istio 的七层能力。 36 | 37 | 如果服务在集群内,可以在 Service 的 port 名称中带上 "tcp" 前缀: 38 | 39 | ```yaml 40 | kind: Service 41 | metadata: 42 | name: myservice 43 | spec: 44 | ports: 45 | - number: 80 46 | name: tcp-web # 指定该端口协议为 tcp 47 | ``` 48 | 49 | 如果服务在集群外,可以通过一个类似如下 ServiceEntry 将服务强制指定为 TCP Service,以避免 envoy 对其进行七层的处理: 50 | 51 | ```yaml 52 | apiVersion: networking.istio.io/v1alpha3 53 | kind: ServiceEntry 54 | metadata: 55 | name: qcloud-cos 56 | spec: 57 | hosts: 58 | - "private-1251349835.cos.ap-guangzhou.myqcloud.com" 59 | location: MESH_INTERNAL 60 | addresses: 61 | - 169.254.0.47 62 | ports: 63 | - number: 80 64 | name: tcp 65 | protocol: TCP 66 | resolution: DNS 67 | ``` 68 | 69 | 更多协议指定方式请参考: [为服务显式指定协议](https://imroc.cc/istio/best-practice/specify-protocol/) 70 | 71 | ## 最佳实践: 使用 EnvoyFilter 指定 Header 规则为首字母大写 72 | 73 | 如果希望 Envoy 对某些请求开启 Header 首字母大写的规则,可以用 EnvoyFilter 来指定: 74 | 75 | ```yaml 76 | apiVersion: networking.istio.io/v1alpha3 77 | kind: EnvoyFilter 78 | metadata: 79 | name: http-header-proper-case-words 80 | namespace: istio-system 81 | spec: 82 | configPatches: 83 | - applyTo: NETWORK_FILTER # http connection manager is a filter in Envoy 84 | match: 85 | # context omitted so that this applies to both sidecars and gateways 86 | listener: 87 | name: XXX # 指定 cos使用的listener name,可以从config_dump中查询到 88 | filterChain: 89 | filter: 90 | name: "envoy.http_connection_manager" 91 | patch: 92 | operation: MERGE 93 | value: 94 | name: "envoy.http_connection_manager" 95 | typed_config: 96 | "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager" 97 | http_protocol_options: 98 | header_key_format: 99 | proper_case_words: {} 100 | ``` 101 | 102 | > 注意替换 listener name 103 | 104 | ## 建议 105 | 106 | 应用程序应遵循 [RFC 2616](https://www.ietf.org/rfc/rfc2616.txt) 规范,对 Http Header 的处理采用大小写不敏感的原则。 -------------------------------------------------------------------------------- /faq/uppercase-header-causes-sticky-sessions-to-not-work/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "httpHeaderName 大写导致会话保持不生效" 3 | type: book 4 | date: "2021-05-12" 5 | weight: 40 6 | id: "92d21dafa9934caea6f55085f9e6199b" 7 | --- 8 | 9 | ## 问题描述 10 | 11 | 在 DestinationRule 中配置了基于 http header 的会话保持,header 名称大写: 12 | 13 | ```yaml 14 | trafficPolicy: 15 | loadBalancer: 16 | consistentHash: 17 | httpHeaderName: User 18 | ``` 19 | 20 | 测试会发现会话保持不生效,每次请求传入相同 Header (如 `User: roc`) 却被转发了不同后端 21 | 22 | ## 原因 23 | 24 | 应该是 envoy 默认把 header 转成小写的缘故导致不生效。 25 | 26 | ## 解决方案 27 | 28 | 定义 `httpHeaderName` 时换成小写,如: 29 | 30 | ```yaml 31 | trafficPolicy: 32 | loadBalancer: 33 | consistentHash: 34 | httpHeaderName: User 35 | ``` 36 | 37 | ## 注意事项 38 | 39 | * 如果之前已经定义了 DestinationRule,不要直接修改,而是先删除,然后再创建修改后的 DestinationRule (实测发现直接修改成可能不生效) 40 | * 客户端请求时设置的 header 大小写可以无所谓。 -------------------------------------------------------------------------------- /faq/virtualservice-not-working/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/faq/virtualservice-not-working/1.png -------------------------------------------------------------------------------- /faq/virtualservice-not-working/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: VirutualService 不生效 3 | type: book 4 | date: "2021-04-26" 5 | weight: 62 6 | --- 7 | 8 | ## 背景 9 | 10 | 使用 istio ,在集群中定义了 `VirutualService`,但测试发现定义的规则似乎没有生效,通常是一些配置问题,本文列举一下常见的可能原因。 11 | 12 | ## 集群内访问: gateway 字段没有显式指定 "mesh" 13 | 14 | 如果 `VirutualService` 没有指定 `gateways` 字段,实际隐含了一层意思,istio 会默认加上一个叫 "mesh" 的保留 Gateway,表示集群内部所有 Sidecar,也就表示此 `VirutualService` 规则针对集群内的访问生效。 15 | 16 | 但如果指定了 `gateways` 字段,istio 不会默认加上 "mesh",如: 17 | 18 | ```yaml 19 | apiVersion: networking.istio.io/v1beta1 20 | kind: VirtualService 21 | metadata: 22 | name: productpage 23 | spec: 24 | gateways: 25 | - istio-test/test-gateway 26 | hosts: 27 | - bookinfo.example.com 28 | http: 29 | - route: 30 | - destination: 31 | host: productpage 32 | port: 33 | number: 9080 34 | ``` 35 | 36 | 这个表示此 `VirualService` 规则仅对 `istio-test/test-gateway` 这个 Gateway 生效,如果是在集群内访问,流量不会经过这个 Gateway,所以此规则也就不会生效。 37 | 38 | 那如果要同时在集群内也生效该怎么做呢?答案是给 `gateways` 显式指定上 "mesh": 39 | 40 | ```yaml 41 | gateways: 42 | - istio-test/test-gateway 43 | - mesh 44 | ``` 45 | 46 | 表示此 `VirutualService` 不仅对 `istio-test/test-gateway` 这个 Gateway 生效,也对集群内部访问生效。 47 | 48 | 参考 [istio 官方文档](https://istio.io/latest/docs/reference/config/networking/virtual-service/#VirtualService) 对此字段的解释: 49 | 50 | ![](1.png) 51 | 52 | 值得注意的是,从集群内访问一般是直接访问 service 名称,这里 `hosts` 就需要加上访问集群内的 service 名称: 53 | 54 | ```yaml 55 | hosts: 56 | - bookinfo.example.com 57 | - productpage 58 | ``` 59 | 60 | ## 通过 ingressgateway 访问: hosts 定义错误 61 | 62 | 若从 ingressgateway 访问,需要确保 `Gateway` 和 `VirtualService` 中的 hosts 均包含实际访问用到的 `Host` 或使用通配符能匹配得上,通常是外部域名。 63 | 64 | 只要有一方 hosts 没定义正确,都可能导致 `404 Not Found`,正确示例: 65 | 66 | ```yaml 67 | apiVersion: networking.istio.io/v1alpha3 68 | kind: Gateway 69 | metadata: 70 | name: test-gateway 71 | namespace: istio-test 72 | spec: 73 | selector: 74 | app: istio-ingressgateway 75 | istio: ingressgateway 76 | servers: 77 | - port: 78 | number: 80 79 | name: HTTP-80-www 80 | protocol: HTTP 81 | hosts: 82 | - bookinfo.example.com # 这里定义外部访问域名 83 | 84 | --- 85 | 86 | apiVersion: networking.istio.io/v1beta1 87 | kind: VirtualService 88 | metadata: 89 | name: productpage 90 | spec: 91 | gateways: 92 | - istio-test/test-gateway 93 | hosts: 94 | - bookinfo.example.com # 这里也要定义外部访问域名 95 | http: 96 | - route: 97 | - destination: 98 | host: productpage 99 | port: 100 | number: 9080 101 | ``` -------------------------------------------------------------------------------- /faq/why-is-tracing-incomplete/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "链路追踪不完整" 3 | type: book 4 | date: "2021-05-18" 5 | weight: 120 6 | --- 7 | 8 | ## 问题描述 9 | 10 | 通过 UI 展示的链路追踪显示不完整,缺失前面或后面的调用链路。 11 | 12 | ## 原因 13 | 14 | 绝大多数情况下都是因为没在业务层面将 tracing 所需要的 http header 正确传递或根本没有传递。 15 | 16 | 要在 istio 中使用链路追踪,并不是说业务无侵入,有个基本要求是:业务收到 tracing 相关的 header 要将其传递给被调服务。这个步骤是无法让 istio 帮你完成的,因为 istio 无法感知你的业务逻辑,不知道业务中调用其它服务的请求到底是该对应前面哪个请求,所以需要业务来传递 header,最终才能将链路完整串起来。 17 | 18 | ## 参考资料 19 | 20 | * [What is required for distributed tracing with Istio](https://istio.io/latest/about/faq/#how-to-support-tracing) 21 | -------------------------------------------------------------------------------- /ha/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 高可用与容灾 3 | type: book # Do not modify. 4 | --- 5 | 6 | 分享 istio 相关高可用与容灾方案 -------------------------------------------------------------------------------- /ha/locality-loadbalancing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "地域感知" 3 | type: book 4 | date: "2021-06-21" 5 | weight: 100 6 | draft: true 7 | --- 8 | 9 | ## 如何启用 -------------------------------------------------------------------------------- /home/hero.md: -------------------------------------------------------------------------------- 1 | +++ 2 | # Hero widget. 3 | widget = "hero" # See https://sourcethemes.com/academic/docs/page-builder/ 4 | headless = true # This file represents a page section. 5 | active = true # Activate this widget? true/false 6 | weight = 10 # Order that this section will appear. 7 | 8 | title = "Istio 学习笔记" 9 | 10 | # Hero image (optional). Enter filename of an image in the `static/media/` folder. 11 | hero_media = "book.svg" 12 | 13 | [design.background] 14 | # Apply a background color, gradient, or image. 15 | # Uncomment (by removing `#`) an option to apply it. 16 | # Choose a light or dark text color by setting `text_color_light`. 17 | # Any HTML color name or Hex value is valid. 18 | 19 | # Background color. 20 | # color = "navy" 21 | 22 | # Background gradient. 23 | gradient_start = "#4bb4e3" 24 | gradient_end = "#2b94c3" 25 | 26 | # Background image. 27 | # image = "" # Name of image in `static/media/`. 28 | # image_darken = 0.6 # Darken the image? Range 0-1 where 0 is transparent and 1 is opaque. 29 | # image_size = "cover" # Options are `cover` (default), `contain`, or `actual` size. 30 | # image_position = "center" # Options include `left`, `center` (default), or `right`. 31 | # image_parallax = true # Use a fun parallax-like fixed background effect? true/false 32 | 33 | # Text color (true=light or false=dark). 34 | text_color_light = true 35 | 36 | # Call to action links (optional). 37 | # Display link(s) by specifying a URL and label below. Icon is optional for `[cta]`. 38 | # Remove a link/note by deleting a cta/note block. 39 | [cta] 40 | url = "basics/" 41 | label = "开始阅读" 42 | icon_pack = "fas" 43 | icon = "arrow-alt-circle-right" 44 | 45 | [cta_alt] 46 | url = "https://github.com/imroc/learning-istio/" 47 | label = "查看源码" 48 | 49 | +++ 50 | 51 | 分享 istio 相关理论知识与实践经验,欢迎关注微信公众号 "云原生知识宇宙"。 52 | 53 | Star 54 | -------------------------------------------------------------------------------- /home/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # Homepage 3 | type: "widget_page" 4 | 5 | # Homepage is headless, other widget pages are not. 6 | headless: true 7 | --- 8 | -------------------------------------------------------------------------------- /ref/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 参考手册 3 | type: book # Do not modify. 4 | --- 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ref/bug/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 已知 BUG 3 | type: book 4 | date: "2021-02-23" 5 | --- 6 | 7 | ## envoy 内存不释放 8 | * 已知受影响的版本: istio 1.5.4 9 | * 已解决版本: istio 1.6.0 10 | * issue: [#25145](https://github.com/istio/istio/issues/25145) 11 | 12 | -------------------------------------------------------------------------------- /ref/envoy-config/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Envoy 配置格式 3 | type: book 4 | date: "2021-02-24" 5 | draft: true 6 | --- -------------------------------------------------------------------------------- /ref/envoy-log/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Envoy 日志格式 3 | type: book 4 | date: "2021-02-24" 5 | draft: true 6 | --- -------------------------------------------------------------------------------- /ref/link/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 链接收集 3 | type: book 4 | date: "2021-02-26" 5 | --- 6 | 7 | ## Istio 相关 8 | * [istio版本跟踪](https://i.cloudnative.to/istio/release/overview) 9 | * [istio-handbook](https://www.servicemesher.com/istio-handbook/) 10 | * [云原生学院B站视频](https://space.bilibili.com/515485124) 11 | * [istio 端口列表](https://istio.io/latest/docs/ops/deployment/requirements/#ports-used-by-istio) 12 | * [istio annotation 列表](https://istio.io/latest/docs/reference/config/annotations/) 13 | * [istio 与 k8s 版本兼容性矩阵](https://istio.io/latest/about/supported-releases/#support-status-of-istio-releases) 14 | 15 | ## Envoy 相关 16 | * [Envoy 15000 管理端口接口列表](https://www.envoyproxy.io/docs/envoy/latest/operations/admin) 17 | * [Envoy response flags](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-response-flags) 18 | -------------------------------------------------------------------------------- /ref/shell/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 实用脚本 3 | type: book 4 | date: "2021-04-15" 5 | weight: 10 6 | --- 7 | 8 | ## istioctl 9 | 10 | ### 查看 sidecar 证书是否正常 11 | 12 | ```bash 13 | $ istioctl proxy-config secret accountdeletecgi-5b9d6b586-wzb7b 14 | RESOURCE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER NOT BEFORE 15 | default Cert Chain ACTIVE true 198001071566761875257861959297039696827 2021-04-16T03:33:03Z 2021-04-15T03:33:03Z 16 | ROOTCA CA ACTIVE true 205820131934050053680230040513977871884 2031-03-24T02:58:23Z 2021-03-26T02:58:23Z 17 | ``` 18 | 19 | ### 查看 sidecar 证书详情 20 | 21 | ```bash 22 | $ istioctl -n istio-test proxy-config secret productpage-v1-578c57988-j8g9d -o json | jq '[.dynamicActiveSecrets[] | select(.name == "default")][0].secret.tlsCertificate.certificateChain.inlineBytes' -r | base64 -d | openssl x509 -noout -text 23 | Certificate: 24 | Data: 25 | Version: 3 (0x2) 26 | Serial Number: 27 | 1b:6c:19:da:04:db:cb:1b:29:48:f8:09:60:35:22:75 28 | Signature Algorithm: sha256WithRSAEncryption 29 | Issuer: L=cls-5u6csmhf, O=Istio, CN=Intermediate CA 30 | Validity 31 | Not Before: Apr 15 03:57:51 2021 GMT 32 | Not After : Apr 16 03:57:51 2021 GMT 33 | Subject: 34 | Subject Public Key Info: 35 | Public Key Algorithm: rsaEncryption 36 | Public-Key: (2048 bit) 37 | Modulus: 38 | 00:dd:4f:bb:65:fd:d2:9c:7d:29:00:a9:6b:8c:b2: 39 | 8b:12:17:5f:6f:1b:d6:db:a2:7a:69:23:21:6a:d1: 40 | 38:4e:44:d0:c9:f4:6d:13:e9:97:86:54:f2:30:e6: 41 | fe:9e:41:7c:95:a7:20:ff:bb:de:62:8e:49:58:90: 42 | 7a:38:be:15:f1:96:6e:ff:7a:c4:61:d8:a8:25:f1: 43 | 92:ee:33:ae:86:bb:63:38:2c:e7:32:a5:11:be:79: 44 | 3e:83:67:17:4e:91:df:0a:3e:52:11:60:9a:83:5d: 45 | e4:92:9a:f6:29:43:7e:60:13:03:4d:ed:fc:d1:5c: 46 | e9:5b:a9:a6:ef:b8:f5:82:78:a1:ef:15:43:17:40: 47 | b3:48:c2:27:33:ac:0e:aa:00:c9:da:3f:ee:5d:1a: 48 | d7:7a:4f:e3:e0:26:e8:67:1a:c1:44:c5:f3:d0:1c: 49 | e1:e4:53:a5:a8:0b:04:47:cd:df:d2:a9:1b:47:8f: 50 | 3e:dc:9a:b6:b3:a8:6d:47:da:4d:68:dd:4f:82:3f: 51 | aa:25:6d:8e:c5:8c:9d:1e:7c:93:4c:55:a3:59:d7: 52 | a6:42:04:05:52:01:6d:a1:c8:8f:67:48:b4:16:4b: 53 | 46:6e:1e:5b:97:65:99:fe:5f:f7:f2:ba:ea:3f:34: 54 | 28:f1:e6:18:4d:d9:de:00:f2:fd:4a:9c:f9:a5:e2: 55 | 9d:5b 56 | Exponent: 65537 (0x10001) 57 | X509v3 extensions: 58 | X509v3 Key Usage: critical 59 | Digital Signature, Key Encipherment 60 | X509v3 Extended Key Usage: 61 | TLS Web Server Authentication, TLS Web Client Authentication 62 | X509v3 Basic Constraints: critical 63 | CA:FALSE 64 | X509v3 Authority Key Identifier: 65 | keyid:A0:62:8E:B4:53:64:D9:1D:DC:21:41:D8:05:93:E4:6D:27:82:20:4E 66 | 67 | X509v3 Subject Alternative Name: critical 68 | URI:spiffe://cluster.local/ns/istio-test/sa/bookinfo-productpage 69 | Signature Algorithm: sha256WithRSAEncryption 70 | ab:0d:b5:e6:df:50:02:d2:85:47:62:18:b0:af:89:cc:3a:06: 71 | a1:19:a8:2c:58:9c:e4:1d:34:3b:f8:a2:a7:f6:f8:0e:af:a8: 72 | 1b:35:79:9d:72:a2:a9:96:14:37:c8:76:e2:50:ae:d4:c6:33: 73 | 43:a5:0e:e4:c9:95:a8:81:9a:6a:72:e5:eb:3c:55:20:70:a4: 74 | 27:3c:6d:88:da:03:75:3a:99:d0:72:c2:b3:2e:66:9e:00:9a: 75 | 13:c5:61:20:fc:35:99:30:93:33:e6:8a:2d:b4:b0:0f:23:3a: 76 | a1:3d:4f:01:bf:cc:2b:38:2a:41:23:13:31:52:84:d7:8d:cb: 77 | 71:63:28:e6:1f:1f:95:20:41:63:1a:a6:5f:a5:d0:3b:35:97: 78 | 4b:8d:6c:55:59:34:e2:36:ff:a0:38:4c:f0:1f:a3:16:bf:bc: 79 | 75:53:35:20:60:b2:0d:4d:bd:d1:ab:a6:28:60:e4:d7:0c:e3: 80 | cc:19:cb:d1:4c:e7:3d:fc:21:aa:eb:e6:f4:a6:0f:ed:cd:da: 81 | db:ae:4c:fa:cf:55:f8:ea:d1:55:d5:6c:51:95:3f:47:13:b7: 82 | 20:e2:5d:cc:b0:ea:8d:99:e1:9f:40:df:d3:97:af:a5:69:f4: 83 | c6:b7:9c:c4:55:67:47:59:2b:53:40:f2:48:88:9b:75:77:00: 84 | 22:98:f7:61:74:05:8c:8b:e4:1f:be:c8:e9:7a:8f:9a:5d:ff: 85 | 1d:48:0a:e9:75:da:1e:35:93:a4:a0:c0:f8:78:bc:25:a2:63: 86 | d3:35:83:1f:15:28:a7:31:de:5a:d8:ae:56:f8:8c:ea:da:13: 87 | 01:81:aa:6f:0f:a5:39:78:e6:b6:e3:1c:ff:7c:03:50:22:04: 88 | 64:0a:dc:14:2c:ed:7d:ec:91:73:dc:44:3e:60:bc:d8:69:c3: 89 | 7c:5b:d5:16:53:1c:24:2e:1b:51:fb:93:31:37:b3:80:e6:f2: 90 | 07:46:09:8d:d5:2c:a4:f4:e3:14:b3:d9:d7:de:de:9c:bf:84: 91 | 67:66:e1:b9:85:26:1c:8f:5c:8d:9f:5f:53:b7:ed:c7:2b:9d: 92 | 57:3f:3c:d6:86:f4:d8:d8:72:c3:4c:be:5e:48:a4:ac:b9:c5: 93 | b1:6c:4b:dc:83:a2:bc:80:c2:34:c3:1a:68:7f:e8:e8:b9:eb: 94 | 39:2a:6d:3d:2d:90:e2:9c:52:dc:a2:99:e3:dc:dc:5a:f7:71: 95 | 9d:5f:67:93:d6:e3:68:a2:f9:7b:6e:64:a6:0c:09:95:f6:28: 96 | 02:e4:3f:63:fc:09:12:f7:8f:ce:4a:c3:38:02:0c:35:64:f1: 97 | 74:93:36:93:6d:e2:8e:5b:07:b9:5a:f8:14:32:69:4f:64:8d: 98 | 6e:a4:b0:95:73:36:b6:92 99 | ``` 100 | * 确保 `Subject Alternative Name` 包含正确的 spiffe URI,如 `URI:spiffe://cluster.local/ns/istio-test/sa/bookinfo-productpage`。 101 | -------------------------------------------------------------------------------- /ref/yaml/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 实用 YAML 3 | type: book 4 | date: "2021-04-26" 5 | weight: 13 6 | --- 7 | 8 | ## sidecar 注入相关 9 | 10 | ### 为指定 workload 取消 sidecar 自动注入 11 | 12 | ```yaml 13 | template: 14 | metadata: 15 | annotations: 16 | sidecar.istio.io/inject: "false" 17 | ``` 18 | 19 | ## proxy 相关 20 | 21 | ### 自定义 request/limit 22 | 23 | ```yaml 24 | template: 25 | metadata: 26 | annotations: 27 | "sidecar.istio.io/proxyCPU": "10m" 28 | "sidecar.istio.io/proxyCPULimit": "2" 29 | "sidecar.istio.io/proxyMemory": "32Mi" 30 | "sidecar.istio.io/proxyMemoryLimit": "1Gi" 31 | ``` 32 | 33 | ### 自定义日志级别 34 | 35 | ```yaml 36 | template: 37 | metadata: 38 | annotations: 39 | "sidecar.istio.io/logLevel": debug # 可选: trace, debug, info, warning, error, critical, off 40 | "sidecar.istio.io/componentLogLevel": "ext_authz:trace,filter:debug" 41 | ``` 42 | * [envoy component logging 说明](https://www.envoyproxy.io/docs/envoy/latest/operations/cli#cmdoption-component-log-level) 43 | 44 | ### 不劫持部分外部地址的流量以提升性能(比如外部数据库) 45 | 46 | ```yaml 47 | template: 48 | metadata: 49 | annotations: 50 | traffic.sidecar.istio.io/excludeOutboundIPRanges: "10.10.31.1/32,10.10.31.2/32" 51 | ``` 52 | 53 | ## mtls 配置 54 | 55 | ### 全局禁用 mtls 56 | 57 | ```yaml 58 | apiVersion: security.istio.io/v1beta1 59 | kind: PeerAuthentication 60 | metadata: 61 | name: default 62 | namespace: istio-system 63 | spec: 64 | mtls: 65 | mode: DISABLE 66 | ``` 67 | 68 | ## DestinationRule 相关 69 | 70 | ### 为某个服务启用地域感知 71 | 72 | 地域感知行为需要显式指定 `outlierDetection` 后才会启用: 73 | 74 | ```yaml 75 | apiVersion: networking.istio.io/v1beta1 76 | kind: DestinationRule 77 | metadata: 78 | name: nginx 79 | spec: 80 | host: nginx 81 | trafficPolicy: 82 | outlierDetection: 83 | consecutive5xxErrors: 3 84 | interval: 30s 85 | baseEjectionTime: 30s 86 | ``` -------------------------------------------------------------------------------- /source/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 源码阅读 3 | type: book # Do not modify. 4 | --- 5 | 6 | 先占位,后续陆续补充笔记 -------------------------------------------------------------------------------- /source/code-index/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 关键代码索引表 3 | type: book 4 | date: "2021-03-17" 5 | weight: 1 6 | --- 7 | 8 | ## 配置相关 9 | 10 | * `istio-sidecar-injector` values 配置格式(message Values): `operator/pkg/apis/istio/v1alpha1/values_types.proto` -------------------------------------------------------------------------------- /source/istiod/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: istiod 启动流程 3 | type: book 4 | date: "2021-01-28" 5 | weight: 3 6 | --- 7 | 8 | ## istiod 9 | 10 | main 函数很简单 (`pilot/cmd/pilot-discovery/main.go`): 11 | 12 | ```go 13 | func main() { 14 | if err := rootCmd.Execute(); err != nil { 15 | log.Error(err) 16 | os.Exit(-1) 17 | } 18 | } 19 | ``` 20 | 21 | 执行命令启动 istiod,失败如果就报错退出。跳转 `rootCmd` 可以看到是一个全局变量: 22 | 23 | ```go 24 | rootCmd = &cobra.Command{ 25 | Use: "pilot-discovery", 26 | Short: "Istio Pilot.", 27 | Long: "Istio Pilot provides fleet-wide traffic management capabilities in the Istio Service Mesh.", 28 | SilenceUsage: true, 29 | } 30 | ``` 31 | 32 | istio 使用了 [cobra](https://github.com/spf13/cobra) 作为 cli 库,通常也是大多其它 golang 程序的选择。命令名称是 `pilot-discovery` (与编译出来的二进制相同,这也是 "潜规则"),但它最核心的还是它的一个叫 `discovery` 的子命令: 33 | 34 | ```go 35 | rootCmd.AddCommand(discoveryCmd) 36 | ``` 37 | 38 | 这也是我们常说的 istiod,来看看它是如何定义的: 39 | 40 | ```go 41 | discoveryCmd = &cobra.Command{ 42 | Use: "discovery", 43 | Short: "Start Istio proxy discovery service.", 44 | ... 45 | RunE: func(c *cobra.Command, args []string) error { 46 | ... 47 | // Create the server for the discovery service. 48 | discoveryServer, err := bootstrap.NewServer(serverArgs) 49 | if err != nil { 50 | return fmt.Errorf("failed to create discovery service: %v", err) 51 | } 52 | ... 53 | // Start the server 54 | if err := discoveryServer.Start(stop); err != nil { 55 | return fmt.Errorf("failed to start discovery service: %v", err) 56 | } 57 | }, 58 | } 59 | ``` 60 | 61 | istiod 启动时会执行 `RunE` 函数中的代码,可以看到核心是创建了一个 `discoveryServer` 并启动,来看看 `Start` 函数都做了些什么。 62 | 63 | 首先是启动了所有内部组件: 64 | 65 | ```go 66 | // Now start all of the components. 67 | for _, fn := range s.startFuncs { 68 | if err := fn(stop); err != nil { 69 | return err 70 | } 71 | } 72 | ``` 73 | 74 | 各个组件的启动函数放在了 `startFuncs` 的函数数组里,依次执行。 75 | 76 | 接着就启动各个服务端口的监听: 77 | 78 | 79 | ```go 80 | if s.SecureGrpcListener != nil { 81 | go func() { 82 | log.Infof("starting secure gRPC discovery service at %s", s.SecureGrpcListener.Addr()) 83 | if err := s.secureGrpcServer.Serve(s.SecureGrpcListener); err != nil { 84 | log.Errorf("error serving secure GRPC server: %v", err) 85 | } 86 | }() 87 | } 88 | 89 | if s.GRPCListener != nil { 90 | go func() { 91 | log.Infof("starting gRPC discovery service at %s", s.GRPCListener.Addr()) 92 | if err := s.grpcServer.Serve(s.GRPCListener); err != nil { 93 | log.Errorf("error serving GRPC server: %v", err) 94 | } 95 | }() 96 | } 97 | 98 | // At this point we are ready - start Http Listener so that it can respond to readiness events. 99 | go func() { 100 | log.Infof("starting Http service at %s", s.HTTPListener.Addr()) 101 | if err := s.httpServer.Serve(s.HTTPListener); isUnexpectedListenerError(err) { 102 | log.Errorf("error serving http server: %v", err) 103 | } 104 | }() 105 | 106 | if s.HTTP2Listener != nil { 107 | go func() { 108 | log.Infof("starting Http2 muxed service at %s", s.HTTP2Listener.Addr()) 109 | h2s := &http2.Server{} 110 | h1s := &http.Server{ 111 | Addr: ":8080", 112 | Handler: h2c.NewHandler(s.httpMux, h2s), 113 | } 114 | if err := h1s.Serve(s.HTTP2Listener); isUnexpectedListenerError(err) { 115 | log.Errorf("error serving http server: %v", err) 116 | } 117 | }() 118 | } 119 | 120 | if s.httpsServer != nil { 121 | go func() { 122 | log.Infof("starting webhook service at %s", s.HTTPListener.Addr()) 123 | if err := s.httpsServer.ListenAndServeTLS("", ""); isUnexpectedListenerError(err) { 124 | log.Errorf("error serving https server: %v", err) 125 | } 126 | }() 127 | } 128 | 129 | ``` 130 | 131 | 最后等待停止信号 (SIGTERM),进行优雅停止: 132 | 133 | ```go 134 | s.waitForShutdown(stop) 135 | ``` 136 | 137 | 去除无关紧要的干扰,聚焦核心逻辑,可以看到主要是: 138 | 1. 启动所有内部组件。 139 | 2. 等都启动完成后就开始监听 gRPC 端口,也就是核心的 xDS 服务。 140 | 141 | -------------------------------------------------------------------------------- /source/project-structure/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 项目结构分析 3 | type: book 4 | date: "2021-01-24" 5 | weight: 2 6 | --- 7 | 8 | ## 核心组件与代码入口 9 | 10 | `make build` 会编译出本项目的二进制可执行文件, 从 `Makefile` 中看下 build 会编译哪些对象: 11 | ``` makefile 12 | .PHONY: build 13 | build: depend ## Builds all go binaries. 14 | GOOS=$(GOOS_LOCAL) GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $(ISTIO_OUT)/ $(STANDARD_BINARIES) 15 | GOOS=$(GOOS_LOCAL) GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $(ISTIO_OUT)/ -tags=agent $(AGENT_BINARIES) 16 | ``` 17 | 18 | 分成了两批二进制(`STANDARD_BINARIES` 和 `AGENT_BINARIES`),进一步查找: 19 | ``` makefile 20 | AGENT_BINARIES:=./pilot/cmd/pilot-agent 21 | STANDARD_BINARIES:=./istioctl/cmd/istioctl \ 22 | ./pilot/cmd/pilot-discovery \ 23 | ./pkg/test/echo/cmd/client \ 24 | ./pkg/test/echo/cmd/server \ 25 | ./operator/cmd/operator \ 26 | ./cni/cmd/istio-cni \ 27 | ./cni/cmd/istio-cni-repair \ 28 | ./cni/cmd/istio-cni-taint \ 29 | ./cni/cmd/install-cni \ 30 | ./tools/istio-iptables \ 31 | ./tools/bug-report 32 | ``` 33 | 34 | 上面列出了本项目所有程序的 main 函数入口目录,比较重要的几个: 35 | * `./pilot/cmd/pilot-discovery`: 这是 istio 最核心的代码入口,也就是我们常说的 istiod,控制面的核心。以前控制面还有 galley, citadel 这些组件,现在全部都合并到 istiod 了。 36 | * `./pilot/cmd/pilot-agent`: 这是数据面生命周期管理组件 pilot-agent,它与 envoy 部署在同一容器中,主要用于管理 envoy 的生命周期 (生成 envoy 初始配置、envoy 健康探测、reload 配置等)。 37 | * `/istioctl/cmd/istioctl`: 管理 istio 的命令行工具。 38 | -------------------------------------------------------------------------------- /source/use-clion-read-envoy-source-code/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/source/use-clion-read-envoy-source-code/1.png -------------------------------------------------------------------------------- /source/use-clion-read-envoy-source-code/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/source/use-clion-read-envoy-source-code/2.png -------------------------------------------------------------------------------- /source/use-clion-read-envoy-source-code/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/source/use-clion-read-envoy-source-code/3.gif -------------------------------------------------------------------------------- /source/use-clion-read-envoy-source-code/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/source/use-clion-read-envoy-source-code/4.png -------------------------------------------------------------------------------- /source/use-clion-read-envoy-source-code/5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/source/use-clion-read-envoy-source-code/5.gif -------------------------------------------------------------------------------- /source/use-clion-read-envoy-source-code/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/source/use-clion-read-envoy-source-code/6.png -------------------------------------------------------------------------------- /source/use-clion-read-envoy-source-code/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 Clion 阅读 Envoy 源码 3 | type: book 4 | date: "2021-01-29" 5 | weight: 100 6 | --- 7 | 8 | ## 背景 9 | 10 | 要想深入学习 istio,还得学习下数据面的实现,istio 的数据面使用了 envoy,在 istio group 下有个叫 [proxy](https://github.com/istio/proxy) 的仓库,包含了一些 istio 用到的一些 envoy 扩展,编译时将 envoy 代码作为库来引用,最终使用 bazel 编译出 istio 版本的 Envoy。 11 | 12 | 13 | 代码量非常庞大,如果没有智能的代码跳转、查找引用与实现,读起来简直低效的要命。如何高效的阅读呢?关键在于 IDE/编辑器 的代码索引能力要好,需要能够准确跳转和查询,vscode 用的同学比较多,但它的 c/c++ 插件不够智能,很多情况无法跳转,而且效率极低;它还有个 clangd 的插件,基于 LSP,但不够成熟。这方面做的最好的目前还是来自 JetBrains CLion,不过它需要依赖 `CMakeLists.txt` 文件来解析项目结构,由于 c/c++ 没有统一的结构标准,不同项目结构千差万别,不太好自动生成 `CMakeLists.txt`,需要我们先理解项目结构,然后编写 `CMakeLists.txt` 来让 CLion 进行解析。 14 | 15 | 虽然社区有人针对 bazel 构建的项目写了一个通用脚本 [bazel-cmakelists](https://github.com/lizan/bazel-cmakelists) ,但很久没维护,测试了用它来生成最新 envoy 的 `CMakeLists.txt` ,由于代码量庞大,最终会 OOM 而失败。 16 | 17 | 所以我们需要另寻更好的方法,不太了解这方面的同学弄起来会比较麻烦,本人也折腾了好一段时间才搞定,本文记录下方法和心得,以供大家参考。 18 | 19 | ## 克隆代码 20 | 21 | 首先克隆 [istio-proxy](https://github.com/istio/proxy) 的代码: 22 | 23 | ``` bash 24 | git clone https://github.com/istio/proxy.git istio-proxy 25 | ``` 26 | 27 | 最好切到某个稳定的 release 分支上: 28 | ``` bash 29 | cd istio-proxy 30 | git checkout -b release-1.9 origin/release-1.9 31 | ``` 32 | 33 | ## 项目分析 34 | 35 | istio-proxy 代码库中主要只包含了在 istio 里用到的一些 envoy 扩展,代码量不大,源码主要分布在 src 与 extensions 目录,但编译需要很久,因为它实际编译的是 envoy,只是利用 bazel 将自身代码作为扩展编译进 envoy (得益于 envoy 的扩展机制),从这个 bazel 的 [BUILD 文件](https://github.com/istio/proxy/blob/master/src/envoy/BUILD) 就能看得出来: 36 | ``` txt 37 | envoy_cc_binary( 38 | name = "envoy", 39 | repository = "@envoy", 40 | visibility = ["//visibility:public"], 41 | deps = [ 42 | "//extensions/access_log_policy:access_log_policy_lib", 43 | "//extensions/attributegen:attributegen_plugin", 44 | "//extensions/metadata_exchange:metadata_exchange_lib", 45 | "//extensions/stackdriver:stackdriver_plugin", 46 | "//extensions/stats:stats_plugin", 47 | "//src/envoy/extensions/wasm:wasm_lib", 48 | "//src/envoy/http/alpn:config_lib", 49 | "//src/envoy/http/authn:filter_lib", 50 | "//src/envoy/tcp/forward_downstream_sni:config_lib", 51 | "//src/envoy/tcp/metadata_exchange:config_lib", 52 | "//src/envoy/tcp/sni_verifier:config_lib", 53 | "//src/envoy/tcp/tcp_cluster_rewrite:config_lib", 54 | "@envoy//source/exe:envoy_main_entry_lib", 55 | ], 56 | ) 57 | ``` 58 | 59 | 其中 `@envoy` 表示引用 envoy 代码库,main 函数也位于 envoy 代码库中。那么 envoy 代码库从哪儿来的呢?bazel 在构建时会自动下载指定的依赖,envoy 的代码来源在 [WORKSPACE](https://github.com/istio/proxy/blob/master/WORKSPACE) 中有指定: 60 | ``` txt 61 | http_archive( 62 | name = "envoy", 63 | sha256 = ENVOY_SHA256, 64 | strip_prefix = ENVOY_REPO + "-" + ENVOY_SHA, 65 | url = "https://github.com/" + ENVOY_ORG + "/" + ENVOY_REPO + "/archive/" + ENVOY_SHA + ".tar.gz", 66 | ) 67 | ``` 68 | bazel 会自动下载指定版本的源码包来编译。 69 | 70 | ## 如果获取依赖源文件? 71 | 72 | 由于 istio-proxy 依赖了大量的第三方源文件,我们要阅读代码需要将这些源文件都下下来,只要将它编译一次,所有依赖源文件以及 generated 的代码都可以自动给你备好,所以我们需要对它进行一次编译。 73 | 74 | 由于编译 envoy 有复杂的工具链依赖,官方推荐使用容器进行编译,在执行 `make` 前加个 `BUILD_WITH_CONTAINER=1` 即可指定使用容器编译,免去复杂的环境依赖。但 bazel 编译会将依赖和 generated 的源文件都软链到临时目录,如果用容器编译,就会丢失这部分代码,而我们阅读 istio-proxy 代码时最关键的就是这部分代码了,所以不能用容器编译。 75 | 76 | ## 安装 bazelisk 77 | 78 | 不用容器编译就需要本机环境基本满足工具链要求,首先是需要安装 bazel,由于 bazel 版本很多,不同 istio-proxy(envoy) 版本依赖的 bazel 版本也不一样,我们可以直接安装 [bazelisk](https://github.com/bazelbuild/bazelisk) ,一个用于 bazel 多版本管理的工具,它可以自动识别项目中 [.bazelversion](https://github.com/istio/proxy/blob/master/.bazelversion) 文件,选取指定版本的 bazel 来进行构建(可以自动下载对应版本的 bazel 二进制)。 79 | 80 | 如果是 macOS 用户,安装很简单: 81 | ``` bash 82 | brew install bazelisk 83 | ``` 84 | > 如果之前已安装过 bazel,可以使用 `brew link --overwrite bazelisk` 强制覆盖。 85 | 86 | 其它平台的可以在 [release](https://github.com/bazelbuild/bazelisk/releases) 页面下载最新的二进制,重命名为 `bazel` 然后放到 `PATH` 下。 87 | 88 | ## 其它依赖 89 | 90 | 如果是 macOS 用户,确保务必安装好 xcode,方便跳转系统库函数,安装命令: 91 | ``` bash 92 | xcode-select --install 93 | ``` 94 | 另外主要还有 python3 (macOS 自带),其它依赖通常都系统自带,可以先不用管,等如果编译报错了再看。 95 | 96 | 更多依赖可参考 [官方文档](https://www.envoyproxy.io/docs/envoy/latest/start/building#requirements) 。 97 | 98 | ## 编译 99 | 100 | 在 istio-proxy 代码根目录执行以下命令进行编译: 101 | ``` bash 102 | make build_envoy 103 | ``` 104 | 环境没问题的话会经过漫长的构建和编译,通常可能几十分钟,取决于电脑配置。 105 | 106 | 编译完后会发现 bazel 为我们生成了一些目录软链: 107 | 108 | ![](1.png) 109 | > bazel 输出目录结构可参考官方文档 [Output Directory Layout](https://docs.bazel.build/versions/master/output_directories.html#layout-diagram) 。 110 | 111 | 我们主要关注以下两个目录: 112 | * **bazel-istio-proxy**: 包含构建 istio-proxy 用到的源文件(包含依赖)。 113 | * **bazel-bin**: 包含一些 generated 代码。 114 | 115 | ## 生成源码文件列表 116 | 117 | 在 istio-proxy 根目录创建脚本文件 `generate-srcs.sh`: 118 | ``` bash 119 | #!/bin/bash 120 | 121 | set -ex 122 | 123 | bazel_dir="bazel-${PWD##*/}" 124 | 125 | find -L -E $bazel_dir/external src extensions -regex '.*\.(cc|c|cpp)' > sourcefiles.txt 126 | ``` 127 | 128 | 执行此脚本可以生成 istio-proxy 及其依赖的源文件列表 (`sourcefiles.txt`),用于在 `CMakeLists.txt` 中引用。 129 | 130 | **注:** `$bazel_dir/external` 下包含内容较多,全部索引的话 CLion 可能会比较卡,很多代码基本也都不会看,可以适当缩小范围,按需来配置,比如先只添加 `$bazel_dir/external/envoy`,后续有需要再添加其它目录,然后 `Reload Cmake Project` 重新索引。 131 | 132 | ## 生成 CMakeLists.txt 133 | 134 | 然后就可以在 istio-proxy 项目根目录创建下 `CMakeLists.txt`: 135 | ``` txt 136 | cmake_minimum_required(VERSION 3.15) 137 | STRING( REGEX REPLACE ".*/(.*)" "\\1" CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} ) 138 | project(istio-proxy) 139 | 140 | macro(print_all_variables) 141 | message(STATUS "print_all_variables------------------------------------------{") 142 | get_cmake_property(_variableNames VARIABLES) 143 | foreach (_variableName ${_variableNames}) 144 | message(STATUS "${_variableName}=${${_variableName}}") 145 | endforeach() 146 | message(STATUS "print_all_variables------------------------------------------}") 147 | endmacro() 148 | 149 | set(CMAKE_CXX_STANDARD 17) 150 | add_definitions(-DNULL_PLUGIN) # enable wasm nullvm navigation 151 | 152 | file(STRINGS sourcefiles.txt all_SRCS) 153 | 154 | message(STATUS "CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}") 155 | message(STATUS "CMAKE_HOME_DIRECTORY=${CMAKE_HOME_DIRECTORY}") 156 | 157 | add_executable(istio-proxy ${all_SRCS}) 158 | 159 | set(istio_include_dirs 160 | "./" 161 | "./src" 162 | "./extensions" 163 | 164 | "./bazel-${CURRENT_FOLDER}/external/envoy" 165 | "./bazel-${CURRENT_FOLDER}/external/envoy/source" 166 | "./bazel-${CURRENT_FOLDER}/external/envoy/include" 167 | "./bazel-${CURRENT_FOLDER}/external/envoy/api/wasm/cpp" 168 | "./bazel-${CURRENT_FOLDER}/external/boringssl/src/include/" 169 | "./bazel-${CURRENT_FOLDER}/external/com_github_gabime_spdlog/include" 170 | "./bazel-${CURRENT_FOLDER}/external/com_github_c_ares_c_ares" 171 | "./bazel-${CURRENT_FOLDER}/external/com_google_absl" 172 | "./bazel-${CURRENT_FOLDER}/external/com_google_cel_cpp" 173 | "./bazel-${CURRENT_FOLDER}/external/com_google_protobuf/src" 174 | "./bazel-${CURRENT_FOLDER}/external/com_github_fmtlib_fmt/include" 175 | "./bazel-${CURRENT_FOLDER}/external/com_github_eile_tclap/include" 176 | "./bazel-${CURRENT_FOLDER}/external/com_github_grpc_grpc/include" 177 | "./bazel-${CURRENT_FOLDER}/external/com_envoyproxy_protoc_gen_validate/" 178 | "./bazel-${CURRENT_FOLDER}/external/com_github_tencent_rapidjson/include/" 179 | "./bazel-${CURRENT_FOLDER}/external/com_github_datadog_dd_opentracing_cpp/include/" 180 | "./bazel-${CURRENT_FOLDER}/external/com_github_libevent_libevent/include" 181 | "./bazel-${CURRENT_FOLDER}/external/com_github_mirror_tclap/include" 182 | "./bazel-${CURRENT_FOLDER}/external/com_github_grpc_grpc" 183 | "./bazel-${CURRENT_FOLDER}/external/com_github_circonus_labs_libcircllhist/src/" 184 | "./bazel-${CURRENT_FOLDER}/external/com_github_nodejs_http_parser" 185 | "./bazel-${CURRENT_FOLDER}/external/com_github_nghttp2_nghttp2/lib/includes/" 186 | "./bazel-${CURRENT_FOLDER}/external/com_github_cyan4973_xxhash/" 187 | "./bazel-${CURRENT_FOLDER}/external/com_github_google_flatbuffers/include/" 188 | "./bazel-${CURRENT_FOLDER}/external/com_github_fmtlib_fmt/test" 189 | 190 | "./bazel-bin" 191 | "./bazel-bin/external/envoy_api" 192 | "./bazel-bin/external/mixerapi_git" 193 | "./bazel-bin/external/com_envoyproxy_protoc_gen_validate" 194 | "./bazel-bin/external/com_google_googleapis" 195 | "./bazel-bin/external/com_github_cncf_udpa" 196 | ) 197 | 198 | target_include_directories(istio-proxy PUBLIC ${istio_include_dirs}) 199 | ``` 200 | 201 | 解释一下: 202 | * `add_executable` 将需要索引的源文件列表 (`sourcefiles.txt`) 加进索引。 203 | * `target_include_directories` 将用到的一些纯头文件目录加进索引 (不包含实现代码,主要是一些接口),这里也是可以按需进行增删。 204 | 205 | ## 使用 CLion 阅读 206 | 207 | 不要直接打开 istio-proxy 目录,而是 Open 时选中 `CMakeLists.txt`,然后 `Open as Project`: 208 | 209 | ![](2.png) 210 | 211 | 弹出 `Load Project` 时不要勾选 `Clean project`,不然退出 CLion 时会执行 `make clean`,导致把 bazel 生成的源文件都给删除掉,就没法跳转了: 212 | 213 | ![](6.png) 214 | 215 | 然后就会开始索引,完成后就可以愉快的看代码了,先从 main 看起吧(`bazel-istio-proxy/external/envoy/source/exe/main.cc`): 216 | 217 | ![](3.gif) 218 | 219 | 查找引用: 220 | 221 | ![](4.png) 222 | 223 | 跳转到实现: 224 | 225 | ![](5.gif) 226 | 227 | ## 小结 228 | 229 | 本文介绍了如何使用 CLion 来阅读 istio-proxy (envoy) 的代码,包含源码结构分析、环境搭建,以及生成 CLion 所需要的 `CMakeLists.txt` 文件的方法,最后也展示了效果,希望对你有所帮助。 -------------------------------------------------------------------------------- /tcm/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: TCM 3 | type: book # Do not modify. 4 | --- 5 | 6 | TCM (Tecnet Cloud Mesh) 相关。 -------------------------------------------------------------------------------- /tcm/best-practice/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "最佳实践" 3 | type: book # Do not modify. 4 | weight: 10000 5 | icon: landmark 6 | icon_pack: fas 7 | --- 8 | 9 | 以下 TCM 相关最佳实践。 -------------------------------------------------------------------------------- /tcm/best-practice/upgrade-mesh/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/best-practice/upgrade-mesh/1.png -------------------------------------------------------------------------------- /tcm/best-practice/upgrade-mesh/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/best-practice/upgrade-mesh/2.png -------------------------------------------------------------------------------- /tcm/best-practice/upgrade-mesh/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/best-practice/upgrade-mesh/3.png -------------------------------------------------------------------------------- /tcm/best-practice/upgrade-mesh/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/best-practice/upgrade-mesh/4.png -------------------------------------------------------------------------------- /tcm/best-practice/upgrade-mesh/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/best-practice/upgrade-mesh/5.png -------------------------------------------------------------------------------- /tcm/best-practice/upgrade-mesh/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/best-practice/upgrade-mesh/6.png -------------------------------------------------------------------------------- /tcm/best-practice/upgrade-mesh/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/best-practice/upgrade-mesh/7.png -------------------------------------------------------------------------------- /tcm/best-practice/upgrade-mesh/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/best-practice/upgrade-mesh/8.png -------------------------------------------------------------------------------- /tcm/best-practice/upgrade-mesh/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 升级网格 3 | type: book 4 | date: "2021-04-26" 5 | weight: 10 6 | --- 7 | 8 | ## 概述 9 | 10 | TCM 升级采用灰度升级策略,首先会进行控制面升级,升级期间新旧控制面共存,直到数据面也完全升级完成才下掉旧的控制面,需要注意的是,数据面升级暂时需要手动重建带 sidecar 的 Pod,触发重新自动注入新版本 sidecar 来实现,本文介绍升级网格的步骤、注意事项与最佳实践。 11 | 12 | ## 找到升级入口 13 | 14 | 在网格基本信息页找到升级入口,点击【前往查看】: 15 | 16 | ![](1.png) 17 | 18 | 点击【升级】: 19 | 20 | ![](2.png) 21 | 22 | ## 升级控制面 23 | 24 | 勾选后点【确定】: 25 | 26 | ![](3.png) 27 | 28 | 开始升级控制面: 29 | 30 | ![](4.png) 31 | 32 | 新控制面一般很快创建完成,点【确定】即可: 33 | 34 | ![](5.png) 35 | 36 | ## 升级数据面 37 | 38 | 接下来勾选要自动注入新数据面的命名空间,可以先尝试勾选一个命名空间,勾选之后该命名空间新建的 Pod 都会自动注入新的数据面 (sidecar),然后就可以将该命名空间中之前已注入了旧数据面的 Pod (通常是全部) 进行重建,如此重复,直到所有命名空间都注入了新的数据面,就可以点【确定】了。 39 | 40 | 当集群内还存在旧版 Sidecar 的话,继续点击【确定】不会成功: 41 | 42 | ![](6.png) 43 | 44 | 重建可以在 TKE 控制台点击 【重新部署】来触发滚动更新: 45 | 46 | ![](7.png) 47 | 48 | 或者使用 kubectl 操作: 49 | 50 | ```bash 51 | kubectl -n test rollout restart deployment/details-v1 52 | ``` 53 | 54 | 如果工作负载太多,嫌挨个重启麻烦,评估批量重启对业务无风险后可以用脚本来实现批量重启(谨慎操作),下面给出脚本示例: 55 | 56 | * 滚动更新某个 namespace 下所有的 deployment: 57 | 58 | ```bash 59 | kubectl -n test get deployments | grep -v NAME | awk '{print $1}' | xargs -I {} kubectl -n test rollout restart deployment/{} 60 | ``` 61 | 62 | > 替换 test 为需要批量升级数据面的 namespace 名称 63 | 64 | * 滚动更新所有开启了自动注入的 namespace 的所有 deployment (风险高,建议只在测试环境中使用): 65 | 66 | ```bash 67 | kubectl get namespace -l istio.io/rev | grep -v NAME | awk '{print $1}' | xargs -I {} bash -c "kubectl -n {} get deployment 2>/dev/null | sed '1d' | awk '{cmd=\"kubectl -n {} rollout restart deployment/\"\$1; system(cmd)}'" 68 | ``` 69 | 70 | ## 升级完成 71 | 72 | 确认所有数据面升级完了之后再去点击前面的【确定】按钮,完成网格的升级: 73 | 74 | ![](8.png) 75 | 76 | ## FAQ 77 | 78 | ### 升级期间是否影响新旧数据面之间的通信 ? 79 | 80 | 不会。升级期间,新旧数据面之间可以正常通信,因为下发的规则都是一样的,连接的新旧控制面使用的证书也是相同的。 81 | 82 | ### 升级过程发现一些问题,能否回滚 ? 83 | 84 | 如果没有完成全部升级,是可以回滚的。取消勾选指定的 namespace,该 namespace 中 Pod 重建后会自动注入旧的数据面,即回滚。如果要全部回滚,就全部取消勾选然后重建注入了新数据面的 Pod 即可。如果全部取消勾选,且点击了 【确定】,就表示下掉新控制面,彻底回滚到旧版本。 -------------------------------------------------------------------------------- /tcm/faq/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 常见问题 3 | type: book # Do not modify. 4 | icon: question-circle 5 | icon_pack: fas 6 | --- 7 | 8 | 以下是 TCM 常见问题。 -------------------------------------------------------------------------------- /tcm/faq/cannot-inject-sidecar/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/faq/cannot-inject-sidecar/1.png -------------------------------------------------------------------------------- /tcm/faq/cannot-inject-sidecar/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "没有自动注入 sidecar" 3 | type: book 4 | date: "2021-04-08" 5 | weight: 40 6 | --- 7 | 8 | ## 问题描述 9 | 10 | 通过 `kubectl label namespace xxx istio-injection=enalbed` 为某个 namespace 开启 sidecar 自动注入,发现不生效。 11 | 12 | ## TCM 自动注入的 label 13 | 14 | TCM 1.6 及其以上的版本并不是用 `istio-inejction=enabled` 这个 label 来开启 namespace 的自动注入,而是用类似 `istio.io/rev=1-8-1` 这样的 label,根据不同的版本不一样: 15 | 16 | * 1.6 是 `istio.io/rev=1-6-9` 17 | * 1.8 是 `istio.io/rev=1-8-1` 18 | 19 | 所以如果用社区常见的 `istio-injection=enalbed` 这样的 label,在 TCM 上是不会生效的,为什么不使用社区常见的 label 呢?请听下文分解。 20 | 21 | ## 网格灰度升级需使用不同 label 22 | 23 | 为了支持服务网格的灰度升级,就需要新旧两个控制面共存一段时间,让旧的数据面逐渐滚动更新升级并连到新的控制面,如果两个控制面都使用同一个 label 来识别是否需要自动注入,在滚动更新的过程中,两个控制面都去注入 sidecar,就会造成冲突。所以 istio 官方给出了 [灰度升级方案](https://istio.io/latest/docs/setup/upgrade/canary/#data-plane) ,正是基于不同控制面使用不同 label 的方法来实现多控制面共存,这也是 TCM 所采用的方案。 24 | 25 | ## TCM 如何开启自动注入 26 | 27 | 在 TCM 控制台,点击 【服务】-【sidecar自动注入】: 28 | 29 | ![](1.png) 30 | 31 | 然后勾选需要开启自动注入的命名空间即可。 32 | 33 | 也可以通过使用 kubectl 给 namespace 打 label 来开启: 34 | 35 | ```bash 36 | kubectl label namespace xxx istio.io/rev=1-8-1 37 | ``` 38 | 39 | > 注意替换 namespace 以及根据版本替换对应的 label 40 | 41 | ## 参考资料 42 | 43 | * [Istio Canary Upgrades](https://istio.io/latest/docs/setup/upgrade/canary/) -------------------------------------------------------------------------------- /tcm/faq/cluster-auth/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/faq/cluster-auth/1.png -------------------------------------------------------------------------------- /tcm/faq/cluster-auth/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/faq/cluster-auth/2.png -------------------------------------------------------------------------------- /tcm/faq/cluster-auth/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/faq/cluster-auth/3.png -------------------------------------------------------------------------------- /tcm/faq/cluster-auth/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/faq/cluster-auth/4.png -------------------------------------------------------------------------------- /tcm/faq/cluster-auth/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/faq/cluster-auth/5.png -------------------------------------------------------------------------------- /tcm/faq/cluster-auth/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 集群权限问题 3 | type: book 4 | date: "2021-04-09" 5 | weight: 20 6 | --- 7 | 8 | ## 背景 9 | 10 | 使用子账号身份登录登录腾讯云控制台操作服务网格,有时可能会遇到权限问题,比如关联集群失败,提示权限问题: 11 | 12 | ![](1.png) 13 | 14 | 或者查看服务时提示权限不足: 15 | 16 | ![](2.png) 17 | 18 | ## 原因 19 | 20 | 核心点在于当前登录的子账号需要有操作网格所关联集群的权限,如果没有或者部分集群没有,就需要给子账号授权一下 (每个集群都需要),下面是授权方法。 21 | 22 | ## 集群授权方法 23 | 24 | 首先登录集群创建者的腾讯云账号 (用该账号为子账号授权),然后进入 [TKE 控制台](https://console.cloud.tencent.com/tke2/cluster) ,再进入 【授权管理】-【RBAC策略生成器】: 25 | 26 | ![](3.png) 27 | 28 | 勾选要授权的子账号: 29 | 30 | ![](4.png) 31 | 32 | 最后授权一下管理员权限即可: 33 | 34 | ![](5.png) -------------------------------------------------------------------------------- /tcm/faq/public-ingressgateway-connect-failed/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/tcm/faq/public-ingressgateway-connect-failed/1.png -------------------------------------------------------------------------------- /tcm/faq/public-ingressgateway-connect-failed/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "公网 ingressgateway 不通" 3 | type: book 4 | date: "2021-03-02" 5 | weight: 10 6 | --- 7 | 8 | ## 问题描述 9 | 10 | 从公网访问 ingressgateway 不通。 11 | 12 | ## 常见原因 13 | 14 | ### 安全组没放通 NodePort 15 | 16 | TCM 安装的 ingressgateway ,暴露方式默认使用 CLB 绑定节点 NodePort,所以流量链路是: client --> CLB --> NodePort --> ingressgateway。 17 | 18 | 关键链路在于 CLB --> NodePort,CLB 转发的数据包不会做 SNAT,所以报文到达节点时源 IP 就是 client 的公网 IP,如果节点安全组入站规则没有放通 client --> NodePort 链路的话,是访问不通的。 19 | 20 | **解决方案1:** 节点安全组入站规则对公网访问 NodePort 区间端口(30000-32768): 21 | 22 | ![](1.png) 23 | 24 | **解决方案2:** 若担心直接放开整个 NodePort 区间所有端口有安全风险,可以只暴露 ingressgateway service 所用到的 Nodeport。 25 | 26 | **解决方案3:** 若只允许固定 IP 段的 client 访问 ingressgateway,可以只对这个 IP 段放开整个 NodePort 区间所有端口。 27 | 28 | **解决方案4:** 为 ingressgateway 启用 CLB 直通 Pod,这样流量就不经过 NodePort,所以就没有此安全组问题。启用 CLB 直通 Pod 需要集群网络支持 VPC-CNI,详细请参考 [如何启用 CLB 直通 Pod](https://imroc.cc/k8s/tke/faq/loadblancer-to-pod-directly/) 。 29 | -------------------------------------------------------------------------------- /trick/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 实用技巧 3 | type: book # Do not modify. 4 | --- 5 | 6 | 记录使用 istio 时的实用技巧 -------------------------------------------------------------------------------- /trick/check/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 常规检查 3 | type: book 4 | date: "2021-02-03" 5 | weight: 3 6 | draft: true 7 | --- 8 | 9 | ## 检查 port name 是否符合规则 10 | 11 | istio 需要根据 svc 的 port name 来检查后端服务用的哪种协议,如果定义不正确,走到了不符合预期的 filter,就会造成一些异常现象。 12 | 13 | -------------------------------------------------------------------------------- /trick/customize-proxy-loglevel/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "自定义 proxy 日志级别" 3 | type: book 4 | date: "2021-05-11" 5 | weight: 4 6 | --- 7 | 8 | ## 概述 9 | 10 | 本文介绍在 istio 中如何自定义数据面 (proxy) 的日志级别,方便我们排查问题时进行调试。 11 | 12 | ## 动态调整 13 | 14 | 调低 proxy 日志级别进行 debug 有助于排查问题,但输出内容较多且耗资源,不建议在生产环境一直开启低级别的日志,istio 默认使用 `warning` 级别。 15 | 16 | 我们可以使用 istioctl 动态调整 proxy 日志级别: 17 | 18 | ```bash 19 | istioctl -n istio-test proxy-config log productpage-v1-7668cb67cc-86q8l --level debug 20 | ``` 21 | 22 | 还可以更细粒度控制: 23 | 24 | ```bash 25 | istioctl -n istio-test proxy-config log productpage-v1-7668cb67cc-86q8l --level grpc:trace,config:debug 26 | ``` 27 | 28 | > 更多 level 可选项参考: `istioctl proxy-config log --help` 29 | 30 | 如果没有 istioctl,也可以直接使用 kubectl 进入 istio-proxy 调用 envoy 接口来动态调整: 31 | 32 | ```bash 33 | kubectl exec -n istio-test productpage-v1-7668cb67cc-86q8l -c istio-proxy -- curl -XPOST -s -o /dev/null http://localhost:15000/logging?level=debug 34 | ``` 35 | 36 | ## 使用 annotation 指定 37 | 38 | 如果不用动态调整,也可以在部署时为 Pod 配置 annotation 来指定 proxy 日志级别: 39 | 40 | ```yaml 41 | template: 42 | metadata: 43 | annotations: 44 | "sidecar.istio.io/logLevel": debug # 可选: trace, debug, info, warning, error, critical, off 45 | ``` 46 | 47 | ## 全局配置 48 | 49 | 如果是测试集群,你也可以全局配置 proxy 日志级别: 50 | 51 | ```bash 52 | kubectl -n istio-system edit configmap istio-sidecar-injector 53 | ``` 54 | 55 | 修改 `values` 里面的 `global.proxy.logLevel` 字段即可。 56 | 57 | 如果使用 istioctl 安装 istio,也可以使用类似以下命令配置全局 proxy 日志级别: 58 | 59 | ```bash 60 | istioctl install --set profile=demo --set values.global.proxy.logLevel=debug 61 | ``` 62 | 63 | ## 配置 envoy componentLogLevel 64 | 65 | 如何细粒度的调整 envoy 自身的内部日志级别呢?可以给 Pod 指定 annotation 来配置: 66 | 67 | ```yaml 68 | template: 69 | metadata: 70 | annotations: 71 | "sidecar.istio.io/componentLogLevel": "ext_authz:trace,filter:debug" 72 | ``` 73 | 74 | * [Envoy component logging 说明](https://www.envoyproxy.io/docs/envoy/latest/operations/cli#cmdoption-component-log-level) 75 | * 该配置最终会作为 envoy 的 `--component-log-level` 启动参数。 -------------------------------------------------------------------------------- /trick/debug/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "调试技巧" 3 | type: book 4 | date: "2021-05-11" 5 | weight: 3 6 | --- 7 | 8 | ## 测试与 istiod 的连通性 9 | 10 | 测试数据面是否能连上 xDS 端口: 11 | 12 | ```bash 13 | nc -vz istiod-1-8-1.istio-system 15012 14 | ``` 15 | 16 | 测试 istiod 监控接口: 17 | 18 | ```bash 19 | $ kubectl -n debug exec debug-6fd7477c9d-brqmq -c istio-proxy -- curl -sS istiod-1-8-1.istio-system:15014/debug/endpointz 20 | [ 21 | 22 | {"svc": "cert-manager-webhook-dnspod.cert-manager.svc.cluster.local:https", "ep": [ 23 | { 24 | "service": { 25 | "Attributes": { 26 | "ServiceRegistry": "Kubernetes", 27 | "Name": "cert-manager-webhook-dnspod", 28 | "Namespace": "cert-manager", 29 | "Labels": { 30 | ``` 31 | > 没报错,正常返回 json 说明数据面能正常连接控制面 32 | 33 | 34 | ## 配置 accesslog 35 | 36 | 配置方法参考 [这里](https://imroc.cc/istio/usage/accesslogs/) 。 37 | 38 | ## 调整 proxy 日志级别 39 | 40 | 配置方法参考 [这里](https://imroc.cc/istio/trick/customize-proxy-loglevel/) 。 41 | 42 | ## 获取 metrics 43 | 44 | ```bash 45 | kubectl -n test exec -c istio-proxy htmall-6657db8f8f-l74qm -- curl -sS localhost:15090/stats/prometheus 46 | ``` -------------------------------------------------------------------------------- /trick/header-authorization/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 实现基于 Header 的授权 3 | type: book 4 | date: "2021-05-07" 5 | weight: 30 6 | --- 7 | 8 | ## 背景 9 | 10 | 部分业务场景在 http header 或 grpc metadata 中会有用户信息,想在 mesh 这一层来基于用户信息来对请求进行授权,如果不满足条件就让接口不返回相应的数据。 11 | 12 | ## 解决方案 13 | 14 | Istio 的 AuthorizationPolicy 不支持基于 Header 的授权,但可以利用 VirtualService 来实现,匹配 http header (包括 grpc metadata),然后再加一个默认路由,使用的固定故障注入返回 401,示例: 15 | 16 | ```yaml 17 | apiVersion: networking.istio.io/v1beta1 18 | kind: VirtualService 19 | metadata: 20 | name: helloworld-server 21 | spec: 22 | hosts: 23 | - helloworld-server 24 | http: 25 | - name: whitelist 26 | match: 27 | - headers: 28 | end-user: 29 | regex: "roc" 30 | route: 31 | - destination: 32 | host: helloworld-server 33 | port: 34 | number: 9000 35 | - name: default 36 | route: 37 | - destination: 38 | host: helloworld-server 39 | port: 40 | number: 9000 41 | fault: 42 | abort: 43 | percentage: 44 | value: 100 45 | httpStatus: 401 46 | ``` -------------------------------------------------------------------------------- /trick/hide-server-header/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 隐藏自动添加的 server header 3 | type: book 4 | date: "2021-03-10" 5 | weight: 5 6 | --- 7 | 8 | ## 背景 9 | 10 | 出于安全考虑,希望隐藏 istio 自动添加的 `server: istio-envoy` 这样的 header。 11 | 12 | ## 解决方案 13 | 14 | 可以配置 envoyfilter ,让 envoy 返回响应时不自动添加 server 的 header,将HttpConnectionManager 的 server_header_transformation 设为 PASS_THROUGH(后端没返回该header时envoy也不会自动添加): 15 | 16 | ```yaml 17 | apiVersion: networking.istio.io/v1alpha3 18 | kind: EnvoyFilter 19 | metadata: 20 | name: hide-headers 21 | namespace: istio-system 22 | spec: 23 | configPatches: 24 | - applyTo: NETWORK_FILTER 25 | match: 26 | context: ANY 27 | listener: 28 | filterChain: 29 | filter: 30 | name: "envoy.http_connection_manager" 31 | patch: 32 | operation: MERGE 33 | value: 34 | typed_config: 35 | "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager" 36 | server_header_transformation: PASS_THROUGH 37 | ``` 38 | 39 | 40 | ## 参考资料 41 | 42 | * [server_header_transformation 字段各个值的解释](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto.html#envoy-v3-api-enum-extensions-filters-network-http-connection-manager-v3-httpconnectionmanager-serverheadertransformation) -------------------------------------------------------------------------------- /trick/multi-version-test-service-with-prism/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 利用 Prism 构造多版本测试服务 3 | type: book 4 | date: "2021-05-07" 5 | weight: 100 6 | --- 7 | 8 | ## 概述 9 | 10 | [Prism](https://github.com/stoplightio/prism) 是一个支持 http mock 的开源工具,可以解析 openapi 配置,根据配置进行相应的响应,我们可以利用它来实现部署多版本服务,用于测试 istio 多版本服务相关的功能。本文给出一个简单的示例。 11 | 12 | ## 准备 OpenAPI 配置 13 | 14 | 我们将 OpenAPI 配置文件存到 ConfigMap 中,用于后续挂载到 prism 作为配置文件 (`prism-conf.yaml`): 15 | 16 | ```yaml 17 | apiVersion: v1 18 | kind: ConfigMap 19 | metadata: 20 | name: prism-conf 21 | data: 22 | mock-v1.yaml: | 23 | openapi: 3.0.3 24 | info: 25 | title: MockServer v2 26 | description: MockServer v2 27 | version: 1.0.0 28 | paths: 29 | '/': 30 | get: 31 | responses: 32 | '200': 33 | content: 34 | 'text/plain': 35 | schema: 36 | type: string 37 | example: v1 38 | mock-v2.yaml: | 39 | openapi: 3.0.3 40 | info: 41 | title: MockServer v2 42 | description: MockServer v2 43 | version: 1.0.0 44 | paths: 45 | '/': 46 | get: 47 | responses: 48 | '200': 49 | content: 50 | 'text/plain': 51 | schema: 52 | type: string 53 | example: v2 54 | ``` 55 | 56 | 这里的配置很简单,两个 OpenAPI 配置文件,`GET` 方式请求 `/` 路径分别响应 `v1` 和 `v2` 的字符串,以便从响应中就能区分出请求转发到了哪个版本的服务。 57 | 58 | 如果想用编辑器或 IDE 的 OpenAPI 插件编辑配置文件来定义更复杂的规则,可以先直接创建原生 OpenAPI 配置文件 (如 `mock-v1.yaml` 和 `mock-v2.yaml`),然后使用类似下面的命令生成 configmap 的 yaml: 59 | 60 | ```bash 61 | kubectl create configmap prism-conf --dry-run=client -o yaml 62 | --from-file=mock-v1.yaml \ 63 | --from-file=mock-v2.yaml | \ 64 | grep -v creationTimestamp > prism-conf.yaml 65 | ``` 66 | 67 | ## 部署多版本服务 68 | 69 | 使用 Deployment 部署两个版本的 prism (注意开启下 sidecar 自动注入),分别挂载不同的 OpenAPI 配置,首先部署第一个版本 (`mockserver-v1.yaml`): 70 | 71 | ```yaml 72 | apiVersion: apps/v1 73 | kind: Deployment 74 | metadata: 75 | name: mockserver-v1 76 | spec: 77 | replicas: 1 78 | selector: 79 | matchLabels: 80 | app: mockserver 81 | version: v1 82 | template: 83 | metadata: 84 | labels: 85 | app: mockserver 86 | version: v1 87 | spec: 88 | containers: 89 | - name: mockserver 90 | image: cr.imroc.cc/library/prism:4 91 | args: 92 | - mock 93 | - -h 94 | - 0.0.0.0 95 | - -p 96 | - "80" 97 | - /etc/prism/mock-v1.yaml 98 | volumeMounts: 99 | - mountPath: /etc/prism 100 | name: config 101 | volumes: 102 | - name: config 103 | configMap: 104 | name: prism-conf 105 | ``` 106 | 107 | 再部署第二个版本 (`mockserver-v2.yaml`): 108 | 109 | ```yaml 110 | apiVersion: apps/v1 111 | kind: Deployment 112 | metadata: 113 | name: mockserver-v2 114 | spec: 115 | replicas: 1 116 | selector: 117 | matchLabels: 118 | app: mockserver 119 | version: v2 120 | template: 121 | metadata: 122 | labels: 123 | app: mockserver 124 | version: v2 125 | spec: 126 | containers: 127 | - name: mockserver 128 | image: cr.imroc.cc/library/prism:4 129 | args: 130 | - mock 131 | - -h 132 | - 0.0.0.0 133 | - -p 134 | - "80" 135 | - /etc/prism/mock-v2.yaml 136 | volumeMounts: 137 | - mountPath: /etc/prism 138 | name: config 139 | volumes: 140 | - name: config 141 | configMap: 142 | name: prism-conf 143 | ``` 144 | 145 | 最后创建 Service (`mockserver-svc.yaml`): 146 | 147 | ```yaml 148 | apiVersion: v1 149 | kind: Service 150 | metadata: 151 | name: mockserver 152 | labels: 153 | app: mockserver 154 | spec: 155 | type: ClusterIP 156 | ports: 157 | - port: 80 158 | protocol: TCP 159 | name: http 160 | selector: 161 | app: mockserver 162 | ``` 163 | 164 | ## 测试访问 165 | 166 | 没有定义任何其它规则,测试访问会随机负载均衡到 v1 和 v2: 167 | 168 | ```bash 169 | $ for i in {1..10};do curl mockserver && echo ""; done 170 | v2 171 | v1 172 | v2 173 | v1 174 | v2 175 | v1 176 | v2 177 | v1 178 | v2 179 | v1 180 | ``` 181 | 182 | ## 使用 DestinationRule 定义多版本服务 183 | 184 | 在 DestinationRule 定义使用 pod label 来区分 v1 和 v2 版本的服务 (`mockserver-dr.yaml`): 185 | 186 | ```yaml 187 | apiVersion: networking.istio.io/v1beta1 188 | kind: DestinationRule 189 | metadata: 190 | name: mockserver 191 | spec: 192 | host: mockserver 193 | subsets: 194 | - labels: 195 | app: mockserver 196 | version: v2 197 | name: v1 198 | - labels: 199 | app: mockserver 200 | version: v2 201 | name: v2 202 | ``` 203 | 204 | ## 使用 VirtualService 定义多版本路由规则 205 | 206 | 这里定义一个简单的规则,v1 版本服务接收 80% 流量,v2 版本接收 20% (`mockserver-vs.yaml`): 207 | 208 | ```yaml 209 | apiVersion: networking.istio.io/v1beta1 210 | kind: VirtualService 211 | metadata: 212 | name: mockserver 213 | spec: 214 | hosts: 215 | - mockserver 216 | http: 217 | - route: 218 | - destination: 219 | host: mockserver 220 | port: 221 | number: 80 222 | subset: v1 223 | weight: 80 224 | - destination: 225 | host: mockserver 226 | port: 227 | number: 80 228 | subset: v2 229 | weight: 20 230 | ``` 231 | 232 | ## 测试验证多版本流量转发规则 233 | 234 | 上面定义了 DestinationRule 和 VirtualService 之后,会根据定义的规则进行转发: 235 | 236 | ```bash 237 | $ for i in {1..10};do curl mockserver && echo ""; done 238 | v1 239 | v2 240 | v1 241 | v1 242 | v2 243 | v1 244 | v1 245 | v1 246 | v1 247 | v1 248 | ``` -------------------------------------------------------------------------------- /trick/partially-enable-accesslog/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "局部启用 accesslog" 3 | type: book 4 | date: "2021-05-12" 5 | weight: 4 6 | --- 7 | 8 | ## 背景 9 | 10 | 在生产环境中,有时我们不想全局启用 accesslog,只想为部分 namespace 或 workload 启用 accesslog,而 istio 对 accesslog 的配置是全局的,如何只为部分数据面启用 accesslog 呢?下面介绍具体操作方法。 11 | 12 | ## 为部分 namespace 启用 accesslog 13 | 14 | 可以使用以下 Envoyfilter 来实现: 15 | 16 | ```yaml 17 | apiVersion: networking.istio.io/v1alpha3 18 | kind: EnvoyFilter 19 | metadata: 20 | name: enable-accesslog 21 | namespace: test # 只为 test 命名空间开启 accesslog 22 | spec: 23 | configPatches: 24 | - applyTo: NETWORK_FILTER 25 | match: 26 | context: ANY 27 | listener: 28 | filterChain: 29 | filter: 30 | name: envoy.http_connection_manager 31 | patch: 32 | operation: MERGE 33 | value: 34 | typed_config: 35 | "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager" 36 | access_log: 37 | - name: envoy.access_loggers.file 38 | typed_config: 39 | "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog 40 | path: "/dev/stdout" 41 | log_format: 42 | json_format: 43 | authority: "%REQ(:AUTHORITY)%" 44 | bytes_received: "%BYTES_RECEIVED%" 45 | bytes_sent: "%BYTES_SENT%" 46 | downstream_local_address: "%DOWNSTREAM_LOCAL_ADDRESS%" 47 | downstream_remote_address: "%DOWNSTREAM_REMOTE_ADDRESS%" 48 | duration: "%DURATION%" 49 | method: "%REQ(:METHOD)%" 50 | path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" 51 | protocol: "%PROTOCOL%" 52 | request_id: "%REQ(X-REQUEST-ID)%" 53 | requested_server_name: "%REQUESTED_SERVER_NAME%" 54 | response_code: "%RESPONSE_CODE%" 55 | response_flags: "%RESPONSE_FLAGS%" 56 | route_name: "%ROUTE_NAME%" 57 | start_time: "%START_TIME%" 58 | upstream_cluster: "%UPSTREAM_CLUSTER%" 59 | upstream_host: "%UPSTREAM_HOST%" 60 | upstream_local_address: "%UPSTREAM_LOCAL_ADDRESS%" 61 | upstream_service_time: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%" 62 | upstream_transport_failure_reason: "%UPSTREAM_TRANSPORT_FAILURE_REASON%" 63 | user_agent: "%REQ(USER-AGENT)%" 64 | x_forwarded_for: "%REQ(X-FORWARDED-FOR)%" 65 | ``` 66 | 67 | * 已测试版本 istio 1.8。 68 | * 若不指定 `log_format` 将会使用 [Envoy Default Format String](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#default-format-string) 。 69 | 70 | 71 | ## 为部分 workload 启用 accesslog 72 | 73 | 如果想要精确到只为指定的 workload 启用 accesslog,可以在 EnvoyFilter 上加一下 `workloadSelector`: 74 | 75 | ```yaml 76 | spec: 77 | workloadSelector: 78 | labels: 79 | app: "nginx" 80 | ``` 81 | 82 | ## 低版本 istio 83 | 84 | 低版本 istio 使用的 envoy 不支持 v3 api,可以使用 v2: 85 | 86 | ```yaml 87 | apiVersion: networking.istio.io/v1alpha3 88 | kind: EnvoyFilter 89 | metadata: 90 | name: enable-accesslog 91 | namespace: test # 只为 test 命名空间开启 accesslog 92 | spec: 93 | configPatches: 94 | - applyTo: NETWORK_FILTER 95 | match: 96 | context: ANY 97 | listener: 98 | filterChain: 99 | filter: 100 | name: envoy.http_connection_manager 101 | patch: 102 | operation: MERGE 103 | value: 104 | typed_config: 105 | "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager" 106 | access_log: 107 | - name: envoy.file_access_log 108 | config: 109 | path: /dev/stdout 110 | ``` -------------------------------------------------------------------------------- /trick/restrict-sidecar-namespace/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "限制 namespace 以减少 sidecar 资源占用" 3 | type: book 4 | date: "2021-05-11" 5 | --- 6 | 7 | ## 背景 8 | 9 | istio 默认会下发 mesh 内集群服务所有可能需要的信息,以便让 sidecar 能够与任意 workload 通信。当集群规模较大,服务数量多,namespace 多,可能就会导致 sidecar 占用资源很高 (比如十倍于业务容器)。 10 | 11 | 如果只有部分 namespace 使用了 istio (sidecar 自动注入),而网格中的服务与其它没有注入 sidecar 的 namespace 的服务没有多大关系,可以配置下 istio 的 `Sidecar` 资源,限制一下 namespace,避免 sidecar 加载大量无用 outbound 的规则。 12 | 13 | ## 配置方法 14 | 15 | ```yaml 16 | apiVersion: networking.istio.io/v1beta1 17 | kind: Sidecar 18 | metadata: 19 | name: default 20 | namespace: istio-system 21 | spec: 22 | egress: 23 | - hosts: 24 | - "prod/*" 25 | - "test/*" 26 | ``` 27 | 28 | * 定义在 istio-system 命名空间下表示 Sidecar 配置针对所有 namespace 生效。 29 | * 在 egress 的 hosts 配置中加入开启了 sidecar 自动注入的 namespace,表示只下发这些跟这些 namespace 相关的 outbound 规则。 30 | 31 | ## 参考资料 32 | 33 | * [Istio Sidecar 资源官方参考文档](https://istio.io/latest/docs/reference/config/networking/sidecar/) -------------------------------------------------------------------------------- /trick/set-max-body-size/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 设置 max_body_size 3 | type: book 4 | date: "2021-03-15" 5 | weight: 10 6 | --- 7 | 8 | ## 背景 9 | 10 | nginx 可以设置 `client_max_body_size`,那么在 istio 场景下如何调整客户端的最大请求大小呢? 11 | 12 | ## 解决方案 13 | 14 | 可以配置 envoyfilter: 15 | 16 | ```yaml 17 | apiVersion: networking.istio.io/v1alpha3 18 | kind: EnvoyFilter 19 | metadata: 20 | name: limit-request-size 21 | namespace: istio-system 22 | spec: 23 | workloadSelector: 24 | labels: 25 | istio: ingressgateway 26 | configPatches: 27 | - applyTo: HTTP_FILTER 28 | match: 29 | context: GATEWAY 30 | listener: 31 | filterChain: 32 | filter: 33 | name: envoy.http_connection_manager 34 | patch: 35 | operation: INSERT_BEFORE 36 | ``` 37 | > 已验证版本: istio 1.8 38 | 39 | * 更改 `workloadSelector` 以选中需要设置的 gateway -------------------------------------------------------------------------------- /trick/troubleshooting-networking/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/trick/troubleshooting-networking/1.png -------------------------------------------------------------------------------- /trick/troubleshooting-networking/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 网络排障 3 | type: book 4 | date: "2021-02-03" 5 | weight: 2 6 | draft: true 7 | --- 8 | 9 | ## 网络性能问题 10 | 现象: 11 | * P99 延时高,部分请求响应较慢 (比如P95为10~20ms,P99中就有1~2s的请求),去掉 sidecar 又比较快 12 | 13 | 思路: 14 | 15 | * 看是否有丢包或者队列满之类的问题导致有重传(重传默认会等1s) 16 | 17 | client 端看是否有重传: 18 | 19 | ![](1.png) 20 | 21 | ## 检查是否有 conntrack 插入失败 22 | 23 | watch: 24 | ``` bash 25 | watch 'conntrack -S | awk -F "insert_failed=" "{print \$2}" | awk -F " " "{print \$1}" | awk "{sum += \$1} END {print sum}"' 26 | ``` 27 | 执行一次: 28 | ``` bash 29 | conntrack -S | awk -F 'insert_failed=' '{print $2}' | awk -F ' ' '{print $1}' | awk '{sum += $1} END {print sum}' 30 | ``` 31 | -------------------------------------------------------------------------------- /troubleshooting/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 问题排查 3 | type: book # Do not modify. 4 | --- 5 | 6 | 先占位,后续陆续补充笔记 -------------------------------------------------------------------------------- /troubleshooting/apollo-on-istio/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/troubleshooting/apollo-on-istio/1.jpg -------------------------------------------------------------------------------- /troubleshooting/apollo-on-istio/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "使用 apollo 的 java 应用启动报 404" 3 | type: book 4 | date: "2021-05-14" 5 | --- 6 | 7 | ## 问题描述 8 | 9 | 项目中使用了 apollo 插件,在非 istio 环境正常运行,但部署到 istio 后启动报类似如下错误: 10 | 11 | ```log 12 | Sync config from upstream repository class com.ctrip.framework.apollo.internals.RemoteConfigRepository failed, reason: Load Apollo Config failed - xxx, url: http://10.5.16.49:8080/configs/agent-center/see-test-02/test.common?ip=10.5.16.46 [Cause: [status code: 404] Could not find config for xxx please check whether the configs are released in Apollo!] 13 | ``` 14 | 15 | 表示请求 apollo 的 config service 返回 404 了。 16 | 17 | ## 排查 accesslog 18 | 19 | 查看 envoy 的 accesslog: 20 | 21 | ![](1.jpg) 22 | 23 | * `response_flags` 为 `NR`,表示找不到路由 (参考 envoy 官网解释: No route configured for a given request in addition to 404 response code, or no matching filter chain for a downstream connection)。 24 | * 请求使用的 `Host` 直接用的 `PodIP:Port`。 25 | * 该 `PodIP:Port` 属于 apollo 服务的 headless service 一个 endpoint (apollo 通过 statefulset 部署)。 26 | 27 | ## headless service 的 xDS 规则 28 | 29 | 进一步分析之前,我们先了解下 istio 对 headless service 的 xDS 支持: 30 | * 下发的 `LDS` 规则中会监听 headless service 所有可能的 `PortIP:Port`,请求 headless service 的 Pod 时,这里先匹配上。 31 | * 然后走到 `RDS` 规则进行路由,路由时会匹配 hosts,hosts 列表中列举了所有可能的 service 地址 (没有 Pod IP),如果都匹配不到就会返回 404。 32 | 33 | ## 问题原因 34 | 35 | 由于请求 apollo 的 config service 时,`Host` 没有使用 service 地址,而是直接使用了 `PodIP:Port`,所以 `RDS` 匹配时找不到相应 hosts,就会返回 404。 36 | 37 | ## 为什么没有使用 service 地址 ? 38 | 39 | 为了实现高可用,apollo 的 java 客户端默认是从 meta server 中获取 config service 的 ip 地址 (服务发现),然后直接对该地址发起请求 (不使用 k8s service),从而导致请求 config service 时没有将其 k8s service 地址作为 `Host`,最后 hosts 匹配不到返回 404。 40 | 41 | ## 如果解决 ? 42 | 43 | 在 istio 场景下 (kubernetes 之上),请求 config service 就不需要不走 apollo meta server 获取 config service 的 ip 来实现高可用,直接用 kubernetes 的 service 做服务发现就行。幸运的是,apollo 也支持跳过 meta server 服务发现,这样访问 config service 时就可以直接请求 k8s service 了,也就可以解决此问题。 44 | 45 | 具体配置方法参考 [Apollo Java 客户端使用指南](https://github.com/ctripcorp/apollo/wiki/Java%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97#1222-%E8%B7%B3%E8%BF%87apollo-meta-server%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0) 。 -------------------------------------------------------------------------------- /troubleshooting/cannot-connect-pod-without-sidecar/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 无法访问不带 sidecar 的 Pod 3 | type: book 4 | date: "2021-02-24" 5 | --- 6 | 7 | ## 问题现象 8 | 9 | 不能从一个带 sidecar proxy 的 pod 访问到 Redis 服务。 10 | 11 | ## 问题分析 12 | 13 | Redis是一个 Headless 服务,而 istio 1.6 之前的版本对 Headless 服务的处理有问题,会缺省启用 mTLS。 14 | 15 | ## 解决方案 16 | 17 | 在 1.6 之前可以采用DR规则禁用该服务的 mTLS 来规避: 18 | 19 | ```yaml 20 | apiVersion: networking.istio.io/v1beta1 21 | kind: DestinationRule 22 | metadata: 23 | name: mysql-service 24 | namespace: test 25 | spec: 26 | host: redis-service 27 | trafficPolicy: 28 | loadBalancer: 29 | simple: ROUND_ROBIN 30 | tls: 31 | mode: DISABLE 32 | ``` 33 | 34 | 该问题在 isitio 1.6 中已经修复: https://github.com/istio/istio/pull/24319 35 | -------------------------------------------------------------------------------- /troubleshooting/grpc-not-loadbalancing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "GRPC 服务负载不均" 3 | type: book 4 | date: "2021-02-02" 5 | weight: 2 6 | --- 7 | 8 | 9 | ## 现象 10 | 11 | grpc 调用,同一个 client 的请求始终只打到同一个 server 的 pod,造成负载不均。 12 | 13 | ## 分析 14 | 15 | grpc 是基于 http2 的长连接,多次请求复用同一个连接。如果不用 istio,只用普通的 k8s service,是不会感知 grpc 协议的,只当成 tcp 来转发,在连接层面做负载均衡,不会在请求层面做负载均衡。但在 istio 中,默认会对 grpc 的请求进行请求级别的负载均衡,如果发现负载不均,通常是没有正确配置。 16 | 要让 grpc 在请求级别进行负载均衡,核心就是让 istio 正确识别是 grpc 协议,不要配置成 tcp,用 tcp 的话就只能在连接级别进行负载均衡了,请求级别可能就会负载不均。 17 | 18 | ## 解决方法 19 | 20 | 1. 如果要对外暴露,gateway 里 protocal 配置 GRPC 不用 TCP,示例: 21 | 22 | ```yaml 23 | apiVersion: networking.istio.io/v1beta1 24 | kind: Gateway 25 | metadata: 26 | name: grpc-gw 27 | namespace: demo 28 | spec: 29 | selector: 30 | app: istio-ingressgateway 31 | istio: ingressgateway 32 | servers: 33 | - hosts: 34 | - '*' 35 | port: 36 | name: grpc-demo-server 37 | number: 9000 38 | protocol: GRPC # 这里使用 GRPC 不用 TCP 39 | ``` 40 | 41 | 2. 如果定义了 vs,需要使用 http 匹配而不用 tcp,因为 grpc 在 istio 中匹配也是用的 http 字段,示例: 42 | 43 | ```yaml 44 | apiVersion: networking.istio.io/v1beta1 45 | kind: VirtualService 46 | metadata: 47 | name: grpc-svc 48 | namespace: demo 49 | spec: 50 | gateways: 51 | - demo/grpc-gw 52 | hosts: 53 | - '*' 54 | http: # 这里使用 http 不用 tcp 55 | - match: 56 | - port: 9000 57 | route: 58 | - destination: 59 | host: grpc.demo.svc.cluster.local 60 | port: 61 | number: 9000 62 | weight: 100 63 | ``` 64 | 65 | 3. 部署服务的 service 的 port name 需要使用 "grpc-" 开头定义,让 istio 能够正确识别,示例: 66 | 67 | ```yaml 68 | apiVersion: v1 69 | kind: Service 70 | metadata: 71 | name: grpc 72 | namespace: demo 73 | spec: 74 | ports: 75 | - name: grpc-9000 # 以 grpc- 开头 76 | port: 9000 77 | protocol: TCP 78 | targetPort: 9000 79 | selector: 80 | app: grpc 81 | type: ClusterIP 82 | ``` 83 | 84 | > 更多协议指定方式请参考 [istio 最佳实践: 为服务显式指定协议](https://imroc.cc/istio/best-practice/specify-protocol/) -------------------------------------------------------------------------------- /troubleshooting/grpc-without-status-code/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "注入 sidecar 后 grpc 请求不响应 status code" 3 | type: book 4 | date: "2021-03-26" 5 | --- 6 | 7 | ## 问题描述 8 | 9 | * 环境信息: 多集群下,子集群中 nginx 为 grpc 服务做了一层反向代理。 10 | * 访问链路:grpc client --> nginx --> grpc server。 11 | * 链路说明: grpc client 对 nginx 发起 grpc 调用,nginx 将请求 proxy_pass 到集群内的 grpc server 的 k8s service。 12 | * 现象: 当 nginx 注入了 istio sidecar 后,grpc client 就收不到 grpc server 响应的 `grpc-status` 的 trailer 了 (即没有 grpc status code)。 13 | 14 | ## 原因 15 | 16 | 测试在 istio 1.6.9 中存在一个 bug,在多集群下,子集群中 envoy sidecar 内部的 http route 中的 domain 中的 vip 采用的是主集群中的 Cluster IP,而不是子集群中该服务对应的 Cluster IP。 如下面 nginx pod 中 envoy proxy 的路由配置: 17 | 18 | ```json 19 | { 20 | "name": "location-svr.default.svc.cluster.local:50222", 21 | "domains": [ 22 | "location-svr.default.svc.cluster.local", 23 | "location-svr.default.svc.cluster.local:50222", 24 | "location-svr", 25 | "location-svr:50222", 26 | "location-svr.default.svc.cluster", 27 | "location-svr.default.svc.cluster:50222", 28 | "location-svr.default.svc", 29 | "location-svr.default.svc:50222", 30 | "location-svr.default", 31 | "location-svr.default:50222", 32 | "172.21.255.24", # 此处的 VIP 是主机群中的 cluster ip,而不是子集群中的 cluster ip 33 | "172.21.255.24:50222" 34 | ], 35 | "routes": [ 36 | ... 37 | ``` 38 | 39 | * grpc client 发起请求时带上的 host 是 nginx 的域名地址,被 nginx proxy_pass 时带上了,但 envoy 是匹配不到这个原始 host 的,就尝试去匹配报文目的 IP (即 cluster ip)。 40 | * 但因为上述 bug,cluster ip 也匹配不到 (istio 1.6.9 子集群中 http route 只有主集群同名 service 的 cluster ip,这里目的 IP 可能只能是子集群自己的 cluster ip),就只有 paasthrough 了。 41 | * 由于 passthrough cluster 并不确认后端的 upstream 支持 http2,因此未设置 `http2_protocol_options` 选项。 42 | * 该 grpc/http2 请求被 enovy 采用 http1.1 发送到 location-svr,导致未能收到 trailer (grpc status code)。 43 | 44 | ## 解决方案 45 | 46 | 有以下三种解决方案: 47 | 1. 经验证 1.8 中该 bug 已经处理,升级到 1.8 可以解决该问题。 48 | 2. nginx proxy_pass 显示指定 proxy_set_header,使用 service 名称作为 Host。 49 | 3. grpc client 请求时显示设置 Host Header 为 grpc server 的 service 名称。 -------------------------------------------------------------------------------- /troubleshooting/istio-token-setup-failed-for-volume-istio-token/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/troubleshooting/istio-token-setup-failed-for-volume-istio-token/1.png -------------------------------------------------------------------------------- /troubleshooting/istio-token-setup-failed-for-volume-istio-token/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Pod 启动卡住: MountVolume.SetUp failed for volume "istio-token"' 3 | type: book 4 | date: "2021-02-02" 5 | weight: 3 6 | --- 7 | 8 | ## 现象 9 | 10 | Istio 相关的 Pod (包括注入了 sidecar 的 Pod) 一直卡在 ContainerCreating,起不来,describe pod 报错 `MountVolume.SetUp failed for volume "istio-token" : failed to fetch token: the server could not find the requested resource`: 11 | 12 | ![](1.png) 13 | 14 | ## 分析 15 | 16 | 根据官方文档([Configure third party service account tokens](https://istio.io/latest/docs/ops/best-practices/security/#configure-third-party-service-account-tokens)) 的描述可以得知: 17 | * istio-proxy 需要使用 K8S 的 ServiceAccount token,而 K8S 支持 `third party` 和 `first party` 两种 token。 18 | * `third party token` 安全性更高,istio 默认使用这种类型。 19 | * 不是所有集群都支持这种 token,取决于 K8S 版本和 apiserver 配置。 20 | 21 | 如果集群不支持 `third party token`,就会导致 ServiceAccount token 不自动创建出来,从而出现上面这种报错。 22 | 23 | ## 什么是 third party token ? 24 | 25 | 其实就是 [ServiceAccountTokenVolumeProjection](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection) 这个特性,在 1.12 beta,1.20 GA。 26 | 27 | 推出该特性是为了增强 ServiceAccount token 的安全性,可以设置有效期(会自动轮转),避免 token 泄露带来的安全风险,还可以控制 token 的受众。 28 | 29 | 该特性在 istio 中用来配合 SDS 以增强安全性,参考 [Istio私钥管理利器SDS浅析](https://developer.aliyun.com/article/742572)。 30 | 31 | 如何判断集群是否启用了该特性呢?可通过一下命令查询: 32 | 33 | ``` bash 34 | kubectl get --raw /api/v1 | jq '.resources[] | select(.name | index("serviceaccounts/token"))' 35 | ``` 36 | 37 | 若返回空,说明不支持;若返回如下 json,说明支持: 38 | 39 | ```json 40 | { 41 | "name": "serviceaccounts/token", 42 | "singularName": "", 43 | "namespaced": true, 44 | "group": "authentication.k8s.io", 45 | "version": "v1", 46 | "kind": "TokenRequest", 47 | "verbs": [ 48 | "create" 49 | ] 50 | } 51 | ``` 52 | 53 | ## 解决方案 54 | 55 | ### 方案一:安装 istio 时不使用 third party token 56 | 57 | 官方称使用 istioctl 安装会自动检测集群是否支持 `third party token`,但据 [issue](https://github.com/istio/istio/issues/21968#issuecomment-607474174) 反馈可能有 bug,还是建议强制指定用 `first party token`,用参数 `--set values.global.jwtPolicy=first-party-jwt` 来显示指定,示例: 58 | 59 | ```bash 60 | istioctl manifest generate --set profile=demo --set values.global.jwtPolicy=first-party-jwtm > istio.yaml 61 | ``` 62 | 63 | ### 方案二:集群启用 ServiceAccountTokenVolumeProjection 64 | 65 | 如何启用 ServiceAccountTokenVolumeProjection 这个特性呢?需要给 apiserver 配置类似如下的参数: 66 | 67 | ```yaml 68 | --service-account-key-file=/etc/kubernetes/pki/sa.key # 这个一般都会配,重要的是下面三个参数 69 | --service-account-issuer=kubernetes.default.svc 70 | --service-account-signing-key-file=/etc/kubernetes/pki/sa.key # 注意替换实际路径 71 | --api-audiences=kubernetes.default.svc 72 | ``` -------------------------------------------------------------------------------- /troubleshooting/traffic-policy-does-not-take-effect/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "trafficPolicy 不生效" 3 | type: book 4 | date: "2021-03-18" 5 | --- 6 | 7 | ## 问题描述 8 | 9 | 为服务配置了 DestinationRule 和 VirtualService,且 VirtualService 绑好了 Gateway,DestinationRule 配置了 trafficPolicy,指定了熔断策略,但使用 ab 压测发现没有触发熔断 (ingressgateway 的 access_log 中 response_flags 没有 "UO")。 10 | 11 | ## 原因分析 12 | 13 | ab 压测时发送的 HTTP/1.0 请求,而 envoy 需要至少 HTTP/1.1,固定返回 426 Upgrade Required,根本不会进行转发,所以也就不会返回 503,response_flags 也不会有。 14 | 15 | ## 解决方案 16 | 17 | 压测工具换用 wrk,默认发送 HTTP/1.1 的请求,可以正常触发熔断。 -------------------------------------------------------------------------------- /troubleshooting/using-istio-reserved-port-causes-pod-start-failed/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/troubleshooting/using-istio-reserved-port-causes-pod-start-failed/1.png -------------------------------------------------------------------------------- /troubleshooting/using-istio-reserved-port-causes-pod-start-failed/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/troubleshooting/using-istio-reserved-port-causes-pod-start-failed/2.jpg -------------------------------------------------------------------------------- /troubleshooting/using-istio-reserved-port-causes-pod-start-failed/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "使用 istio 保留端口导致 pod 启动失败" 3 | type: book 4 | date: "2021-05-19" 5 | --- 6 | 7 | ## 问题现象 8 | 9 | 所有新启动的 Pod 无法 ready,sidecar 报错: 10 | 11 | ```log 12 | warning envoy config gRPC config for type.googleapis.com/envoy.config.listener.v3.Listener rejected: Error adding/updating listener(s) 0.0.0.0_15090: error adding listener: '0.0.0.0_15090' has duplicate address '0.0.0.0:15090' as existing listener 13 | ``` 14 | 15 | 同时 istiod 也报错: 16 | 17 | ```log 18 | ADS:LDS: ACK ERROR sidecar~172.18.0.185~reviews-v1-7d46f9dd-w5k8q.istio-test~istio-test.svc.cluster.local-20847 Internal:Error adding/updating listener(s) 0.0.0.0_15090: error adding listener: '0.0.0.0_15090' has duplicate address '0.0.0.0:15090' as existing listener 19 | ``` 20 | 21 | ## 猜想 22 | 23 | 看报错应该是 sidecar 启动时获取 LDS 规则,istiod 发现 `0.0.0.0:15090` 这个监听重复了,属于异常现象,下发 xDS 规则就会失败,导致 sidecar 一直无法 ready。 24 | 25 | ## 分析 config_dump 26 | 27 | 随便找一个还未重启的正常 Pod,看一下 envoy config_dump: 28 | 29 | ```bash 30 | kubectl exec debug-68b799694-n9q66 -c istio-proxy -- curl localhost:15000/config_dump 31 | ``` 32 | 33 | 分析 json 发现 static 配置中有监听 `0.0.0.0:15090`: 34 | 35 | ![](1.png) 36 | 37 | ## 定位原因 38 | 39 | 猜测是 dynamic 配置中也有 `0.0.0.0:15090` 的监听导致的冲突,而 dynamic 中监听来源通常是 Kubernetes 的服务发现(Service, ServiceEntry),检查一下是否有 Service 监听 15090: 40 | 41 | ```bash 42 | kubectl get service --all-namespaces -o yaml | grep 15090 43 | ``` 44 | 45 | 最终发现确实有 Service 用到了 15090 端口,更改成其它端口即可恢复。 46 | 47 | ## 深入挖掘 48 | 49 | 搜索一下,可以发现 15090 端口是 istio 用于暴露 envoy prometheus 指标的端口,是 envoy 使用的端口之一: 50 | 51 | ![](2.jpg) 52 | 53 | > 参考 [Ports used by Istio](https://istio.io/latest/docs/ops/deployment/requirements/) 。 54 | 55 | 但并不是所有 envoy 使用的端口都被加入到 static 配置中的监听,只有 15090 和 15021 这两个端口在 static 配置中有监听,也验证了 Service 使用 15021 端口也会有相同的问题。 56 | 57 | Service 使用其它 envoy 的端口不会造成 sidecar 不 ready 的问题,但至少要保证业务程序也不能去监听这些端口,因为会跟 envoy 冲突,istio 官网也说明了这一点: `To avoid port conflicts with sidecars, applications should not use any of the ports used by Envoy`。 58 | 59 | ## 使用建议 60 | 61 | 根据上面分析,得出以下使用建议: 62 | 1. Service/ServiceEntry 不能定义 15090 和 15021 端口,不然会导致 Pod 无法启动成功。 63 | 2. 业务进程不能监听 envoy 使用到的所有端口: 15000, 15001, 15006, 15008, 15020, 15021, 15090 。 -------------------------------------------------------------------------------- /usage/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用实践 3 | type: book # Do not modify. 4 | --- 5 | 6 | 先占位,后续陆续补充笔记 -------------------------------------------------------------------------------- /usage/accesslogs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 配置 accesslog 3 | type: book 4 | date: "2021-05-12" 5 | weight: 30 6 | id: "144e712ca6d9c92e0b502fc325a3dc83" 7 | --- 8 | 9 | ## 概述 10 | 11 | 本文介绍如何配置 istio 的 accesslog。 12 | 13 | ## 全局配置方法 14 | 15 | 修改 istio 配置: 16 | 17 | ```bash 18 | kubectl -n istio-system edit configmap istio 19 | ``` 20 | 21 | 编辑 yaml: 22 | 23 | ```yaml 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: istio 28 | namespace: istio-system 29 | data: 30 | mesh: | 31 | accessLogEncoding: JSON 32 | accessLogFile: /dev/stdout 33 | accessLogFormat: "" 34 | ``` 35 | 36 | * `accessLogEncoding`: 表示 accesslog 输出格式,istio 预定义了 `TEXT` 和 `JSON` 两种日志输出格式。默认使用 `TEXT`,通常我们习惯改成 `JSON` 以提升可读性,同时也利于日志采集。 37 | * `accessLogFile`: 表示 accesslog 输出到哪里,通常我们指定到 `/dev/stdout` (标准输出),以便使用 `kubectl logs` 来查看日志,同时也利于日志采集。 38 | * `accessLogFormat`: 如果不想使用 istio 预定义的 `accessLogEncoding`,我们也可以使用这个配置来自定义日志输出格式。完整的格式规则与变量列表参考 [Envoy 官方文档](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage) 。 39 | 40 | 如果使用 istioctl 安装的 istio,也可以用类似以下命令进行配置: 41 | 42 | ```bash 43 | istioctl install --set profile=demo --set meshConfig.accessLogFile="/dev/stdout" --set meshConfig.accessLogEncoding="JSON" 44 | ``` 45 | 46 | ## 局部启用 accesslog 47 | 48 | 在生产环境中,有时我们不想全局启用 accesslog,我们可以利用 EnvoyFilter 来实现只为部分 namespace 或 workload 启用 accesslog,参考 [实用技巧: 局部启用 accesslog](https://imroc.cc/istio/trick/partially-enable-accesslog/) 49 | 50 | ## TEXT 格式 51 | 52 | istio 的 text accesslog 配置格式见 [源码](https://github.com/istio/istio/blob/1.8.3/pilot/pkg/networking/core/v1alpha3/accesslog.go#L38) 。转换成字符串为: 53 | 54 | ```txt 55 | [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% "%UPSTREAM_TRANSPORT_FAILURE_REASON%" %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" %UPSTREAM_CLUSTER% %UPSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_REMOTE_ADDRESS% %REQUESTED_SERVER_NAME% %ROUTE_NAME% 56 | ``` 57 | 58 | ## JSON 格式 59 | 60 | istio 的 json accesslog 配置格式见 [源码](https://github.com/istio/istio/blob/1.8.3/pilot/pkg/networking/core/v1alpha3/accesslog.go#L63) 。转换成字符串为: 61 | 62 | ```json 63 | { 64 | "authority": "%REQ(:AUTHORITY)%", 65 | "bytes_received": "%BYTES_RECEIVED%", 66 | "bytes_sent": "%BYTES_SENT%", 67 | "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", 68 | "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", 69 | "duration": "%DURATION%", 70 | "istio_policy_status": "%DYNAMIC_METADATA(istio.mixer:status)%", 71 | "method": "%REQ(:METHOD)%", 72 | "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", 73 | "protocol": "%PROTOCOL%", 74 | "request_id": "%REQ(X-REQUEST-ID)%", 75 | "requested_server_name": "%REQUESTED_SERVER_NAME%", 76 | "response_code": "%RESPONSE_CODE%", 77 | "response_flags": "%RESPONSE_FLAGS%", 78 | "route_name": "%ROUTE_NAME%", 79 | "start_time": "%START_TIME%", 80 | "upstream_cluster": "%UPSTREAM_CLUSTER%", 81 | "upstream_host": "%UPSTREAM_HOST%", 82 | "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", 83 | "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", 84 | "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%", 85 | "user_agent": "%REQ(USER-AGENT)%", 86 | "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%" 87 | } 88 | ``` 89 | 90 | ## 参考资料 91 | 92 | * [istio 官方文档给出的常见变量的示例](https://istio.io/latest/docs/tasks/observability/logs/access-log/#default-access-log-format) -------------------------------------------------------------------------------- /usage/cors/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 corsPolicy 解决跨域问题 3 | type: book 4 | date: "2021-04-29" 5 | --- 6 | 7 | ## 概述 8 | 9 | 通常解决跨域问题都是在 web 框架中进行配置,使用 istio 后我们可以将其交给 istio 处理,业务不需要关心。本文介绍如何利用 Istio 配置来对 HTTP 服务启用跨域支持。 10 | 11 | ## 配置方法 12 | 13 | Istio 中通过配置 VirtualService 的 corsPolicy 可以实现跨域支持,示例: 14 | 15 | ```yaml 16 | apiVersion: networking.istio.io/v1beta1 17 | kind: VirtualService 18 | metadata: 19 | name: nginx 20 | namespace: istio-demo 21 | spec: 22 | gateways: 23 | - istio-demo/nginx-gw 24 | hosts: 25 | - 'nginx.example.com' 26 | - 'test.example.com' 27 | http: 28 | - corsPolicy: 29 | allowOrigins: 30 | - regex: "https?://nginx.example.com|https?://test.example.com" 31 | route: 32 | - destination: 33 | host: nginx.istio-demo.svc.cluster.local 34 | port: 35 | number: 80 36 | ``` 37 | 38 | * 关键配置在于 `allowOrigins`,表示允许带哪些 Origin 地址的请求。 39 | * 若有多个域名,使用 `regex` 匹配,`|` 符号分隔。 40 | * 若同时支持 http 和 https,`regex` 中的地址在 `http` 后面加 `s?`,表示匹配 `http` 或 `https`,即两种协议同时支持。 41 | * 关于 `corsPolicy` 更多配置,参考 [Istio CorsPolicy 官方文档](https://istio.io/latest/docs/reference/config/networking/virtual-service/#CorsPolicy) 。 42 | 43 | ## 一些误区 44 | 45 | 有同学测试发现,当请求带上了错误的 `Origin` 或没带 `Origin` 时,响应内容也正常返回了: 46 | ```bash 47 | $ curl -I -H 'Origin: http://fake.example.com' 1.1.1.1:80 48 | HTTP/1.1 200 OK 49 | ... 50 | ``` 51 | 52 | 为什么能正常返回呢?corsPolicy 没生效吗?有这样疑问的同学可能对 [CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS) 理解不太到位。 53 | 54 | 控制请求能否跨域的逻辑核心在于浏览器,浏览器通过判断请求响应的 [access-control-allow-origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) header 中是否有当前页面的地址,来判断该跨域请求能否被允许。所以业务要对跨域支持的关键点在于对 `access-control-allow-origin` 这个头的支持,通常一些 web 框架支持跨域也主要是干这个,为响应自动加上 `access-control-allow-origin` 响应头,istio 也不例外。 55 | 56 | 所以这里请求一般都能正常返回,只是如果跨域校验失败的话不会响应 `access-control-allow-origin` 这个 header 以告知浏览器该请求不能跨域,但响应的 body 是正常的,不会做修改。 -------------------------------------------------------------------------------- /usage/iphash/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 基于 iphash 进行负载均衡 3 | type: book 4 | date: "2021-03-02" 5 | --- 6 | 7 | ## 场景 8 | 9 | 根据源 IP 进行负载均衡,在 istio 中如何配置呢 ? 10 | 11 | ## 用法 12 | 13 | 配置 `DestinationRule`,指定 `useSourceIp` 负载均衡策略: 14 | 15 | 16 | ```yaml 17 | apiVersion: networking.istio.io/v1beta1 18 | kind: DestinationRule 19 | metadata: 20 | name: bookinfo-ratings 21 | spec: 22 | host: ratings.prod.svc.cluster.local 23 | trafficPolicy: 24 | loadBalancer: 25 | consistentHash: 26 | useSourceIp: true 27 | ``` 28 | 29 | ## 参考资料 30 | 31 | * 官方参考文档: [LoadBalancerSettings.ConsistentHashLB](https://istio.io/latest/docs/reference/config/networking/destination-rule/#LoadBalancerSettings-ConsistentHashLB) 32 | -------------------------------------------------------------------------------- /usage/oidc/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/usage/oidc/1.png -------------------------------------------------------------------------------- /usage/oidc/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/usage/oidc/2.png -------------------------------------------------------------------------------- /usage/oidc/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imroc/learning-istio/47d5f43c6bfa3c8341130543196d3d18bf2f8f05/usage/oidc/3.png -------------------------------------------------------------------------------- /usage/oidc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 OIDC 授权 Istio 3 | type: book 4 | date: "2021-04-27" 5 | draft: true 6 | --- 7 | 8 | ## 场景 9 | 10 | 在 [Developer settings](https://github.com/settings/developers) 中新建一个 OAuth App 11 | 12 | ![](1.png) 13 | 14 | ![](2.png) 15 | 16 | ![](3.png) 17 | 18 | ## 参考资料 19 | 20 | * [Istio OIDC Authentication](https://www.jetstack.io/blog/istio-oidc/) 21 | * [oauth2-proxy 项目地址](https://github.com/oauth2-proxy/oauth2-proxy) 22 | * [istio 1.9: Better External Authorization](https://istio.io/v1.9/blog/2021/better-external-authz/) 23 | -------------------------------------------------------------------------------- /usage/websocket/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 websocket 协议 3 | type: book 4 | date: "2021-03-02" 5 | --- 6 | 7 | ## 场景 8 | 9 | 业务使用的 websocket 协议,想跑在 istio 中,那么在 istio 中如何配置 websocket 呢? 10 | 11 | ## 用法 12 | 13 | 由于 websocket 本身基于 HTTP,所以在 istio 中直接按照普通 http 来配就行了: 14 | 15 | ```yaml 16 | apiVersion: networking.istio.io/v1alpha3 17 | kind: Gateway 18 | metadata: 19 | name: tornado-gateway 20 | spec: 21 | selector: 22 | istio: ingressgateway 23 | servers: 24 | - port: 25 | number: 80 26 | name: http 27 | protocol: HTTP 28 | hosts: 29 | - "*" 30 | --- 31 | apiVersion: networking.istio.io/v1alpha3 32 | kind: VirtualService 33 | metadata: 34 | name: tornado 35 | spec: 36 | hosts: 37 | - "*" 38 | gateways: 39 | - tornado-gateway 40 | http: 41 | - match: 42 | - uri: 43 | prefix: / 44 | route: 45 | - destination: 46 | host: tornado 47 | weight: 100 48 | ``` 49 | 50 | ## 参考资料 51 | 52 | * 官方 sample: [Tornado - Demo Websockets App](https://github.com/istio/istio/tree/master/samples/websockets) 53 | --------------------------------------------------------------------------------