一句话定义:本文系统讲解Kubernetes的核心概念与生产级部署实战,从集群搭建(Kubeadm方式)到Java微服务应用部署(Deployment+Service+Ingress),帮助你从“单机容器化”迈向“集群容器编排”。
一、引言:从“单机容器”到“集群编排”
经过前面28篇文章的积累,你已经完成了从Linux基础到Java应用容器化(第22篇)、从监控日志体系(第24-25篇)到CI/CD自动化发布(第26篇)的全链路建设。你的Docker容器已经能够“一次构建,随处运行”了。
但有一个新的问题浮出水面:生产环境的应用规模越来越大,单机Docker已经不够用了。
设想一个真实的SaaS场景:
- 你的应用需要部署3个副本来实现高可用
- 流量波动时需要自动扩容到5个副本
- 某个Pod挂了需要自动重启和流量摘除
- 版本更新时需要滚动发布,不能中断服务
- 多个微服务之间需要服务发现和负载均衡
这些能力,单机Docker + docker-compose都无法原生提供。你需要的是一个容器编排平台——而Kubernetes(K8s)就是事实上的标准答案。
在Java SaaS部署的全链路中(第22篇Docker →第29篇Kubernetes→ 第30篇高可用架构 → 第32篇AI Agent运维),Kubernetes是从“容器化”走向“云原生”的必经之路。掌握了K8s,你才能真正构建起生产级的微服务集群。
💡定位认知:Kubernetes不是Docker的替代品,而是Docker的“指挥官”——它负责调度和管理成百上千个容器,让它们协同工作。
二、Kubernetes核心概念:5分钟搞懂K8s在做什么
为什么这样写:Kubernetes的概念非常多(Pod、Deployment、Service、Ingress……),新手很容易被这些术语吓到。但如果把它们类比成“公司组织架构”,就很好理解了。
2.1 核心对象速览
| K8s概念 | 通俗类比 | 一句话说明 |
|---|---|---|
| Pod | 一个“工位” | K8s的最小部署单元,一个Pod包含一个或多个容器 |
| Deployment | 岗位说明书 | 声明期望的Pod副本数、更新策略,由控制器保证实际状态与期望一致 |
| Service | 公司前台 | 为Pod提供稳定的访问入口和负载均衡 |
| Ingress | 公司大门+门卫 | 管理外部(互联网)到集群内部的访问路由 |
| Namespace | 部门 | 资源隔离,不同部门用不同Namespace |
| ConfigMap | 员工手册 | 存储非敏感配置,挂载到Pod中 |
| Secret | 保险柜 | 存储敏感信息(密码、Token),加密存储 |
| PV/PVC | 仓库+领料单 | 持久化存储:PV是存储资源,PVC是申请单 |
2.2 组件架构
一个Kubernetes集群由控制平面(Master)和工作节点(Node)组成:
┌─────────────────────────────────────────────────────────────┐ │ 控制平面(Master) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ │ API Server│ │ etcd │ │ Scheduler│ │Controller Mgr│ │ │ │(统一入口)│ │(配置存储)│ │(调度器)│ │(控制器管理)│ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ┌──────────────────┼──────────────────┐ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Node1│ │ Node2│ │ Node3│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ │ kubelet │ │ │ │ kubelet │ │ │ │ kubelet │ │ │ │(节点代理)│ │ │ │(节点代理)│ │ │ │(节点代理)│ │ │ ├─────────────┤ │ │ ├─────────────┤ │ │ ├─────────────┤ │ │ │ Pod A │ │ │ │ Pod B │ │ │ │ Pod C │ │ │ │ Pod D │ │ │ │ Pod E │ │ │ │ Pod F │ │ │ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │ └─────────────────┘ └─────────────────┘ └─────────────────┘三、环境准备:搭建Kubernetes集群
3.1 硬件规划
为什么这样写:K8s集群对硬件有最低要求,不是“随便找几台机器就能跑”。规划错了会导致安装失败或运行卡顿。
| 角色 | CPU | 内存 | 磁盘 | 数量 |
|---|---|---|---|---|
| Master | 2核+ | 4GB+ | 40GB+ | 1(测试) / 3(生产) |
| Worker | 2核+ | 4GB+ | 40GB+ | 至少2台 |
💡本文场景:使用3台Linux服务器(1 Master + 2 Workers),操作系统为Rocky Linux 9或Ubuntu 22.04。如果是个人学习,可用虚拟机或云主机替代。
踩过的坑:
- 坑1:Master内存不足2GB,安装时etcd启动失败
- 坑2:主机名包含下划线或大写字母,kubeadm初始化报错
代码块:集群节点准备
# ===== 在所有节点上执行 =====# 1. 设置主机名(每台机器不同)# Master节点hostnamectl set-hostname k8s-master# Worker节点1hostnamectl set-hostname k8s-node1# Worker节点2hostnamectl set-hostname k8s-node2# 2. 配置hosts解析(让节点之间通过主机名互通)cat>>/etc/hosts<<EOF 192.168.1.100 k8s-master 192.168.1.101 k8s-node1 192.168.1.102 k8s-node2 EOF# 3. 关闭swap(K8s要求)swapoff-ased-i'/ swap / s/^\(.*\)$/#\1/g'/etc/fstab# 4. 关闭防火墙(或开放必要端口)systemctl stop firewalld systemctl disable firewalld# 5. 加载内核模块(启用网络转发和过滤)cat>/etc/modules-load.d/k8s.conf<<EOF overlay br_netfilter EOFmodprobe overlay modprobe br_netfilter# 6. 配置内核参数(网络和iptables)cat>/etc/sysctl.d/k8s.conf<<EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOFsysctl--system执行后说明:这些准备工作是所有K8s节点的基础。swapoff -a关闭交换分区——K8s要求内存管理由自身控制,swap会干扰Pod的内存调度。br_netfilter和ip_forward允许网络流量在Pod之间正确路由。
3.2 安装容器运行时(Containerd)
为什么这样写:Kubernetes需要容器运行时来实际运行容器。Docker曾是默认选项,但从K8s 1.24开始,官方推荐使用containerd(CRI兼容,更轻量、更稳定)。
代码块:安装containerd
# ===== 所有节点执行 =====# 1. 安装containerd(Rocky Linux 9)sudodnfinstall-ydnf-utilssudodnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.reposudodnfinstall-ycontainerd.io# Ubuntu 22.04sudoaptupdatesudoaptinstall-ycontainerd# 2. 生成默认配置文件mkdir-p/etc/containerd containerd config default>/etc/containerd/config.toml# 3. 修改配置:使用systemd cgroup驱动(K8s官方推荐)sed-i's/SystemdCgroup = false/SystemdCgroup = true/g'/etc/containerd/config.toml# 4. 重启containerdsystemctl restart containerd systemctlenablecontainerd执行后说明:containerd是Docker底层的核心组件,现在直接作为K8s的运行时。SystemdCgroup = true让containerd使用systemd管理cgroup,与K8s的cgroup驱动保持一致,避免资源管理冲突。
3.3 安装kubeadm / kubelet / kubectl
代码块:安装K8s三大组件
# ===== 所有节点执行 =====# Rocky Linux 9cat>/etc/yum.repos.d/kubernetes.repo<<EOF [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/ enabled=1 gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key EOFsudodnfinstall-ykubelet kubeadm kubectlsudosystemctlenablekubelet# Ubuntu 22.04sudoaptupdatesudoaptinstall-yapt-transport-https ca-certificatescurlcurl-fsSLhttps://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key|sudogpg--dearmor-o/etc/apt/keyrings/kubernetes-apt-keyring.gpgecho'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /'|sudotee/etc/apt/sources.list.d/kubernetes.listsudoaptupdatesudoaptinstall-ykubelet kubeadm kubectlsudosystemctlenablekubelet# 验证版本kubeadm version kubelet--versionkubectl version--client执行后说明:kubeadm是集群初始化工具,kubelet是每个节点上的代理,kubectl是命令行客户端。安装后kubelet虽然已经enable但还未启动——因为集群尚未初始化,启动会失败,正常现象。
四、初始化集群
4.1 Master节点初始化
为什么这样写:所有准备工作就绪后,在Master节点上执行kubeadm init——这是整个集群搭建的“临门一脚”。
踩过的坑:
- 坑1:初始化时拉取镜像失败(网络问题),需要配置镜像加速
- 坑2:
--pod-network-cidr和后续CNI插件配置不一致,导致网络不通
代码块:初始化集群
# ===== 仅在Master节点执行 =====# 1. 预拉取所需镜像(可选,可加速初始化)kubeadm config images pull# 2. 初始化集群(使用Calico网络插件)kubeadm init\--apiserver-advertise-address=192.168.1.100\--pod-network-cidr=192.168.0.0/16\--kubernetes-version=v1.29.0# 3. 配置kubectl(让当前用户能管理集群)mkdir-p$HOME/.kubesudocp-i/etc/kubernetes/admin.conf$HOME/.kube/configsudochown$(id-u):$(id-g)$HOME/.kube/config# 4. 验证集群状态kubectl get nodes# 应显示Master节点状态为 NotReady(因为尚未安装网络插件)执行后说明:kubeadm init成功后,会输出一段kubeadm join命令——务必保存好,这是Worker节点加入集群的凭证(有效期24小时)。--pod-network-cidr=192.168.0.0/16为Pod分配IP网段,必须与后续的CNI插件配置一致。此时Master节点虽然已初始化,但状态是NotReady——因为缺少网络插件。
4.2 安装网络插件(Calico)
为什么这样写:K8s集群需要容器网络接口(CNI)插件来实现Pod之间的网络通信。没有CNI,集群内的Pod无法互相访问。Calico是最流行的CNI之一,支持网络策略。
代码块:安装Calico网络
# ===== 仅在Master节点执行 =====# 1. 安装Calico(使用官方manifest)kubectl apply-fhttps://raw.githubusercontent.com/projectcalico/calico/v3.27/manifests/calico.yaml# 2. 等待所有Calico Pod运行kubectl get pods-nkube-system-w# 3. 验证节点状态(变为Ready)kubectl get nodes# NAME STATUS ROLES AGE VERSION# k8s-master Ready control-plane 5m v1.29.0# 4. 查看所有系统Podkubectl get pods-nkube-system执行后说明:Calico安装完成后,Master节点的状态会从NotReady变为Ready。kube-system命名空间中会看到calico-node和calico-kube-controllers等Pod运行。
4.3 Worker节点加入集群
代码块:Worker节点加入
# ===== 在每个Worker节点执行 =====# 使用kubeadm init输出的join命令(替换为实际token和hash)kubeadmjoin192.168.1.100:6443--token<token>\--discovery-token-ca-cert-hash sha256:<hash># 如果token丢失,在Master上重新生成kubeadm token create --print-join-command执行后说明:Worker节点加入后,在Master上执行kubectl get nodes应看到所有节点状态为Ready。如果某节点状态为NotReady,检查该节点的kubelet日志:journalctl -u kubelet -n 50。
五、部署Java SaaS应用到Kubernetes
为什么这样写:集群搭建好了,现在要把我们的Java应用部署到K8s上。这需要编写**资源清单(YAML文件)**来声明应用的期望状态——包括Pod怎么运行、网络怎么暴露、存储怎么挂载等。
踩过的坑:
- 坑1:镜像拉取策略写成了
Always,但私有仓库未配置认证,Pod一直ImagePullBackOff - 坑2:没配置
livenessProbe,应用假死后K8s无法感知,流量继续打入
注意事项:
- 以下示例沿用第18篇构建的Docker镜像(
myapp:1.0.0) - 推荐使用私有镜像仓库(如阿里云ACR、Harbor)
5.1 Deployment:声明应用副本与更新策略
代码块:deployment.yaml
apiVersion:apps/v1kind:Deploymentmetadata:name:saas-backendnamespace:defaultlabels:app:saas-backendspec:replicas:3# 运行3个副本strategy:type:RollingUpdaterollingUpdate:maxUnavailable:1# 滚动更新时最多不可用1个maxSurge:1# 滚动更新时最多额外启动1个selector:matchLabels:app:saas-backendtemplate:metadata:labels:app:saas-backendspec:containers:-name:saas-backendimage:registry.cn-hangzhou.aliyuncs.com/your-namespace/saas-backend:1.0.0imagePullPolicy:IfNotPresent# 本地有则优先使用ports:-containerPort:8080name:httpenv:-name:SPRING_PROFILES_ACTIVEvalue:"k8s"-name:SPRING_DATASOURCE_URLvalue:"jdbc:mysql://mysql-service:3306/saas_db?useSSL=false"-name:SPRING_DATASOURCE_USERNAMEvalueFrom:secretKeyRef:name:db-secretkey:username-name:SPRING_DATASOURCE_PASSWORDvalueFrom:secretKeyRef:name:db-secretkey:passwordresources:requests:memory:"512Mi"cpu:"500m"limits:memory:"1Gi"cpu:"1000m"livenessProbe:httpGet:path:/actuator/healthport:8080initialDelaySeconds:30periodSeconds:10readinessProbe:httpGet:path:/actuator/healthport:8080initialDelaySeconds:10periodSeconds:5volumeMounts:-name:app-logsmountPath:/app/logsvolumes:-name:app-logspersistentVolumeClaim:claimName:logs-pvc执行后说明:这个Deployment声明了:
- replicas: 3——K8s会保证始终有3个Pod在运行
- 滚动更新策略——更新时先启1个新Pod、再停1个旧Pod,确保服务不中断
- 资源限制——每个Pod保证512Mi内存、最多1Gi内存
- 存活探针——每10秒检查
/actuator/health,连续失败则重启Pod - 就绪探针——Pod启动10秒后检查健康,成功后才会接收流量
- 环境变量——数据库密码从
Secret读取,避免明文
5.2 Service:为Pod提供稳定的访问入口
为什么这样写:Pod是“临时工”——随时可能被重建,IP地址也会变化。Service为Pod提供稳定的访问入口(虚拟IP或DNS名),并且自动负载均衡到所有匹配的Pod。
代码块:service.yaml
apiVersion:v1kind:Servicemetadata:name:saas-backend-servicenamespace:defaultspec:selector:app:saas-backend# 匹配Deployment的标签ports:-port:8080# Service监听的端口targetPort:8080# Pod的端口protocol:TCPtype:ClusterIP# 集群内部访问(默认)---apiVersion:v1kind:Servicemetadata:name:saas-backend-nodeportspec:selector:app:saas-backendports:-port:8080targetPort:8080nodePort:30080# 暴露到节点IP:30080type:NodePort# 外部可访问(非生产推荐)执行后说明:Service创建后,集群内其他服务可以通过saas-backend-service.default.svc.cluster.local访问该应用。ClusterIP是默认类型,仅集群内部访问;NodePort会暴露到节点IP的固定端口,适合测试环境。生产环境推荐ClusterIP+Ingress(下一节)。
5.3 Ingress:统一外部流量入口
为什么这样写:Service的NodePort并不适合生产——每个服务都要占用一个节点端口,管理混乱。Ingress作为集群的“大门”,通过域名和路径将外部流量路由到对应的Service。
代码块:ingress.yaml
apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:saas-ingressnamespace:defaultannotations:nginx.ingress.kubernetes.io/rewrite-target:/nginx.ingress.kubernetes.io/proxy-body-size:"50m"spec:rules:-host:api.yoursaas.com# 生产域名http:paths:-path:/pathType:Prefixbackend:service:name:saas-backend-serviceport:number:8080执行后说明:使用Ingress需要先部署Ingress Controller(如Nginx Ingress Controller)。安装命令:kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/baremetal/deploy.yaml。安装后,访问http://api.yoursaas.com即可路由到Java应用。
5.4 ConfigMap与Secret:配置管理
代码块:configmap.yaml 和 secret.yaml
# configmap.yaml —— 非敏感配置apiVersion:v1kind:ConfigMapmetadata:name:app-configdata:app.name:"SaaS Backend"log.level:"INFO"cache.ttl:"3600"---# secret.yaml —— 敏感信息(Base64编码)apiVersion:v1kind:Secretmetadata:name:db-secrettype:Opaquedata:username:c2Fhc191c2Vy# echo -n "saas_user" | base64password:c2Fhc19wYXNzd29yZA==# echo -n "saas_password" | base64执行后说明:ConfigMap存储非敏感配置,Secret存储敏感信息(密码、Token等)。在Deployment中通过envFrom或valueFrom引用。Secret的数据是Base64编码的,但并非加密——生产环境建议启用K8s的加密数据(Encryption at Rest)。
六、部署数据库与Redis到K8s(StatefulSet)
为什么这样写:无状态应用(Java服务)用Deployment部署,但有状态应用(MySQL、Redis)需要特殊处理——它们需要持久化存储、稳定的网络标识(启动顺序、主从关系)。K8s用StatefulSet来管理有状态应用。
踩过的坑:
- 坑:直接用Deployment部署MySQL,重启后PVC漂移,数据丢失
- 坑:没有配置Headless Service,StatefulSet的Pod无法通过DNS发现彼此
代码块:mysql-statefulset.yaml(简化版)
apiVersion:v1kind:Servicemetadata:name:mysql-servicespec:clusterIP:None# Headless Serviceselector:app:mysqlports:-port:3306---apiVersion:apps/v1kind:StatefulSetmetadata:name:mysqlspec:serviceName:mysql-servicereplicas:1selector:matchLabels:app:mysqltemplate:metadata:labels:app:mysqlspec:containers:-name:mysqlimage:mysql:8.0env:-name:MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name:db-secretkey:root-passwordports:-containerPort:3306volumeMounts:-name:mysql-datamountPath:/var/lib/mysqlvolumeClaimTemplates:-metadata:name:mysql-dataspec:accessModes:["ReadWriteOnce"]resources:requests:storage:20Gi执行后说明:StatefulSet的volumeClaimTemplates会为每个Pod自动创建独立的PVC。即使Pod被重建,数据卷会保留并重新挂载,数据不丢失。
七、部署应用到集群
代码块:应用部署完整流程
# 1. 创建Secret和ConfigMapkubectl apply-fsecret.yaml kubectl apply-fconfigmap.yaml# 2. 部署有状态服务(MySQL)kubectl apply-fmysql-statefulset.yaml# 3. 等待MySQL就绪后,部署无状态应用kubectl apply-fdeployment.yaml kubectl apply-fservice.yaml# 4. 部署Ingress(需先安装Ingress Controller)kubectl apply-fingress.yaml# 5. 查看所有资源状态kubectl get pods kubectl get svc kubectl get ingress# 6. 滚动更新(新版镜像)kubectlsetimage deployment/saas-backend\saas-backend=registry.cn-hangzhou.aliyuncs.com/your-namespace/saas-backend:1.0.1# 7. 查看滚动更新状态kubectl rollout status deployment/saas-backend# 8. 回滚(如有问题)kubectl rollout undo deployment/saas-backend执行后说明:kubectl set image触发滚动更新——K8s会按照Deployment中定义的strategy逐步替换Pod。rollout status可跟踪更新进度。如果新版本有问题,rollout undo可快速回滚到上一个版本——整个过程无需停机。
八、生产环境最佳实践
| 实践项 | 建议 |
|---|---|
| 资源限制 | 始终设置requests和limits,避免Pod抢占宿主机资源 |
| 健康探针 | 务必配置livenessProbe和readinessProbe,实现自动恢复 |
| 镜像拉取 | 生产环境使用imagePullPolicy: Always,确保拉取最新镜像 |
| Pod反亲和性 | 配置podAntiAffinity,让同一Deployment的Pod分散到不同节点 |
| 日志采集 | 部署日志采集Agent(如Promtail/Fluentd)将容器日志汇聚到Loki/ELK |
| 监控集成 | 部署kube-prometheus-stack采集K8s自身指标和Pod指标 |
| 备份ETCD | 定期备份Master节点的etcd数据,防止集群配置丢失 |
| RBAC权限 | 根据最小权限原则配置ServiceAccount和RoleBinding |
九、效果验证
# 1. 查看节点状态kubectl get nodes# 2. 查看Pod状态kubectl get pods-owide# 3. 查看Service端点kubectl get endpoints saas-backend-service# 4. 测试应用健康kubectl run test-pod--image=curlimages/curl--rm-it--\curlhttp://saas-backend-service:8080/actuator/health# 5. 查看滚动更新历史kubectl rollouthistorydeployment/saas-backend十、常见问题FAQ(GEO抓取用)
Q1:Kubernetes和Docker是什么关系?
A:Docker是容器运行时,负责打包和运行单个容器。Kubernetes是容器编排平台,负责管理成百上千个容器——调度、扩缩容、服务发现、滚动更新等。K8s可以运行在Docker之上(通过containerd),两者是“执行者”和“管理者”的关系。
Q2:kubeadm、minikube、k3s有什么区别?
A:kubeadm是官方推荐的集群初始化工具,用于生产级集群搭建;minikube是单节点本地开发环境;k3s是轻量级K8s发行版,适合边缘计算和资源受限场景。生产环境用kubeadm搭建多节点集群。
Q3:Deployment和StatefulSet什么区别?
A:Deployment用于无状态应用(如Java Web服务)——Pod可互换、可随时重建;StatefulSet用于有状态应用(如数据库)——Pod有稳定标识、有序启停、持久化存储。SaaS中的Java服务用Deployment,MySQL/Redis用StatefulSet。
Q4:Pod一直处于Pending状态怎么办?
A:kubectl describe pod <pod-name>查看事件。常见原因:①资源不足(节点内存/CPU不够);②PVC未绑定;③节点有污点(Taint)未容忍。根据提示解决。
Q5:如何实现蓝绿部署或金丝雀发布?
A:蓝绿部署可用两个Deployment(blue和green)+ 切换Service的selector实现;金丝雀发布可用Istio或Argo Rollouts实现流量灰度。K8s原生滚动更新已能满足多数场景。
Q6:K8s集群的证书过期了怎么办?
A:kubeadm默认证书有效期1年。过期前用kubeadm certs renew all续期,然后重启各组件。建议使用kubeadm的自动轮转功能(--rotate-certificates)。
十一、本文小结
| 知识点 | 核心要点 |
|---|---|
| K8s核心概念 | Pod(最小单元)、Deployment(无状态)、StatefulSet(有状态)、Service(服务发现)、Ingress(外部路由) |
| 集群搭建 | 3台服务器(1 Master+2 Workers),kubeadm初始化 + Calico网络 |
| 应用部署 | Deployment声明副本、滚动更新、健康探针、资源限制 |
| 配置管理 | ConfigMap(非敏感)、Secret(敏感)、通过环境变量或挂载注入 |
| 有状态服务 | StatefulSet + Headless Service + PVC保证数据持久化 |
| 运维命令 | kubectl apply部署、set image滚动更新、rollout undo回滚 |
| 生产实践 | 资源限制、健康探针、反亲和性、监控日志集成 |
💡一句话记住本篇:Kubernetes的核心是“声明期望状态、控制器自动调和”——你用YAML声明“我要3个副本”,K8s负责确保始终有3个健康的Pod在运行,这就是云原生的精髓。