从 Docker Compose 到集群部署:K8s 核心概念与迁移
从 Docker Compose 到集群部署:K8s 核心概念与迁移教程
当你从单机上的 Docker Compose 转向服务器集群(如 Kubernetes)时,应用的管理方式会发生根本性的变化。集群提供了高可用性、弹性和自动扩展能力,但也引入了新的抽象概念。
第一章:核心概念解析
在集群环境中,我们不再直接管理“容器”,而是管理“负载”(Workload)和“服务”(Service)。以下是你提到的几个关键概念:
1. 无状态负载 (Stateless Workload)
-
对应 Kubernetes 对象:
Deployment -
核心理念: 这种负载中的所有实例(Pod)都是完全相同的、可互换的。它们不保存任何持久化数据。如果一个实例崩溃了,集群可以毫秒级地创建另一个新实例来替换它,而不会丢失任何状态。
-
例子: Web 服务器(Nginx, Apache)、API 后端、微服务网关。
-
类比: 像是一家快餐店的收银员。任何一个收银员都可以为你服务,他们不需要“记住”你昨天的订单。
2. 有状态负载 (StatefulSet)
-
对应 Kubernetes 对象:
StatefulSet -
核心理念: 这是为你提供的 Neo4j(数据库)准备的。这种负载中的每个实例(Pod)都有一个稳定且唯一的标识(例如
neo4j-0,neo4j-1),并且通常绑定了自己专属的持久化存储。 -
关键特性:
-
稳定的网络 ID: 即使 Pod 重启,它的主机名和 DNS 记录也不会改变。
-
稳定的持久存储: 每个 Pod 都有自己独立的存储卷(PVC),当 Pod 被重新调度时,它会重新挂载到完全相同的那个存储卷。
-
-
例子: 数据库(MySQL, PostgreSQL, Neo4j)、消息队列(Kafka, RabbitMQ)、分布式文件系统。
-
类比: 像是银行的保险箱。每个保险箱都有唯一的编号(身份),并且里面存放着独一无二的物品(数据)。你不能随意替换它们。
3. 存储卷 (PersistentVolume - PV)
-
核心理念: 这是集群中实际的存储资源。它由集群管理员(运维)提前准备好。
-
来源: 它可以是云服务商的磁盘、NFS、或像你示例中(
nas.csi.volcengine.com)一样的 NAS(网络附加存储)。 -
类比: 像是仓库里一个已经划分好、贴上标签的货架。它有固定的大小和类型。
4. 存储声明 (PersistentVolumeClaim - PVC)
-
核心理念: 这是应用程序对存储的“请求”。它由开发者(你)在部署应用时创建。
-
工作方式:
-
静态供应(本教程采用): 你创建一个 PV(货架),再创建一个 PVC(提货单)来精确地“声明”要那个特定的 PV。
-
动态供应: 你只创建 PVC(提货单),K8s 会通过
StorageClass(存储类)自动为你创建并绑定一个 PV(货架)。
-
-
好处: 将应用(“我需要存储”)与基础设施(“存储具体在哪里”)解耦了。
-
类比: 像是一张提货单。
5. 服务 (Service)
-
核心理念: 在集群中,Pod(运行容器的实例)的 IP 地址是会变化的(比如重启后)。
Service(服务)的作用是提供一个稳定、不变的入口点(固定的 IP 地址和 DNS 名称),用于访问一组功能相同的 Pod。 -
功能:
-
服务发现: 其他应用可以通过服务名(例如
neo4j-service)来访问 Neo4j,而不需要知道具体 Pod 的 IP。 -
负载均衡: 如果你有多个 Neo4j 实例(高可用),
Service会自动将请求分发给它们。 -
端口映射: 类似于 Docker Compose 中的
ports,它定义了服务监听的端口(如 7474)和 Pod 容器的端口(如 7474)。
-
-
类比: 像是一个团队的总机号码。
6. 路由 (Ingress)
-
核心理念:
Service主要处理集群 内部 的网络通信。而Ingress(入口/路由)是专门为 HTTP/HTTPS 流量设计的,用于将集群外部的请求路由到集群内部的Service。 -
功能:
-
基于域名的路由: 例如,将
http://neo4j.your-domain.com路由到你的neo4j-service。 -
SSL/TLS 终止: 集中处理 HTTPS 加密。
-
-
类比: 像是一栋办公楼的前台/接待处。
7. 保密字典 (Secret)
-
对应 Kubernetes 对象:
Secret -
核心理念:
Secret(保密字典)是专门用于存储小块敏感数据(如密码、API 密钥、Token)的对象。 -
工作方式: 你先创建一个
Secret对象来安全地保存你的数据。然后,在创建 Pod(如StatefulSet)时,你可以引用这个Secret,将其中的数据作为环境变量或文件挂载到容器中。 -
好处: 实现了配置与安全的解耦。你的应用配置(
StatefulSet.yml)中不再包含明文密码,变得更安全,也更容易管理和分享。 -
类比: 像是一个带锁的保险箱。你先把密码(
neo4j/indemind@@)锁在保险箱(Secret)里,然后只把保险箱的钥匙(secretKeyRef)交给你的应用程序(StatefulSet)。
第二章:Docker Compose vs Kubernetes 迁移对比
下面我们将你提供的 docker-compose.yml 文件,拆解并翻译成 Kubernetes 所需的一系列 YAML 配置文件。
你的 Docker Compose 文件
version: '3.8'
services:
neo4j:
image: neo4j:5.20.0
container_name: neo4j_db
ports:
- "7474:7474"
- "7687:7687"
environment:
- NEO4J_AUTH=neo4j/indemind@@
volumes:
- /home/indemind/zy_/neo4j/docker_data:/data
healthcheck:
test: ["CMD", "curl", -f, "http://localhost:7474"]
interval: 10s
timeout: 10s
retries: 5
迁移到 Kubernetes (K8s) 的配置(静态供应方案)
总览:
-
PersistentVolume (PV) - 存储卷: (手动创建)定义一块指向你 NAS 的存储。
-
PersistentVolumeClaim (PVC) - 存储声明: (手动创建)申请使用上面那块 PV。
-
Secret (保密字典): (手动创建)用于安全地存储 Neo4j 密码。
-
StatefulSet (有状态负载): (部署应用)管理 Neo4j,并使用 PVC 和 Secret。
-
Service (服务): 暴露 Neo4j 的端口。
-
Ingress (路由): (可选) 暴露 Web 界面到集群外。
1. (对应存储) -> PersistentVolume (PV)
首先,我们根据你提供的信息,创建一个 PersistentVolume (PV) 文件。
配置说明:
-
我们不能使用你
modelsPV 的/enas-cnbja0e0eda9ce19fe/model路径,因为那个是共享的。 -
你必须首先在你的 NAS 上创建一个新目录,例如
/enas-cnbja0e0eda9ce19fe/neo4j-data。 -
accessModes: 对于数据库,我们使用ReadWriteOnce(RWO),这意味着这个存储卷一次只能被一个 Pod 挂载为读写。这能有效防止数据被多个实例同时写入而损坏。 -
volumeHandle: 这是一个唯一的名称,我们命名为neo4j-pv。
你可以把以下内容保存为 k8s-01-persistent-volume.yml。
apiVersion: v1
kind: PersistentVolume
metadata:
name: neo4j-pv # PV 的唯一名称
spec:
capacity:
storage: 10Gi # 必须与 PVC 的申请大小一致或更大
volumeMode: Filesystem
accessModes:
- ReadWriteOnce # 数据库必须用 RWO
persistentVolumeReclaimPolicy: Retain # 数据保留策略
csi:
driver: nas.csi.volcengine.com # 使用你指定的 CSI 驱动
volumeHandle: neo4j-pv # 卷的唯一句柄
volumeAttributes:
# !! 以下信息基于你的 NAS 配置 !!
server: "cnbja0e0eda9ce19fe.vpc-1a1zcgj9sn8cg8nveplg3708b.nas.ivolces.com"
# !! 假设你已经在 NAS 上创建了 'neo4j-data' 目录 !!
path: "/enas-cnbja0e0eda9ce19fe/neo4j-data"
fsType: "Extreme" # 基于你提供的示例
mountOptions:
- nolock,proto=tcp,noresvport
- vers=3
2. (对应存储) -> PersistentVolumeClaim (PVC)
接下来,我们创建一个 PersistentVolumeClaim (PVC) 来“绑定”上面那个 PV。
配置说明:
-
storageClassName: "":设置为空字符串,是告诉 K8s 不要使用任何 StorageClass(动态供应)。 -
volumeName: 我们可以直接在spec中指定volumeName: neo4j-pv,这样 PVC 就会只绑定那个 PV。
你可以把以下内容保存为 k8s-02-persistent-volume-claim.yml。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: neo4j-pvc # PVC 的名称,StatefulSet 将引用这个名称
spec:
accessModes:
- ReadWriteOnce # 必须与 PV 的 accessModes 匹配
resources:
requests:
storage: 10Gi # 申请的存储大小,必须小于等于 PV 的容量
# !! 关键:指定我们要绑定的 PV !!
volumeName: neo4j-pv
# !! 关键:禁用动态供应 !!
storageClassName: ""
3. (对应 environment) -> Secret (保密字典)
现在,我们创建 Secret 来存储你的 NEO4J_AUTH 密码。
配置说明:
-
stringData:允许你以便于阅读的明文字符串形式提供机密数据。K8s 在存储时会自动对其进行 Base64 编码。 -
NEO4J_AUTH:这是Secret内部的一个“键”(Key),StatefulSet将通过这个键来引用对应的“值”。
你可以把以下内容保存为 k8s-03-secret.yml。
apiVersion: v1
kind: Secret
metadata:
name: neo4j-secret # Secret 的名称,StatefulSet 将引用这个名称
type: Opaque # 默认的 Secret 类型
stringData:
# 将你的密码存储在这里
NEO4J_AUTH: "neo4j/indemind@@"
4. (对应 services: neo4j, volumes) -> StatefulSet
现在我们创建 StatefulSet,并让它使用我们准备好的 neo4j-pvc 和 neo4j-secret。
配置说明:
-
replicas: 1: 非常重要! 因为我们是手动创建的一个 RWO 存储卷,这个存储卷只能被一个 Pod 读写。 -
volumes: 引用我们创建的neo4j-pvc。 -
env(环境变量): 不再使用value明文写入密码,而是使用valueFrom和secretKeyRef来从neo4j-secret中安全地引用密码。 -
enableServiceLinks: false: 解决SERVICE.PORT.7687.TCP.PROTO错误的关键配置。
你可以把以下内容保存为 k8s-04-statefulset.yml。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: neo4j
spec:
# serviceName 必须匹配下面 Service 的 metadata.name
serviceName: "neo4j-service"
# !! 警告:使用静态 PV/PVC 时,副本数必须为 1 !!
replicas: 1
selector:
matchLabels:
app: neo4j
template:
metadata:
labels:
app: neo4j
spec:
# --- 解决 SERVICE.PORT 错误的关键 ---
# 禁止 K8s 自动注入服务环境变量,避免 Neo4j 误读
enableServiceLinks: false
# --- 卷定义:使用已存在的 PVC ---
volumes:
- name: data # 对应下面的 volumeMounts.name
persistentVolumeClaim:
# 引用我们手动创建的 PVC 的名称
claimName: neo4j-pvc
containers:
- name: neo4j
# <-- 镜像源已更新 -->
image: [swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/neo4j:5.22.0](https://swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/neo4j:5.22.0)
ports:
- containerPort: 7474
name: http
- containerPort: 7687
name: bolt
# (对应 environment) --- 从 Secret 引用密码 ---
env:
- name: NEO4J_AUTH
valueFrom:
secretKeyRef:
name: neo4j-secret # 引用 k8s-03-secret.yml 中创建的 Secret
key: NEO4J_AUTH # 引用 Secret 中的 'key'
# (可选) 添加资源请求和限制
resources:
limits:
cpu: "2"
memory: 4Gi
requests:
cpu: "1"
memory: 2Gi
# (对应 healthcheck) K8s 的健康检查
readinessProbe:
httpGet:
path: /
port: 7474
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 10
failureThreshold: 5
livenessProbe:
httpGet:
path: /
port: 7474
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 10
failureThreshold: 5
# (对应 volumes) Pod 内的挂载点
volumeMounts:
- name: data # 必须匹配 volumes.name
mountPath: /data
# --- 不再需要 volumeClaimTemplates ---
# volumeClaimTemplates: ...
5. (对应 ports) -> Service
这个 Service 会为你的 StatefulSet(所有带 app: neo4j 标签的 Pod)创建一个集群内部的统一入口。
你可以把以下内容保存为 k8s-05-service.yml。
apiVersion: v1
kind: Service
metadata:
name: neo4j-service # 必须匹配 StatefulSet 的 serviceName
spec:
type: ClusterIP
selector:
app: neo4j # 选择所有带 'app: neo4j' 标签的 Pod
ports:
- port: 7474
targetPort: 7474
name: http
- port: 7687
targetPort: 7687
name: bolt
6. (对应 “路由”) -> Ingress (可选)
如果你想通过一个域名(例如 neo4j.my-cluster.com)从外部浏览器访问 Neo4j 的 Web 界面(7474 端口),你可以创建一个 Ingress 资源。
你可以把以下内容保存为 k8s-06-ingress.yml。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: neo4j-ingress
# annotations:
# nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: "neo4j.your-domain.com" # !! 修改为你自己的域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: neo4j-service
port:
number: 7474
更多推荐
所有评论(0)