第一阶段:配置存储入口
第一步:安装本地存储插件 (StorageClass)
这是必须要做的,否则后续部署 MySQL、Redis 和 Prometheus 时,PVC 会一直卡在 Pending 状态。
- 安装 Rancher Local Path Provisioner
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.24/deploy/local-path-storage.yaml
2. 将其设为默认存储类 (关键) 这样以后不必每次都指定 StorageClass 的名字。
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
3. 验证
kubectl get sc
第二步:安装 Ingress 控制器 (流量入口)
需要 Ingress Nginx 来管理外部访问。由于是本地环境,使用兼容性最好的 baremetal 版本。
- 安装 Ingress-Nginx Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/baremetal/deploy.yaml
2. 验证 Pod 启动
kubectl get pods -n ingress-nginx
第三步:为项目创建专属空间
为了不污染系统环境,我们把电商项目都放在独立的 Namespace 里。
kubectl create namespace ecommerce
✅ 阶段性检查
预期输出:应看到 local-path (default)。
预期输出:需要等待一会儿,直到 ingress-nginx-controller 变为 Running。
一旦确认无误,就可以直接部署数据库了!
kubectl get sc
kubectl get pods -n ingress-nginx
第二阶段:数据层部署
任何电商系统都离不开数据。我们将部署 MySQL(存储订单和商品)和 Redis(做缓存)。使用 ConfigMap 自动初始化数据库表结构,实现“启动即用”。
1. 创建数据层部署文件
请直接在终端创建一个名为 db-layer.yaml 的文件:
vim db-layer.yaml
apiVersion: v1
kind: Namespace
metadata:
name: ecommerce
---
# ===========================
# 1. MySQL 配置 (自动建库建表)
# ===========================
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-init-script
namespace: ecommerce
data:
init.sql: |
CREATE DATABASE IF NOT EXISTS shop;
USE shop;
CREATE TABLE IF NOT EXISTS product (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
price DECIMAL(10,2),
stock INT
);
CREATE TABLE IF NOT EXISTS orders (
id INT AUTO_INCREMENT PRIMARY KEY,
product_id INT,
user_id INT,
amount DECIMAL(10,2),
status VARCHAR(20) DEFAULT 'PENDING'
);
-- 初始化一些测试数据
INSERT INTO product (name, price, stock) VALUES ('iPhone 15', 5999.00, 100);
INSERT INTO product (name, price, stock) VALUES ('MacBook Pro', 12999.00, 50);
---
# ===========================
# 2. MySQL 部署 (有状态应用)
# ===========================
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: ecommerce
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7 # 使用 5.7 版本,资源占用少
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456" # 模拟项目为了方便直接写明文,生产环境请用 Secret
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
- name: init-script
mountPath: /docker-entrypoint-initdb.d # 这里是 MySQL 自动初始化的关键目录
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc
- name: init-script
configMap:
name: mysql-init-script
---
# ===========================
# 3. MySQL 存储声明 (PVC)
# ===========================
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
namespace: ecommerce
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
# ===========================
# 4. MySQL 服务暴露
# ===========================
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: ecommerce
spec:
ports:
- port: 3306
selector:
app: mysql
clusterIP: None # Headless Service,适合数据库内部通信
---
# ===========================
# 5. Redis 部署 (缓存层)
# ===========================
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: ecommerce
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
---
# ===========================
# 6. Redis 服务暴露
# ===========================
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: ecommerce
spec:
ports:
- port: 6379
selector:
app: redis
2. 执行部署
运行以下命令应用配置:
kubectl apply -f db-layer.yaml
3. 验证部署状态
需要确认三件事:
PVC 绑定成功:看 MySQL 是否成功申请到了存储。
Pod 运行正常:看数据库是否启动。
数据初始化成功:看表是不是建好了。
查看 PVC 状态 (STATUS 必须是 Bound)
kubectl get pvc -n ecommerce
查看 Pod 状态 (必须是 Running)
kubectl get pods -n ecommerce
(进阶验证) 进入 MySQL 容器查看表是否存在
等 Pod Running 后执行:
kubectl exec -it -n ecommerce $(kubectl get pod -n ecommerce -l app=mysql -o jsonp
第三阶段:微服务应用层(Application Layer)
使用 ConfigMap-as-Code 把 Python 业务代码直接写在 Kubernetes 的 ConfigMap 里,然后挂载到容器中运行。
1. 部署微服务群 (应用层)
创建一个新文件 app-layer.yaml:
vim app-layer.yaml
逻辑说明:
Product Service: 提供 /products 接口,模拟从数据库查商品。
Pay Service: 提供 /pay 接口,模拟 20% 的支付失败率(用于测试重试机制)。
Order Service: 调用上面两个服务,模拟下单全流程。
部署策略: 每个服务 2 个副本,演示负载均衡。
apiVersion: v1
kind: ConfigMap
metadata:
name: app-code
namespace: ecommerce
data:
# ===========================
# 1. 商品服务代码 (模拟)
# ===========================
product.py: |
from flask import Flask, jsonify
import os
app = Flask(__name__)
@app.route('/product/<id>')
def get_product(id):
# 模拟从数据库查询 (为了演示简化,直接返回静态 JSON)
return jsonify({
"id": id,
"name": "iPhone 15 Pro",
"price": 8999,
"stock": 100,
"pod": os.getenv("HOSTNAME") # 返回 Pod 名字,证明负载均衡在工作
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
# ===========================
# 2. 支付服务代码 (模拟高可用故障)
# ===========================
pay.py: |
from flask import Flask, jsonify
import random, time, os
app = Flask(__name__)
@app.route('/pay', methods=['POST'])
def pay():
# 模拟 20% 的随机支付失败
if random.random() < 0.2:
return jsonify({"error": "Payment Gateway Timeout"}), 500
# 模拟网络延迟
time.sleep(0.1)
return jsonify({"status": "success", "pod": os.getenv("HOSTNAME")})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
# ===========================
# 3. 订单服务代码 (核心聚合逻辑)
# ===========================
order.py: |
from flask import Flask, jsonify
import requests, os
app = Flask(__name__)
@app.route('/create_order')
def create_order():
# 1. 调用商品服务
try:
prod = requests.get('http://product-service:8080/product/1').json()
except:
return jsonify({"error": "Product Service Down"}), 503
# 2. 调用支付服务
try:
pay = requests.post('http://pay-service:8080/pay').json()
except:
return jsonify({"error": "Payment Failed"}), 500
# 3. 生成订单
return jsonify({
"status": "Order Created",
"product": prod['name'],
"price": prod['price'],
"payment": pay,
"served_by": os.getenv("HOSTNAME")
})
@app.route('/')
def home():
return "Welcome to K8s Shop! Try /create_order"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
---
# ===========================
# 通用部署模板 (3个微服务共用配置)
# ===========================
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-service
namespace: ecommerce
spec:
replicas: 2
selector:
matchLabels:
app: product
template:
metadata:
labels:
app: product
spec:
containers:
- name: app
image: python:3.9-slim
command: ["/bin/sh", "-c"]
# 启动时现场安装 Flask 并运行代码
args: ["pip install flask requests && python /app/product.py"]
ports:
- containerPort: 8080
volumeMounts:
- name: code-volume
mountPath: /app
volumes:
- name: code-volume
configMap:
name: app-code
---
apiVersion: v1
kind: Service
metadata:
name: product-service
namespace: ecommerce
spec:
selector:
app: product
ports:
- port: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: pay-service
namespace: ecommerce
spec:
replicas: 2
selector:
matchLabels:
app: pay
template:
metadata:
labels:
app: pay
spec:
containers:
- name: app
image: python:3.9-slim
command: ["/bin/sh", "-c"]
args: ["pip install flask requests && python /app/pay.py"]
ports:
- containerPort: 8080
volumeMounts:
- name: code-volume
mountPath: /app
volumes:
- name: code-volume
configMap:
name: app-code
---
apiVersion: v1
kind: Service
metadata:
name: pay-service
namespace: ecommerce
spec:
selector:
app: pay
ports:
- port: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: ecommerce
spec:
replicas: 2
selector:
matchLabels:
app: order
template:
metadata:
labels:
app: order
spec:
containers:
- name: app
image: python:3.9-slim
command: ["/bin/sh", "-c"]
args: ["pip install flask requests && python /app/order.py"]
ports:
- containerPort: 8080
volumeMounts:
- name: code-volume
mountPath: /app
volumes:
- name: code-volume
configMap:
name: app-code
---
apiVersion: v1
kind: Service
metadata:
name: order-service
namespace: ecommerce
spec:
selector:
app: order
ports:
- port: 8080
---
# ===========================
# Ingress 入口配置 (对外暴露)
# ===========================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
namespace: ecommerce
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: order.shop.local # 虚拟域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: order-service
port:
number: 8080
2. 执行部署
kubectl apply -f app-layer.yaml
这些 Pod 启动可能需要 1-2 分钟,因为它们需要在容器启动时运行 pip install 下载 Flask 库。
使用以下命令观察:
加上 -w 参数,实时观察状态变化
kubectl get pods -n ecommerce -w
等到 product-service, pay-service, order-service 全部变成 Running。
3. 最终验证
现在整个系统已经跑起来了,我们需要访问它。
快速验证 (在虚拟机内部) 直接用 curl 访问 Ingress Controller,强制指定 Host 头。
模拟访问下单接口
curl -H "Host: order.shop.local" http://127.0.0.1:<端口号>/create_order
成功说明:
流量入口:curl 请求通过 Ingress (Nginx) 进入集群。
总指挥:流量被路由到 Order Service (served_by 显示的那个 Pod)。
服务发现与调用:
Order Service 通过 K8s DNS 找到了 product-service,获取了 iPhone 信息。
Order Service 找到了 pay-service,完成扣款(由 pay-service-8b65... 这个 Pod 处理)。
结果聚合:Order Service 把所有结果打包成 JSON 返回。
第四阶段:连接真实数据库
需要修改 app-layer.yaml 中的 Product Service 部分:
安装驱动:在启动命令里加上 pymysql 库。
修改代码:把返回死数据改成连接 MySQL 查询。
执行 vim app-layer.yaml,找到 app-code ConfigMap 里的 product.py 和下面的 Deployment,按以下内容修改:
- 修改 ConfigMap 里的 product.py
把原来的 product.py 内容替换为:
product.py: |
from flask import Flask, jsonify
import os, pymysql
app = Flask(__name__)
def get_db_connection():
return pymysql.connect(
host='mysql', # K8s Service 名字,自动解析 IP
user='root',
password='123456', # 之前设置的密码
db='shop',
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor
)
@app.route('/product/<id>')
def get_product(id):
try:
connection = get_db_connection()
with connection.cursor() as cursor:
# 真的去查数据库了!
sql = "SELECT * FROM product WHERE id=%s"
cursor.execute(sql, (id,))
result = cursor.fetchone()
connection.close()
if result:
# 把 Pod 名字加上,方便调试
result['pod'] = os.getenv("HOSTNAME")
# 转换 Decimal 类型为 float 避免 JSON 报错
result['price'] = float(result['price'])
return jsonify(result)
else:
return jsonify({"error": "Not Found"}), 404
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
- 修改 Deployment 的启动命令
找到 product-service 的 Deployment 部分,修改 args 这一行,加上 pymysql:
containers:
- name: app
# ... 镜像和其他配置不变 ...
# 注意:加上了 pymysql
args: ["pip install -i https://mirrors.aliyun.com/pypi/simple/ flask requests pymysql && python /app/product.py"]
执行更新
1. 应用新配置
kubectl apply -f app-layer.yaml
2. 重启 Product 服务 (让它加载新代码)
kubectl delete pods -n ecommerce -l app=product
等新的 Product Pod 跑起来之后,再次执行 curl:
curl -H "Host: order.shop.local" http://127.0.0.1:30628/create_order
观察变化: 如果你看到的价格变成了 5999.0(在数据库初始化脚本里写的价格),而不是代码里写死的 8999,那就证明已经成功构建了一个包含:流量入口(Ingress)、服务发现(Service)、负载均衡、故障模拟、以及持久化数据库(MySQL)的完整 Kubernetes 微服务电商系统!