源码地址:https://github.com/nangongchengfeng/Kubernetes/tree/main/wordpress-example
上一篇文件我们使用pod的构建两个容器,但是问题也是一大堆,根本不适合生产方面。
所以我们慢慢解决上面的问题,优化架构。
版本2思路:将 Pod 中的两个容器进行拆分,将 Wordpress 和 MySQL 分别部署
Wordpress 用多个副本进行部署就可以实现应用的高可用了
由于 MySQL 是有状态应用,一般来说需要用 StatefulSet 来进行管理,但是我们这里部署的 MySQL 并不是集群模式,而是单副本的,所以用 Deployment 也是没有问题的。如果生产环境的话,一般都是集群的,分别在宿主机上部署。
mysql资源清单
mysql.yaml
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
namespace: kube-example
labels:
app: wordpress
spec:
selector:
app: wordpress
tier: mysql
ports:
- port: 3306
targetPort: dbport
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
namespace: kube-example
labels:
app: wordpress
tier: mysql
spec:
replicas: 1
template:
metadata:
name: wordpress-mysql
labels:
app: wordpress
tier: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
args:
- --default_authentication_plugin=mysql_native_password
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
ports:
- containerPort: 3306
name: dbport
env:
- name: MYSQL_ROOT_PASSWORD
value: rootPassW0rd
- name: MYSQL_DATABASE
value: wordpress
- name: MYSQL_USER
value: wordpress
- name: MYSQL_PASSWORD
value: wordpress
imagePullPolicy: IfNotPresent
restartPolicy: Always
selector:
matchLabels:
app: wordpress
tier: mysql
这里给 MySQL 应用添加了一个 Service 对象,是因为 Wordpress 应用需要来连接数据库,之前在同一个 Pod 中用 localhost
即可,现在需要通过 Service 的 DNS 形式的域名进行连接。直接创建上面资源对象
# kubectl apply -f mysql.yaml
service/wordpress-mysql created
deployment.apps/wordpress-mysql created
Wordpress 清单
wordpress.yaml
apiVersion: v1
kind: Service
metadata:
name: wordpress
namespace: kube-example
spec:
selector:
app: wordpress
tier: frontend
ports:
- port: 80
name: web
targetPort: wdport
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
namespace: kube-example
labels:
app: wordpress
tier: frontend
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
replicas: 4
template:
metadata:
name: wordpress
labels:
app: wordpress
tier: frontend
spec:
containers:
- name: wordpress
image: wordpress:5.3.2-apache
ports:
- containerPort: 80
name: wdport
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql:3306
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_PASSWORD
value: wordpress
imagePullPolicy: IfNotPresent
restartPolicy: Always
注意这里的环境变量 WORDPRESS_DB_HOST
的值将之前的 localhost
地址更改成了上面 MySQL 服务的 DNS 地址,完整的域名应该是 wordpress-mysql.kube-example.svc.cluster.local:3306
,由于这两个应该都处于同一个命名空间,所以直接简写成 wordpress-mysql:3306
也是可以的。
[root@k8s-master1 v2]# kubectl apply -f wordpress.yaml
service/wordpress created
deployment.apps/wordpress created
[root@k8s-master1 v2]# kubectl get pods -l app=wordpress -n kube-example
NAME READY STATUS RESTARTS AGE
wordpress-ddb4ff6cf-g2xf8 1/1 Running 0 9s
wordpress-ddb4ff6cf-jjt5k 1/1 Running 0 9s
wordpress-ddb4ff6cf-zmbc4 1/1 Running 0 9s
wordpress-mysql-d9b4b8985-bqv2b 1/1 Running 0 32s
测试
可以看到都已经是 Running
状态了,然后我们需要怎么来验证呢?是不是我们能想到的就是去访问下我们的 Wordpress 服务就可以了,我们这里还是使用的一个 NodePort 类型的 Service 来暴露服务:
[root@k8s-master1 v2]# kubectl get svc,ep -n kube-example
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/wordpress NodePort 10.0.0.49 <none> 80:30455/TCP 92s
service/wordpress-mysql ClusterIP 10.0.0.142 <none> 3306/TCP 115s
NAME ENDPOINTS AGE
endpoints/wordpress 10.244.0.5:80,10.244.1.135:80,10.244.1.136:80 92s
endpoints/wordpress-mysql 10.244.1.134:3306 115s
可以看到 wordpress 服务产生了一个 30455的端口,现在我们就可以通过 http://<任意节点的NodeIP>:30012
访问我们的应用了,在浏览器中打开,如果看到 wordpress 跳转到了安装页面,证明我们的安装是正确的,如果没有出现预期的效果,那么就需要去查看下 Pod 的日志来排查问题了,根据页面提示,填上对应的信息,点击“安装”
即可,最终安装成功后
为什么会有单点故障的问题呢?
我们不是部署了多个副本的 Wordpress 应用吗?
当我们设置 replicas=1
的时候肯定会存在单点故障问题,如果大于 1 但是所有副本都调度到了同一个节点的是不是同样就会存在单点问题了
这个节点挂了所有副本就都挂了,所以我们不仅需要设置多个副本数量,还需要让这些副本调度到不同的节点上,来打散避免单点故障
Pod 反亲和性来实现了,我们可以防止单点故障出现。
但是要考虑要用弱策略还是硬策略
我们首先弱的,如果硬策略,如果节点有问题,pod就不会被创建
apiVersion: v1
kind: Service
metadata:
name: wordpress
namespace: kube-example
spec:
selector:
app: wordpress
tier: frontend
ports:
- port: 80
name: web
targetPort: wdport
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
namespace: kube-example
labels:
app: wordpress
tier: frontend
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
replicas: 4 #多副本+pod的反亲合力可以实现pod的高可用
template:
metadata:
name: wordpress
labels:
app: wordpress
tier: frontend
spec:
containers:
- name: wordpress
image: wordpress:5.3.2-apache
ports:
- containerPort: 80
name: wdport
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql:3306
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_PASSWORD
value: wordpress
imagePullPolicy: IfNotPresent
affinity: #pod的反亲和力
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wordpress
restartPolicy: Always
PDB策略
PDB能够限制同时中断的pod的数量,以保证集群的高可用性
有些时候线上的某些节点需要做一些维护操作,比如要升级内核,这个时候我们就需要将要维护的节点进行驱逐操作,驱逐节点首先是将节点设置为不可调度,这样可以避免有新的 Pod 调度上来,然后将该节点上的 Pod 全部删除,ReplicaSet 控制器检测到 Pod 数量减少了就会重新创建一个新的 Pod,调度到其他节点上面的,这个过程是先删除,再创建,并非是滚动更新,因此更新过程中,如果一个服务的所有副本都在被驱逐的节点上,则可能导致该服务不可用。
如果服务本身存在单点故障,所有副本都在同一个节点,驱逐的时候肯定就会造成服务不可用了,这种情况我们使用上面的反亲和性和多副本就可以解决这个问题。但是如果我们的服务本身就被打散在多个节点上,这些节点如果都被同时驱逐的话,那么这个服务的所有实例都会被同时删除,这个时候也会造成服务不可用了,这种情况下我们可以通过配置 PDB(PodDisruptionBudget)对象来避免所有副本同时被删除,比如我们可以设置在驱逐的时候 wordpress 应用最多只有一个副本不可用,其实就相当于逐个删除并在其它节点上重建
pdb.yaml
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wordpress-pdb
namespace: kube-example
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wordpress
tier: frontend
[root@k8s-master1 v2]# kubectl apply -f pdb.yaml
poddisruptionbudget.policy/wordpress-pdb created
[root@k8s-master1 v2]# kubectl get pdb -n kube-example
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
wordpress-pdb N/A 1 1 15s
PDB 的更多详细信息可以查看官方文档:https://kubernetes.io/docs/tasks/run-application/configure-pdb/。
此版本实现高可用,但是还有一些问题
问题(1)pod的健康检查,如果没有这些,pod额度重启策略都无法执行
(2)内存,cpu等资源限制,服务质量Qos
(3)数据持久化
(4)mysql账号密码注入等
这些一些列问题,都会在后面的版本架构中优化