从Jar包到K8s上线:全流程拆解+高可用实战
一、先抛业务场景:我们要做什么?
假设我们是一个电商团队,刚开发完订单服务(一个Spring Boot Jar包),需要把它部署到生产环境,要求:
用户能通过域名
order.example.com访问;不怕单个服务器挂掉(高可用);
能自动处理故障,不用人工干预。
接下来,我们会走完“代码提交→镜像构建→CI/CD→K8s部署→暴露访问→高可用保障”的完整链路,逐一解决每个环节的问题。
二、第一步:把Jar包变成Docker镜像
K8s只认识容器镜像,所以得先把Jar包打包成Docker镜像——这是上云的“原材料”。
1. 写Dockerfile:镜像的“ recipe ”
Dockerfile是构建镜像的说明书,告诉Docker如何一步步生成镜像。比如订单服务的Dockerfile:
# 基础镜像:用OpenJDK 17(和本地开发一致)
FROM openjdk:17-jdk-slim
# 作者信息(可选)
LABEL maintainer="your-email@example.com"
# 把本地Jar包拷贝到镜像里的/app目录
COPY order-service-1.0.jar /app/order.jar
# 暴露容器的8080端口(和Jar包的server.port一致)
EXPOSE 8080
# 容器启动时执行的命令:运行Jar包
ENTRYPOINT ["java", "-jar", "/app/order.jar"]2. 构建并推送镜像到仓库
用docker build构建镜像,然后推送到容器镜像仓库(比如Harbor、阿里云镜像仓库、Docker Hub):
# 构建镜像,打标签(格式:仓库地址/项目名/镜像名:版本)
docker build -t harbor.example.com/order-service:1.0 .
# 登录镜像仓库(如果是私有仓库需要这步)
docker login harbor.example.com
# 推送镜像到仓库
docker push harbor.example.com/order-service:1.0这一步完成后,镜像就像“预制菜”,随时可以在K8s节点上“加热”运行
三、第二步:CI/CD流水线:自动化部署
手动部署太麻烦,我们需要CI/CD流水线(持续集成+持续部署):代码提交后自动构建镜像、测试、部署到K8s。
1. 选工具:GitLab CI(或Jenkins)
以GitLab CI为例,只需要在项目根目录写.gitlab-ci.yml,定义流水线阶段:
stages:- build # 构建Jar包- test # 单元测试- push # 推送镜像到仓库- deploy # 部署到K8s# 全局变量:镜像仓库地址、K8s配置
variables:IMAGE_REPO: harbor.example.com/order-serviceK8S_NAMESPACE: production-orderKUBE_CONFIG: $KUBE_CONFIG_PROD # 从GitLab Secrets拿K8s配置# 1. 构建Jar包(如果用Maven,这里跑mvn package)
build_jar:stage: buildimage: maven:3.8.6-openjdk-17script:- mvn clean package -DskipTestsartifacts:paths:- target/order-service-1.0.jar# 2. 单元测试(可选,但建议做)
unit_test:stage: testimage: maven:3.8.6-openjdk-17script:- mvn test# 3. 推送镜像到仓库
push_image:stage: pushimage: docker:24.0.7services:- docker:24.0.7-dind # Docker-in-Docker,用于构建镜像before_script:- docker login -u $HARBOR_USER -p $HARBOR_PASSWORD harbor.example.comscript:- docker build -t $IMAGE_REPO:1.0 ./target- docker push $IMAGE_REPO:1.0# 4. 部署到K8s
deploy_to_k8s:stage: deployimage: bitnami/kubectl:1.28 # 用kubectl镜像执行部署命令script:- kubectl config use-context production-cluster # 切换到生产集群- kubectl set image deployment/order-service order-service=$IMAGE_REPO:1.0 -n $K8S_NAMESPACE# 或者用apply:kubectl apply -f k8s/deployment.yaml -n $K8S_NAMESPACE流水线的作用:开发提交代码→触发CI/CD→自动构建、测试、推送镜像→部署到K8s,全程不用人工干预。
四、第三步:K8s部署:定义应用的“运行规则”
K8s不是直接运行容器,而是通过资源对象(比如Deployment、Service)定义应用的运行方式。我们需要写两个核心文件:deployment.yaml(管理Pod)和service.yaml(暴露服务)。
1. Deployment:管理Pod的“副本集”
Deployment是K8s的“应用管理者”,负责创建、更新、重启Pod。比如订单服务的Deployment:
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment # 资源类型:Deployment
metadata:name: order-service # Deployment名字namespace: production-order # 命名空间(隔离环境)
spec:replicas: 3 # 运行3个Pod副本(高可用关键)selector:matchLabels:app: order-service # 选择带有app=order-service标签的Podtemplate: # Pod的模板(每个Pod都按这个创建)metadata:labels:app: order-service # Pod的标签,和selector对应spec:# 1. 容器定义containers:- name: order-containerimage: harbor.example.com/order-service:1.0 # 用的镜像ports:- containerPort: 8080 # 容器暴露的端口(和Dockerfile的EXPOSE一致)# 2. 资源限制(避免Pod占用太多资源导致节点崩溃)resources:requests: # 请求的资源(调度时保证节点有足够资源)cpu: "500m" # 0.5核CPUmemory: "1Gi" # 1G内存limits: # 最大允许使用的资源cpu: "1000m"memory: "2Gi"# 3. 健康检查(确保Pod是“活的”)livenessProbe: # 存活检查:如果失败,K8s会重启PodhttpGet:path: /actuator/health/liveness # Spring Boot的健康检查接口port: 8080initialDelaySeconds: 30 # 容器启动后30秒开始检查periodSeconds: 10 # 每10秒检查一次readinessProbe: # 就绪检查:如果失败,K8s不会把Pod加入Service的负载均衡httpGet:path: /actuator/health/readinessport: 8080initialDelaySeconds: 30periodSeconds: 10# 4. Pod反亲和性(高可用关键:让Pod分布在不同节点)affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution: # 必须满足的规则- labelSelector:matchExpressions:- key: appoperator: Invalues:- order-servicetopologyKey: kubernetes.io/hostname # 按节点主机名分布,确保Pod不在同一个节点2. Service:暴露Pod的“负载均衡器”
Service是K8s的“服务发现+负载均衡”组件,负责把流量转发到健康的Pod。订单服务的Service:
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:name: order-servicenamespace: production-order
spec:type: ClusterIP # 默认类型:集群内部访问(外部需要Ingress)selector:app: order-service # 关联带有这个标签的Podports:- protocol: TCPport: 80 # Service的端口(集群内部访问用)targetPort: 8080 # 转发到Pod的8080端口3. Ingress:暴露到公网的“入口”
如果要让外部用户访问,需要Ingress——相当于K8s的“反向代理”,把域名转发到Service。比如用Nginx Ingress:
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: order-ingressnamespace: production-orderannotations:nginx.ingress.kubernetes.io/rewrite-target: /$1 # 重写URL(可选)
spec:ingressClassName: nginx # 用Nginx Ingress Controllerrules:- host: order.example.com # 绑定的域名http:paths:- path: /(.*) # 匹配所有路径pathType: Prefixbackend:service:name: order-service # 关联的Serviceport:number: 80 # Service的端口4. 部署到K8s
把这三个文件放到k8s目录,然后用kubectl apply部署:
kubectl apply -f k8s/或者用CI/CD里的kubectl set image命令(更灵活,直接更新镜像版本)。
五、第四步:典型问题排查指南——Pending状态怎么办?
部署后,用kubectl get pods查看Pod状态,如果出现Pending,说明Pod没调度成功,需要排查:
1. Pending的常见原因及解决
Pending的本质是:Pod没找到合适的节点运行。常见原因:
(1)资源不足(最常见)
比如节点的CPU/内存不够,无法满足Pod的requests。
排查:
kubectl describe pod order-service-xxx -n production-order,看Events里的提示,比如Insufficient cpu或Insufficient memory。解决:
调整Pod的
resources.requests(比如把CPU从500m降到250m);扩容节点(加机器,或者给现有节点加资源)。
(2)镜像拉取失败
比如私有镜像仓库没配置Secret,或者镜像名称错了。
排查:
Events里会有Failed to pull image或ErrImagePull。解决:
创建docker-registry secret:
kubectl create secret docker-registry regcred --docker-server=harbor.example.com --docker-username=xxx --docker-password=xxx -n production-order;在Deployment的Pod模板里引用secret:
imagePullSecrets: - name: regcred。
(3)存储卷未绑定
如果Pod依赖PVC(比如数据库存储),但PVC没绑定到PV。
排查:
Events里有MountVolume failed for volume "pvc-xxx"。解决:检查PVC状态:
kubectl get pvc -n production-order,确保存在可用的PV。
六、第五步:高可用保障——不怕节点挂,不怕Pod死
K8s的核心优势就是自动化高可用,我们来看看它是怎么做到的:
1. 多副本(Replicas):不怕Pod死
Deployment里设置replicas: 3,意味着会创建3个相同的Pod。如果其中一个Pod挂了,Deployment会自动在其他节点创建新的Pod,保持总数3个。
2. Pod反亲和性:不怕节点挂
前面Deployment里的podAntiAffinity规则,强制让Pod分布在不同节点。比如3个Pod会在3个不同的节点上运行——即使一个节点挂了,也只剩2个Pod,不会全军覆没。
3. 健康检查:自动清理“死Pod”
Liveness Probe:检查Pod是否“活着”。比如订单服务的
/actuator/health/liveness接口返回200才算活,否则K8s会重启Pod。Readiness Probe:检查Pod是否“准备好接收流量”。比如启动时需要加载数据,没加载完前
readinessProbe失败,K8s不会把Pod加入Service的负载均衡,避免用户访问到未就绪的Pod。
4. 自动重建:节点挂了也不怕
如果某个节点挂了(比如硬件故障),K8s的节点控制器会检测到节点不可用,标记该节点上的Pod为NotReady。然后Deployment会自动在其他健康节点创建新的Pod,替换掉挂掉的Pod。
5. 流量自动转移:用户无感知
当Pod被替换或重启时,Service的selector会自动关联新的Pod(因为Pod的标签没变)。Service的负载均衡会把流量转发到健康的Pod,用户根本不会察觉到变化。
七、第六步:验证上线——用户能访问了吗?
部署完成后,我们需要验证:
检查Pod状态:
kubectl get pods -n production-order,所有Pod应该是Running状态。检查Service:
kubectl get service -n production-order,Service的Endpoints应该有3个Pod的IP(说明关联了所有健康Pod)。检查Ingress:
kubectl get ingress -n production-order,确认ADDRESS有值(Ingress Controller分配的IP)。测试访问:用浏览器或curl访问
http://order.example.com/health,应该返回Spring Boot的健康状态(比如{"status":"UP"})。
八、总结:从Jar包到上线的完整链路
打包:用Dockerfile把Jar包变成镜像,推送到仓库;
CI/CD:自动化构建、测试、部署;
K8s部署:用Deployment管理Pod副本,Service暴露服务,Ingress暴露到公网;
高可用:多副本、反亲和性、健康检查,确保节点或Pod故障时自动恢复;
验证:检查Pod、Service、Ingress状态,测试用户访问。
最后:K8s给我们的价值
自动化:不用手动登录服务器部署,CI/CD搞定一切;
高可用:自动处理故障,用户无感知;
弹性:可以随时扩容副本数(比如大促时把replicas从3改成10);
可观测:通过
kubectl和监控工具(比如Prometheus)随时查看应用状态。
其实,K8s的本质是“应用的操作系统”——它帮我们管理应用的生命周期,让我们专注于写业务代码,不用操心服务器、网络、故障这些问题。
附:关键命令速查
查看Pod:
kubectl get pods -n <namespace>查看Pod详情:
kubectl describe pod <pod-name> -n <namespace>查看Service:
kubectl get service -n <namespace>查看Ingress:
kubectl get ingress -n <namespace>查看集群事件:
kubectl get events -n <namespace> --sort-by='.metadata.creationTimestamp'
如果想深入,可以再学K8s的监控(Prometheus+Grafana)、日志(ELK Stack)、弹性伸缩(HPA)——这些是生产环境的必备技能。
