Py学习  »  Python

Python项目容器化实践(八) - 将lyanna应用部署在本地Kubernetes上运行

Python之美 • 6 年前 • 545 次点击  

上篇已经演示了lyanna用到的Memcached、Redis、Mariadb、arq等内容,这篇介绍lyanna应用以及怎么在本地跑起来它。

lyanna应用

应用配置都在k8s/app.yaml里面,直接在代码中注释:

  1. apiVersion: apps/v1

  2. kind: Deployment # lyanna应用是无状态的应用,所以选择用Deployment这种资源对象

  3. metadata:

  4. name: lyanna-deployment

  5. labels:

  6. app.kubernetes.io/name: lyanna

  7. spec:

  8. replicas: 4 # 启动4个副本(进程)接受请求

  9. selector:

  10. matchLabels:

  11. app.kubernetes.io/name: lyanna # 这个和Service部分一致,服务请求会打到符合这个标记的Pod上

  12. strategy:

  13. rollingUpdate: # 使用滚动更新

  14. maxSurge: 25% # 指定可以超过期望的Pod数量的最大百分比,当然写个数也可以,这里表示最多可以部署5个

  15. maxUnavailable: 25% # 指定在升级过程中不可用Pod的最大数量,这里表示允许最多一个Pod不可用

  16. type: RollingUpdate

  17. template:

  18. metadata:

  19. labels:

  20. app.kubernetes.io/name: lyanna

  21. spec:

  22. containers : # Pod里只有一个容器

  23. - image: dongweiming/lyanna:latest # 每次push代码到master后,Docker服务会自动构建latest标签的镜像

  24. name: lyanna-web

  25. command: ['sh', '-c', './gunicorn app:app --bind 0.0.0.0:8000 --worker-class sanic.worker.GunicornWorker --threads=2'] # 运行的命令和之前类似,没有直接用python app.py,而是用Python应用服务器gunicorn

  26. env: # 指定环境变量,之前配置的各种服务都是通过环境变量让应用获得的(当然变量的值可能是一个内部域名)

  27. - name: MEMCACHED_HOST

  28. valueFrom:

  29. configMapKeyRef:

  30. name: lyanna-cfg

  31. key: memcached_host

  32. - name: DB_URL

  33. valueFrom:

  34. configMapKeyRef:

  35. name: lyanna-cfg

  36. key: db_url

  37. - name: REDIS_SENTINEL_SVC_HOST

  38. valueFrom:

  39. configMapKeyRef:

  40. name: lyanna-cfg

  41. key: redis_sentinel_host

  42. - name: REDIS_SENTINEL_SVC_POST

  43. valueFrom:

  44. configMapKeyRef:

  45. name: lyanna-cfg

  46. key: redis_sentinel_port

  47. - name: PYTHONPATH # 依然需要PYTHONPATH

  48. value: $PYTHONPATH:/usr/local/src/aiomcache:/usr/local/src/tortoise:/usr/local/src/arq:/usr/local/src

  49. #imagePullPolicy: Always

  50. imagePullPolicy: IfNotPresent

  51. ports:

  52. - containerPort: 8000

  53. name: http

  54. protocol: TCP

  55. initContainers: # 使用了init容器配置,也就是先要做下面三种检查,都可用了才启动容器lyanna-web

  56. - name: init-memcached # 确认memcached服务正常

  57. image: busybox

  58. command: ['sh', '-c', 'until nslookup lyanna-memcached; do echo waiting for Memcached; sleep 0.5; done;']

  59. - name: init-redis # 确认redis服务正常

  60. image: busybox

  61. command: ['sh', '-c', 'until nslookup redis-sentinel; do echo waiting for Redis; sleep 0.5; done;']

  62. - name: init-mariadb # 确认mariadb服务正常

  63. image: busybox

  64. command: ['sh', '-c', 'until nslookup lyanna-mariadb; do echo waiting for MariaDB; sleep 0.5; done;']

  65. restartPolicy: Always

  66. ---

  67. apiVersion: v1

  68. kind: Service # 对外提供Service的配置

  69. metadata:

  70. name: lyanna-svc

  71. labels:

  72. app.kubernetes.io/name: lyanna

  73. spec:

  74. ports:

  75. - name: http

  76. port: 80 # 暴露80端口

  77. protocol: TCP

  78. targetPort : 8000 # 流量转发到Pod的8000端口

  79. selector:

  80. app.kubernetes.io/name: lyanna # 这个要和前面Deployment的选择器条件一致

  81. sessionAffinity: ClientIP # 用户的所有后续请求都会被分发到同一台应用服务器上

  82. type: NodePort

  83. ---

  84. apiVersion: extensions/v1beta1

  85. kind: Ingress # Ingress服务

  86. metadata:

  87. name: lyanna-ing

  88. labels:

  89. app.kubernetes.io/name: lyanna

  90. spec:

  91. rules:

  92. - host: lyanna.local

  93. http:

  94. paths:

  95. - backend:

  96. serviceName: lyanna-svc # 后端都由lyanna-svc服务来处理

  97. servicePort: 80

  98. path: /

这里面有2个重点:

command。容器里面运行的命令没有指定workers参数(-w),也就是没有显式的控制Gunicorn进程数,事实上在容器里面只运行了一个Master+一个Worker。一开始我理解它是单个进程,所以按照之前官方优化建议(延伸阅读链接1)加了这么一段

  1. --workers=`python -c "import multiprocessing;print(multiprocessing.cpu_count() * 2 + 1)"`

虚拟机一会就卡顿到崩溃甚至需要我重建虚拟机... 一个不成熟的建议是希望大家不要试图在容器中使用多进程。

Ingress。之前的文章中我使用Traefik作为Ingress控制器,后来问了下我厂用的是官方的NGINX Ingress(延伸阅读链接2),所以这次我也换用了它。另外用那种Ingress控制器,配置文件都是一样的不用动。

安装NGINX Ingress

在Minikube虚拟机搭建NGINX Ingress非常方便:

  1. minikube addons enable ingress

  2. ingress was successfully enabled

  3. echo "$(minikube ip) lyanna.local" | sudo tee -a /etc/hosts # 指一下本机的host文件,一会访问这个域名就能看到本地运行效果了

这样就可以了,稍等一会可以在系统Pod列表中找到控制器是运行状态:

  1. kubectl get pods -n kube-system

  2. kubectl get pods -n kube-system | grep nginx- ingress-controller

  3. nginx-ingress-controller-57bf9855c8-lb8nl 1/1 Running 71 24h

如果发现镜像没有拉下来是网络问题,可以使用代理后手动pull或者使用国内源再打回标签:

  1. # 方法1: 使用代理后

  2. docker pull quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.1 # 未来可能镜像版本会改变

  3. # 方法2: 使用国内源

  4. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:0.25.1

  5. docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:0.25.1 quay.io/kubernetes-ingress -controller/nginx-ingress-controller:0.25.1

  6. docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:0.25.1

本地部署lyanna

我重新把整个部署过程演示一遍:

  1. minikube start --vm-driver hyperkit --cache-images --image-repository =registry.cn-hangzhou.aliyuncs.com/google_containers # 启动使用hyperkit驱动的虚拟机,使用国内源

  2. minikube ssh # 进入虚拟机

  3. $ sudo mkdir -p /etc/docker

  4. $ sudo tee /etc/docker/daemon.json <'EOF' # 添加国内源,要不然拉镜像很慢

  5. {

  6. "registry-mirrors" : [

  7. "http://hub-mirror.c.163.com",

  8. "http://docker.mirrors.ustc.edu.cn",

  9. "http://dockerhub.azk8s.cn"

  10. ],

  11. "experimental" : false,

  12. "debug" : true

  13. }

  14. EOF

  15. $ sudo systemctl daemon-reload

  16. $ sudo systemctl restart docker # 重启Docker进程

  17. $ exit # 退出虚拟机

  18. kubectl apply -f k8s/ -R # 创建lyanna需要的全部资源对象

  19. deployment.apps/lyanna-deployment created

  20. service/lyanna-svc created

  21. ingress.extensions/lyanna-ing created

  22. daemonset.apps/lyanna- arq created

  23. configmap/lyanna-cfg created

  24. statefulset.apps/lyanna-memcached created

  25. service/lyanna-memcached created

  26. persistentvolume/mariadb-master created

  27. persistentvolume/mariadb-slave created

  28. configmap/lyanna-mariadb-master created

  29. configmap/lyanna-mariadb-slave created

  30. statefulset.apps/lyanna-mariadb-master created

  31. statefulset.apps/lyanna-mariadb-slave created

  32. service/lyanna-mariadb created

  33. service/lyanna-mariadb-slave created

  34. pod/redis-master created

  35. service/redis-sentinel created

  36. replicaset.apps/redis created

  37. replicaset.apps/redis-sentinel created

  38. # 第一次需要拉镜像需要等几分钟,之后再看都应该是Running状态

  39. kubectl get pods

  40. NAME READY STATUS RESTARTS AGE

  41. lyanna-arq-g22j8 1/1 Running 1 8m45s

  42. lyanna-deployment-5c59c6b5d5-6lvwn 1/1 Running 0 8m45s

  43. lyanna-deployment-5c59c6b5d5-7hz5c 1/1 Running 0 8m44s

  44. lyanna-deployment-5c59c6b5d5-8vmcv 1/1 Running 0 8m44s

  45. lyanna-deployment-5c59c6b5d5-rqf78 1/1 Running 0 8m44s

  46. lyanna-mariadb-master-0 1/1 Running 0 8m44s

  47. lyanna-mariadb-slave-0 1/1 Running 0 8m45s

  48. lyanna-memcached-0 1/1 Running 0 8m45s

  49. lyanna-memcached-1 1/1 Running 0 4m32s

  50. lyanna-memcached-2 1/1 Running 0 4m24s

  51. redis- jnrwn 1/1 Running 1 8m44s

  52. redis-kzkv5 1/1 Running 1 8m44s

  53. redis-master 2/2 Running 0 8m46s

  54. redis-sentinel-6v5kq 1/1 Running 0 8m44s

  55. # 基本看Pod的名字就知道它属于那一部分了

  56. # 接着需要支持初始化脚本setup.sh,在有lyanna源代码的任意Pod上执行即可,例如下面这样:

  57. kubectl exec -it lyanna-deployment-5c59c6b5d5-6lvwn -- sh -c ./setup.sh

  58. Init Finished!

  59. User admin created!!! ID: 1

  60. # 在看看Service/DaemonSet/StatefulSet/ReplicaSet/Ingress

  61. kubectl get svc # Service

  62. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

  63. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16m # 系统自己的

  64. lyanna-mariadb ClusterIP None <none> 3306/TCP 12m

  65. lyanna-mariadb-slave ClusterIP None <none> 3306/TCP 12m

  66. lyanna-memcached ClusterIP None <none> 11211/TCP 12m

  67. lyanna-svc NodePort 10.104.253.19 <none> 80:30362/TCP 12m

  68. redis-sentinel ClusterIP 10.102.28.182 <none> 26379/TCP 12m

  69. kubectl get ds # DaemonSet

  70. NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE

  71. lyanna-arq 1 1 1 1 1 <none> 12m

  72. kubectl get statefulset

  73. NAME READY AGE

  74. lyanna-mariadb-master 1/1 13m

  75. lyanna-mariadb-slave 1/1 13m

  76. lyanna-memcached 3/3 13m

  77. kubectl get rs # ReplicaSet

  78. NAME DESIRED CURRENT READY AGE

  79. lyanna-deployment-5c59c6b5d5 4 4 4 13m

  80. redis 2 2 2 13m

  81. redis-sentinel 2 2 2 13m

  82. kubectl get ing # Ingress

  83. NAME HOSTS ADDRESS PORTS AGE

  84. lyanna-ing lyanna.local 80 14m

到这里,一切正常,浏览器访问

有同学问「为什么MariaDB的配置要放在独立的子目录下(k8s/optional)?」,前面介绍了我的博客运行在云服务器上,用的是云数据库所以使用k8s部署时不需要跑MariaDB,我不需要使用 - R参数:

  1. kubectl apply -f k8s/ # 这样就不创建子目录下的那部分资源对象了

当然Redis、Memcached等都可以作为可选的服务安装,只要修改k8s/config.yaml文件对应的设置项能正确连接即可。

后记

全部源码能在lyanna项目的k8s目录下找到

延伸阅读

  1. http://docs.gunicorn.org/en/latest/design.html#how-many-workers

  2. https://github.com/kubernetes/ingress-nginx

  3. https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/

  4. https://github.com/dongweiming/lyanna/tree/master/k8s


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/49709