使用KubeSphere3.3在Ubuntu20.04的Kubernetes1.24上部署Word Press
前言
之前已经部署了KubeSphere和K8S的基础环境:https://lizhiyong.blog.csdn.net/article/details/126236516
部署了大数据统一文件编排层Alluxio:https://lizhiyong.blog.csdn.net/article/details/126815426
以及Saas服务的简单体验:https://lizhiyong.blog.csdn.net/article/details/129868719
所以这次尝试部署一个Word Press服务,成功的话,下次就可以参照着部署在Saas服务搭个私有Blog,可能比租ECS或者轻量服务器还便宜。。。
具体参照官方文档:https://www.kubesphere.io/zh/docs/v3.3/quick-start/wordpress-deployment/

这个应用的后端服务是MySQL,持久化到PVC中,账户密码存在Secret中,暴露一个Service给前端服务使用。
前端服务就是Word Press,持久化到PVC中,账户密码存在另一个Secret中,暴露一个Service给Pod外部的租户使用。
这个案例大体上涵盖了K8S部署常见的有状态应用和无状态应用。部署成功后,外部租户就可以访问到Pod中的前端服务Word Press,前端服务也能够把数据持久化到后端的MySQL。在使用时,租户大概率不会发觉底层的变化。。。
实操
本文不是给纯小白看的,不过得益于KubeSphere对K8S的界面化封装,其实纯小白耐心点也能学会基础操作。。。应付工作还是足够的。
有点追求的话,还是建议深入理解下相关的原理。多说无益。
做平台开发当然是习惯了超级管理员。。。而且是自己玩,懒得创建项目和租户了。。。
http://192.168.88.20:30880
admin
Aa123456
直接上。。。
创建密钥
MySQL的密钥

下一步默认的方式即可:

此处Key=MYSQL_ROOT_PASSWORD,Value=123456。
此时的yaml:
apiVersion: v1
kind: Secret
metadata:
  namespace: default
  labels: {}
  name: zhiyong-mysql-secret
  annotations:
    kubesphere.io/description: WordPress的MySQL密钥
type: Opaque
spec:
  template:
    metadata:
      labels: {}
data:
  MYSQL_ROOT_PASSWORD: MTIzNDU2
这种方式和:
kubectl create -f secret.yaml
做法其实是一致的。。。Base64加密的方式也很不安全。
Word Press的密钥

下一步:

此处Key=WORDPRESS_DB_PASSWORD,Value=123456。
此时的yaml:
apiVersion: v1
kind: Secret
metadata:
  namespace: default
  labels: {}
  name: zhiyong-wordpress-secret
  annotations:
    kubesphere.io/description: WordPress的密钥
type: Opaque
spec:
  template:
    metadata:
      labels: {}
data:
  WORDPRESS_DB_PASSWORD: MTIzNDU2
其实还有另外的模式:

以后可以试试。此时创建了2个Secret:

创建PVC

使用默认的Local模式,10个G空间:

这种模式也没啥元数据:

保持默认即可,此时的yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  namespace: default
  name: zhiyong-wordpress-pvc
  labels: {}
  annotations:
    kubesphere.io/description: word press存储卷
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: local
此时PVC创建完毕:

创建应用

此时并没有创建应用的选项。。。
老的3.2版本也有这个应用的选项:https://v3-2.docs.kubesphere.io/zh/docs/quick-start/wordpress-deployment/
所以还是应该老老实实的先创建租户和项目。。。貌似KubeSphere的设计中不允许网管部署应用。。。
接下来参考官网:https://www.kubesphere.io/zh/docs/v3.3/quick-start/create-workspace-and-project/
由于新老版本涉及一些变化:https://www.kubesphere.io/zh/docs/v3.3/release/release-v331/

并不能照搬最新版官网文档。
可能还需要参考老版本的:https://v3-2.docs.kubesphere.io/zh/docs/quick-start/create-workspace-and-project/
创建租户
新版本:
| 内置角色 | 描述 | 
|---|---|
| platform-self-provisioner | 创建企业空间并成为所创建企业空间的管理员。 | 
| platform-regular | 平台普通用户,在被邀请加入企业空间或集群之前没有任何资源操作权限。 | 
| platform-admin | 平台管理员,可以管理平台内的所有资源。 | 
备注:内置角色由 KubeSphere 自动创建,无法编辑或删除。
老版本:
| 内置角色 | 描述 | 
|---|---|
| workspaces-manager | 企业空间管理员,管理平台所有企业空间。 | 
| users-manager | 用户管理员,管理平台所有用户。 | 
| platform-regular | 平台普通用户,在被邀请加入企业空间或集群之前没有任何资源操作权限。 | 
| platform-admin | 平台管理员,可以管理平台内的所有资源。 | 
所以还是要按照老版本的模式:

可以在这个界面创建各种租户。

先创建一个用户管理员:
users-manager
users-manager@zhiyong.com
Aa123456
然后切换到这个用户,创建如下4个租户:
| 用户 | 指定的平台角色 | 用户权限 | 
|---|---|---|
| ws-manager | workspaces-manager | 创建和管理所有企业空间。 | 
| ws-admin | platform-regular | 被邀请到企业空间后,管理该企业空间中的所有资源(在此示例中,此用户用于邀请新成员加入该企业空间)。 | 
| project-admin | platform-regular | 创建和管理项目以及 DevOps 项目,并邀请新成员加入项目。 | 
| project-regular | platform-regular | project-regular将由project-admin邀请至项目或 DevOps 项目。该用户将用于在指定项目中创建工作负载、流水线和其他资源。 | 

ws-manager
ws-manager@zhiyong.com
Aa123456

ws-admin
ws-admin@zhiyong.com
Aa123456

project-admin
project-admin@zhiyong.com
Aa123456

project-regular
project-regular@zhiyong.com
Aa123456

此时可以看到新建4杆租户成功!!!
创建企业空间【NameSpace】
以 ws-manager 租户的身份登录 KubeSphere。

创建一个Demo的工作空间:

demo-workspace
管理员:ws-admin

遇到了这个报错:
Forbidden
workspacetemplates.tenant.kubesphere.io is forbidden: User "ws-manager" cannot create resource "workspacetemplates" in API group "tenant.kubesphere.io" at the cluster scope
把4个普通租户挨个激活以后还是有这种问题。。。所以使用admin超级管理员来创建:

即可成功。。。这是KuberSphere的Bug,3.3.1的新版本已经修复。。。
然后以 ws-admin 身份重新登录,邀请 project-admin 和 project-regular 这2杆租户进入企业空间:

分别授予 demo-workspace-self-provisioner 和 demo-workspace-viewer 角色:


此时租户拉取配置成功:

接下来就可以创建项目。
创建项目
切换为:project-admin这个租户,创建项目:

然后邀请普通租户,分配给operator身份:

项目的Ingress网关和配额暂时不管。之后切换到普通租户project-regular,就可以部署项目:

重新创建密钥
删除之前的密钥,再重新操作一遍即可。不再赘述。

此时准备好了MySQL后端的Secret与Word Press的前端Secret。
重新创建PVC
删除之前的PVC,再重新操作一遍即可,不再赘述。

Forbidden
volumesnapshotclasses.snapshot.storage.k8s.io is forbidden: User "project-regular" cannot list resource "volumesnapshotclasses" in API group "snapshot.storage.k8s.io" at the cluster scope
不过有这个问题。。。不需要用admin来创建PVC,只是个展示的问题。
自制应用
此时可以自制应用:


暂时不用Sidecar做应用治理,所以先关闭它。
添加 MySQL 后端组件

先创建MySQL的有状态服务。




此时的yaml:
apiVersion: apps/v1
kind: StatefulSet
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: mysql-wordpress
  name: mysql-wordpress-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      version: v1
      app: mysql-wordpress
  template:
    metadata:
      labels:
        version: v1
        app: mysql-wordpress
    spec:
      containers:
        - name: container-rt5e06
          imagePullPolicy: IfNotPresent
          image: 'mysql:5.6'
          resources:
            requests:
              cpu: '0.1'
              memory: 100Mi
            limits:
              cpu: '1.5'
              memory: 2000Mi
          ports:
            - name: tcp-3306
              protocol: TCP
              containerPort: 3306
              servicePort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: zhiyong-mysql-secret
                  key: MYSQL_ROOT_PASSWORD
      serviceAccount: default
      initContainers: []
      volumes: []
      imagePullSecrets: null
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0
  serviceName: mysql-wordpress
---
apiVersion: v1
kind: Service
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: mysql-wordpress
  annotations:
    kubesphere.io/serviceType: statefulservice
    kubesphere.io/description: 给WordPress用的MySQL
  name: mysql-wordpress
spec:
  sessionAffinity: None
  selector:
    app: mysql-wordpress
  ports:
    - name: tcp-3306
      protocol: TCP
      port: 3306
      targetPort: 3306
  clusterIP: None
然后从PVC挂载PV:

挂载PVC以后的yaml:
apiVersion: apps/v1
kind: StatefulSet
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: mysql-wordpress
  name: mysql-wordpress-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      version: v1
      app: mysql-wordpress
  template:
    metadata:
      labels:
        version: v1
        app: mysql-wordpress
      annotations:
        logging.kubesphere.io/logsidecar-config: '{}'
    spec:
      containers:
        - name: container-rt5e06
          imagePullPolicy: IfNotPresent
          image: 'mysql:5.6'
          resources:
            requests:
              cpu: '0.1'
              memory: 100Mi
            limits:
              cpu: '1.5'
              memory: 2000Mi
          ports:
            - name: tcp-3306
              protocol: TCP
              containerPort: 3306
              servicePort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: zhiyong-mysql-secret
                  key: MYSQL_ROOT_PASSWORD
          volumeMounts:
            - readOnly: false
              mountPath: /var/lib/mysql
              name: zhiyong-mysql
      serviceAccount: default
      initContainers: []
      volumes: []
      imagePullSecrets: null
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0
  serviceName: mysql-wordpress
  volumeClaimTemplates:
    - spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 10Gi
        storageClassName: local
      metadata:
        name: zhiyong-mysql
        namespace: word-press-project
---
apiVersion: v1
kind: Service
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: mysql-wordpress
  annotations:
    kubesphere.io/serviceType: statefulservice
    kubesphere.io/description: 给WordPress用的MySQL
  name: mysql-wordpress
spec:
  sessionAffinity: None
  selector:
    app: mysql-wordpress
  ports:
    - name: tcp-3306
      protocol: TCP
      port: 3306
      targetPort: 3306
  clusterIP: None
由于是All In One的模式:

也就没得选了。。。如果是多节点模式,那么还可以指定Pod运行在哪台机器。
添加 WordPress 前端组件
相似的操作,创建无状态应用:



自己玩玩,也就不搞探针探活了。。。
此时的yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: wordpress
  name: wordpress-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      version: v1
      app: wordpress
  template:
    metadata:
      labels:
        version: v1
        app: wordpress
    spec:
      containers:
        - name: container-7ydkpj
          imagePullPolicy: IfNotPresent
          image: 'wordpress:4.8-apache'
          resources:
            requests:
              cpu: '0.1'
              memory: 100Mi
            limits:
              cpu: '1'
              memory: 2000Mi
          ports:
            - name: tcp-80
              protocol: TCP
              containerPort: 80
              servicePort: 80
          env:
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: zhiyong-wordpress-secret
                  key: WORDPRESS_DB_PASSWORD
            - name: WORDPRESS_DB_HOST
              value: mysql-wordpress
      serviceAccount: default
      initContainers: []
      volumes: []
      imagePullSecrets: null
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
---
apiVersion: v1
kind: Service
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: wordpress
  annotations:
    kubesphere.io/serviceType: statelessservice
    kubesphere.io/description: WordPress服务
  name: wordpress
spec:
  sessionAffinity: None
  selector:
    app: wordpress
  ports:
    - name: tcp-80
      protocol: TCP
      port: 80
      targetPort: 80

挂载PVC以后的yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: wordpress
  name: wordpress-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      version: v1
      app: wordpress
  template:
    metadata:
      labels:
        version: v1
        app: wordpress
      annotations:
        logging.kubesphere.io/logsidecar-config: '{}'
    spec:
      containers:
        - name: container-7ydkpj
          imagePullPolicy: IfNotPresent
          image: 'wordpress:4.8-apache'
          resources:
            requests:
              cpu: '0.1'
              memory: 100Mi
            limits:
              cpu: '1'
              memory: 2000Mi
          ports:
            - name: tcp-80
              protocol: TCP
              containerPort: 80
              servicePort: 80
          env:
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: zhiyong-wordpress-secret
                  key: WORDPRESS_DB_PASSWORD
            - name: WORDPRESS_DB_HOST
              value: mysql-wordpress
          volumeMounts:
            - name: volume-jsr71k
              readOnly: false
              mountPath: /var/www/html
      serviceAccount: default
      initContainers: []
      volumes:
        - name: volume-jsr71k
          persistentVolumeClaim:
            claimName: zhiyong-wordpress-pvc
      imagePullSecrets: null
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
---
apiVersion: v1
kind: Service
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: wordpress
  annotations:
    kubesphere.io/serviceType: statelessservice
    kubesphere.io/description: WordPress服务
  name: wordpress
spec:
  sessionAffinity: None
  selector:
    app: wordpress
  ports:
    - name: tcp-80
      protocol: TCP
      port: 80
      targetPort: 80
此时创建好了2个组件。
路由

路由暂且不设置。此时的yaml:
apiVersion: app.k8s.io/v1beta1
kind: Application
metadata:
  name: word-press
  namespace: word-press-project
  labels:
    app.kubernetes.io/version: v1
    app.kubernetes.io/name: word-press
  annotations:
    servicemesh.kubesphere.io/enabled: 'false'
    kubesphere.io/description: WordPress应用
spec:
  selector:
    matchLabels:
      app.kubernetes.io/version: v1
      app.kubernetes.io/name: word-press
  addOwnerRef: true
  componentKinds:
    - group: ''
      kind: Service
    - group: apps
      kind: Deployment
    - group: apps
      kind: StatefulSet
    - group: networking.k8s.io
      kind: Ingress
    - group: servicemesh.kubesphere.io
      kind: Strategy
    - group: servicemesh.kubesphere.io
      kind: ServicePolicy
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: word-press-project
  labels:
    app.kubernetes.io/version: v1
    app.kubernetes.io/name: word-press
  name: word-press-ingress-rb11gi
spec:
  rules: []
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: mysql-wordpress
    app.kubernetes.io/version: v1
    app.kubernetes.io/name: word-press
  name: mysql-wordpress-v1
  annotations:
    servicemesh.kubesphere.io/enabled: 'false'
spec:
  replicas: 1
  selector:
    matchLabels:
      version: v1
      app: mysql-wordpress
      app.kubernetes.io/version: v1
      app.kubernetes.io/name: word-press
  template:
    metadata:
      labels:
        version: v1
        app: mysql-wordpress
        app.kubernetes.io/version: v1
        app.kubernetes.io/name: word-press
      annotations:
        logging.kubesphere.io/logsidecar-config: '{}'
        sidecar.istio.io/inject: 'false'
    spec:
      containers:
        - name: container-rt5e06
          imagePullPolicy: IfNotPresent
          image: 'mysql:5.6'
          ports:
            - name: tcp-3306
              protocol: TCP
              containerPort: 3306
              servicePort: 3306
          resources:
            requests:
              cpu: '0.1'
              memory: 100Mi
            limits:
              cpu: '1.5'
              memory: 2000Mi
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: zhiyong-mysql-secret
                  key: MYSQL_ROOT_PASSWORD
          volumeMounts:
            - readOnly: false
              mountPath: /var/lib/mysql
              name: zhiyong-mysql
      serviceAccount: default
      initContainers: []
      volumes: []
      imagePullSecrets: null
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0
  serviceName: mysql-wordpress
  volumeClaimTemplates:
    - spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 10Gi
        storageClassName: local
      metadata:
        name: zhiyong-mysql
        namespace: word-press-project
totalReplicas: ''
---
apiVersion: v1
kind: Service
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: mysql-wordpress
    app.kubernetes.io/version: v1
    app.kubernetes.io/name: word-press
  annotations:
    kubesphere.io/serviceType: statefulservice
    kubesphere.io/description: 给WordPress用的MySQL
    servicemesh.kubesphere.io/enabled: 'false'
  name: mysql-wordpress
spec:
  sessionAffinity: None
  selector:
    app: mysql-wordpress
    app.kubernetes.io/version: v1
    app.kubernetes.io/name: word-press
  ports:
    - name: tcp-3306
      protocol: TCP
      port: 3306
      targetPort: 3306
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: wordpress
    app.kubernetes.io/version: v1
    app.kubernetes.io/name: word-press
  name: wordpress-v1
  annotations:
    servicemesh.kubesphere.io/enabled: 'false'
spec:
  replicas: 1
  selector:
    matchLabels:
      version: v1
      app: wordpress
      app.kubernetes.io/version: v1
      app.kubernetes.io/name: word-press
  template:
    metadata:
      labels:
        version: v1
        app: wordpress
        app.kubernetes.io/version: v1
        app.kubernetes.io/name: word-press
      annotations:
        logging.kubesphere.io/logsidecar-config: '{}'
        sidecar.istio.io/inject: 'false'
    spec:
      containers:
        - name: container-7ydkpj
          imagePullPolicy: IfNotPresent
          image: 'wordpress:4.8-apache'
          resources:
            requests:
              cpu: '0.1'
              memory: 100Mi
            limits:
              cpu: '1'
              memory: 2000Mi
          ports:
            - name: tcp-80
              protocol: TCP
              containerPort: 80
              servicePort: 80
          env:
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: zhiyong-wordpress-secret
                  key: WORDPRESS_DB_PASSWORD
            - name: WORDPRESS_DB_HOST
              value: mysql-wordpress
          volumeMounts:
            - name: volume-jsr71k
              readOnly: false
              mountPath: /var/www/html
      serviceAccount: default
      initContainers: []
      volumes:
        - name: volume-jsr71k
          persistentVolumeClaim:
            claimName: zhiyong-wordpress-pvc
      imagePullSecrets: null
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
---
apiVersion: v1
kind: Service
metadata:
  namespace: word-press-project
  labels:
    version: v1
    app: wordpress
    app.kubernetes.io/version: v1
    app.kubernetes.io/name: word-press
  annotations:
    kubesphere.io/serviceType: statelessservice
    kubesphere.io/description: WordPress服务
    servicemesh.kubesphere.io/enabled: 'false'
  name: wordpress
spec:
  sessionAffinity: None
  selector:
    app: wordpress
    app.kubernetes.io/version: v1
    app.kubernetes.io/name: word-press
  ports:
    - name: tcp-80
      protocol: TCP
      port: 80
      targetPort: 80
验证资源
耐心等一阵子,待拉取镜像和调度Pod成功后:

root@zhiyong-ksp1:/home/zhiyong# kubectl get pod -owide --all-namespaces | grep word
word-press-project             mysql-wordpress-v1-0                                              1/1     Running     0               14m     10.233.107.217   zhiyong-ksp1   <none>           <none>
word-press-project             wordpress-v1-799755f6cc-wjt54                                     1/1     Running     1 (5m34s ago)   14m     10.233.107.216   zhiyong-ksp1   <none>           <none>
此时PVC也挂载成功:

暴露服务


使用NodePort暴露即可。
暴露服务成功后:

直接使用IP+端口访问:
http://192.168.88.20:43302/


随便写点啥。然后访问:
http://192.168.88.20:43302/2023/05/29/伟大的虎鲸/

此时WordPress已经可用。。。
总结
KubeSphere的多租户做的还是比较完善,不能随意地使用超级管理员操作所有的功能。规范性也是相当的到位,划分了多种租户。必须遵循这套模式,按部就班地来。
通过这个简单的案例,K8S学徒工也可以照猫画虎部署一些应用了。平台化还是有好处的,让开发人员不必要掌握繁琐的kubectl命令,也无需专职的yaml攻城狮了。。。KubeSphere是个好东西。。。
K8S最适合的还是无状态的应用,所以简单的无状态应用【例如:Flink的实时ETL pipeline、简单规则的超限告警】都可以参照着这么玩了。。。
有状态应用要麻烦一点了。。。
K8S的Saas服务这么操作下来,应该也是可行的。。。
转载请注明出处:https://lizhiyong.blog.csdn.net/article/details/130939203



















