部署 Wordpress 示例

前面的课程中我们基本上了解了Kubernetes当中常见的一些对象资源,这节课我们就来利用前面学习的知识点来部署一个实际的应用 - 将Wordpress应用部署到我们的集群当中,我们前面是不是已经用docker-compose的方式部署过了,我们可以了解到要部署一个Wordpress应用主要涉及到两个镜像:wordpressmysqlwordpress是应用的核心程序,mysql是用于数据存储的。

现在我们来看看如何来部署我们的这个wordpress应用

一个Pod

我们知道一个Pod中可以包含多个容器,那么很明显我们这里是不是就可以将wordpress部署成一个独立的Pod?我们将我们的应用都部署到blog这个命名空间下面,所以先创建一个命名空间:

  1. $ kubectl create namespace blog
  2. namespace "blog" created

然后来编写YAML文件:(wordpress-pod.yaml)

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: wordpress
  5. namespace: blog
  6. spec:
  7. containers:
  8. - name: wordpress
  9. image: wordpress
  10. ports:
  11. - containerPort: 80
  12. name: wdport
  13. env:
  14. - name: WORDPRESS_DB_HOST
  15. value: localhost: 3306
  16. - name: WORDPRESS_DB_USER
  17. value: wordpress
  18. - name: WORDPRESS_DB_PASSWORD
  19. value: wordpress
  20. - name: mysql
  21. image: mysql:5.7
  22. imagePullPolicy: IfNotPresent
  23. args: # 新版本镜像有更新,需要使用下面的认证插件环境变量配置才会生效
  24. - --default_authentication_plugin=mysql_native_password
  25. - --character-set-server=utf8mb4
  26. - --collation-server=utf8mb4_unicode_ci
  27. ports:
  28. - containerPort: 3306
  29. name: dbport
  30. env:
  31. - name: MYSQL_ROOT_PASSWORD
  32. value: rootPassW0rd
  33. - name: MYSQL_DATABASE
  34. value: wordpress
  35. - name: MYSQL_USER
  36. value: wordpress
  37. - name: MYSQL_PASSWORD
  38. value: wordpress
  39. volumeMounts:
  40. - name: db
  41. mountPath: /var/lib/mysql
  42. volumes:
  43. - name: db
  44. hostPath:
  45. path: /var/lib/mysql

要注意这里针对mysql这个容器我们做了一个数据卷的挂载,这是为了能够将mysql的数据能够持久化到节点上,这样下次mysql容器重启过后数据不至于丢失。 然后创建上面的Pod:

  1. $ kubectl create -f wrodpress-pod.yaml
  2. pod "wordpress" created

接下来就是等待拉取镜像,启动容器,同样我们可以使用describe指令查看详细信息:

  1. $ kubectl describe pod wordpress -n blog

大家可以看看我们现在这种单一Pod的方式有什么缺点?假如我们现在需要部署3个Wordpress的副本,该怎么办?是不是我们只需要在上面的YAML文件中加上replicas: 3这个属性就可以了啊?但是有个什么问题呢?是不是不仅仅是Wordpress这个容器会被部署成3份,连我们的MySQL数据库也会被部署成3份了呢?MySQL数据库单纯的部署成3份他们能联合起来使用吗?不能,如果真的这么简单的话就不需要各种数据库集群解决方案了,所以我们这里部署3个Pod实例,实际上他们互相之间是独立的,因为数据不想通,明白吧?所以该怎么办?拆分呗,把wordpressmysql这两个容器部署成独立的Pod是不是就可以了。

另外一个问题是我们的wordpress容器需要去连接mysql数据库吧,现在我们这里放在一起能保证mysql先启动起来吗?貌似没有特别的办法,前面学习的InitContainer也是针对Pod来的,所以无论如何,我们都需要将他们进行拆分。

两个Pod

现在来把上面的一个Pod拆分成两个Pod,我们前面也反复强调过要使用Deployment来管理我们的Pod,上面只是为了单纯给大家说明怎么来把前面的Docker环境下的wordpress转换成Kubernetes环境下面的Pod,有了上面的Pod模板,我们现在来转换成Deployment很容易了吧。

第一步,创建一个MySQLDeployment对象:(wordpress-db.yaml)

  1. ---
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: mysql-deploy
  6. namespace: blog
  7. labels:
  8. app: mysql
  9. spec:
  10. selector:
  11. matchLabels:
  12. app: mysql
  13. template:
  14. metadata:
  15. labels:
  16. app: mysql
  17. spec:
  18. containers:
  19. - name: mysql
  20. image: mysql:5.7
  21. imagePullPolicy: IfNotPresent
  22. args:
  23. - --default_authentication_plugin=mysql_native_password
  24. - --character-set-server=utf8mb4
  25. - --collation-server=utf8mb4_unicode_ci
  26. ports:
  27. - containerPort: 3306
  28. name: dbport
  29. env:
  30. - name: MYSQL_ROOT_PASSWORD
  31. value: rootPassW0rd
  32. - name: MYSQL_DATABASE
  33. value: wordpress
  34. - name: MYSQL_USER
  35. value: wordpress
  36. - name: MYSQL_PASSWORD
  37. value: wordpress
  38. volumeMounts:
  39. - name: db
  40. mountPath: /var/lib/mysql
  41. volumes:
  42. - name: db
  43. hostPath:
  44. path: /var/lib/mysql

如果我们只创建上面的Deployment这个对象,那么我们应该怎样让后面的Wordpress来访问呢?貌似没办法是吧,之前在一个Pod里面还可以使用localhost来进行访问,现在分开了该怎样访问呢?还记得前面的Service吗?没错,使用Service就可以了,所以我们在上面的wordpress-db.yaml文件中添加上Service的信息:

  1. ---
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: mysql
  6. namespace: blog
  7. spec:
  8. selector:
  9. app: mysql
  10. ports:
  11. - name: mysqlport
  12. protocol: TCP
  13. port: 3306
  14. targetPort: dbport

然后创建上面的wordpress-db.yaml文件:

  1. $ kubectl create -f wordpress-db.yaml
  2. service "mysql" created
  3. deployment.apps "mysql-deploy" created

然后我们查看Service的详细情况:

  1. $ kubectl describe svc mysql -n blog
  2. Name: mysql
  3. Namespace: blog
  4. Labels: <none>
  5. Annotations: <none>
  6. Selector: app=mysql
  7. Type: ClusterIP
  8. IP: 10.98.27.19
  9. Port: mysqlport 3306/TCP
  10. TargetPort: dbport/TCP
  11. Endpoints: 10.244.2.213:3306
  12. Session Affinity: None
  13. Events: <none>

可以看到Endpoints部分匹配到了一个Pod,生成了一个clusterIP10.98.27.19,现在我们是不是就可以通过这个clusterIP加上定义的3306端口就可以正常访问我们这个mysql服务了。

第二步. 创建Wordpress服务,将上面的wordpressPod转换成Deployment对象:(wordpress.yaml)

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: wordpress-deploy
  5. namespace: blog
  6. labels:
  7. app: wordpress
  8. spec:
  9. selector:
  10. matchLabels:
  11. app: wordpress
  12. template:
  13. metadata:
  14. labels:
  15. app: wordpress
  16. spec:
  17. containers:
  18. - name: wordpress
  19. image: wordpress
  20. imagePullPolicy: IfNotPresent
  21. ports:
  22. - containerPort: 80
  23. name: wdport
  24. env:
  25. - name: WORDPRESS_DB_HOST
  26. value: 10.98.27.19:3306
  27. - name: WORDPRESS_DB_USER
  28. value: wordpress
  29. - name: WORDPRESS_DB_PASSWORD
  30. value: wordpress

注意这里的环境变量WORDPRESS_DB_HOST的值将之前的localhost地址更改成了上面mysql服务的clusterIP地址了,然后创建上面的Deployment对象:

  1. $ kubectl create -f wordpress.yaml
  2. deployment.apps "wordpress-deploy" created

创建完成后,我们可以看看我们创建的Pod的状态:

  1. $ kubectl get pods -n blog
  2. NAME READY STATUS RESTARTS AGE
  3. mysql-deploy-86bdcc7484-fv2dj 1/1 Running 0 19m
  4. wordpress-deploy-784cfd6dd4-d9f52 1/1 Running 0 23s

可以看到都已经是Running状态了,然后我们需要怎么来验证呢?是不是去访问下我们的wordpress服务就可以了,要访问,我们就需要建立一个能让外网用户访问的Service,前面我们学到过是不是NodePort类型的Service就可以?所以在上面的wordpress.yaml文件中添加上Service的信息:

  1. ---
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: wordpress
  6. namespace: blog
  7. spec:
  8. type: NodePort
  9. selector:
  10. app: wordpress
  11. ports:
  12. - name: wordpressport
  13. protocol: TCP
  14. port: 80
  15. targetPort: wdport

注意要添加属性type: NodePort,然后重新更新wordpress.yaml文件:

  1. $ kubectl apply -f wordpress.yaml
  2. service "wordpress" created
  3. Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
  4. deployment.apps "wordpress-deploy" configured

创建完成后,查看下svc

  1. $ kubectl get svc -n blog
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. mysql ClusterIP 10.98.27.19 <none> 3306/TCP 25m
  4. wordpress NodePort 10.101.7.69 <none> 80:32255/TCP 1m

可以看到wordpress服务产生了一个32255的端口,现在我们是不是就可以通过任意节点的NodeIP加上32255端口,就可以访问我们的wordpress应用了,在浏览器中打开,如果看到wordpress跳转到了安装页面,证明我们的嗯安装是没有任何问题的了,如果没有出现预期的效果,那么就需要去查看下Pod的日志来查看问题了: wordpress

然后根据页面提示,填上对应的信息,点击“安装”即可,最终安装成功后,我们就可以看到熟悉的首页界面了: wordpress-home

提高稳定性

现在wordpress应用已经部署成功了,那么就万事大吉了吗?如果我们的网站访问量突然变大了怎么办,如果我们要更新我们的镜像该怎么办?如果我们的mysql服务挂掉了怎么办?

所以要保证我们的网站能够非常稳定的提供服务,我们做得还不够,我们可以通过做些什么事情来提高网站的稳定性呢?

第一. 增加健康检测,我们前面说过liveness proberediness probe是提高应用稳定性非常重要的方法:

  1. livenessProbe:
  2. tcpSocket:
  3. port: 80
  4. initialDelaySeconds: 3
  5. periodSeconds: 3
  6. readinessProbe:
  7. tcpSocket:
  8. port: 80
  9. initialDelaySeconds: 5
  10. periodSeconds: 10

增加上面两个探针,每10s检测一次应用是否可读,每3s检测一次应用是否存活

第二. 增加 HPA,让我们的应用能够自动应对流量高峰期:

  1. $ kubectl autoscale deployment wordpress-deploy --cpu-percent=10 --min=1 --max=10 -n blog
  2. deployment "wordpress-deploy" autoscaled

我们用kubectl autoscale命令为我们的wordpress-deploy创建一个HPA对象,最小的 pod 副本数为1,最大为10,HPA会根据设定的 cpu使用率(10%)动态的增加或者减少pod数量。当然最好我们也为Pod声明一些资源限制:

  1. resources:
  2. limits:
  3. cpu: 200m
  4. memory: 200Mi
  5. requests:
  6. cpu: 100m
  7. memory: 100Mi

更新Deployment后,我们可以可以来测试下上面的HPA是否会生效:

  1. $ kubectl run -i --tty load-generator --image=busybox /bin/sh
  2. If you don't see a command prompt, try pressing enter.
  3. / # while true; do wget -q -O- http://10.244.1.62:80; done

观察Deployment的副本数是否有变化

  1. $ kubectl get deployment wordpress-deploy
  2. NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
  3. wordpress-deploy 3 3 3 3 4d

第三. 增加滚动更新策略,这样可以保证我们在更新应用的时候服务不会被中断:

  1. replicas: 2
  2. revisionHistoryLimit: 10
  3. minReadySeconds: 5
  4. strategy:
  5. type: RollingUpdate
  6. rollingUpdate:
  7. maxSurge: 1
  8. maxUnavailable: 1

第四. 我们知道如果mysql服务被重新创建了的话,它的clusterIP非常有可能就变化了,所以上面我们环境变量中的WORDPRESS_DB_HOST的值就会有问题,就会导致访问不了数据库服务了,这个地方我们可以直接使用Service的名称来代替host,这样即使clusterIP变化了,也不会有任何影响,这个我们会在后面的服务发现的章节和大家深入讲解的:

  1. env:
  2. - name: WORDPRESS_DB_HOST
  3. value: mysql:3306

第五. 我们在部署wordpress服务的时候,mysql服务以前启动起来了吗?如果没有启动起来是不是我们也没办法连接数据库了啊?该怎么办,是不是在启动wordpress应用之前应该去检查一下mysql服务,如果服务正常的话我们就开始部署应用了,这是不是就是InitContainer的用法:

  1. initContainers:
  2. - name: init-db
  3. image: busybox
  4. command: ['sh', '-c', 'until nslookup mysql; do echo waiting for mysql service; sleep 2; done;']

直到mysql服务创建完成后,initContainer才结束,结束完成后我们才开始下面的部署。

最后,我们把部署的应用整合到一个YAML文件中来:(wordpress-all.yaml)

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: mysql
  5. namespace: blog
  6. labels:
  7. app: mysql
  8. spec:
  9. selector:
  10. matchLabels:
  11. app: mysql
  12. template:
  13. metadata:
  14. labels:
  15. app: mysql
  16. spec:
  17. containers:
  18. - name: mysql
  19. image: mysql:5.7
  20. args:
  21. - --default_authentication_plugin=mysql_native_password
  22. - --character-set-server=utf8mb4
  23. - --collation-server=utf8mb4_unicode_ci
  24. ports:
  25. - containerPort: 3306
  26. name: dbport
  27. env:
  28. - name: MYSQL_ROOT_PASSWORD
  29. value: rootPassW0rd
  30. - name: MYSQL_DATABASE
  31. value: wordpress
  32. - name: MYSQL_USER
  33. value: wordpress
  34. - name: MYSQL_PASSWORD
  35. value: wordpress
  36. volumeMounts:
  37. - name: db
  38. mountPath: /var/lib/mysql
  39. volumes:
  40. - name: db
  41. hostPath:
  42. path: /var/lib/mysql
  43. ---
  44. apiVersion: v1
  45. kind: Service
  46. metadata:
  47. name: mysql
  48. namespace: blog
  49. spec:
  50. selector:
  51. app: mysql
  52. ports:
  53. - name: mysqlport
  54. protocol: TCP
  55. port: 3306
  56. targetPort: dbport
  57. ---
  58. apiVersion: apps/v1
  59. kind: Deployment
  60. metadata:
  61. name: wordpress
  62. namespace: blog
  63. labels:
  64. app: wordpress
  65. spec:
  66. selector:
  67. matchLabels:
  68. app: wordpress
  69. minReadySeconds: 5
  70. strategy:
  71. type: RollingUpdate
  72. rollingUpdate:
  73. maxSurge: 1
  74. maxUnavailable: 1
  75. template:
  76. metadata:
  77. labels:
  78. app: wordpress
  79. spec:
  80. initContainers:
  81. - name: init-db
  82. image: busybox
  83. command: ['sh', '-c', 'until nslookup mysql; do echo waiting for mysql service; sleep 2; done;']
  84. containers:
  85. - name: wordpress
  86. image: wordpress
  87. imagePullPolicy: IfNotPresent
  88. ports:
  89. - containerPort: 80
  90. name: wdport
  91. env:
  92. - name: WORDPRESS_DB_HOST
  93. value: mysql:3306
  94. - name: WORDPRESS_DB_USER
  95. value: wordpress
  96. - name: WORDPRESS_DB_PASSWORD
  97. value: wordpress
  98. readinessProbe:
  99. tcpSocket:
  100. port: 80
  101. initialDelaySeconds: 5
  102. periodSeconds: 10
  103. resources:
  104. limits:
  105. cpu: 200m
  106. memory: 256Mi
  107. requests:
  108. cpu: 100m
  109. memory: 100Mi
  110. ---
  111. apiVersion: v1
  112. kind: Service
  113. metadata:
  114. name: wordpress
  115. namespace: blog
  116. spec:
  117. selector:
  118. app: wordpress
  119. type: NodePort
  120. ports:
  121. - name: wordpressport
  122. protocol: TCP
  123. port: 80
  124. nodePort: 32255
  125. targetPort: wdport

我们这里主要是针对的wordpress来做的提高稳定性的方法,如何对mysql提高一些稳定性呢?大家下去可以试一试,我们接下来会和大家讲解mysql这类有状态的应用在Kubernetes当中的使用方法。

最后,我们来把前面我们部署的相关服务全部删掉,重新通过上面的YAML文件来创建:

  1. $ kubectl create -f wordpress-all.yaml
  2. deployment.apps "mysql-deploy" created
  3. service "mysql" created
  4. deployment.apps "wordpress-deploy" created
  5. service "wordpress" created

看看最后能不能得到我们的最终成果呢?


点击查看本文视频

扫描下面的二维码(或微信搜索k8s技术圈)关注我们的微信公众帐号,在微信公众帐号中回复 加群 即可加入到我们的 kubernetes 讨论群里面共同学习。

k8s技术圈二维码