虽然我们已经成功地将一个静态网站成功地在Kubernetes里部署起来了,但还有很多细节可以完善。我们就在这一节里逐步优化。
1. ConfigMap
问题最明显的是。重温一下我们静态网站之前使用的Dockerfile:
1 | FROM nginx:alpine |
首先是Nginx配置default.conf。
网站的源代码不应该干涉网站怎么部署。到底部署在Apache,Nginx还是Node.js,是否要在部署的时候添加自定义Header,都不该是开发者关注的事情。我们也不希望修改网站的timeout配置还需要动到源代码。从耦合性的角度来看,这个Nginx网站的配置文件不应该放到源代码中。
对于这类配置文件,Kubernetes里有专门的对象ConfigMap来保存。
从ConfigMap这个名字就可以猜得到,它存储的是配置信息,存储的格式是Map类型,即键值对。
配置信息可以是像本篇中的Nginx config配置,可以设置环境变量,可以是Java的properties和application.yml配置文件,可以是Redis和MySQL的配置文件。它很适合需要在一套Kubernetes集群上部署多个环境(例如特性分支/sit/uat)的情况。(当然我们的Java应用将使用Spring Cloud Config配置中心,所以目前不会用ConfigMap管理配置)
本篇POC的ConfigMap如下:
1 | apiVersion: v1 |
default.conf为key(键),下面的内容为value(值)。结构非常简单。
在Kubernetes中,ConfigMap是一种特殊的Volume(卷):Projected Volume。可以认为ConfigMap是Kubernetes中的数据被投射(Project)到容器中的。
关于Volume我们会在后续展开讨论,这里只是先提一下:要在容器中使用volume,需要先在spec中定义,然后mount到容器中。所以添加了ConfigMap后的Deployment定义YAML如下:
1 | apiVersion: apps/v1 |
poc-web-config中的default.conf被挂到了Nginx镜像的/etc/nginx/conf.d目录下。
Nginx的nginx.conf中定义了会加载conf.d下所有conf后缀的Nginx配置:
1 | include /etc/nginx/conf.d/*.conf; |
而Projected Volume的挂载是在容器启动步骤最开始就进行的。所以当容器启动之前,会从ConfigMap中获取default.conf配置文件,放到/etc/nginx/conf.d目录中。当Nginx进程启动的时候,就会读到该站点的配置。
既然Nginx的配置已经由ConfigMap提供,我们就可以不需要在源代码中包含。于是Dockerfile就被精简为:
1 | FROM nginx:alpine |
不管Pod在哪台宿主机上,都可以访问到ConfigMap,所以我们很容易就能猜到ConfigMap的数据保存在etcd上。
除了以YAML方式定义,还可以通过–from-file参数将文件创建为ConfigMap。
在一般情况下ConfigMap会先覆盖掉挂载目录然后再将ConfigMap中的内容作为文件挂载进行。如果想要不覆盖原本文件夹下的文件可以使用subPath参数。
1.1 热更新
更新ConfigMap不会触发Pod的滚动更新,所以每次需要修改Pod Annotation的方式来强制触发滚动更新。具体命令如:
1 | kubectl patch deployment <Deployment名> --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "20180411" }}}}}' |
更多可以参考:
ConfigMap的热更新
从这个角度来说,ConfigMap不太适合保存频繁更新的配置。
2. Secret
除了ConfigMap之外,还有一种Projected Volume:Secret。从名字就可以猜得到,保存的是敏感信息,包括密码,认证token,密钥key等。
像这些比较敏感的信息,直接写在Kubernetes的Deployment YAML定义里肯定不合适。放在Secret中会比较安全和灵活。除了保存信息是加密的之外,Secret和ConfigMap并没有太大差别。
Kubernetes官网有一个生成用户名和密码的简单范例:使用 Secret 安全地分发凭证 - Kubernetes,这里就不多复述了。
这里介绍Secret,主要是因为我们接下来要给网站添加强制HTTPS访问。
要开启HTTPS访问,就先需要一个SSL证书。如果我们没有SSL证书的话,可以自己签发一个:
1 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=traefik-ui.demosite.net" |
然后我们就可以通过如下的命令,根据SSL证书生成Secret:
1 | kubectl create secret tls traefik-cert --key=tls.key --cert=tls.crt -n kube-system |
由于是供Traefik用的,所以创建在kube-system的namespace里。接下来我们修改Traefik配置。
3. 配置TLS
3.1 配置traefik.toml
我们接下来为Traefik Ingress Controller配置证书。
Traefik的配置文件是traefik.toml。我们按照第1节的方式,将其配置为ConfigMap:
1 | apiVersion: v1 |
其中的[entryPoints.http.redirect]
就是强制重定向的配置。
更多可选配置可以参考官方文档。
3.2 修改Traefik Ingress Controller配置
在Ingress Controller引入ConfigMap中的配置和Secret中的证书,增加443端口。先上个配置全文:
1 |
|
首先把ConfigMap和Secret以Projected Volume的形式挂上:
1 | volumes: |
然后mount到container上:
1 | volumeMounts: |
在container和service里增加443端口:
1 | volumeMounts: |
启动参数里增加config文件:
1 | args: |
因为不太确定怎么热加载Ingress Controller,所以我采用了先delete然后重新apply。然后我们就能以https形式访问了。
顺带着Traefik Admin UI也变成https了:
4. 参考资料
其他加载ConfigMap的方式,以及加载为环境变量的Demo
K8S学习笔记之Kubernetes 配置管理 ConfigMap - 时光飞逝,逝者如斯 - 博客园
ConfigMap加载为命令行参数和非覆盖加载的Demo
Kubernetes对象之ConfigMap - 简书
官方的ConfigMap Demo
使用ConfigMap来配置Redis - Kubernetes
网上有些配置较为过时,配置后虽然没有报错,但访问https地址就是遇到Connection Refused。下面这篇比较新一些,是本篇的主要参考:
kubeasz/ingress-tls.md at master · easzlab/kubeasz