第 44篇 k8s之实战:将 Web 应用迁移到 Kubernetes(上)

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 思维 !

相关推荐
晓杰'1 小时前
从0到1实现Balatro游戏后端(7):Boss Blind与特殊规则实现
后端·websocket·typescript·node.js·游戏开发·项目实战·nestjs
MariaH1 小时前
Node.js 架构理解
后端
我登哥MVP1 小时前
Spring Boot 从“会用”到“精通”:请求映射原理
java·spring boot·后端·spring·servlet·maven·intellij-idea
MariaH1 小时前
Node-fs模块
后端
峰子20121 小时前
PG 管控系统技术方案
数据库·后端·pg
晓杰'1 小时前
从0到1实现Balatro游戏后端(6):Blind关卡状态设计与回合推进实现
后端·websocket·typescript·游戏开发·项目实战·nestjs·状态管理
墨香幽梦客2 小时前
GraphQL在ERP数据集成中的革命性应用:从N+1查询到批量优化的实践
后端·graphql
chimchim662 小时前
Azure Data Factory (ADF)‌ 之databricks使用
后端·python·flask
喵个咪2 小时前
技术复盘:基于 GoWind Admin 实现 Kratos 框架单体轻量化落地
后端·架构·go