不会飞的章鱼

熟能生巧,勤能补拙;念念不忘,必有回响。

Kubernetes集群部署WordPress网站-版本2

版本更新描述

完全舍弃了 Docker,把所有的应用都放在 Kubernetes 集群里运行,部署方式也不再是裸 Pod,而是使用 Deployment,稳定性大幅度提升。

架构图

原来的 Nginx 的作用是反向代理,那么在 Kubernetes 里它就升级成了具有相同功能的 Ingress Controller。WordPress 原来只有一个实例,现在变成了两个实例(你也可以任意横向扩容),可用性也就因此提高了不少。而 MariaDB 数据库因为要保证数据的一致性,暂时还是一个实例。

因为 Kubernetes 内置了服务发现机制 Service,我们再也不需要去手动查看 Pod 的 IP 地址了,只为它们定义了 Service 对象,然后使用域名就可以访问 MariaDB、WordPress 这些服务。

网站对外提供服务我选择了两种方式:

  • 1,让 WordPress 的 Service 对象以 NodePort 的方式直接对外暴露端口 30088,方便测试;
  • 2,给 Nginx Ingress Controller 添加“hostNetwork”属性,直接使用节点上的端口号,类似 Docker 的 host 网络模式,好处是可以避开 NodePort 的端口范围限制。

搭建步骤

WordPress 网站部署 MariaDB

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: ConfigMap
metadata:
name: maria-cm

data:
DATABASE: 'db'
USER: 'wp'
PASSWORD: '123'
ROOT_PASSWORD: '123'

然后我们需要把 MariaDB 由 Pod 改成 Deployment 的方式,replicas 设置成 1 个,template 里面的 Pod 部分没有任何变化,还是要用 envFrom把配置信息以环境变量的形式注入 Pod,相当于把 Pod 套了一个 Deployment 的“外壳”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: maria-dep
name: maria-dep

spec:
replicas: 1
selector:
matchLabels:
app: maria-dep

template:
metadata:
labels:
app: maria-dep
spec:
containers:
- image: mariadb:10
name: mariadb
ports:
- containerPort: 3306

envFrom:
- prefix: 'MARIADB_'
configMapRef:
name: maria-cm

我们还需要再为 MariaDB 定义一个 Service 对象,映射端口 3306,让其他应用不再关心 IP 地址,直接用 Service 对象的名字来访问数据库服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
labels:
app: maria-dep
name: maria-svc

spec:
ports:
- port: 3306
protocol: TCP
targetPort: 3306
selector:
app: maria-dep

因为这三个对象都是数据库相关的,所以可以在一个 YAML 文件里书写,对象之间用 — 分开,这样用 kubectl apply 就可以一次性创建好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# wp-maria.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: maria-cm

data:
DATABASE: 'db'
USER: 'wp'
PASSWORD: '123'
ROOT_PASSWORD: '123'
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: maria-dep
name: maria-dep

spec:
replicas: 1
selector:
matchLabels:
app: maria-dep

template:
metadata:
labels:
app: maria-dep
spec:
containers:
- image: mariadb:10
name: mariadb
ports:
- containerPort: 3306

envFrom:
- prefix: 'MARIADB_'
configMapRef:
name: maria-cm
---
apiVersion: v1
kind: Service
metadata:
labels:
app: maria-dep
name: maria-svc

spec:
ports:
- port: 3306
protocol: TCP
targetPort: 3306
selector:
app: maria-dep

执行命令后,你应该用 kubectl get 查看对象是否创建成功,是否正常运行:

1
2
3
4
# kubectl apply -f wp-maria.yml 
configmap/maria-cm created
deployment.apps/maria-dep created
service/maria-svc created

WordPress 网站部署 WordPress

因为刚才创建了 MariaDB 的 Service,所以在写 ConfigMap 配置的时候“HOST”就不应该是 IP 地址了,而应该是 DNS 域名,也就是 Service 的名字maria-svc,这点需要特别注意

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: ConfigMap
metadata:
name: wp-cm

data:
HOST: 'maria-svc'
USER: 'wp'
PASSWORD: '123'
NAME: 'db'

WordPress 的 Deployment 写法和 MariaDB 也是一样的,给 Pod 套一个 Deployment 的“外壳”,replicas 设置成 2 个,用字段“envFrom”配置环境变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: wp-dep
name: wp-dep

spec:
replicas: 2
selector:
matchLabels:
app: wp-dep

template:
metadata:
labels:
app: wp-dep
spec:
containers:
- image: wordpress:5
name: wordpress
ports:
- containerPort: 80

envFrom:
- prefix: 'WORDPRESS_DB_'
configMapRef:
name: wp-cm

然后我们仍然要为 WordPress 创建 Service 对象,这里我使用了“NodePort”类型,并且手工指定了端口号“30088”(必须在 30000~32767 之间):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Service
metadata:
labels:
app: wp-dep
name: wp-svc

spec:
ports:
- name: http80
port: 80
protocol: TCP
targetPort: 80
nodePort: 30088

selector:
app: wp-dep
type: NodePort

用 kubectl apply 部署 WordPress:

因为 WordPress 的 Service 对象是 NodePort 类型的,我们可以在集群的每个节点上访问 WordPress 服务。

比如一个节点的 IP 地址是“192.168.10.210”,那么你就在浏览器的地址栏里输入“http://192.168.10.210:30088”,其中的“30088”就是在 Service 里指定的节点端口号,然后就能够看到 WordPress 的安装界面了:

WordPress 网站部署 Nginx Ingress Controller

首先我们需要定义 Ingress Class,名字就叫“wp-ink”:

1
2
3
4
5
6
7
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: wp-ink

spec:
controller: nginx.org/ingress-controller

执行kuebctl apply -f wp-ink.yml后,可能会出现

1
error: unable to recognize "wp-ink.yml": no matches for kind "IngressClass" in version "networking.k8s.io/v1"

这样的报错信息,我当前的kubernetes版本为v.18.9

然后用 kubectl create 命令生成 Ingress 的样板文件,指定域名是“wp.test”,后端 Service 是“wp-svc:80”,Ingress Class 就是刚定义的“wp-ink”:

1
$ kubectl create ing wp-ing --rule="wp.test/=wp-svc:80" --class=wp-ink $out

得到的 Ingress YAML 就是这样,注意路径类型我还是用的前缀匹配“Prefix”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wp-ing

spec:
ingressClassName: wp-ink

rules:
- host: wp.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wp-svc
port:
number: 80

接下来就是最关键的 Ingress Controller 对象了,它仍然需要从 Nginx 项目的示例 YAML 修改而来,要改动名字、标签,还有参数里的 Ingress Class。

这个 Ingress Controller 不使用 Service,而是给它的 Pod 加上一个特殊字段 hostNetwork,让 Pod 能够使用宿主机的网络,相当于另一种形式的 NodePort:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: apps/v1
kind: Deployment
metadata:
name: wp-kic-dep
namespace: nginx-ingress

spec:
replicas: 1
selector:
matchLabels:
app: wp-kic-dep

template:
metadata:
labels:
app: wp-kic-dep

spec:
serviceAccountName: nginx-ingress

# use host network
hostNetwork: true

containers:
...

准备好 Ingress 资源后,我们创建这些对象:

1
$ kubectl apply -f wp-ing.yml -f wp-kic.yml

总结

Kubernetes 是云原生时代的操作系统,它能够管理大量节点构成的集群,让计算资源“池化”,从而能够自动地调度运维各种形式的应用。

搭建多节点的 Kubernetes 集群是一件颇具挑战性的工作,好在社区里及时出现了 kubeadm 这样的工具,可以“一键操作”,使用 kubeadm init、kubeadm join 等命令从无到有地搭建出生产级别的集群。

kubeadm 使用容器技术封装了 Kubernetes 组件,所以只要节点上安装了容器运行时(Docker、containerd 等),它就可以自动从网上拉取镜像,然后以容器的方式运行组件,非常简单方便。

在这个更接近实际生产环境的 Kubernetes 集群里,学习了Deployment、DaemonSet、Service、Ingress、Ingress Controller 等 API 对象:

  • Deployment 是用来管理 Pod 的一种对象,它代表了运维工作中最常见的一类在线业务,在集群中部署应用的多个实例,而且可以很容易地增加或者减少实例数量,从容应对流量压力。
    Deployment 的定义里有两个关键字段:一个是 replicas,它指定了实例的数量;另一个是 selector,它的作用是使用标签“筛选”出被 Deployment 管理的 Pod,这是一种非常灵活的关联机制,实现了 API 对象之间的松耦合。

  • DaemonSet 是另一种部署在线业务的方式,它很类似 Deployment,但会在集群里的每一个节点上运行一个 Pod 实例,类似 Linux 系统里的“守护进程”,适合日志、监控等类型的应用。
    DaemonSet 能够任意部署 Pod 的关键概念是“污点”(taint)和“容忍度”(toleration)。Node 会有各种“污点”,而 Pod 可以使用“容忍度”来忽略“污点”,合理使用这两个概念就可以调整 Pod 在集群里的部署策略。

由 Deployment 和 DaemonSet 部署的 Pod,在集群中处于“动态平衡”的状态,总数量保持恒定,但也有临时销毁重建的可能,所以 IP 地址是变化的,这就为微服务等应用架构带来了麻烦。

  • Service 是对 Pod IP 地址的抽象,它拥有一个固定的 IP 地址,再使用 iptables 规则把流量负载均衡到后面的 Pod,节点上的 kube-proxy 组件会实时维护被代理的 Pod 状态,保证 Service 只会转发给健康的 Pod。
    Service 还基于 DNS 插件支持域名,所以客户端就不再需要关心 Pod 的具体情况,只要通过 Service 这个稳定的中间层,就能够访问到 Pod 提供的服务。
    Service 是四层的负载均衡,但现在的绝大多数应用都是 HTTP/HTTPS 协议,要实现七层的负载均衡就要使用 Ingress 对象。

  • Ingress 定义了基于 HTTP 协议的路由规则,但要让规则生效,还需要 Ingress Controller 和 Ingress Class 来配合工作。
    Ingress Controller 是真正的集群入口,应用 Ingress 规则调度、分发流量,此外还能够扮演反向代理的角色,提供安全防护、TLS 卸载等更多功能。
    Ingress Class 是用来管理 Ingress 和 Ingress Controller 的概念,方便我们分组路由规则,降低维护成本。

不过 Ingress Controller 本身也是一个 Pod,想要把服务暴露到集群外部还是要依靠 Service。Service 支持 NodePort、LoadBalancer 等方式,但 NodePort 的端口范围有限,LoadBalancer 又依赖于云服务厂商,都不是很灵活。折中的办法是用少量 NodePort 暴露 Ingress Controller,用 Ingress 路由到内部服务,外部再用反向代理或者 LoadBalancer 把流量引进来。

------ 本文结束------
如果本篇文章对你有帮助,可以给作者加个鸡腿~(*^__^*),感谢鼓励与支持!