Python项目容器化实践(九) - 将lyanna应用部署到线上的Kubernetes上运行啦!
/ / / 阅读数:6920前言
这篇文件记录了我将博客应用部署在真实服务器的 Kubernetes 上运行的整个过程❤️。
安装和配置 k8s 集群环境
第一步是先安装需要的软件包:
# 先安装Docker ➜ ~ sudo apt install -y apt-transport-https ca-certificates curl software-properties-common ➜ ~ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - ➜ ~ sudo apt-key fingerprint 0EBFCD88 # 由于我在服务器安装了Python3.7,/usr/bin/python3链接不再不再是原来系统自带的Python3.5,需要明确指定 ➜ ~ sudo sed -i 's/python3/python3.5/g' /usr/bin/add-apt-repository ➜ ~ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" ➜ ~ sudo apt-get update ➜ ~ sudo apt install docker-ce ➜ ~ sudo systemctl enable docker # 安装k8s用到的三个包 ➜ ~ cat <<EOF | sudo tee -a /etc/apt/sources.list.d/kubernetes.list deb http://mirrors.ustc.edu.cn/kubernetes/apt kubernetes-xenial main EOF ➜ ~ sudo apt-get update W: GPG error: http://mirrors.ustc.edu.cn/kubernetes/apt kubernetes-xenial InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 6A030B21BA07F4FB W: The repository 'http://mirrors.ustc.edu.cn/kubernetes/apt kubernetes-xenial InRelease' is not signed. N: Data from such a repository can't be authenticated and is therefore potentially dangerous to use. N: See apt-secure(8) manpage for repository creation and user configuration details. # 由于没有对应的公钥所以不能验证这个源包的签名,我这里给大家看墙内对应解决方案,否则可以用官网提供的名字添加 # 上面的输出有个 6A030B21BA07F4FB 下面2句都要用到它: ➜ ~ gpg --keyserver keyserver.ubuntu.com --recv-keys 6A030B21BA07F4FB ➜ ~ gpg --export --armor 6A030B21BA07F4FB | sudo apt-key add - OK ➜ ~ sudo apt-get update # 这次就正常了 ➜ ~ sudo apt-get install -y kubelet kubeadm kubectl ➜ ~ sudo apt-mark hold kubelet kubeadm kubectl # 停止它们的自动更新 ➜ ~ kubelet --version Kubernetes v1.16.1 |
这次安装的 3 个软件包分别用来:
- kubeadm。用来初始化集群
- kubelet。在集群中的每个节点上用来启动 Pod (豆荚) 和 container (容器) 等。
- kubectl。用来与集群通信的命令行工具
写文本时,Kubernetes 版本为v1.16.1
。
搭建主节点环境
我的博客用的云服务器是单节点的,而且由于它已经是虚拟主机 (腾讯用的的是自研的基于 KVM 的 Havisor) 所以无法再被虚拟化成多台主机,所以只能搭建只有一个主节点 (没有 Node 节点) 的集群:
# 下面这部分每个服务器都要执行: ➜ ~ sudo swapoff -a # 从v1.8开始要求关闭系统的Swap,但永久关闭需要编辑/etc/fstab注释掉交换分区所在行 ➜ ~ cat <<EOF | sudo tee -a /etc/docker/daemon.json # 统一cgroup驱动为systemd, 并且使用国内源 pipe heredoc> { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2", "registry-mirrors": [ "https://registry.docker-cn.com", "http://hub-mirror.c.163.com", "http://docker.mirrors.ustc.edu.cn", "http://dockerhub.azk8s.cn" ] } pipe heredoc> EOF ➜ ~ sudo systemctl daemon-reload ➜ ~ sudo systemctl restart docker # 下面这部分只在Master执行: # 初始化,使用阿里云源,指定Pod网络地址(10.244.0.0/16网段) ,要下载image,需要花费几分钟 ➜ ~ sudo kubeadm init --image-repository registry.aliyuncs.com/google_containers -v 5 --pod-network-cidr 10.244.0.0/16 ... # 省略输出 Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 172.17.87.14:6443 --token r4aa9m.n3xtkspn48x4j0s4 \ --discovery-token-ca-cert-hash sha256:047fbe9327aae75856433d95d298b97ca6de2f3efe253cfed618369448e49ff5 ➜ ~ mkdir -p $HOME/.kube ➜ ~ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config ➜ ~ sudo chown $(id -u):$(id -g) $HOME/.kube/config |
使用 Calico
容器网络是容器选择连接到其他容器、主机和外部网络的机制,但据说 Docker 原生的通信方案效率很差,所有通常会选择开源的 CNI 插件在配置或销毁容器时动态配置适当的网络配置和资源。全部可选网络插件列表可以看:https://kubernetes.io/docs/concepts/cluster-administration/addons/#networking-and-network-policy
最主流的是 Flannel 和 Calico,它们之间的对比我没有实际经验,可以参考延伸阅读链接 2。出于我个人对于性能的极端追求选择了「Calico+BGP 模式 (默认)」,当然对于小博客来说我想多了~
➜ ~ kubectl apply -f https://docs.projectcalico.org/v3.10/manifests/calico.yaml # 等待1-2分钟,查看kube-system命名空间下的pod状态都是Running的了: ➜ ~ kubectl get pod -n kube-system NAME READY STATUS RESTARTS AGE calico-kube-controllers-6b64bcd855-mf2nw 1/1 Running 0 2m18s calico-node-fttbw 1/1 Running 0 2m18s coredns-58cc8c89f4-px2kz 1/1 Running 0 3m17s coredns-58cc8c89f4-xhxsr 1/1 Running 0 3m17s etcd-master 1/1 Running 0 2m35s kube-apiserver-master 1/1 Running 0 2m33s kube-controller-manager-master 1/1 Running 0 2m16s kube-flannel-ds-amd64-jmbpf 1/1 Running 0 2m57s kube-proxy-7hl5l 1/1 Running 0 3m17s kube-scheduler-master 1/1 Running 0 2m28s # 我这个博客应用没有其他Node节点,所以会主节点上安排Pod,但出于安全原因考虑需要主节点隔离: ➜ ~ kubectl taint nodes --all node-role.kubernetes.io/master- node/vm-17-14-ubuntu untainted |
搭建节点环境 (可选)
下面这部分只在 Node 执行,如果你搭建的环境多 Node 需要执行下面这部分:
# 按之前的kubeadm init输出提示,每个节点执行join加入集群 ➜ ~ sudo kubeadm join 172.17.87.14:6443 --token r4aa9m.n3xtkspn48x4j0s4 --discovery-token-ca-cert-hash sha256:047fbe9327aae75856433d95d298b97ca6de2f3efe253cfed618369448e49ff5 # 等待几分钟后,就可以查看集群状态了: ➜ ~ kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready master 9m v1.16.1 ... |
这部分由于我们这次实验没有节点并没有执行。
安装 NGINX Ingress 控制器
和之前在 Minikube 虚拟机或者 Docker for Mac 上安装用的配置文件不同,需要这样:
➜ ~ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml ➜ ~ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml # 稍等1分钟: ➜ ~ kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx NAMESPACE NAME READY STATUS RESTARTS AGE ingress-nginx nginx-ingress-controller-568867bf56-7b4qb 1/1 Running 0 87s |
这个 Pod 里面运行了控制器和 Nginx,控制器会把 Ingress 资源转化为 Nginx 反向代理配置文件。
偏个题,之前几篇文章都是在 Minikube 虚拟机里面,主要是当时 macOS 上使用 Ingress 控制器不知道怎么把域名绑定在 localhost 上,这次实验正好找到了方案,如果你想本地跑 NGINX Ingress 控制器,不必使用 Minikube 并minikube addons enable ingress
,还可以:
➜ ~ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml |
看一下 cloud-generic.yaml 的内容:
kind: Service apiVersion: v1 metadata: name: ingress-nginx namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx spec: externalTrafficPolicy: Local type: LoadBalancer selector: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx ports: - name: http port: 80 targetPort: http - name: https port: 443 targetPort: https |
所以重点是type: LoadBalancer
和 externalTrafficPolicy (将外部流量路由到节点本地),#TIL
另外提一下,基于 Nginx 的 Ingress Controller 有两种,一种是 K8s 社区提供的 ingress-nginx (上面我们用到的),另一种是 Nginx 社区提供的 kubernetes-igress ,他俩们之间的区别可以看延伸阅读链接 4
后记
然后应用配置并初始化数据库:
➜ ~ kubectl apply -f k8s ➜ ~ kubectl get pods -l app.kubernetes.io/name=lyanna NAME READY STATUS RESTARTS AGE lyanna-deployment-579f79ff47-q84gm 1/1 Running 0 5m31s lyanna-deployment-579f79ff47-rfvgv 1/1 Running 0 5m31s lyanna-deployment-579f79ff47-w7dgh 1/1 Running 0 5m31s ➜ ~ kubectl exec -it lyanna-deployment-579f79ff47-q84gm -- sh -c ./setup.sh # 在其中一个Pod上执行 |
现在【集群】已经搭建好了,指定 host 后就可以访问http://lyanna.local/或者https://lyanna.local/(当然证书是不安全的) 了
修改 lyanna 的配置文件
上篇文章写好的 k8s 配置文件 可以在本地 k8s 上运行了,但是放到生产环境还有一些要修改的。我们先分析一下用云服务器会有什么不同:
- MySQL 服务使用云服务,所以不需要部署 mariadb.yaml
- Memcached 要跑在 k8s 上,考虑到 aiomcache 多个 Memcached 实例存在缓存共享问题 (不像我厂 libmc 那样提供的集群配置),Pod 已经保证了有一个 Memcached 运行状态,且现在博客应用就使用着单个进程,我会把副本数调整成 1
- Redis 不仅要跑在 k8s 上,还需要持久化存储数据,并且要考虑 Redis 导数据到 k8s 中的容器
- 需要应用
local_settings.py
中配置 (也包含数据库密码) - 需要把之前的 Nginx 的配置应用到 NGINX Ingress 控制器上
- HTTPS 支持 (之前在博客就使用了 Certbot + Let's Encrypt 的方案)
- 本地用的域名是 lyanna.local,正式域名是 dongwm.com 怎么在不影响现有代码逻辑的前提下让域名配置方便呢?
问题还挺多,我们挨个解决吧。
线上不部署 MariaDB
其实在一开始时已经考虑到这点,上篇文章也提过:我特意把 mariadb.yaml 放在了 optional 子目录,不使用 - R 参数即可
应用生产环境配置
k8s/config.yaml
这个配置文件中包含了数据库、Redis 的设置,除此之外会改变的还有 Ingress 设置中的域名,所以我把 Ingress 部分也迁了进来。这个配置文件已经写好了默认设置,不过来看一下我实际使用的效果 (当然已隐去关键信息)。先看 ConfigMap 部分:
apiVersion: v1 kind: ConfigMap metadata: name: lyanna-cfg data: db_port: "3306" database: YOUR_DB db_user: DB_USER db_password: DB_PASSWD memcached_host: lyanna-memcached replication-password: lyanna redis_sentinel_host: "" redis_sentinel_port: "" redis_url: redis://YOUR_SERVER_IP:6379 db_url: mysql://DB_USER:DB_PASSWD@YOUR_SERVER_IP:3306/YOUR_DB?charset=utf8 |
ConfigMap 部分使用了外部的 Redis-server 和 MySQL。MySQL 是我预想的,因为我的服务器资源有限 (2 核 CPU/4G 内存),数据库是非常占资源且重要的,另外还要自己做监控和数据备份等麻烦,所以一开始我就使用了云 MySQL,准备在 k8s 里面使用外部的 MySQL 服务。
而 Redis 也用外部的是我没有预计到的在我试用时发现 k8s 非常占 CPU 和内存,直接让服务器负载涨到 40+,且内存占用 > 90%,所以第一步我先限制了不同类型的 Pod 占用资源,比如 lyanna 应用是这样写的:
resources:
limits:
cpu: 300m # 1m等于千分之一个CPU
memory: 200Mi
requests:
cpu: 200m
memory: 100Mi
|
也就是每个 Pod 最多占用 0.3CPU 和 200M 内存,请求最多 0.2CPU 和 100M 内存 (为容器管理计算资源部分内容可以看延伸阅读链接 5)。不用的 Pod 限制的量是不一样的,但是架不住 Pod 多,由于服务器还运行很多内容,所以一部分 Pod 就已经占满服务器剩余资源,每次都有 1-2 个 Pod 是 Pending 状态.... 哎,当时写的时候 Redis Sentinel 集群有 4 个 Pod,其实我有点设计复杂了,只跑一个 Master 就算了。所以我索性珍把 Redis 也迁到 k8s/optional 下作为备选,直接用了之前已经购买用于其他用途的云 Redis 服务。
一个细节,如果想使用单 Redis 的话,除了要设置redis_url
还需要让redis_sentinel_host
或者redis_sentinel_port
为空,这样 lyanna 就不会把 Redis 当做 Redis Sentinel 集群了。
应用 Nginx 的配置
现在线上运行的 Nginx 使用了 srv/templates/nginx.conf 中的配置,其中有多个 location 设置各不相同,还有一些全局的如keepalive_timeout
等配置,在 k8s 中需要遵循 Nginx Ingress 控制器的思路:把对应配置项写入 ConfigMap 里,或者使用对应 Annotation。
Label 和 Annotation 都可以将元数据关联到 Kubernetes 资源对象。Label 主要用于选出挑选出满足特定条件的对象,而 Annotation (注解) 更宽泛,可以为对象附加任意的非标识的元数据,可以说按开发者和团队的意愿随意添加,注解中的元数据,可以很小也可以很大,可以是结构化的,也可以是非结构化的,能够包含标签不允许的字符。
全局的 Nginx 设置 (k8s/nginx.yaml):
apiVersion: v1 data: keep-alive: "5" http-snippet: | proxy_cache_path /tmp/nginx-cache levels=1:2 keys_zone=static-cache:2m max_size=100m inactive=7d use_temp_path=off; proxy_cache_key $scheme$proxy_host$request_uri; proxy_cache_lock on; proxy_cache_use_stale updating; kind: ConfigMap metadata: name: nginx-configuration namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx |
Nginx Ingress 控制器已经内置了很多 Nginx 相关配置项,可以通过 ConfigMap 的方式指定,如keep-alive
,具体的可以看延伸阅读链接 7。http-snippet
是在 http 区块下把 Nginx Ingress 控制器不支持的 Nginx 配置直接写进去。
然后就是 Ingress 部分。和之前文档提的单个 Ingress 不同,这里有三个 Ingress 指向一个域名 (blog.dongwm.com):
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: lyanna-ing-static annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/use-regex: "true" nginx.ingress.kubernetes.io/proxy-buffering: "on" nginx.ingress.kubernetes.io/configuration-snippet: | include /etc/nginx/mime.types; proxy_cache static-cache; proxy_ignore_headers Cache-Control; proxy_cache_valid any 30m; add_header X-Cache-Status $upstream_cache_status; spec: rules: - host: blog.dongwm.com http: paths: - path: /static backend: serviceName: lyanna-svc servicePort: 80 - path: /img backend: serviceName: lyanna-svc servicePort: 80 - path: /fonts backend: serviceName: lyanna-svc servicePort: 80 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: lyanna-ing-admin annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/use-regex: "true" nginx.ingress.kubernetes.io/configuration-snippet: | allow "1.2.3.4"; deny all; spec: rules: - host: blog.dongwm.com http: paths: - path: /admin backend: serviceName: lyanna-svc servicePort: 80 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: lyanna-ing annotations: kubernetes.io/ingress.class: "nginx" spec: rules: - host: blog.dongwm.com http: paths: - path: / backend: serviceName: lyanna-svc servicePort: 80 |
上述部分也在k8s/config.yaml
里面。三个 Ingress 的后端都是 lyanna-svc 服务,总体是根据 Nginx 配置的不同来分开的:
- lyanna-ing-static。/static、/img、fonts 这三个静态文件子路由
- lyanna-ing-admin。管理后台 /admin 部分,只允许某些 IP 访问
- lyanna-ing。Web 服务,除上述 2 个 Ingress 管理的其他路由
通过 annotations 键来设置 Nginx:
kubernetes.io/ingress.class: "nginx"
。指定使用 Nginx Ingress 控制器,其实在我们这个例子中不指定也行,反正只有一个控制器nginx.ingress.kubernetes.io/use-regex: "true"
。如果使用正则需要加这个 Annotationnginx.ingress.kubernetes.io/configuration-snippet
。把对应 location 下的 Nginx 配置写进去,这部分基于来自原来的 nginx.conf
注意静态文件部分的 Ingress 里面有这么一句add_header X-Cache-Status $upstream_cache_status;
,这是 Nginx Upstream 缓存,服务跑起来之后可以请求静态文件体验一下:
➜ ~ kubectl exec nginx-ingress-controller-568867bf56-7b4qb -n ingress-nginx cat /etc/nginx/nginx.conf|less # 生成的nginx.conf,可以测试配置是否生效 # 本地试验缓存效果: ❯ curl --head http://lyanna.local/static/css/index.min.css ... X-Cache-Status: MISS ❯ curl --head http://lyanna.local/static/css/index.min.css ... X-Cache-Status: HIT ❯ curl --head http://lyanna.local/static/css/index.min.css ... X-Cache-Status: HIT |
k8s 和外部服务通信的问题
k8s 里面的容器之前是可以方便的内部通信的,但和 MySQL 和 Redis 这 2 个外部云服务怎么通信呢?
我一开始是觉得应该自带了某个域名指向主节点的 IP,在我的 macOS 上的 /etc/hosts 不知道什么时候写入了127.0.0.1 kubernetes.docker.internal
,后来发现 kubernetes.docker.internal 这个域名只在 Docker for Mac 下可用,在实际服务器上没有找到这样的域名,但是尝试了一下在容器中其实可以 ping 通主节点的那些 ip,但是 ping 不同和主节点 ip 同网站的其他 IP (云服务所在 IP),所以我想到了通过 iptables 做端口转发,转发云服务流量,比如 MySQl 是这样操作的:
# 172.17.17.14是主节点IP,172.17.17.13是MySQL服务IP ➜ ~ sudo iptables -t nat -A OUTPUT -o lo -d 172.17.17.14 -p tcp --dport 3306 -j DNAT --to-destination 172.17.17.13:3306 ➜ ~ sudo iptables -t nat -A POSTROUTING -d 172.17.17.13 -p tcp --dport 3306 -j SNAT --to 172.17.17.14 # 我的服务器是Ubuntu,所以用下面方法让策略生效 ➜ ~ sudo sh -c "iptables-save > /etc/iptables/rules.v4" ➜ ~ sudo sh -c "iptables-restore < /etc/iptables/rules.v4" |
如本例,把 k8s/config.yaml 中的YOUR_SERVER_IP
改成172.17.17.14
就能实现连通了。
让外部流量转到到 Nginx Ingress 上
本来我是想直接让 Nginx Ingress 接收用户请求,不知道你有没有发现有什么问题?
在整个过程中,我使用 kubectl 都用了一个非 root 账号,全程没有速度,如果让用户通过 HTTP/HTTPS 协议访问服务必要要通过 80/443 端口,但是 1024 以下的端口是 root 管理的,所以我们需要有一种转发方案。
我在这里用了 2 层 Nginx 结构:本机的 Nginx 接受用户请求,用 upstream 把请求转发给 nginx-ingress。
当然用 iptables 做 NAT 服务器转发给 nginx-ingress 也可以,但是请教我厂平台工程师说曾经试验有性能问题就作罢了。
那 upstream 里面怎么找到 ingress 的 ip 呢?大家还记不记得之前曾经用baremetal/service-nodeport.yaml
创建过一个 Service?
➜ ~ kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.105.221.35 <none> 80:31255/TCP,443:30786/TCP 1h16m ➜ ~ ping 10.105.221.35 # ping不同 PING 10.105.221.35 (10.105.221.35) 56(84) bytes of data. ^C --- 10.105.221.35 ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2011ms ➜ ~ curl 10.105.221.35 # 但是curl可以 <html> <head><title>404 Not Found</title></head> <body> <center><h1>404 Not Found</h1></center> <hr><center>openresty/1.15.8.2</center> </body> </html> ➜ ~ curl localhost:31255 # 使用Node的对应随机端口也可以 <html> <head><title>404 Not Found</title></head> <body> <center><h1>404 Not Found</h1></center> <hr><center>openresty/1.15.8.2</center> </body> </html> |
我们可以直接用服务的 NodePort,另外注意服务 ip 是虚拟的可以访问但是不能 ping 通。选择 2 层 Nginx 还有一个原因,就是本机 Nginx 里面还有其他 Web 服务的配置,需要不影响它们。
ok,到这里我们可以直接把这部分写入本机的 Nginx 配置中:
# 新建/etc/nginx/sites-enabled/lyanna.conf server { server_name blog.dongwm.com; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header X-NginX-Proxy true; proxy_redirect off; proxy_pass http://10.105.221.35; proxy_http_version 1.1; } } |
重启 Nginx 后,访问http://blog.dongwm.com/就可以看到博客效果了 (当然前提是要在域名解析里面加好 blog.dongwm.com 的 A 记录)!
支持 HTTPS
本来我是想在 Nginx Ingress+Let’s Encrypt 实现外部服务的自动化 HTTPS (用 cert-manager,具体文档延伸阅读链接 9),现在既然外部有 Nginx,还用原来的 Certbot 实现即可:
➜ ~ sudo certbot -d blog.dongwm.com
... # 省略输出
Congratulations! You have successfully enabled https://blog.dongwm.com
...
|
现在再访问https://blog.dongwm.com就可以啦,如果访问http://blog.dongwm.com会自动跳转到https://blog.dongwm.com
管理 local_settings.py
在 k8s 中管理文件我觉得最好的方法是用 ConfigMap,看一下 k8s/config.yaml 的 ConfigMap 中的最后一部分:
apiVersion: v1 kind: ConfigMap metadata: name: lyanna-cfg data: ... local_settings.py: | SITE_TITLE = '小明明s à domicile(Kubernetes版)' BLOG_URL = 'https://blog.dongwm.com' # 省略了很多配置 |
接着在 Deployment 的容器配置中挂载它:
containers: - image: dongweiming/lyanna:latest ... volumeMounts: - name: config-volume mountPath: /app/local_settings.py subPath: local_settings.py volumes: - name: config-volume configMap: name: lyanna-cfg |
这样就可以了。
后记
很曲折,不过终于都解决了。重新kubectl apply
让全部修改生效,就完成了。大家现在可以访问https://blog.dongwm.com/体验 Kubernetes 版的 lyanna 博客效果啦~
Kubernetes 版本的问题
现在博客主页面还是原来的https://www.dongwm.com/,是因为我觉得切换的时机还不到,因为在整个过程我发现了很多问题:
- 访问速度慢。我对博客页面打开速度的要求是 & lt;100ms,期望超过一半的请求能 & lt;50ms。但是 k8s 版本博客我实测 300-700ms,偶尔请求会超过 2s!这是无法接受的。这其中原因很多,如 2 层 Nginx 架构、Memcached、lyanna app (Gunicorn) 等进程不直接在本机运行而是在容器 (需要网络请求)、服务器配置低影响 k8s 性能
- 占用服务器资源。现在博客的负载基本在 4+(原来基本 & lt;0.3)、CPU>60%(原来 & lt;20%)、MEM>96%(原来 & lt;40%),可以感受到差别是巨大的。虽然之前在配置文件中对相关应用的 Pod 的资源占用都做了限制,但是对于 k8s 自己和 Nginx-ingress 的 Pod 都没有做限制,如果那个环节资源请求提上来就会直接把内存占满,现在这个 96% 可以侧面说明情况确实如此
- kubectl apply 卡。在本机 macOS 里面其实也会 k8s 做了资源限制 (可以看 Docker Desktop 的 Advanced Tab), 我这里是默认的 2CPU+2MEM,可见比服务器的还少,从来不会
kubectl apply -f XX
会卡住 5-10 分钟,哪怕cd lyanna
这样的命令也会卡很久,这段时间服务器负载可以达到 50+!虽然不做和 lyanna 有关的不怎么卡,但这也太吓人了。不知道这是服务器资源太低还是因为不是物理机器 (前面说了,我的服务器其实是也是个虚拟机)
我觉得大部分开发者的博客服务器配置都不会高于我这个吧?所以个人博客使用 k8s 目前看不太合适。当然如果未来我能解决这一系列问题,就另当别论了。
延伸阅读
- https://kubernetes.io/zh/
- https://juejin.im/entry/599d33ad6fb9a0247804d430
- https://github.com/nginxinc/kubernetes-ingress/blob/master/docs/nginx-ingress-controllers.md
- https://github.com/kubernetes/ingress-nginx/issues/739
- https://kubernetes.io/zh/docs/concepts/configuration/manage-compute-resources-container/
- https://kubernetes.io/zh/docs/concepts/overview/working-with-objects/annotations/
- https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
- https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
- https://docs.cert-manager.io/