1/22

第十四章:多容器应用:Kubernetes 开发环境搭建

这一章开始使用 Kubernetes 部署更复杂的项目,使用的是学习 docker-compose 时的 docker-complex 那个应用,Kubernetes 版本的架构如下图:

imgkubernetes-complex项目架构

一共需要11个配置文件。

启动模板可以使用咪咪的:

https://github.com/lyf61/Docker-and-Kubernetes-The-Complete-Guide-Complex-App-Kubernetes-Starter

不过由于我们不会部署在生产环境,且我们使用的 Docker 镜像都是讲师已经发布的,所以开发环境下完全不使用模板也可以。

 

首先创建 client 的 service 与 deployment,在项目根目录下创建 k8s 文件夹,随后创建两个文件:

client-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: client-deployment
spec:
replicas: 3
selector:
  matchLabels:
    component: web
template:
  metadata:
    labels:
      component: web
  spec:
    containers:
      - name: client
        image: stephengrider/multi-client
        ports:
          - containerPort: 3000

client-cluster-ip-service.yaml

apiVersion: v1
kind: Service
metadata:
name: client-cluster-ip-service
spec:
type: ClusterIP
selector:
  component: web
ports:
  - port: 3000
    targetPort: 3000

随后使用 kubectl apply 应用配置测试一下,这次 -f 选项传入文件夹,kubectl 会自动应用文件夹下所有配置:

wuxianmimi kubernetes-complex % kubectl apply -f k8s
service/client-cluster-ip-service created
deployment.apps/client-deployment created
wuxianmimi kubernetes-complex % kubectl get deployment
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
client-deployment   3/3     3            3           66s
wuxianmimi kubernetes-complex % kubectl get pod
NAME                                 READY   STATUS   RESTARTS   AGE
client-deployment-7cb6c958f7-4c4pc   1/1     Running   0         72s
client-deployment-7cb6c958f7-4kp2x   1/1     Running   0         72s
client-deployment-7cb6c958f7-gv78d   1/1     Running   0         72s
wuxianmimi kubernetes-complex % kubectl get svc
NAME                       TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
client-cluster-ip-service   ClusterIP   10.109.207.213   <none>        3000/TCP   80s
kubernetes                 ClusterIP   10.96.0.1       <none>        443/TCP   18h
wuxianmimi kubernetes-complex %

可以看到启动成功了。

继续创建 server 与 worker 相关对象配置:

server-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: server-deployment
spec:
replicas: 3
selector:
  matchLabels:
    component: server
template:
  metadata:
    labels:
      component: server
  spec:
    containers:
      - name: server
        image: stephengrider/multi-server
        ports:
          - containerPort: 5000

server-cluster-ip-service.yaml

apiVersion: v1
kind: Service
metadata:
name: server-cluster-ip-service
spec:
type: ClusterIP
selector:
  component: server
ports:
  - port: 5000
    targetPort: 5000

worker-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: worker-deployment
spec:
replicas: 3
selector:
  matchLabels:
    component: worker
template:
  metadata:
    labels:
      component: worker
  spec:
    containers:
      - name: worker
        image: stephengrider/multi-worker

测试后,server Pod 中的日志应该显示数据库连接不上,说明其他配置没问题:

wuxianmimi kubernetes-complex % kubectl get pods
NAME                                 READY   STATUS   RESTARTS   AGE
client-deployment-7cb6c958f7-4c4pc   1/1     Running   0         32m
client-deployment-7cb6c958f7-4kp2x   1/1     Running   0         32m
client-deployment-7cb6c958f7-gv78d   1/1     Running   0         32m
server-deployment-9bff8dfb-cc7ls     1/1     Running   0         3m13s
server-deployment-9bff8dfb-dktx4     1/1     Running   0         3m13s
server-deployment-9bff8dfb-m9mg8     1/1     Running   0         3m13s
worker-deployment-666c96ffc5-n6gh8   1/1     Running   0         3m13s
wuxianmimi kubernetes-complex % kubectl logs server-deployment-9bff8dfb-m9mg8

> @ start /app
> node index.js

Listening
{ Error: connect ECONNREFUSED 127.0.0.1:5432
  at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1161:14)
errno: 'ECONNREFUSED',
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 5432 }

接下来配置 Redis 与 Postgres:

redis-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-deployment
spec:
replicas: 1
selector:
  matchLabels:
    component: redis
template:
  metadata:
    labels:
      component: redis
  spec:
    containers:
      - name: redis
        image: redis:4-alpine
        ports:
          - containerPort: 6379

redis-cluster-ip-service.yaml

apiVersion: v1
kind: Service
metadata:
name: redis-cluster-ip-service
spec:
type: ClusterIP
selector:
  component: redis
ports:
  - port: 6379
    targetPort: 6379

postgres-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-deployment
spec:
replicas: 1
selector:
  matchLabels:
    component: postgres
template:
  metadata:
    labels:
      component: postgres
  spec:
    containers:
      - name: postgres
        image: postgres:10.5
        ports:
          - containerPort: 5432

postgres-cluster-ip-service.yaml

apiVersion: v1
kind: Service
metadata:
name: postgres-cluster-ip-service
spec:
type: ClusterIP
selector:
  component: postgres
ports:
  - port: 5432
    targetPort: 5432
  • Kubernetes 中的 Volume

在本项目架构图中,我们发现一个叫 Postgres PVC 的东东。如果没有这个对象,当我们 postgres 容器被销毁的时候,postgres 中的数据也同时被销毁了。所以这个对象有点类似我们之前学习 Docker 时 Volume 的概念,但是 Docker 中 Volume 是一种让容器访问外部文件系统的机制,而 Kubernetes 中的 Volume 是一个对象,在 Pod 层面存储数据。

imgPostgres PVC

  • PersistentVolume

Kubernetes 中的 Volume 在 Pod 层面存储数据,所以 Pod 的生命周期也会影响数据,这不太好。而 PersistentVolume 解决了这个问题,把数据从 Pod 中“剥离”了出来。

  • PersistentVolumeClaim

乍一眼看上去与 PersistentVolume 很像,这两个有什么区别?在课程的 P186 视频讲师用火柴人动画讲解了很清楚,可以看一下。简单的来说就是 PersistentVolume 是预先静态分配好的一个存储资源,而 PersistentVolumeClaim 是对存储空间的请求和申领,等需要时才会提供。

 

创建我们的 PersistentVolumeClaim,在 k8s 目录创建文件 database-persistent-volume-cliam.yaml:

apiVersion: v1
# 对于我们来说新的对象类型
kind: PersistentVolumeClaim
metadata:
name: database-persistent-volume-claim
spec:
 # 访问模式,一共有四种取值:
 # ReadWriteOnce   卷可以被 一个节点 以读写方式挂载
 # ReadOnlyMany     卷可以被 多个节点 只读方式挂载
 # ReadWriteMany   卷可以被 多个节点 以读写方式挂载
 # ReadWriteOncePod 卷可以被 单个Pod 以读写方式挂载
accessModes:
  - ReadWriteOnce
resources:
  requests:
     # 要求2GB存储空间
    storage: 2Gi
  • StorageClass

存储 "类" 的方法,简单来说就是申请一块存储空间时的策略和方法的提供者。

在本地环境中只有一种方式,在云服务上有各个云服务器厂商提供的“类”,可以通过命令行 kubectl get storageclass 来查看当前支持哪些 StorageClass:

wuxianmimi kubernetes-complex % kubectl get storageclass
NAME                 PROVISIONER               RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
standard (default)   k8s.io/minikube-hostpath   Delete         Immediate           false                 20h
wuxianmimi kubernetes-complex %

 

随后在 postgres-deployment.yaml 中增加 Volume 的相关配置:

apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-deployment
spec:
replicas: 1
selector:
  matchLabels:
    component: postgres
template:
  metadata:
    labels:
      component: postgres
  spec:
     # 可以由属于 Pod 的容器挂载的卷列表
    volumes:
      - name: postgres-storage
        persistentVolumeClaim:
          claimName: database-persistent-volume-claim
    containers:
      - name: postgres
        image: postgres:10.5
        ports:
          - containerPort: 5432
         # 要挂载到容器文件系统中的 Pod 卷
        volumeMounts:
          - name: postgres-storage
             # 在容器内 Volume 的挂载路径
            mountPath: /var/lib/postgresql/data
             # Volume 中的路径,默认为 ""(卷的根)。
            subPath: postgres

应用配置后,我们在命令行查看 pv 与 pvc 的情况:

wuxianmimi kubernetes-complex % kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                     STORAGECLASS   REASON   AGE
pvc-a62cbd16-f788-4daf-b6e2-e3ec77ebdc32   2Gi       RWO           Delete           Bound   default/database-persistent-volume-claim   standard               28s
wuxianmimi kubernetes-complex % kubectl get pvc
NAME                               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
database-persistent-volume-claim   Bound   pvc-a62cbd16-f788-4daf-b6e2-e3ec77ebdc32   2Gi       RWO           standard       30s
wuxianmimi kubernetes-complex %
  • 环境变量

到目前为止我们还没有设置我们的环境变量,那如何设置数据库的连接信息呢?

在 worker-deployment.yaml 中增加 Redis 的环境变量:

apiVersion: apps/v1
kind: Deployment
metadata:
name: worker-deployment
spec:
replicas: 1
selector:
  matchLabels:
    component: worker
template:
  metadata:
    labels:
      component: worker
  spec:
    containers:
      - name: worker
        image: stephengrider/multi-worker
         # 设置环境变量
        env:
          - name: REDIS_HOST
            value: redis-cluster-ip-service
          - name: REDIS_PORT
            value: '6379'

在 server-deployment.yaml 中,增加 Redis 和 Postgres 的环境变量:

apiVersion: apps/v1
kind: Deployment
metadata:
name: server-deployment
spec:
replicas: 3
selector:
  matchLabels:
    component: server
template:
  metadata:
    labels:
      component: server
  spec:
    containers:
      - name: server
        image: stephengrider/multi-server
        ports:
          - containerPort: 5000
         # 设置环境变量
        env:
          - name: REDIS_HOST
            value: redis-cluster-ip-service
          - name: REDIS_PORT
            value: '6379'
          - name: PGUSER
            value: postgres
          - name: PGHOST
            value: postgres-cluster-ip-service
          - name: PGPORT
            value: '5432'
          - name: PGDATABASE
            value: postgres
  • Secret

还剩最后的 Postgres 密码没有配置,这种敏感信息用明文写在配置文件中不太合适,Kubernetes 为我们提供了一种方式来存储这些敏感信息,这个对象叫 Secret。

使用 kubectl create secret generic pgpassword --from-literal PGPASSWORD=12345asdf 命令创建 Secret。

获取目前 sercet:

wuxianmimi kubernetes-complex % kubectl get secrets
NAME                 TYPE                                 DATA   AGE
default-token-mdzch   kubernetes.io/service-account-token   3     26h
pgpassword           Opaque                                1     22s
wuxianmimi kubernetes-complex %

sercet 是存在环境变量中的,不同的机器上部署要重新设置 secret。

随后在 server 与 postgres 中添加密码(secretKeyRef):

server-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: server-deployment
spec:
replicas: 3
selector:
  matchLabels:
    component: server
template:
  metadata:
    labels:
      component: server
  spec:
    containers:
      - name: server
        image: stephengrider/multi-server
        ports:
          - containerPort: 5000
         # 设置环境变量
        env:
          - name: REDIS_HOST
            value: redis-cluster-ip-service
          - name: REDIS_PORT
            value: '6379'
          - name: PGUSER
            value: postgres
          - name: PGHOST
            value: postgres-cluster-ip-service
          - name: PGPORT
            value: '5432'
          - name: PGDATABASE
            value: postgres
          - name: PGPASSWORD
             # 值来自我们刚刚设置的 Secret
            valueFrom:
              secretKeyRef:
                name: pgpassword
                key: PGPASSWORD

postgres-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-deployment
spec:
replicas: 1
selector:
  matchLabels:
    component: postgres
template:
  metadata:
    labels:
      component: postgres
  spec:
     # 可以由属于 Pod 的容器挂载的卷列表
    volumes:
      - name: postgres-storage
        persistentVolumeClaim:
          claimName: database-persistent-volume-claim
    containers:
      - name: postgres
        image: postgres:10.5
        ports:
          - containerPort: 5432
         # 要挂载到容器文件系统中的 Pod 卷
        volumeMounts:
          - name: postgres-storage
             # 在容器内 Volume 的挂载路径
            mountPath: /var/lib/postgresql/data
             # Volume 中的路径,默认为 ""(卷的根)。
            subPath: postgres
        env:
          - name: PGPASSWORD
             # 值来自我们刚刚设置的 Secret
            valueFrom:
              secretKeyRef:
                name: pgpassword
                key: PGPASSWORD
 
posted @ 2025-01-22 15:56  Hbro  阅读(10)  评论(0)    收藏  举报