部署Kubernetes PostgreSQL实例

2020.03.01

pg-in-k8s

本文主要演示如何通过Kubernetes提供的持久化卷(PV)和Statefulset来部署Pg数据库容器。

镜像准备

我们打算使用官方提供的Postgresql 12.1版本:https://hub.docker.com/_/postgres 版本的镜像作为我们的部署镜像,这个版本的镜像提供了齐全的功能,并且支持我们使用编排文件配置环境变量,如用户名、密码、默认数据库、pgdata路径等。

下面我们可以正式开始实例的演示了…

创建PV

这个步骤里我们编写第一份配置声明:pg-pv.yaml

下面是主要例子,这里我们创建一个名字为 postgresql-pv的持久化卷,并使用hostpath作为pv的持久化类型,你也可以根据需要配置为gce或nfs等。作为测试的例子,我们先配置一个10G的大小。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: postgresql-pv
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/data/svr/pgdb"

然后通过kubectl执行创建命令

$ kubectl create -f pg-pv.yaml

创建PVC

接下来,我们再创建pvc配置– pg-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgresql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

执行创建命令

$ kubectl create -f pg-pvc.yaml

到了这里,我们可以从环境中检查刚刚创建的资源情况:

$ kubectl get pv

NAME            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS   REASON   AGE
postgresql-pv   10Gi       RWO            Retain           Bound    default/postgresql-pv-claim   manual                  63m


$ kubectl get pvc

NAME                  STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
postgresql-pv-claim   Bound    postgresql-pv   10Gi       RWO            manual         60m

从上面的环境信息输出可以看到,postgresql-pv 的状态已经处于 Bound,这表明我们的pv已经我pvc绑定成功。

创建 Deployment

我们可以通过 Kubernetes Deployment 的方式来创建一个有状态服务,然后使用PVC(PersistentVolumeClaim)来连接已经存在的PV。

下面我们通过编写Deployment 的YAML文件描述了一个运行PostgreSQL并使用PVC的Deployment。文件定义了一个mount到/var/lib/postgresql/data的卷(因为官方Pg容器initdb的时候数据会创建在此)

注意:密码定义在YAML配置文件中,这是不安全的,建议使用ConfigMap的方式,这里只是方便测试,所以就直接定义在配置文件中。

pg-deployment.yaml 配置如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgresql-deployment
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: postgresql
  template:
    metadata:
      labels:
        app: postgresql
    spec:
      containers:
        - image: postgres:12.1
          name: postgresql
          env:
            - name: POSTGRES_PASSWORD
              value: pgsql@123
          ports:
            - containerPort: 5432
              name: postgresql
          volumeMounts:
            - name: postgresql-persistent-storage
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: postgresql-persistent-storage
          persistentVolumeClaim:
            claimName: postgresql-pv-claim

通过kubectl创建Deployment

$ kubectl create -f pg-deployment.yaml

$ kubectl get deployment -o wide
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE    CONTAINERS   IMAGES          SELECTOR
postgresql-deployment   1/1     1            1           1m     postgresql   postgres:12.1   app=postgresql

此时,我们目标的pod也会被创建

$ kubectl get pods

NAME                                     READY   STATUS    RESTARTS   AGE
postgresql-deployment-7cfb6c95db-5r2v8   1/1     Running   0          1m

创建Service

为了能正常访问deployment或者容器,我们还需要暴露一个Servive。

Kubernetes提供了不同的Service访问类型,如:ClusterIP、NodePort、LoadBlancer、NodePort。

本例子,我们利用NodePort暴露一个服务端口,用于外部服务能正常访问我们的数据库。

pg-service.yaml定义如下,我们定义nodePort为30432,这样集群里外的服务都能使用这个端口进行访问。

apiVersion: v1
kind: Service
metadata:
  name: postgresql-client-service
  labels:
    app: postgresql
spec:
  type: NodePort
  ports:
    - port: 5432
      targetPort: 5432
      nodePort: 30432
      protocol: TCP
  selector:
    app: postgresql

接下来,就可以创建Service了:

$ kubectl apply -f pg-service.yaml

$ kubectl get service -o wide

NAME                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE    SELECTOR
kubernetes                  ClusterIP   10.96.0.1        <none>        443/TCP          12d    <none>
postgresql-client-service   NodePort    10.96.41.96      <none>        5432:30432/TCP   1m     app=postgresql

连接数据库

这里我们使用psql客户端进行测试连接

集群内

$ psql -U postgres -h 10.96.41.96 -p 5432

$ psql -U postgres -h localhost -p 30432

正常情况下,我们可以顺利连接上:

Password for user postgres:
psql (10.1, server 12.1 (Debian 12.1-1.pgdg100+1))
WARNING: psql major version 10, server major version 12.
         Some psql features might not work.
Type "help" for help.

postgres=#

为了继续测试环境是否有状态,我们接下来会创建一个新的datase和表,并插入数据,再销毁pod。

然后观察新的pod是否能继续持有之前的数据,达到数据状态持久化的目的?

create database pgtest

连接测试数据库

$ psql -U postgres -h localhost -p 30432 pgtest

$ psql -U postgres -h 10.96.41.96 -p 5432 pgtest

再创建测试表,并插入数据

CREATE TABLE test(
   ID INT PRIMARY KEY     NOT NULL,
   NAME           TEXT    NOT NULL
);

insert into test (id, name) values (1, "tom");

删除 pod

下面,我们开始删除刚刚通过Deployment的pod

kubectl delete pod/postgresql-deployment-7cfb6c95db-5r2v8
pod "postgresql-deployment-7cfb6c95db-5r2v8" deleted

这时,我们再观察环境中pod的情况可以发现,通过deployment,一个新的pg pod被创建出来了

kubectl get pods

NAME                                     READY   STATUS    RESTARTS   AGE
postgresql-deployment-7cfb6c95db-hd8ks   1/1     Running   0          13s

这个时候,我们通过上面的psql客户端工具,重新连接我们的db,观察我们之前维护的数据是否还正常保持?

$ ./psql -U postgres -h localhost -p 30432 pgtest -c "select * from test"

 id | name
----+------
  1 | tom
(1 row)

跟我们预期一样,哪怕pod被删除了,我们的K8s环境依旧有能力通过持久化卷恢复数据。