在项目开发中,Docker Compose 是一种常见的工具,能够帮助开发者在本地轻松启动多容器应用。然而,当应用扩展到生产环境,单节点的 Docker Compose 可能无法满足高可用性、容错和扩展需求。这时候,我们可以考虑将 Docker Compose 部署迁移到 Kubernetes 这样的集群环境中,实现应用的集群化管理。
本文将以我之前开发的一个 Node.js 应用(一个在线流程图系统)为例,使用 PostgreSQL
、Adminer
管理数据库,介绍如何将单节点的 Docker Compose 部署转化为 Kubernetes 集群部署。
这是一个典型的 docker-compose.yml
文件,它定义了一个使用 PostgreSQL 作为数据库,Adminer 作为数据库管理界面,Node.js 应用作为服务的环境。
version: "3.8"
services:
db:
image: postgres
volumes:
- pg_data:/var/lib/postgresql/data
restart: always
ports:
- 5432:5432
environment:
POSTGRES_DB: pro
POSTGRES_USER: admin
POSTGRES_PASSWORD: example
adminer:
image: adminer
restart: always
ports:
- 8080:8080
app:
image: maqi1520/cloneprocesson
ports:
- "3000:3000"
environment:
DATABASE_URL: "postgresql://admin:example@db:5432/pro?schema=public"
JWT_SECRET: "xxxx"
GITHUB_CLIENT_ID: "xxxx"
GITHUB_CLIENT_SECRET: "xxxx"
DOMAIN: "http://localhost:3000"
EMAIL_USER: "xxxx@163.com"
EMAIL_USER_NAME: "xxxxx"
EMAIL_HOST: "smtp.163.com"
EMAIL_PASS: "xxxx"
depends_on:
- db
volumes:
pg_data:
此配置可以在本地使用 docker-compose up -d
命令启动,但如果要将其部署在生产环境中并确保高可用性,则需要迁移到 Kubernetes。接下来,我们将介绍如何完成这一转化。
在 Kubernetes 中,使用多个资源来管理应用的不同组件。与 Docker Compose 的单一文件不同,Kubernetes 使用 Deployment
、StatefulSet
、Service
、ConfigMap
、Secret
等多个配置文件来描述和管理容器化应用。
为了将上述 Docker Compose 配置转移到 Kubernetes,我们需要以下几种关键组件:
在 Kubernetes 中,StatefulSet
用于管理有状态应用(例如数据库),它确保数据库实例的持久性和稳定性。我们首先需要创建一个用于存储数据库凭证的 Secret
,然后再创建 StatefulSet
。
apiVersion: v1
kind: Secret
metadata:
name: postgres-secret
type: Opaque
data:
DATABASE_URL: cG9zdGdyZXNxbDovL2FkbWluOmV4YW1wbGVAcG9zdGdyZXM6NTQzMi9wcm8/c2NoZW1hPXB1YmxpYw== # base64编码的 "postgresql://admin:example@postgres:5432/pro?schema=public"
POSTGRES_DB: "cHJv" # "pro" encoded in base64
POSTGRES_USER: "YWRtaW4=" # "admin" encoded in base64
POSTGRES_PASSWORD: "ZXhhbXBsZQ==" # "example" encoded in base64
在 Kubernetes 中,Secret 中的数据可以使用 base64 编码是一种常见的做法,但不是必须的。
在 Linux 和 macOS 系统中,可以使用 base64 命令。例如,要对字符串 “postgresql://admin:example@postgres:5432/pro?schema=public” 进行编码,可以在终端中执行以下命令:
echo -n "postgresql://admin:example@postgres:5432/pro?schema=public" | base64
在 Windows 系统中,可以使用 PowerShell 中的[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("postgresql://admin:example@postgres:5432/pro?schema=public"))
命令。
Kubernetes 的 Secret 对象可以存储任意数据,但为了确保数据的安全性和可读性,通常会进行某种形式的编码或加密。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: "postgres"
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:latest
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: postgres-secret
key: POSTGRES_DB
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: postgres-secret
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: POSTGRES_PASSWORD
volumeMounts:
- name: pg-data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: pg-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
通过 StatefulSet
,我们确保了数据库数据的持久化存储和稳定的网络标识。每个副本都可以访问持久存储卷。
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
ports:
- port: 5432
selector:
app: postgres
Service
用于暴露 PostgreSQL 服务,使得应用可以通过集群内部的 DNS 名称 postgres
访问数据库。
接下来,我们为 Node.js 应用创建一个 Deployment
,并通过 Secret
管理敏感数据,例如数据库 URL、JWT 密钥和 GitHub API 凭证。
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
jwt-secret: "YOUR_ENCODED_JWT_SECRET"
github-client-id: "YOUR_ENCODED_GITHUB_CLIENT_ID"
github-client-secret: "YOUR_ENCODED_GITHUB_CLIENT_SECRET"
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: maqi1520/cloneprocesson:latest
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secret
key: DATABASE_URL
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: app-secret
key: jwt-secret
- name: GITHUB_CLIENT_ID
valueFrom:
secretKeyRef:
name: app-secret
key: github-client-id
- name: GITHUB_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: app-secret
key: github-client-secret
apiVersion: v1
kind: Service
metadata:
name: app
spec:
type: LoadBalancer
ports:
- port: 3000
selector:
app: app
最后,Adminer 作为数据库管理界面,也需要用 Deployment
和 Service
部署。
apiVersion: apps/v1
kind: Deployment
metadata:
name: adminer
spec:
replicas: 1
selector:
matchLabels:
app: adminer
template:
metadata:
labels:
app: adminer
spec:
containers:
- name: adminer
image: adminer:latest
ports:
- containerPort: 8080
apiVersion: v1
kind: Service
metadata:
name: adminer
spec:
type: LoadBalancer
ports:
- port: 8080
selector:
app: adminer
将 Secret
和 Deployment
等资源应用到 Kubernetes 集群:
kubectl apply -f postgres-secret.yml
kubectl apply -f postgres-statefulset.yml
kubectl apply -f postgres-service.yml
kubectl apply -f app-secret.yml
kubectl apply -f app-deployment.yml
kubectl apply -f app-service.yml
kubectl apply -f adminer-deployment.yml
kubectl apply -f adminer-service.yml
这将会启动 PostgreSQL 数据库实例,并为数据库提供持久存储,应用程序将通过 Secret
获取敏感信息,并连接到 PostgreSQL 数据库。Service
将会为外部客户端提供访问 Node.js 应用的入口。
Adminer 将作为数据库管理界面通过集群的 LoadBalancer
公开出来,允许你直接管理 PostgreSQL 数据。
完成部署后,你可以通过以下方式验证集群的运行情况:
查看所有资源是否成功创建:
kubectl get pods
kubectl get services
kubectl get statefulsets
验证 PostgreSQL 连接:
可以通过访问 Adminer 服务,验证 PostgreSQL 数据库是否正常工作。你可以使用浏览器访问 http://<LoadBalancer_IP>:8080
,然后使用 Secret 中的数据库凭证登录。
验证应用程序连接:
访问 Node.js 应用的 Service
地址,查看应用是否能正常连接 PostgreSQL 数据库,并成功启动。
如果使用本地部署可以使用minikube service app
看到应用地址,他会自动打开部署 url 端口。
相比于 Docker Compose 单节点部署,Kubernetes 的集群化部署具备以下几个显著优势:
将 Docker Compose 的单节点部署迁移到 Kubernetes,不仅能够使应用从本地开发阶段顺利过渡到生产环境,还可以通过 Kubernetes 提供的集群管理能力来提高系统的高可用性、弹性扩展和安全性。
在本文中,我们通过将 Docker Compose 配置中的 PostgreSQL、Adminer 和自定义 Node.js 应用迁移到 Kubernetes,学习了如何利用 StatefulSet
、Deployment
、Secret
和 Service
等 Kubernetes 资源来管理和部署复杂的集群化应用。这种方式可以帮助你在生产环境中实现更加灵活、安全和可扩展的容器管理。