
本文基于真实项目经验,完整还原某股份制银行电子商城系统从传统虚机架构向 Kubernetes 高可用集群迁移的部署过程。涵盖架构设计、集群搭建、应用编排、监控告警及灾难恢复等核心环节,所有配置均经过生产环境验证。
该银行电子商城日活用户约 50 万,峰值 QPS 达 8000+,原有 20+ 个微服务部署在虚拟机 + Tomcat 集群上,存在以下痛点:
技术选型:Kubernetes v1.28(因银行内部镜像仓库仅支持该版本)、Calico(网络策略)、Ceph RBD(持久化存储)、HAProxy + Keepalived(四层负载)、Prometheus + Thanos(监控)。
采用 3 Master + 3 Worker 混合部署(Master 也允许调度业务 Pod,但打上污点只运行系统组件),同时部署 3 节点 etcd 集群(与 Master 堆叠部署,节省机器)。
+-------------------+
| VIP: 10.0.0.100 |
+-------------------+
| HAProxy (主/备) |
+-------------------+
/ | \
/ | \
+--------+ +--------+ +--------+
| Master1| | Master2| | Master3| (kube-apiserver, scheduler, ccm)
| etcd | | etcd | | etcd |
+--------+ +--------+ +--------+
| | |
+--------+ +--------+ +--------+
|Worker1 | |Worker2 | |Worker3 | (业务 Pod)
+--------+ +--------+ +--------+组件 | 高可用方案 |
|---|---|
kube-apiserver | 前置 HAProxy 四层负载,Keepalived 提供 VIP |
etcd | 3 节点 Raft 集群,容忍 1 节点故障 |
kube-scheduler/controller-manager | 通过 leader-election 选主(--leader-elect=true) |
Worker 节点 | 多副本 Pod + 反亲和性,跨可用区(机房内不同机架) |
存储 | Ceph 三副本,故障自动迁移 |
10.244.0.0/16(Calico 默认)10.96.0.0/1210.0.0.0/24(物理内网)5.4.278-1.el7.elrepo.x86_64(支持 BBR 和更多 eBPF 特性)。systemctl stop firewalld && systemctl disable firewalld
setenforce 0 && sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/configcat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.ipv4.tcp_tw_recycle = 0
vm.swappiness = 0
vm.overcommit_memory = 1
kernel.panic_on_oom = 0
fs.inotify.max_user_watches = 524288
fs.file-max = 2097152
EOF
sysctl -p /etc/sysctl.d/k8s.conf安装 containerd 1.7.13,并配置 systemd cgroup 驱动:
# /etc/containerd/config.toml (节选)
version = 2
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.k8s.io/pause:3.9"
[plugins."io.containerd.grpc.v1.cri".containerd]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true版本 v1.28.5,配置 kubelet 使用 containerd:
yum install -y kubelet-1.28.5 kubeadm-1.28.5 kubectl-1.28.5
systemctl enable kubelet使用 kubeadm config print init-defaults 生成模板,按需修改。关键配置:
# kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 10.0.0.11 # Master1 IP
bindPort: 6443
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.28.5
controlPlaneEndpoint: "10.0.0.100:6443" # VIP + HAProxy 端口
imageRepository: registry.k8s.io
etcd:
local:
extraArgs:
- "listen-metrics-urls=http://0.0.0.0:2381"
serverCertSANs:
- 10.0.0.11
- 10.0.0.12
- 10.0.0.13
- 127.0.0.1
peerCertSANs:
- 10.0.0.11
- 10.0.0.12
- 10.0.0.13
networking:
serviceSubnet: "10.96.0.0/12"
podSubnet: "10.244.0.0/16"
dnsDomain: "cluster.local"
apiServer:
extraArgs:
- "enable-admission-plugins=NodeRestriction,PodSecurity"
- "anonymous-auth=false"
- "profiling=false"
extraVolumes:
- name: "audit"
hostPath: "/var/log/audit"
mountPath: "/var/log/audit"
readOnly: false
extraVolumeMounts:
- name: "audit"
mountPath: "/var/log/audit"
readOnly: false
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
failSwapOn: false
systemReserved:
cpu: 500m
memory: 1Gi
kubeReserved:
cpu: 500m
memory: 1Gikubeadm init --config=kubeadm-config.yaml --upload-certs初始化成功后,记录生成的 kubeadm join 命令(包含证书密钥),用于添加其他 Master 和 Worker。
mkdir -p $HOME/.kube
cp /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config在 Master2 和 Master3 上执行带有 --control-plane 的 join 命令:
kubeadm join 10.0.0.100:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane --certificate-key <key>kubeadm join 10.0.0.100:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>在 Master 节点前部署两台独立 LB 主机(或与 Master 复用,但生产建议独立)。此处以独立 LB1 (10.0.0.101) 和 LB2 (10.0.0.102) 为例。
global
log /dev/log local0
maxconn 4096
user haproxy
group haproxy
defaults
log global
mode tcp
option tcplog
timeout connect 5s
timeout client 60s
timeout server 60s
frontend k8s-api
bind *:6443
mode tcp
option tcplog
default_backend k8s-api-backend
backend k8s-api-backend
mode tcp
balance roundrobin
option tcp-check
server master1 10.0.0.11:6443 check fall 3 rise 2
server master2 10.0.0.12:6443 check fall 3 rise 2
server master3 10.0.0.13:6443 check fall 3 rise 2LB1 (MASTER):
global_defs {
router_id LVS_DEVEL
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1234
}
virtual_ipaddress {
10.0.0.100/24 dev eth0 label eth0:0
}
}LB2 (BACKUP) 只需将 state 改为 BACKUP,priority 改为 90。
# 在任意节点 curl 测试
curl -k https://10.0.0.100:6443/version
# 返回 k8s 版本信息即成功采用 Calico v3.27,使用 BGP 模式,并开启网络策略(银行需要精细控制 Pod 间访问)。
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27/manifests/calico.yaml自定义配置(通过 ConfigMap calico-config)调整 IP 池和 MTU:
# 修改默认 IPPool 的 MTU 为 1450(适配 Overlay 或物理网络限制)
kubectl patch ippool default-ipv4-ippool -p '{"spec":{"mtu":1450}}'验证:kubectl get pods -n kube-system -l k8s-app=calico-node -o wide,确保全部 Running。
银行电子商城有状态服务(MySQL、Redis、附件)需持久化。我们部署 Ceph 集群(独立 3 节点),通过 CSI 插件提供 RBD 块存储。
使用 v3.10.0,创建 Secret、StorageClass 和 PVC。
StorageClass 示例:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ceph-rbd-sc
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
clusterID: rook-ceph
pool: replicapool
imageFeatures: layering
csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
reclaimPolicy: Retain # 银行要求数据不自动删除
allowVolumeExpansion: trueapiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: ceph-rbd-sc
resources:
requests:
storage: 10Gi商城拆分为:前端(Vue + Nginx)、网关(Spring Cloud Gateway)、订单、商品、库存、用户、支付等微服务。
prod:生产环境staging:预发布monitoring:监控组件编写 Dockerfile(略),构建镜像并推送到银行内部 Harbor(harbor.bank.local/library/order-service:v1.0)。
Deployment 清单 (order-service.yaml):
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: prod
labels:
app: order-service
spec:
replicas: 3
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: "kubernetes.io/hostname"
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: order-service
containers:
- name: order-service
image: harbor.bank.local/library/order-service:v1.0
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1"
memory: "2Gi"
env:
- name: SPRING_PROFILES_ACTIVE
value: "k8s"
- name: DB_HOST
valueFrom:
secretKeyRef:
name: mysql-secret
key: host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
volumeMounts:
- name: logs
mountPath: /var/log/order
volumes:
- name: logs
persistentVolumeClaim:
claimName: order-logs-pvc # 使用 Ceph 存储日志,便于集中采集
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: order-service
topologyKey: "kubernetes.io/hostname"Service:
apiVersion: v1
kind: Service
metadata:
name: order-service-svc
namespace: prod
spec:
selector:
app: order-service
ports:
- port: 8080
targetPort: 8080
type: ClusterIPIngress(使用 Nginx Ingress Controller):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-ingress
namespace: prod
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts:
- api.bank.com
secretName: bank-tls
rules:
- host: api.bank.com
http:
paths:
- path: /order
pathType: Prefix
backend:
service:
name: order-service-svc
port:
number: 8080使用 Kubernetes Secret 存储敏感信息(DB 密码、Redis 密码、JWT 密钥)。使用 External Secrets Operator 同步HashiCorp Vault 中的密钥,但此处简化:
kubectl create secret generic mysql-secret -n prod \
--from-literal=host=mysql-svc.prod.svc.cluster.local \
--from-literal=password=Bank@2026#Secure部署 kube-prometheus-stack(v56.0),配置服务发现、告警规则。
关键调整:持久化存储使用 Ceph PVC,保留 30 天数据
# values.yaml for helm
prometheus:
prometheusSpec:
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: ceph-rbd-sc
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
retention: 30d
alertmanager:
alertmanagerSpec:
storage:
volumeClaimTemplate:
spec:
storageClassName: ceph-rbd-sc
resources:
requests:
storage: 10Gi使用 Fluentd 采集容器日志,输出至 Elasticsearch 7.x,再通过 Kibana 展示。
Fluentd DaemonSet 配置(节选):
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: logging
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.16-debian-elasticsearch8-1
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch.logging.svc.cluster.local"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
volumeMounts:
- name: varlog
mountPath: /var/log
- name: dockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: dockercontainers
hostPath:
path: /var/lib/docker/containers--anonymous-auth=false)。PodSecurity 标准(restricted)。apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: prod
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# 允许订单服务访问 MySQL(在 prod 命名空间内)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-order-to-mysql
namespace: prod
spec:
podSelector:
matchLabels:
app: mysql
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: order-service
ports:
- port: 3306模拟 Master1 宕机(关机),观察服务是否中断:
驱逐 Worker1 上的 Pod(kubectl drain worker1 --ignore-daemonsets),Pod 自动迁移至其他节点,业务短暂抖动(因 readinessProbe 需重新检查)。
若整个集群崩溃(如数据中心断电),按以下步骤恢复:
--config 指定原有配置,并导入 etcd 快照)。使用 Cluster Autoscaler 结合云平台(但银行是私有云,我们基于资源预留手动扩容)。
针对订单服务设置 CPU 阈值:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
namespace: prod
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70利用 GitLab CI + ArgoCD 实现 GitOps,每次 merge 自动触发构建镜像并更新 Deployment 的镜像 tag,ArgoCD 自动同步。
通过 kube-cost 分析各服务资源使用,调整 requests/limits,避免过度分配。
node-stage-secret 中的 encrypted 选项,或调整 CSI 的 timeout 参数。本次基于 kubeadm 构建的银行级高可用 Kubernetes 集群,经受了 6 个月的生产运行,平均可用性达到 99.99%(仅一次因网络交换机故障导致分钟级抖动)。整个部署过程强调“声明式配置、自动化恢复、可观测性”,为传统企业上云提供了一套可复制的参考范式。
后续规划:引入 Istio 服务网格实现更细粒度的流量管理和灰度发布;将 Ceph 替换为本地 PV + OpenEBS 以降低延迟;并探索基于 eBPF 的无侵入监控。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。