从 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,其中之一是 <none>
。只需忽略那个并使用实际的 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