• 部署 Wordpress 示例
    • 一个Pod
    • 两个Pod
    • 提高稳定性

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

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