官网链接
本教程将使用PV部署一个WordPress和MySQL。
K8s集群搭建以及镜像源配置
搭建好一套k8s集群,可以参考我写的这篇教程:搭建k8s集群
k8s官方的镜像站在国内是拉不下来的,有几种方法解决:
- 在拉取镜像的虚拟机/服务器上科学上网
- 配置k8s的镜像源,目前国内只有阿里云支持改版后的k8s镜像源(registry.k8s.io)。
- 需要拉取镜像的时候,指定拉取策略为本地拉取(
imagePullPolicy:Never),每次需要拉取镜像前都手动拉取/上传一份镜像到服务器上再导入镜像
这里给出阿里云镜像源的配置教程:
旧版的k8s直接修改/etc/containerd/config.toml里的mirror信息,添加上阿里云的镜像站就行。但是新版的不支持inline或者说暂时兼容,未来不支持。所以这里就只给出新版k8s镜像源配置教程。
修改/etc/containerd/config.yaml,填入下列信息(如果你已经有了config.yaml且这个配置文件是从containerd默认配置里生成的,那直接备份,然后使用下面的内容)。sudo vim /etc/containerd/config.yaml
1
2
3
4
5
6
7
8
9
10
|
version = 2
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
|
创建/etc/containerd/certs.d目录,在这个目录填入docker.io和registry.k8s.io的镜像源。
注意:k8s里修改镜像源之后,使用kubectl describe pod <pod_name> 查看时还是显示的docker.io和registry.k8s.io。配置镜像源只物理修改从哪里修改,不改镜像拉取的逻辑源。所以改好镜像源之后也不太好验证成功,随便拉个镜像sudo crictl pull nginx:1.14.2,能拉下来就是成了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# Docker Hub 加速
sudo mkdir -p /etc/containerd/certs.d/docker.io
sudo tee /etc/containerd/certs.d/docker.io/hosts.toml << 'EOF'
server = "https://registry-1.docker.io"
[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]
EOF
# K8s 镜像加速
sudo mkdir -p /etc/containerd/certs.d/registry.k8s.io
sudo tee /etc/containerd/certs.d/registry.k8s.io/hosts.toml << 'EOF'
server = "https://registry.k8s.io"
[host."https://registry.cn-hangzhou.aliyuncs.com/google_containers"]
capabilities = ["pull", "resolve"]
override_path = true
EOF
|
到这里,镜像源就配置好了,如果不出意外,文件目录应该是下面这样:
1
2
3
4
5
6
7
|
rust@k8s1:/etc/containerd$ ll
总计 28
drwxr-xr-x 3 root root 4096 2月 4 10:31 ./
drwxr-xr-x 144 root root 12288 2月 2 17:01 ../
drwxr-xr-x 4 root root 4096 2月 2 16:44 certs.d/
-rw-r--r-- 1 root root 423 2月 2 19:02 config.toml
-rw-r--r-- 1 root root 886 12月 19 02:48 config.toml.dpkg-dist
|
修改完配置文件后需要重启containerd:
1
2
|
sudo systemctl restart containerd
sudo systemctl status containerd
|
给本地自建k8s集群添加默认存储类
K8s “产品”本身不自带 StorageClass;
但“常见的集群环境”(云厂商、本地发行版)一般都会预置一个或多个 StorageClass,并把其中一个标为默认。例如:
- minikube:通常有一个 standard StorageClass,并标记为 (default)。
- GKE:一般会自带 standard-rwo(或类似名字)默认类。
- EKS:AWS EBS CSI 会安装 gp2 / gp3 类并设为默认
kubectl get sc查看集群存储类。有的话可以跳过此节
- 在三个节点上创建存储基础目录(xcall是自建的脚本)。
后续动态创建PV时,会按PVC来建子目录,每个PVC会有自己的一块目录。
1
2
|
xcall sudo mkdir -p /opt/local-path-provisioner
xcall sudo chmod 755 /opt/local-path-provisioner
|
以后想换路径可以修改local-path-config这个ConfigMap里的nodePathMap配置
- 安装 local-path-provisioner(自带一个 StorageClass:local-path)
直接用官方的 YAML(以当前稳定版 v0.0.34 为例)
1
|
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.34/deploy/local-path-storage.yaml
|
这个 YAML 里面已经包含:
- Namespace:local-path-storage
- RBAC(ServiceAccount / Role / ClusterRole / RoleBinding / ClusterRoleBinding)
- Deployment:local-path-provisioner
- 一个 StorageClass:名字叫 local-path,provisioner 是 rancher.io/local-path
- 一个 ConfigMap:local-path-config,里面配置了默认路径 /opt/local-path-provisioner
- 确认安装成功,检查 Pod 是否 Running,存储类是否已经存在(名字是local-path)
1
2
3
4
5
6
7
8
|
kubectl get pods -n local-path-storage
kubectl describe pod <pod_name> -n local-path-storage
kubectl get sc
rust@k8s1:~/k8s_try$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path rancher.io/local-path Delete WaitForFirstConsumer false 4m40s
|
- 把 local-path 设置为默认StorageClass。
Kubernetes 默认 StorageClass 是通过注解 storageclass.kubernetes.io/is-default-class 来标记的。
local-path-provisioner 的 YAML 里默认没有给它打这个注解,
直接用 kubectl patch 打注解:
1
2
3
4
5
6
7
8
9
10
|
kubectl patch storageclass local-path \
-p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
rust@k8s1:~/k8s_try$ kubectl patch storageclass local-path \
-p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
storageclass.storage.k8s.io/local-path patched
# 打上注解后再查看存储类,发现local-path以标记为默认存储类
rust@k8s1:~/k8s_try$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 9m22s
|
现在集群就可以自动制取持久卷了。
创建PV和PVC
MySQL和Wordpress 都需要一个 PersistentVolume 来存储数据。它们的 PersistentVolumeClaims将在部署步骤中创建。
我们刚刚创建了集群的默认存储类,创建PVC时可以动态创建PV。
创建 kustomization.yaml
创建 Secret 生成器
Secret 是存储诸如密码或密钥之类敏感数据的对象。从 1.14 开始,kubectl 支持使用一个kustomization文件来管理 Kubernetes 对象。可以通过 kustomization.yaml中的生成器创建一个Secret或者ConfigMap。
通过以下命令在 kustomization.yaml 中添加一个 Secret 生成器。请注意将YOUR_PASSWORD替换为自己要用的密码。
1
2
3
4
5
6
|
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: mysql-pass
literals:
- password=YOUR_PASSWORD
EOF
|
补充 MySQL 和 WordPress 的资源配置
注意:MySQL和WordPress的deployment都没有限制资源使用,可能会由于资源使用过量而被驱逐,在生产环境千万不能这么做。
在kustomization.yaml同级目录创建mysql和wordpress的deployment yaml文件。
1
|
vim mysql-deployment.yaml
|
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:8.0
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
- name: MYSQL_DATABASE
value: wordpress
- name: MYSQL_USER
value: wordpress
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
|
1
|
vim wordpress-deployment.yaml
|
注意,由于我的k8s集群是部署在本地的,所以loadBlancer用不了。
下面的yaml文件和官网的不同,区别在Service的type是NodePort。这个问题在k8s实践练习3部署无状态应用的时候也出现了。
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
targetPort: 80 # 明确指定容器端口(通常是80)
nodePort: 30080 # 可选,不指定则系统自动分配一个 30000-32767 之间的端口
selector:
app: wordpress
tier: frontend
type: NodePort
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:6.2.1-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
- name: WORDPRESS_DB_USER
value: wordpress
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim
|
将上述内容追加到 kustomization.yaml 文件
1
2
3
4
5
|
cat <<EOF >>./kustomization.yaml
resources:
- mysql-deployment.yaml
- wordpress-deployment.yaml
EOF
|
应用和验证
kustomization.yaml包含用于部署 WordPress 网站以及 MySQL 数据库的所有资源。通过以下方式应用目录:
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
|
kubectl apply -k ./
# 验证Secret是否存在
kubectl get secrets
# 验证是否已经动态制备PVC
kubectl get pvc
rust@k8s1:~/k8s_try$ kubectl apply -k ./
secret/mysql-pass-kkcc2b926b created
service/wordpress created
service/wordpress-mysql created
persistentvolumeclaim/mysql-pv-claim created
persistentvolumeclaim/wp-pv-claim created
deployment.apps/wordpress created
deployment.apps/wordpress-mysql created
rust@k8s1:~/k8s_try$ kubectl get pods
NAME READY STATUS RESTARTS AGE
wordpress-7ccff4687c-gbfgk 0/1 ContainerCreating 0 8s
wordpress-mysql-5447c445d4-lw47g 1/1 Running 0 8s
rust@k8s1:~/k8s_try$ kubectl get secrets
NAME TYPE DATA AGE
mysql-pass-kkcc2b926b Opaque 1 3m6s
rust@k8s1:~/k8s_try$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-pv-claim Bound pvc-4feb1fcc-f28e-470c-91b1-6f379efffedf 20Gi RWO local-path <unset> 6m5s
wp-pv-claim Bound pvc-8d654ca7-9812-475d-953b-cdc4fd9177f8 20Gi RWO local-path <unset> 6m5s
|
mysql和wordpress的镜像都不小,拉镜像可能要两三分钟,制取和绑定PV可能还需要几分钟。过一段时间后查看pod状态
1
2
3
4
5
6
|
kubectl get pods -o wide
rust@k8s1:~/k8s_try$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
wordpress-7ccff4687c-gbfgk 1/1 Running 0 7m44s 172.16.109.88 k8s2 <none> <none>
wordpress-mysql-5447c445d4-lw47g 1/1 Running 0 7m44s 172.16.109.87 k8s2 <none> <none>
|
验证service状态,EXTERNAL-IP为none。
1
2
3
4
5
|
kubectl get services wordpress
rust@k8s1:~/k8s_try$ kubectl get services wordpress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
wordpress NodePort 10.103.140.50 <none> 80:30080/TCP 43s
|
在浏览器中查看wordPress状态(在wordpress的service文件中指定了端口为30080),访问k8s1:30080(改成你的k8s集群上的节点ip:指定的端口)

警告:
不要在此页面上保留 WordPress 安装。如果其他用户找到了它,他们可以在你的实例上建立一个网站并使用它来提供恶意内容。
通过创建用户名和密码来安装 WordPress 或删除你的实例。
清理现场
运行下面的命令删除创建的的 Secret、Deployment、Service 和 PersistentVolumeClaims。