Linkerd 2.10 中文手册持续修正更新中:
- https://linkerd.hacker-linner.com
从 Linkerd 2.9 版开始,有两种方式可以让 Linkerd 代理 与您的 Ingress Controller 一起运行。
默认模式
当 ingress controller 注入 linkerd.io/inject: enabled annotation 时, Linkerd 代理将遵守 ingress controller 做出的负载平衡决策, 而不是应用自己的 EWMA 负载平衡。这也意味着 Linkerd 代理不会为此流量使用服务配置文件(Service Profiles), 因此不会公开每个路由的指标(per-route metrics)或进行流量拆分(traffic splitting)。
如果您的 Ingress controller 注入没有特定于 Ingress 的额外配置, Linkerd 代理将在默认模式下运行。
代理 Ingress Mode
如果您需要 Linkerd 功能,如服务配置文件(Service Profiles)、流量拆分(Traffic Splits)等, 则需要进行额外的配置才能使 Ingress 控制器的 Linkerd 代理在入口模式下运行。这会导致 Linkerd 根据其 :authority、Host 或 l5d-dst-override headers 而不是原始目的地来路由请求,这允许 Linkerd 执行自己的负载平衡 并使用服务配置文件(Service Profiles)来公开每个路由的指标并启用流量拆分(traffic splitting)。
通过在 Ingress Controller 的 Pod Spec 中添加以下注释, 即 linkerd.io/inject: ingress,可以使 Ingress 控制器 deployment 的代理 在 ingress 模式下运行。
同样可以通过在注入命令中使用 --ingress 标志来完成。
- kubectl get deployment <ingress-controller> -n <ingress-namespace> -o yaml | linkerd inject --ingress - | kubectl apply -f -
这可以通过检查 Ingress 控制器的 pod 是否具有相关的 annotation 集来验证。
- kubectl describe pod/<ingress-pod> | grep "linkerd.io/inject: ingress"
对于 ingress,大多数控制器默认情况下不会将传入 header (example.com) 重写 为内部服务名称(example.default.svc.cluster.local)。在这种情况下,当 Linkerd 收到传出请求时,它认为该请求的目的地 是 example.com 而不是 example.default.svc.cluster.local。这会造成一个非常令人沮丧的无限循环!
幸运的是,许多入口控制器允许您修改 Host header 或向传出请求添加自定义标头。以下是常见入口控制器的一些说明:
- Nginx
- Traefik
- GCE
- Ambassador
- Gloo
- Contour
- Kong
如果您的 ingress controller 正在终止 HTTPS, Linkerd 将只为传入请求提供 TCP 统计信息, 因为代理看到的所有流量都是加密的。它将提供从控制器到后端服务的传出请求的完整统计信息, 因为这是从控制器到 Linkerd 的纯文本。
如果请求在注入您的 ingress controller 后遇到 2-3 秒的延迟, 这可能是因为 type: LoadBalancer 的服务隐藏了客户端源 IP。您可以通过在入口的服务定义中设置 externalTrafficPolicy: Local 来解决此问题。
虽然 Kubernetes Ingress API 定义允许 backend 的 servicePort 是字符串值, 但 Linkerd 只能使用数字 servicePort 值。如果遇到字符串值,Linkerd 将默认使用端口 80。
Nginx
这里以 emojivoto 为例
示例入口定义是:
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: web-ingress
- namespace: emojivoto
- annotations:
- kubernetes.io/ingress.class: "nginx"
- nginx.ingress.kubernetes.io/configuration-snippet: |
- proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port;
- grpc_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port;
- spec:
- rules:
- - host: example.com
- http:
- paths:
- - backend:
- serviceName: web-svc
- servicePort: 80
这里的重要 annotation 是:
- nginx.ingress.kubernetes.io/configuration-snippet: |
- proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port;
- grpc_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port;
如果您使用的是 auth-url, 则还需要添加以下代码段。
- nginx.ingress.kubernetes.io/auth-snippet: |
- proxy_set_header l5d-dst-override authn-name.authn-namespace.svc.cluster.local:authn-port;
- grpc_set_header l5d-dst-override authn-name.authn-namespace.svc.cluster.local:authn-port;
这个例子结合了 NGINX 用于代理 HTTP 和 gRPC 流量的两个指令。实际上,根据服务使用的协议,只需要设置 proxy_set_header 或 grpc_set_header 指令, 但是 NGINX 将忽略任何不需要的指令。
此示例入口定义为具有使用不同端口的多个端点的应用程序使用单个入口。
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: web-ingress
- namespace: emojivoto
- annotations:
- kubernetes.io/ingress.class: "nginx"
- nginx.ingress.kubernetes.io/configuration-snippet: |
- proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port;
- grpc_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port;
- spec:
- rules:
- - host: example.com
- http:
- paths:
- - path: /
- backend:
- serviceName: web-svc
- servicePort: 80
- - path: /another-endpoint
- backend:
- serviceName: another-svc
- servicePort: 8080
Nginx 将添加一个 l5d-dst-override header 来 指示 Linkerd 请求的目的地是什么服务。您需要同时包含 Kubernetes service FQDN (web-svc.emojivoto.svc.cluster.local) 和目标 servicePort。
要对此进行测试,您需要获取控制器的外部 IP 地址。如果您通过 helm 安装了 nginx-ingress,则可以通过运行以下命令获取该 IP 地址:
- kubectl get svc --all-namespaces \
- -l app=nginx-ingress,component=controller \
- -o=custom-columns=EXTERNAL-IP:.status.loadBalancer.ingress[0].ip
然后你可以通过 curl 使用这个 IP:
- curl -H "Host: example.com" http://external-ip
如果您使用默认后端,则需要为该后端创建入口定义 以确保设置了 l5d-dst-override header。例如:
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: default-ingress
- namespace: backends
- annotations:
- kubernetes.io/ingress.class: "nginx"
- nginx.ingress.kubernetes.io/configuration-snippet: |
- proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port;
- grpc_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port;
- spec:
- backend:
- serviceName: default-backend
- servicePort: 80
Traefik
这里以 emojivoto 为例,看一下 getting started 以复习如何安装它。
使用 Traefik 作为 Linkerd ingress 的最简单方法是使用 ingress.kubernetes.io/custom-request-headers 配置 Kubernetes Ingress resource,如下所示:
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: web-ingress
- namespace: emojivoto
- annotations:
- kubernetes.io/ingress.class: "traefik"
- ingress.kubernetes.io/custom-request-headers: l5d-dst-override:web-svc.emojivoto.svc.cluster.local:80
- spec:
- rules:
- - host: example.com
- http:
- paths:
- - backend:
- serviceName: web-svc
- servicePort: 80
这里的重要 annotation 是:
- ingress.kubernetes.io/custom-request-headers: l5d-dst-override:web-svc.emojivoto.svc.cluster.local:80
Traefik 将添加一个 l5d-dst-override header 来指示 Linkerd 请求的目的地是什么服务。您需要同时包含 Kubernetes service FQDN (web-svc.emojivoto.svc.cluster.local) 和目标 servicePort。有关更多信息,请参阅 Traefik 网站。
要对此进行测试,您需要获取控制器的外部 IP 地址。如果您通过 helm 安装了 Traefik,则可以通过运行以下命令获取该 IP 地址:
- kubectl get svc --all-namespaces \
- -l app=traefik \
- -o='custom-columns=EXTERNAL-IP:.status.loadBalancer.ingress[0].ip'
然后你可以通过 curl 使用这个 IP:
- curl -H "Host: example.com" http://external-ip
如果您使用 Traefik 的 service weights,此解决方案将不起作用, 因为 Linkerd 将始终向 l5d-dst-override 中的服务名称发送请求。一种解决方法是使用 traefik.frontend.passHostHeader: "false" 代替。请注意,如果您使用 TLS,Traefik 和后端服务之间的连接将不会被加密。有一个open issue 可以跟踪此问题的解决方案。
Traefik 2.x
Traefik 2.x 通过名为 IngressRoute 的 Custom Resource Definition (CRD) 添加了 对基于路径(path)的请求路由的支持。
如果您选择使用 IngressRoute 而不是默认的 Kubernetes Ingress resource, 那么您还需要使用 Traefik 的 Middleware Custom Resource Definition 来添加 l5d-dst-override header。
下面的 YAML 使用 Traefik CRD 为 emojivoto 应用程序生成相同的结果,如上所述。
- apiVersion: traefik.containo.us/v1alpha1
- kind: Middleware
- metadata:
- name: l5d-header-middleware
- namespace: traefik
- spec:
- headers:
- customRequestHeaders:
- l5d-dst-override: "web-svc.emojivoto.svc.cluster.local:80"
- ---
- apiVersion: traefik.containo.us/v1alpha1
- kind: IngressRoute
- metadata:
- annotations:
- kubernetes.io/ingress.class: traefik
- creationTimestamp: null
- name: emojivoto-web-ingress-route
- namespace: emojivoto
- spec:
- entryPoints: []
- routes:
- - kind: Rule
- match: PathPrefix(`/`)
- priority: 0
- middlewares:
- - name: l5d-header-middleware
- services:
- - kind: Service
- name: web-svc
- port: 80
GCE
这个例子和 Traefik 类似,也以 emojivoto 为例。查看 getting started 以复习如何安装它。
除了在 Traefik 示例中找到的自定义 headers 之外, 它还展示了如何将 Google Cloud Static External IP Address 和 TLS 与 Google-managed certificate 一起使用。
示例入口定义是:
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: web-ingress
- namespace: emojivoto
- annotations:
- kubernetes.io/ingress.class: "gce"
- ingress.kubernetes.io/custom-request-headers: "l5d-dst-override: web-svc.emojivoto.svc.cluster.local:80"
- ingress.gcp.kubernetes.io/pre-shared-cert: "managed-cert-name"
- kubernetes.io/ingress.global-static-ip-name: "static-ip-name"
- spec:
- rules:
- - host: example.com
- http:
- paths:
- - backend:
- serviceName: web-svc
- servicePort: 80
要使用此示例定义,请将 managed-cert-name 和 static-ip-name 替换为您项目中定义的短名称(n.b. 使用 IP 地址的名称,而不是地址本身)。
托管证书将需要大约 30-60 分钟来提供,但 ingress 的状态应该在几分钟内是健康的。提供托管证书后,ingress 应该对 Internet 可见。
Ambassador
这里以 emojivoto 为例, 看一下 getting started 以复习如何安装它。
Ambassador 不使用 Ingress 资源,而是依赖 Service。示例服务定义是:
- apiVersion: v1
- kind: Service
- metadata:
- name: web-ambassador
- namespace: emojivoto
- annotations:
- getambassador.io/config: |
- ---
- apiVersion: ambassador/v1
- kind: Mapping
- name: web-ambassador-mapping
- service: http://web-svc.emojivoto.svc.cluster.local:80
- host: example.com
- prefix: /
- add_linkerd_headers: true
- spec:
- selector:
- app: web-svc
- ports:
- - name: http
- port: 80
- targetPort: http
这里的重要 annotation 是:
- add_linkerd_headers: true
Ambassador 将添加一个 l5d-dst-override header 来指示 Linkerd 的请求是为什么服务。这将包含 Kubernetes service FQDN (web-svc.emojivoto.svc.cluster.local) 和 目标 servicePort。
要使其全局化,请将 add_linkerd_headers 添加到您的 Module 配置中。
要对此进行测试,您需要获取控制器的外部 IP 地址。如果您通过 helm 安装了 Ambassador,则可以通过运行以下命令获取该 IP 地址:
- kubectl get svc --all-namespaces \
- -l "app.kubernetes.io/name=ambassador" \
- -o='custom-columns=EXTERNAL-IP:.status.loadBalancer.ingress[0].ip'
如果您已经安装了管理界面,这将返回两个 IP,其中之一是
然后你可以通过 curl 使用这个 IP:
- curl -H "Host: example.com" http://external-ip
您还可以在此处 从 Buoyant 的人们那里找到有关将 Linkerd 与 Emissary Ingress(又名Ambassador)结合使用的更详细指南。
Gloo
这里以 books 为例,查看 Demo: Books 了解如何运行它。
如果您使用 Gateway method(gloo install gateway)安装了 Gloo, 那么您将需要一个 VirtualService 才能将流量路由到您的 Books 应用程序。
要将 Gloo 与 Linkerd 一起使用,您可以选择两个选项之一。
自动的
从 Gloo v0.13.20 开始,Gloo 与 Linkerd 进行了原生集成, 因此会自动添加所需的 Linkerd header。
假设您将 gloo 安装到默认位置,您可以通过运行以下命令启用本机集成:
- kubectl patch settings -n gloo-system default \
- -p '{"spec":{"linkerd":true}}' --type=merge
Gloo 现在会自动向上游的每个 kubernetes 添加 l5d-dst-override header。
现在只需添加一条到上游 books app 的路由:
- glooctl add route --path-prefix=/ --dest-name booksapp-webapp-7000
手动的
如本文档开头所述,您需要指示 Gloo 添加一个 header,以允许 Linkerd 识别将流量发送到何处。
- apiVersion: gateway.solo.io/v1
- kind: VirtualService
- metadata:
- name: books
- namespace: gloo-system
- spec:
- virtualHost:
- domains:
- - '*'
- name: gloo-system.books
- routes:
- - matcher:
- prefix: /
- routeAction:
- single:
- upstream:
- name: booksapp-webapp-7000
- namespace: gloo-system
- routePlugins:
- transformations:
- requestTransformation:
- transformationTemplate:
- headers:
- l5d-dst-override:
- text: webapp.booksapp.svc.cluster.local:7000
- passthrough: {}
这里的重要 annotation 是:
- routePlugins:
- transformations:
- requestTransformation:
- transformationTemplate:
- headers:
- l5d-dst-override:
- text: webapp.booksapp.svc.cluster.local:7000
- passthrough: {}
使用 Gloo 中内置的内容转换引擎,您可以指示它添加所需的 l5d-dst-override header, 该 header 在上面的示例中指向服务的 FDQN 和端口:webapp.booksapp.svc.cluster.local:7000
Test
为了轻松测试这一点,您可以通过运行以下命令获取 Gloo 代理的 URL:
- glooctl proxy URL
这将返回类似于:
- $ glooctl proxy url
- http://192.168.99.132:30969
对于上面的示例 VirtualService,它侦听任何域(domain)和路径(path), 在浏览器中访问代理 URL (http://192.168.99.132:30969) 应该会打开 Books 应用程序。
Contour
Contour 不支持自动设置 l5d-dst-override header。以下示例使用 Contour getting started 来演示如何手动设置所需的 header:
首先,将 Linkerd 注入您的 Contour 安装:
- linkerd inject https://projectcontour.io/quickstart/contour.yaml | kubectl apply -f -
Envoy 不会自动挂载 service account token。要解决此问题,您需要设置 automountServiceAccountToken: true。您可以选择创建一个专用 service account 以避免使用 default。
- # create a service account (optional)
- kubectl apply -f - << EOF
- apiVersion: v1
- kind: ServiceAccount
- metadata:
- name: envoy
- namespace: projectcontour
- EOF
- # add service account to envoy (optional)
- kubectl patch daemonset envoy -n projectcontour --type json -p='[{"op": "add", "path": "/spec/template/spec/serviceAccount", "value": "envoy"}]'
- # auto mount the service account token (required)
- kubectl patch daemonset envoy -n projectcontour --type json -p='[{"op": "replace", "path": "/spec/template/spec/automountServiceAccountToken", "value": true}]'
验证您的 Contour 和 Envoy 安装有一个正在运行的 Linkerd sidecar。
接下来我们将部署一个 demo service:
- linkerd inject https://projectcontour.io/examples/kuard.yaml | kubectl apply -f -
要将外部流量路由到您的服务,您需要提供一个 HTTPProxy:
- apiVersion: projectcontour.io/v1
- kind: HTTPProxy
- metadata:
- name: kuard
- namespace: default
- spec:
- routes:
- - requestHeadersPolicy:
- set:
- - name: l5d-dst-override
- value: kuard.default.svc.cluster.local:80
- services:
- - name: kuard
- namespace: default
- port: 80
- virtualhost:
- fqdn: 127.0.0.1.xip.io
请注意,l5d-dst-override header 显式设置为目标 service。
最后,您可以测试您的 working service mesh :
- kubectl port-forward svc/envoy -n projectcontour 3200:80
- http://127.0.0.1.xip.io:3200
如果您将 Contour 与 flagger 一起使用, l5d-dst-override 请求头将自动设置。
Kong
Kong 不自动支持标头 l5d-dst-override。本文档将使用以下元素:
- Kong
- Emojivoto
在安装 Emojivoto demo 应用程序之前,请在您的集群上安装 Linkerd 和 Kong。记得在注入 Kong 部署时使用 上面 提到的 --ingress 标志(或注解)!
我们还需要声明这些对象:
- KongPlugin,Kong 提供的 CRD
- Ingress
- apiVersion: configuration.konghq.com/v1
- kind: KongPlugin
- metadata:
- name: set-l5d-header
- namespace: emojivoto
- plugin: request-transformer
- config:
- add:
- headers:
- - l5d-dst-override:$(headers.host).svc.cluster.local
- ---
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: web-ingress
- namespace: emojivoto
- annotations:
- kubernetes.io/ingress.class: "kong"
- konghq.com/plugins: set-l5d-header
- spec:
- rules:
- - http:
- paths:
- - path: /api/vote
- backend:
- serviceName: web-svc
- servicePort: http
- - path: /api/list
- backend:
- serviceName: web-svc
- servicePort: http
我们在 KongPlugin 中明确设置了 l5d-dst-override。使用 templates as values, 我们可以使用来自请求的 host header,并基于此设置 l5d-dst-override 值。
最后,让我们安装 Emojivoto,以便它的 deploy/vote-bot 以 ingress 为目标, 并包含 web-svc.emojivoto 服务的 host header。
在应用注入的(injected) Emojivoto 应用程序之前,对 vote-bot 部署进行以下更改:
- env:
- # Target the Kong ingress instead of the Emojivoto web service
- - name: WEB_HOST
- value: kong-proxy.kong:80
- # Override the host header on requests so that it can be used to set the l5d-dst-override header
- - name: HOST_OVERRIDE
- value: web-svc.emojivoto
【编辑推荐】