导言
在本文中,我们将讲述如何使用linkerd作为service mesh在不需要修改任何应用代码的情况下,也能添加TLS至所有的“服务到服务”HTTP请求中。
注意:这是关于Linkerd、Kubernetes和service mesh的系列文章其中一篇,其余部分包括:
- Top-line service metrics
- Pods are great, until they’re not
- Encrypting all the things (本文)
- Continuous deployment via traffic shifting
- Dogfood environments, ingress, and edge routing
- Staging microservices without the tears
- Distributed tracing made easy
- Linkerd as an ingress controller
- gRPC for fun and profit
- The Service Mesh API
- Egress
- Retry budgets, deadline propagation, and failing gracefully
- Autoscaling by top-line metrics
在该系列的第一部分,我讲述了当linkerd被作为service mesh安装后如何监控顶层指标(成功率、延迟、请求率)。本文中,我将讲述service mesh的另一优势:它允许你将应用程序的协议与网络上使用的协议分离。换句话说,应用可以使用与在电缆上传输字节不同的协议。
在不需要数据转换的情况下,linkerd可以利用这个解耦功能进行自动的协议升级。linkerd可以做协议升级的例子包括HTTP/1.x至HTTP/2,thrift至thrift-mux,还有HTTP至HTTPS,这也是本文的主题。
Kubernetes的service mesh
当linkerd在Kubernetes中作为service mesh部署后,我们使用DaemonSet在每个宿主机上部署一个linkerd实例。对于HTTP服务而言,pod可以通过使用http_proxy环境变量发送HTTP流量到它们本地的linkerd。(非HTTP流量情况下会稍微复杂些)
我们之前曾讲过在TLS中通过在连接的两端代理始发TLS和终止TLS,来使用linkerd“打包”HTTP请求的基本模式。然而,现在我们有了service mesh,问题显然变得简单了。将全部的跨宿主机通信加密是service mesh提供TLS证书的关键。
接下来举具体例子来说。接下来的例子的前两步与本系列PRAT 1相同,安装linkerd并安装简单的微服务“hello world”应用程序。如果你已经做过这些了,可以直接跳到第三步。
第一步:安装Linkerd
使用Kubernetes配置文件安装Linkerd。它会将Linkerd作为DaemonSet安装,且运行在Kubernetes的default命名空间:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/linkerd.yml
你可以通过查看Linkerd的管理页面确认是否安装成功:
INGRESS_LB=$(kubectl get svc l5d -o jsonpath="{.status.loadBalancer.ingress[0].*}") open http://$INGRESS_LB:9990 # on OS X
如果集群不支持外部负载均衡,使用hostIP:
HOST_IP=$(kubectl get po -l app=l5d -o jsonpath="{.items[0].status.hostIP}") open http://$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[2].nodePort}') # on OS X
第二步:安装样例应用程序
在default命名空间下安装两个Service,“hello”和“world”。这些应用程序依赖于Kubernetes downward API提供的nodeName来发现Linkerd。为了检测你的集群是否支持nodeName,你可以运行:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/node-name-test.yml
然后看它的日志:
kubectl logs node-name-test
如果你看到了一个IP就说明支持。接下来继续部署hello world应用程序如下:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world.yml
如果你看到报错“server can’t find…”,就部署旧版本hello-world,它依赖于hostIP而非nodeName:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world-legacy.yml
这两个Service——“hello”和“world”——功能在一起使得高度可扩展,“hello world”微服务(“hello”Service调用“world”Service完成这个请求)。
你可以通过给Linkerd的外部IP发送请求来查看此操作:
http_proxy=$INGRESS_LB:4140 curl -s http://hello
或者直接使用hostIP:
http_proxy=$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[0].nodePort}') curl -s http://hello
你应该可以看到字符串“Hello world”。
第三步:为使用TLS配置linkerd
现在linkerd已经安装好了,我们可以使用它来给流量加密了。我们在每一个宿主机上安装TLS证书,并为使用这些证书而配置linkerd。
我们将使用一个我们自己生成的全局证书。因为这个证书没有绑定公共DNS域名,我们就不需要使用Let’s Encrypt这样的服务了。我们可以生成自己的CA证书,并使用它来签署我们的mesh证书(“自签名”)。我们将为每个Kubernetes节点分配三样东西:CA证书,mesh密钥和mesh证书。
接下来的这个脚本使用我们自己生成的样本证书。请不要在生产环境中使用这些证书。
第四步:为Kubernetes部署证书并配置变更
我们已经做好准备为给流量加密而更新linkerd。我们将如Kubernetes secrets对象那样分配样例证书。
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/certificates.yml
现在,我们将通过这个配置文件配置linkerd并重启它以使用这些证书:
kubectl delete ds/l5d configmap/l5d-config kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/linkerd-tls.yml
第五步:成功
此时,linkerd应该透明地包裹TLS中的这些服务之间的所有通信。我们通过运行与之前相同的命令来验证这一点:
http_proxy=$INGRESS_LB:4140 curl -s http://hello
或者使用hostIP:
http_proxy=$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[0].nodePort}') curl -s http://hello
如果一切顺利进行,你应该仍然可以看到字符串“Hello world”。但是实际上,hello和world服务之间的通信正在被加密。我们可以通过直接向端口4141发送HTTPS请求来进行验证,其中,linkerd正在监听来自其他linkerd实例的请求:
curl -skH 'l5d-dtab: /svc=>/#/io.l5d.k8s/default/admin/l5d;' https://$INGRESS_LB:4141/admin/ping
或者使用hostIP:
curl -skH 'l5d-dtab: /svc=>/#/io.l5d.k8s/default/admin/l5d;' https://$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[1].nodePort}')/admin/ping
这里,我们要求用curl进行HTTPS调用,并告诉它跳过TLS验证(因为curl期望一个站点,而不是linkerd)。我们还添加了一个dtab override来将请求路由到linkerd实例自己的管理界面。如果一切顺利,你应该再次看到一个成功的“pong”响应。恭喜! 你已加密了跨服务流量。
结束语
在这篇文章中,我们已经讲述了如何使用诸如linkerd的service mesh来透明地加密Kubernetes集群中的所有跨节点通信。我们还使用TLS来确保linkerd实例可以验证它们是否与其他linkerd实例进行通信,从而防止中间人攻击(和配置错误)。当然,这个应用程序仍然不知道任何这些变化。
TLS是一个复杂的主题,我们已经考虑了一些重要的安全注意事项以使演示变得简单些。在你于生产集群上尝试此过程之前,请确保你已经花时间充分了解了相关步骤。
最后,将TLS添加到通信中只是service mesh可以实现的许多事情之一。一定要查看本系列的其余文章以了解更多。