一、前言
本文简要介绍 kubernetes(后文简称 k8s )的基本原理,随后重点介绍了基于 k8s 部署 springboot 微服务应用集群的方法。
为了更好的演示 k8s 部署应用集群的过程,我们开发了一个 springcloud 微服务项目 springcloudk8small ,springcloudk8small 和部署方案都是采用最简化的处理,目的是为了帮助大家快速入门 k8s。

二、kubernetes 简介
1. 什么是 k8s?
从官方的定义来说,kubernetes就是云原生微服务应用的编排器。但是编排器这个感念似乎并不好理解,所以个人认为下面这个介绍更能帮助理解 k8s 的本质。
k8s 是云的操作系统,使用户不必关心应用是在谁的云或服务器上运行。就像linux和windows意味着用户不必关心应用是运行在戴尔还是ibm的服务器上一样。
k8s 帮我们解决服务器,存储,网络的调度(编排)问题,使我们能够专注于应用的部署。就类似于linux或者windows帮我们解决的cpu,内存,io的调度,使我们可以专注于应用的开发和使用。
2. 为什么我们需要 k8s 呢?
1)公司可以不必关心应用是在谁的云或服务器上运行,避免被某个云厂商或服务器厂商绑架;
2)简化部署流程,快速扩缩容,快速恢复环境:
在日常开发运维中,你是否遇到过以下场景“
- 项目一开始需要搭建开发环境,你登录每一台服务器安装jdk、filebeat、安装nginx、安装nacos集群…,花了两三天时间才把集群搭建好。
- 项目开发好了,进入sit、uat环节,又需要重复两遍遍上述环节,又花了几天;项目顺利发布上线,搭建prod环境,这次服务器更多,又花了更多时间部署环境;
- 项目运行了一段时间,安全排查出filebeat或者nginx版本存在安全漏洞,然后又登录各个环境的各台服务器一个个升级中间件;
- 有时候在prod改了某个中间件的配置,但是不确定是否同步到了其他环境,随着时间的推移,各个环境开始不一致,为项目稳定运行埋下了未知风险。
从上述过程可以看出了,每一个环境的搭建,我们都需要执行相同的过程,同时,很难方便的对比各个环境的差异。
k8s一键创建/销毁环境:
如果使用 k8s 部署,则只需要为每个环境配置一组 yaml 文件,执行一行简单命令,即可在几分钟之内完成环境搭建或销毁。
同时只需要对比不同环境的yaml文件,即可知道他们之间的差异。
接下来,我们就通过 演示项目 springcloudk8small 看看如何通过k8s实现这种能力。
# 一键创建或者销毁环境 kubectl apply/delete -f ./deploy/ #每个环境配置一组部署配置文件 |--[dev/test/prod]/deploy/ |--mall-frontend-deployment.yaml |--mall-gateway-deployment.yaml |--nacos-statefulset.yaml |--xxx.yaml
三、kubernetes原理简述
1. k8s架构

kubernetes 集群由控制平面和一个或多个工作节点组成。
以下是主要组件的简要概述:
1.控制平面组件:管理集群的整体状态(不重要,部署应用不需要和这些组件打交道,了解即可):
- kube-apiserver: 公开 kubernetes http api 的核心组件服务器;
- etcd: 具备一致性和高可用性的键值存储,用于所有 api 服务器的数据存储;
- kube-scheduler: 查找尚未绑定到节点的 pod,并将每个 pod 分配给合适的节点;
- kube-controller-manager: 运行控制器来实现 kubernetes api 行为。
- cloud-controller-manager: 与底层云驱动集成
2.node 组件:在每个节点上运行,维护运行的 pod 并提供 kubernetes 运行时环境:
- kubelet: 确保 pod 及其容器正常运行;
- kube-proxy: 维护节点上的网络规则以实现 service 的功能;
- 容器运行时(container runtime);
我们所有的应用和中间件都是运行在 node 组件的容器运行时中,通过kube-proxy向集群内外提供网络访问。
2. k8s的一些基本概念
k8s引入了许多概念,但是本文只简单介绍容器、pod、deployment、service等四个核心概念,因为通过这四个内容,你就可以把你的微服务集群运行起来了。
1)容器
容器和虚拟机相比,它实际上是一种资源隔离的进程,运行在容器中的应用比独占一个虚拟机消耗的资源更少,启动速度更快。所谓的资源隔离主要只指进程隔离、文件隔离、网络隔离:
- 进程隔离:最基本的隔离就是进程之间看不到彼此,这是由linux的namespace机制实现的;
- 文件隔离:第二种隔离就是隔离系统真实的文件系统。docker利用linux的mount机制,给每个隔离进程挂载了一个虚拟的文件系统,使得一个隔离进程只能访问这个虚拟的文件系统,无法看到系统真实的文件系统;
- 网络隔离:第三种隔离就是网络协议栈的隔离;
一个容器进程本质上是一个运行在沙盒中的隔离进程,由linux系统本身负责隔离,docker只是提供了一系列工具,帮助我们设置好隔离环境后,启动这个进程。
2)pod
上面我们已经提过了,所有的应用和中间件都是运行在容器运行时中,那么我们如何管理容器运行时呢?
其实就是通过pod,pod 是可以在 kubernetes 中创建和管理的、最小的可部署的计算单元。
pod 是一个或者一组容器的封装器;这些容器共享存储、网络、以及怎样运行这些容器的规约。可以把 pod 简单理解为传统虚拟化部署中的虚拟机,只不过类似容器和虚拟机的差别,pod占用的资源更小而已。
3)deployment
一个 deployment 为 pod 提供声明式的更新能力,也就是服务更新和扩缩容的能力。
所谓的声明式,就是说用户可以通过 deployment 声明 pod 的期望状态,deployment 会自动帮你把 pod 保持在期望状态。而期望状态,一般是指你需要这个 pod 有几个副本,如何更新,如何定义 pod 的健康状态的信息。
4)service
kubernetes 网络模型由几个部分构成:
- 集群中的每个 pod 都会获得自己的、独一无二的集群范围 ip 地址。
- pod 网络(也称为集群网络)处理 pod 之间的通信。它确保所有 pod 可以与所有其他 pod 进行通信, 无论它们是在同一个节点还是在不同的节点上。
- service api 允许你为由一个或多个后端 pod 实现的服务提供一个稳定(长效)的 ip 地址或主机名, 其中组成服务的各个 pod 可以随时变化。
k8s网络模型有两个重要的特点:
- 对外界不可见;
- pod之间可以互访,但却是不稳定的。集群会为每个pod生成ip,但是pod没有重启的概念,所谓服务重启就是销毁旧 pod,创建新 pod。在某些场景下,我们需要服务保持稳定的网络,比如注册中心,各个微服务需要配置固定的注册中心地址。
service就是为了解决上述两个问题:
- 对集群外暴露访问入口;
- 对集群内提供稳定的服务访问地址;
四、kubernetes安装
注:如果你已经有了k8s运行环境,可以跳过这部分
minikube是一个本地kubernetes,它提供和k8s一样的交互命令,安装更加简单,占用的资源也更少,所以在学习环境,我们通过wmware虚拟机安装minikube进行学习。
环境及版本说明
1.vmware:
- vmware® workstation 16 pro 16.2.4 build-20089737
2.操作系统:
- centos stream release 9
3.minikube:
- minikube v1.34.0
- kubernetes v1.31.0
1. 安装docker
卸载docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
安装docker
yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum makecache fast yum -y install docker-ce
配置dockers容器镜像(xxxxx.mirror.aliyuncs.com地址自行到 阿里云容器镜像服务——》镜像加速器获取)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'eof'
{
"registry-mirrors": ["https://xxxxx.mirror.aliyuncs.com"]
}
eof
sudo systemctl daemon-reload
sudo systemctl restart docker
2.安装kubectl
使用阿里云镜像安装kubectl,你可以按照以下步骤操作:
1) 添加阿里云的kubernetes仓库。
cat <<eof > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=0 repo_gpgcheck=0 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg eof
2) 安装kubectl
yum install -y kubectl #验证安装是否成功。 kubectl version --client
3.安装minikube
1) 获取官方最新版minikube
curl -lo https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 install minikube-linux-amd64 /usr/local/bin/minikube
2) 设置默认驱动
minikube config set driver docker
3) 启动minikube 启动一个k8s节点
minikube delete ; minikube start --force --base-image='registry.cn-hangzhou.aliyuncs.com/google_containers/kicbase:v0.0.44' #--force是以root身份启动的docker的必须选项 #--base-image为指定minikube start 采用的基础镜像,上面docker pull拉取了什么镜像,这里就改成什么镜像(不指定可能会报错)
4) 验证minikube
[root@minikube ~]# minikube status minikube type: control plane host: running kubelet: running apiserver: running kubeconfig: configured
5) 检查minikuke相关的资源是否正常启动(全部都是running状态表示正常)
[root@minikube ~]# kubectl get pod,svc,deployment,rc -n kube-system name ready status restarts age pod/coredns-6f6b679f8f-6gdhm 1/1 running 0 7m5s pod/etcd-minikube 1/1 running 0 7m11s pod/kube-apiserver-minikube 1/1 running 0 7m11s pod/kube-controller-manager-minikube 1/1 running 0 7m11s pod/kube-proxy-f4vnw 1/1 running 0 7m5s pod/kube-scheduler-minikube 1/1 running 0 7m11s pod/storage-provisioner 1/1 running 1 (6m47s ago) 7m9s name type cluster-ip external-ip port(s) age service/kube-dns clusterip 10.96.0.10 <none> 53/udp,53/tcp,9153/tcp 7m10s name ready up-to-date available age deployment.apps/coredns 1/1 1 1 7m10s
五、kubernetes部署springcloud微服务应用集群
1. 演示项目 springcloudk8small 应用介绍
如下图所示,springcloudk8small 是前后端分离的 b/s 应用。
前端是一个基于 vue 3 和 element plus 的商城演示项目,展示了一个微服务架构的商城系统,包括用户管理、商品管理、订单管理等功能。
后端是一个基于spring cloud的微服务商城应用,以nacos为注册中心,包含用户微服务、商品微服务、订单微服务、所有后端微服务通过网关微服务向外暴露接口。

2. 镜像管理
1) 打包镜像
我们知道,k8s是管理容器应用的编排器,所以第一步就是要打包镜像,即应用容器化。
前提说明:从2024年7月起阿里云镜像加速器调整了 规则 ,只能在阿里云服务器使用阿里云镜像加速器。我使用的方式是购买了一台基础版的云服务器,用于打包镜像,打包后推动到免费的 阿里云容器镜像中心 ,再从本地虚拟机拉取镜像,运行容器。
下面,以前端工程为例,在工程根目录编写如下 dockerfile 文件。
# dockerfile # 使用 node.js 17 作为基础镜像 from node:17 as build-stage # 设置工作目录 workdir /app # 复制 package.json 和 package-lock.json (如果存在) copy package*.json ./ # 安装项目依赖 run npm install # 复制项目文件和文件夹到工作目录 copy . . # 构建应用 run npm run build # 生产阶段 from nginx:stable-alpine as production-stage # 从构建阶段复制构建好的文件到 nginx copy --from=build-stage /app/dist /usr/share/nginx/html # 暴露 80 端口 expose 80 # 启动 nginx cmd ["nginx", "-g", "daemon off;"]
执行 docker build 命令完成镜像打包
docker build -t mall-frontend .
执行 docker images 命令可以查看镜像信息
[root@aliyun mall]# docker images | grep mall-frontend mall-frontend latest de465df980c9 2 weeks ago 29.7mb
现在我们可以直接在本地运行 docker 容器,验证镜像是否可以正常执行
# 运行容器 docker run -d -p 18080:80 mall-frontend # 查看容器运行状态 [root@aliyun mall]# docker ps container id image command created status ports names 66340f2a68f7 mall-frontend "/docker-entrypoint.…" 5 seconds ago up 3 seconds 0.0.0.0:18080->80/tcp, :::18080->80/tcp awesome_swartz # 测试访问 curl http://localhost:18080
2) 使用阿里云镜像中心管理镜像
1、免费申请试用阿里云镜像服务
登录 阿里云镜像服务 申请免费试用,进入 命名空间 创建一个自己的命名空间就可以开始使用了。
2、在打包镜像的服务器和运行k8s的服务器中登录镜像服务
# 登录阿里云docker registry,用于登录的用户名为阿里云账号全名,密码为开通服务时设置的密码。 docker login --username=<username> registry.cn-hangzhou.aliyuncs.com
3、在打包镜像的服务器中将镜像推送到镜像中心
# 标记镜像(注意将推送地址更换为你自己的命名空间(namespace)) docker tag mall-frontend:latest registry.cn-hangzhou.aliyuncs.com/namsapce01/mall-frontend:latest # 推送镜像 docker push registry.cn-hangzhou.aliyuncs.com/namsapce01/mall-frontend:latest
4、在运行k8s的服务器中拉取镜像
# 拉取镜像 docker pull registry.cn-hangzhou.aliyuncs.com/namsapce01/mall-frontend:latest
5、从docker加载镜像到minikube
上述拉取镜像的动作只是将镜像拉取到了本地docker中,由于我们是使用minikube运行k8s集群,因此还需要将镜像加载到minikube中
# 加载镜像到minikube [root@minikube ~]# minikube image load registry.cn-hangzhou.aliyuncs.com/namsapce01/qsk-book:1.1 [root@minikube ~]# minikube image ls registry.cn-hangzhou.aliyuncs.com/namsapce01/qsk-book:1.1
3. 小结
我们以前端工程为例介绍了镜像打包和镜像管理的全过程,后端工程镜像打包和管理唯一的差异点就是dockerfile文件不同,这里不再赘述,大家可以自行查看对于的dockerfile文件。
2. k8s部署前端
1. 定义 pod
如前所述,pod 是k8s中应用执行的最小单位,pod 的作用即是封装容器。
因此,要在k8s中部署应用,第一步就是要定义应用的pod。如下所示,pod 的定义文件由两部分组成,spec 之前定义了诸如 api 版本、资源类型、pod 名称等基本信息,spec 定义了容器的信息。
# mall-frontend-pod.yaml
apiversion: v1 # 定义k8s api的版本
kind: pod # 定义创建资源的类型
metadata:
name: mall-frontend-pod # pod的名称
spec: # 规格说明,定义容器信息
containers:
- name: mall-frontend-pod
image: registry.cn-hangzhou.aliyuncs.com/namsapce01/mall-frontend:latest # 镜像 这里的namespace01需要替换为自己的命名空间
imagepullpolicy: ifnotpresent # 镜像拉取策略:always、never、ifnotpresent
ports:
- containerport: 80
定义好 pod 对应的 yaml 文件之后,只需要执行 kubectl apply 命令,即可在 k8s 集群中运行对应的 pod。pod 的状态为 running 即说明已经成功运行 pod,不过此时的 pod 只能在k8s集群内访问,并不能在集群外访问。
# 运行pod [root@minikube deploy]# kubectl apply -f mall-frontend-pod.yaml pod/mall-frontend-pod created 查看pod状态 [root@minikube deploy]# kubectl get pods name ready status restarts age mall-frontend-pod 1/1 running 0 10s
2.使用 deployment 管理一组 pod
在上一节我们了解了如何定义并运行一个 pod,但在实际运维中,我们并不会这样直接运行一个 pod。因为 pod 实际上是一个临时资源,你不应该期待单个 pod 既可靠又耐用。
在实际应用中,k8s 是通过 deployment 管理一组 pod,通过 deployment 定义一组 pod 的期望状态,k8s 会自动帮忙实现并保持在这个期望状态。
所谓 pod 的期望状态,即一个pod 运行几个副本,如何更新等状态。
下面这个文件定义了前端项目的 deployment 文件,从文件中可以看到两个关键信息:
- replicas: 定义 pod 的副本数,即有2个 mall-frontend 的 pod 运行;
- selector: 选择器,这是一个k8s中很关键的概念,这里指定这个 deployment 需要管理的 pod,这里即表示管理的是 app=mall-frontend 的 pod。
# mall-frontend-deployment.yaml
apiversion: apps/v1 # 定义使用的 kubernetes api 版本
kind: deployment # 定义要创建的资源类型
metadata: # 元数据
name: mall-frontend # deployment 的名称
labels:
app: mall-frontend # 为 deployment 添加标签
spec: # deployment规格说明
replicas: 2 # 定义期望运行的副本数
selector: # 定义如何选择要管理的 pod
matchlabels:
app: mall-frontend
template: # 定义 pod 的模板
metadata: # pod 的元数据
labels: # 为 pod 添加标签
app: mall-frontend
spec: # pod 的规格
containers: # 定义容器
- name: mall-frontend
image: registry.cn-hangzhou.aliyuncs.com/namsapce01/mall-frontend:latest # 镜像
imagepullpolicy: ifnotpresent # 镜像拉取策略:always、never、ifnotpresent
ports: # 定义容器端口
- containerport: 80同样的,我们执行 kubectl apply 即运行对应的 deployment
# 创建deployment [root@minikube deploy]# kubectl apply -f mall-frontend-deployment.yaml deployment.apps/mall-frontend created # 查看dployment [root@minikube deploy]# kubectl get deploy -o wide name ready up-to-date available age containers images selector mall-frontend 2/2 2 2 13m mall-frontend registry.cn-hangzhou.aliyuncs.com/namsapce01/mall-frontend:latest app=mall-frontend
从deployment的状态可以看出,deady为2/2,即表示期望的pod数量为2,当前成功运行的数量也为2。这时继续查看 pod 的状态可以发现,deployment 自动生成了两个 pod。
[root@minikube deploy]# kubectl get pod name ready status restarts age mall-frontend-6bb5768dbc-25jh8 1/1 running 0 22m mall-frontend-6bb5768dbc-vmqvj 1/1 running 0 22m
为什么k8s特别强调了期望数量和实际数量呢?这就是k8s提供的强大的声明式的更新能力,我们只需要定义我们期望的状态,k8s会自动创建并保持在这个状态。
现在我们来看一下如果其中的一个 pod 被删除或者意外宕机会发生什么?
# 删除pod mall-frontend-6bb5768dbc-25jh8 [root@minikube deploy]# kubectl delete pod mall-frontend-6bb5768dbc-25jh8 pod "mall-frontend-6bb5768dbc-25jh8" deleted # 查看pod状态 [root@minikube deploy]# kubectl get pod name ready status restarts age mall-frontend-6bb5768dbc-vmqvj 1/1 running 0 23m mall-frontend-6bb5768dbc-xf5jd 1/1 running 0 4s
从上面的过程可以看到,k8s 提供的 deployment 自动新建了一个 pod,仍然将 pod 的数量保持在我们期望的数量2。
到这里,前端服务就已经部署好了,但是目前这个服务只能在集群内访问,集群外还无法访问到这个服务。
3.使用service暴露服务的入口
我们在前文中已经提过,service主要解决两个问题:
- 对集群外暴露访问入口;
- 对集群内提供稳定的服务访问地址;
这里我们先来演示第一个能力,即对集群外暴露访问入口。
从mall-frontend-service.yaml我们看到两组关键信息:
- selector: 定义这个service对于的pod
port: 定义如何映射端口
- targetport: 对于pod中容器的端口
- port: service 暴露的端口,我们可以在集群内通过service ip + 这个端口访问pod的服务
- nodeport: 提供一个集群外端口,通过集群ip访问

# mall-frontend-service.yaml
apiversion: v1
kind: service
metadata:
name: mall-frontend-service # service 的名称
spec: # service 规格说明
selector: # 定义如何选择要暴露的 pod
app: mall-frontend # 选择标签为 app: mall-frontend 的 pod
ports:
# 定义单个端口映射
- protocol: tcp # 使用 tcp 协议
port: 80 # service 暴露的端口
targetport: 80 # pod 中容器的端口
nodeport: 30080 # 提供一个集群外访问的端口,只能在30000-32767范围内
type: nodeport # 定义 service 类型,通过每个节点上的 ip 和静态端口(nodeport)公开 service。 为了让 service 可通过节点端口访问,kubernetes 会为 service 配置集群 ip 地址, 相当于你请求了 type: clusterip 的 service。执行kubectl apply 命令创建service
[root@minikube deploy]# kubectl apply -f mall-frontend-service.yaml service/mall-frontend-service created
查看service状态
[root@minikube deploy]# kubectl get svc name type cluster-ip external-ip port(s) age mall-frontend-service nodeport 10.102.36.9 <none> 80:30080/tcp 77m
现在我们就可以通过集群ip+service端口访问前端服务了
# 查看集群ip [root@minikube deploy]# minikube ip 192.168.49.2 # 在集群外访问前端服务 [root@minikube deploy]# curl 192.168.49.2:30080 <!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="x-ua-compatible" content="ie=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico" rel="external nofollow" ><title>mall</title><script defer="defer" src="/js/chunk-vendors.8382f0a2.js"></script><script defer="defer" src="/js/app.0cafe880.js"></script><link href="/css/chunk-vendors.5c8b628d.css" rel="external nofollow" rel="stylesheet"><link href="/css/app.db76374a.css" rel="external nofollow" rel="stylesheet"></head><body><noscript><strong>we're sorry but mall doesn't work properly without javascript enabled. please enable it to continue.</strong></noscript><div id="app"></div></body></html>
到这里,前端服务就全部部署完成了,但是由于我们的k8s是运行vmware虚拟机中的,如果我们要在外部的windows机器上 访问服务,还需要建立一个代理服务:
# 在运行vmware的windows机器上执行以下命令
ssh -l {本地访问的端口}:{minikube的ip}:{service暴露的端口} {虚拟机(如centos、debian、ubuntu等)的用户名}@{虚拟机的ip}
# 如执行,则可以在windows机器上通过浏览器访问127.0.0.1:9000访问对应的服务
ssh -l 9000:192.168.49.2:30080 root@192.168.8.128
2. k8s部署后端微服务集群
后端微服务集群的部署和前端差异不大,主要的区别就是 nacos 注册中心不应该使用 deployment 管理负载,而应该使用 statefulset 管理负载。
deployment 和 statefulset 都是用于管理应用负载的,主要差别是 deployment 主要用于管理无状态服务,statefulset 用于管理有状态服务。
有状态无状态:
- 无状态:不会对本地环境产生任何依赖。
- 有状态:会对本地环境(存储、网络等)产生依赖,会存储数据到本地内存、磁盘等,如redis,db;
由于我们使用的是 nacos 本地 db 缓存数据,所以应该使用 statefulset 部署。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论