K8s实践练习8_有状态应用二番战,部署MySQL和WordPress

官网链接

本教程将使用PV部署一个WordPress和MySQL。

K8s集群搭建以及镜像源配置

搭建好一套k8s集群,可以参考我写的这篇教程:搭建k8s集群

k8s官方的镜像站在国内是拉不下来的,有几种方法解决:

  1. 在拉取镜像的虚拟机/服务器上科学上网
  2. 配置k8s的镜像源,目前国内只有阿里云支持改版后的k8s镜像源(registry.k8s.io)。
  3. 需要拉取镜像的时候,指定拉取策略为本地拉取(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.ioregistry.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查看集群存储类。有的话可以跳过此节

  1. 在三个节点上创建存储基础目录(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配置

  1. 安装 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
  1. 确认安装成功,检查 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
  1. 把 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。

1
kubectl delete -k ./
网站总访客数:Loading
网站总访问量:Loading
使用 Hugo 构建
主题 StackJimmy 设计