IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。
在第 10 篇中,我们通过 docker run 命令和自定义网络,手动启动了 Flask + Redis 计数器应用。在第 16 篇中,我们用 Docker Compose 将整个过程声明化,一条 docker compose up -d 就能启动整个应用栈。
现在,经过了 33 篇 Kubernetes 核心知识的学习------从 Pod、Deployment、Service、Ingress,到 ConfigMap、Secret、PVC、探针、资源管理、调度策略、RBAC、Helm------是时候把这一切串起来了。
这篇和下篇,我们将完成一次完整的应用迁移:把运行在 Docker Compose 中的 Flask + Redis 计数器应用,迁移到 Kubernetes 集群上,并达到生产级标准。今天先完成基础架构部署和配置管理。
一、回顾与规划:从 Compose 到 K8s 的架构映射
1.1 Docker Compose 时代的旧架构
回顾第 10 篇和第 16 篇,我们的 Compose 文件定义了以下组件:
bash
services:
redis:
image: redis:alpine
volumes:
- redis-data:/data
networks:
- app-net
flask-app:
image: flask-redis-counter:3.0
ports:
- "5000:5000"
environment:
- REDIS_HOST=redis
networks:
- app-net
volumes:
redis-data:
networks:
app-net:
这个架构在单机上运行良好,但迁移到 K8s 需要把每个 Compose 概念映射到对应的 K8s 对象。我们在第 18 篇做过概念映射,现在是把它落地的时候了。
1.2 Kubernetes 目标架构
迁移后的 K8s 架构如下:
bash
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes 集群 │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Flask Deployment │ │ Redis Deployment │ │
│ │ replicas: 3 │ │ replicas: 1 │ │
│ │ ┌──────┐┌──────┐ │ │ ┌──────┐ │ │
│ │ │ Pod1 ││ Pod2 │ │ │ │ Pod │ │ │
│ │ └──────┘└──────┘ │ │ └──────┘ │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │
│ ┌────────▼─────────┐ ┌────────▼─────────┐ │
│ │ Flask Service │ │ Redis Service │ │
│ │ ClusterIP │ │ ClusterIP │ │
│ └────────┬─────────┘ └──────────────────┘ │
│ │ │
│ ┌────────▼─────────┐ │
│ │ Ingress │ │
│ │ counter.local │ │
│ └──────────────────┘ │
│ │
│ 存储: Redis PVC (1Gi) 配置: ConfigMap + Secret │
│ 监控: Prometheus 日志: Loki │
│ 安全: RBAC + NetworkPolicy │
└─────────────────────────────────────────────────────────────┘
迁移对照表:
二、准备镜像
2.1 构建并加载镜像
bash
# 构建 Flask 应用镜像(基于第 5 篇的多阶段 Dockerfile)
docker build -t flask-redis-counter:3.0 .
# 输出关键行:
# [+] Building 35.2s (17/17) FINISHED
# => [runtime 8/9] COPY --chown=appuser:appuser . .
# => => naming to docker.io/library/flask-redis-counter:3.0
# 加载到 Minikube 环境
minikube image load flask-redis-counter:3.0
# 验证镜像已加载
minikube ssh docker images | grep flask-redis-counter
# flask-redis-counter 3.0 a1b2c3d4e5f6 138MB
2.2 Docker Compose 启动(回顾用)
如果你还想看看 Compose 版本的运行效果作为对照:
bash
[+] Running 3/3
✔ Network flask-redis-counter_app-net Created 0.1s
✔ Container redis Healthy 5.2s
✔ Container flask-app Started 5.5s
测试:
bash
curl http://localhost:5000
# Hello World! I have been seen 1 times.
关键:从这里开始,我们不再使用 Compose,一切由 K8s 接管。
三、部署 Redis(StatefulSet + Service + PVC)
首先部署应用的基石------Redis 存储层。我们使用 Deployment(单副本,配合 PVC 实现持久化)。
3.1 创建 Redis PVC
bash
# redis-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: standard
bash
kubectl apply -f redis-pvc.yaml
# persistentvolumeclaim/redis-pvc created
kubectl get pvc
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
# redis-pvc Bound pvc-a1b2c3d4-e5f6-7890-abcd-ef1234567890 1Gi RWO standard
3.2 部署 Redis
bash
# redis-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
volumeMounts:
- name: redis-data
mountPath: /data
volumes:
- name: redis-data
persistentVolumeClaim:
claimName: redis-pvc
---
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
type: ClusterIP
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
bash
kubectl apply -f redis-deployment.yaml
# deployment.apps/redis created
# service/redis-service created
kubectl get pods -l app=redis
# NAME READY STATUS RESTARTS AGE
# redis-xxxxxxxxx-xxxxx 1/1 Running 0 30s
kubectl get svc redis-service
# NAME TYPE CLUSTER-IP PORT(S) AGE
# redis-service ClusterIP 10.96.100.50 6379/TCP 30s
四、创建配置(ConfigMap + Secret)
4.1 创建 ConfigMap(非敏感应用配置)
bash
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: flask-config
data:
FLASK_ENV: "production"
LOG_LEVEL: "info"
REDIS_HOST: "redis-service"
REDIS_PORT: "6379"
bash
kubectl apply -f configmap.yaml
# configmap/flask-config created
kubectl describe configmap flask-config
# Name: flask-config
# Namespace: default
# Data
# ====
# FLASK_ENV:
# ----
# production
# LOG_LEVEL:
# ----
# info
4.2 创建 Secret(敏感信息)
bash
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: flask-secret
type: Opaque
stringData:
SECRET_KEY: "my-production-secret-key-2025"
bash
kubectl apply -f secret.yaml
# secret/flask-secret created
五、部署 Flask 应用(Deployment + Service)
5.1 创建 Flask Deployment
bash
# flask-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-deployment
labels:
app: flask-counter
spec:
replicas: 3
selector:
matchLabels:
app: flask-counter
template:
metadata:
labels:
app: flask-counter
spec:
containers:
- name: flask
image: flask-redis-counter:3.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
envFrom:
- configMapRef:
name: flask-config
- secretRef:
name: flask-secret
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
startupProbe:
httpGet:
path: /health
port: 5000
periodSeconds: 5
failureThreshold: 12
livenessProbe:
httpGet:
path: /health
port: 5000
periodSeconds: 15
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 5000
periodSeconds: 5
failureThreshold: 2
bash
kubectl apply -f flask-deployment.yaml
# deployment.apps/flask-deployment created
验证部署状态:
bash
kubectl get pods -l app=flask-counter -w
# NAME READY STATUS RESTARTS AGE
# flask-deployment-xxxxxxxxx-xxxxx 0/1 ContainerCreating 0 2s
# flask-deployment-xxxxxxxxx-xxxxx 1/1 Running 0 15s
# flask-deployment-xxxxxxxxx-yyyyy 0/1 ContainerCreating 0 2s
# flask-deployment-xxxxxxxxx-yyyyy 1/1 Running 0 15s
# flask-deployment-xxxxxxxxx-zzzzz 0/1 ContainerCreating 0 2s
# flask-deployment-xxxxxxxxx-zzzzz 1/1 Running 0 15s
查看 Deployment 整体状态:
bash
kubectl get deployments
# NAME READY UP-TO-DATE AVAILABLE AGE
# flask-deployment 3/3 3 3 30s
# redis 1/1 1 1 2m
5.2 创建 Flask Service
bash
# flask-service.yaml
apiVersion: v1
kind: Service
metadata:
name: flask-service
spec:
type: ClusterIP
selector:
app: flask-counter
ports:
- port: 5000
targetPort: 5000
bash
kubectl apply -f flask-service.yaml
# service/flask-service created
kubectl get svc
# NAME TYPE CLUSTER-IP PORT(S) AGE
# flask-service ClusterIP 10.96.200.80 5000/TCP 10s
# redis-service ClusterIP 10.96.100.50 6379/TCP 2m
六、验证内部通信与外部访问
6.1 验证内部通信
bash
# 从 Flask Pod 访问 Redis
kubectl exec deploy/flask-deployment -- redis-cli -h redis-service PING
# PONG
Flask 应用通过 redis-service 这个 Service 名称成功连接到了 Redis Pod。DNS 解析和 Service 负载均衡均工作正常。
6.2 端口转发验证
在没有 Ingress 的情况下,用端口转发快速验证:
bash
kubectl port-forward svc/flask-service 5000:5000
打开另一个终端:
bash
curl http://localhost:5000
# Hello World! I have been seen 1 times.
curl http://localhost:5000
# Hello World! I have been seen 2 times.
计数器正常工作,说明 Flask ↔ Redis 通信链路已打通。
6.3 检查各个对象状态
bash
kubectl get all
# NAME READY STATUS RESTARTS AGE
# pod/flask-deployment-xxxxxxxxx-xxxxx 1/1 Running 0 2m
# pod/flask-deployment-xxxxxxxxx-yyyyy 1/1 Running 0 2m
# pod/flask-deployment-xxxxxxxxx-zzzzz 1/1 Running 0 2m
# pod/redis-xxxxxxxxx-xxxxx 1/1 Running 0 5m
#
# NAME TYPE CLUSTER-IP PORT(S) AGE
# service/flask-service ClusterIP 10.96.200.80 5000/TCP 2m
# service/redis-service ClusterIP 10.96.100.50 6379/TCP 5m
#
# NAME READY UP-TO-DATE AVAILABLE AGE
# deployment.apps/flask-deployment 3/3 3 3 2m
# deployment.apps/redis 1/1 1 1 5m
七、本篇总结
-
架构转换:将 Compose 的 services、ports、environment、volumes 分别映射为 K8s 的 Deployment、Service、ConfigMap、PVC。
-
存储持久化 :Redis 使用 PVC 实现数据持久化,存储类为
standard(Minikube 默认)。 -
配置管理 :ConfigMap 管理非敏感配置(Redis 连接信息、日志级别),Secret 管理敏感信息(SECRET_KEY)。Flask Pod 通过
envFrom一次性注入。 -
生产化配置:包含三种探针(startup/liveness/readiness)、资源限制(Requests/Limits),确保应用稳定运行。
下一篇------第 45 篇:实战:将 Web 应用迁移到 Kubernetes(下),我们将继续为应用配置 Ingress 外部入口、HPA 自动伸缩、滚动更新验证,并接入 Prometheus 监控和 Loki 日志系统,完成从开发环境到生产级 K8s 集群的完整迁移。
想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !