☰
虚拟化→容器→Docker→Kubernetes:云原生技术演进全链路解析
2026/10/13 9:31:13 网站建设 项目流程

1. 这不是概念堆砌,而是一条真实的技术演进路径

你打开终端输入kubectl get pods的那一刻,背后跑着的可能是一台物理服务器、一个虚拟机、一个容器镜像,甚至是一段被调度到边缘节点的轻量级进程——但你根本不需要知道它在哪台机器上,也不用关心它用了多少内存配额。这种“看不见却稳稳运行”的体验,就是今天我们要聊的整条技术链路:虚拟化技术 → 容器 → Docker → Kubernetes。这四个词不是并列关系,而是层层递进、解决不同层级问题的演化结果。很多人学K8S卡在第一步,不是因为YAML写不对,而是没真正搞懂:为什么非得先有容器?为什么容器又必须依赖Linux内核的cgroups和namespaces?为什么Docker火了几年后,大家突然开始集体转向K8S?这些不是考试题,而是你在部署一个Spring Boot服务时,会真实踩到的坑。

我带过不少刚转云原生的开发同学,他们最常问的问题是:“Docker不就够用了?加个docker-compose不就搞定了?”——这话在单机开发环境里完全成立。但当你要把23个微服务模块、4类数据库中间件、7种定时任务脚本,稳定运行在12台异构服务器上,并且要求任意节点宕机后5秒内自动恢复服务,同时还要让运维能一眼看清每个服务的CPU水位、网络延迟、重启次数……这时候,Docker只是个“打包工具”,它不负责调度、不负责健康检查、不负责跨主机通信、更不负责滚动升级的灰度控制。而Kubernetes,就是为解决这一整套生产级协同问题而生的操作系统——不是运行在硬件上的操作系统,而是运行在“容器集群”之上的分布式操作系统。

这篇文章不讲PPT式定义,不列教科书式对比表。我会用你每天都在做的真实操作来反推原理:比如你执行docker run -p 8080:80 nginx时,Linux内核到底做了什么;比如你删掉一个Pod后,K8S如何在3秒内决定该把它重建在哪台Node上;比如为什么一个Java应用在Docker里内存只占512MB,一上K8S却总被OOMKilled——这些都不是玄学,而是可验证、可调试、可复现的技术事实。如果你正准备从单体架构迁移到云原生,或者正在被CI/CD流水线卡住进度,又或者只是想搞明白自己每天敲的kubectl apply -f到底在调度什么,那这篇内容就是为你写的。它不假设你懂Go语言,也不要求你会写CRD,但会确保你合上页面后,能对着自己公司的K8S集群说出每一层技术存在的真实理由。

2. 技术演进逻辑:从硬件隔离到进程编排的四次跃迁

2.1 虚拟化技术:把一台物理机“切”成多台逻辑机

我们先回到2001年。那时某公司采购了5台戴尔服务器,分别跑OA、CRM、财务、HR和测试环境。每台服务器都装着Windows Server或Red Hat,但实际CPU利用率常年低于15%,硬盘I/O几乎闲置,内存更是只用了不到三分之一。老板看着电费账单直皱眉——这不是在烧钱吗?

虚拟化技术(Virtualization)就是为解决这个问题诞生的。它的核心思想非常朴素:让一台物理服务器,同时运行多个相互隔离的操作系统实例。VMware Workstation、VirtualBox、KVM都是这个思路的实现。它们通过Hypervisor(虚拟机监控器)在硬件和操作系统之间插入一层抽象,把CPU、内存、磁盘、网卡等资源“切片”分配给不同的虚拟机(VM)。每个VM都拥有完整的内核、驱动、用户空间,彼此互不可见,就像租了一整套独立服务器。

但问题很快浮现:每个VM都要装一整套操作系统。一个CentOS 7镜像动辄2GB,启动要2分钟,内存开销至少1GB起。当你需要快速扩缩容时,等2分钟启动一个VM?显然不现实。更重要的是,VM之间的通信要走物理网卡+虚拟交换机+防火墙规则,网络延迟高、配置复杂。2013年前后,某电商做618大促压测,光是准备200台VM就花了整整两天,而业务方只给了3小时窗口——这就是虚拟化在敏捷交付场景下的硬伤。

提示:虚拟化解决的是“资源利用率低”问题,但它把“隔离性”做到了极致,代价是启动慢、体积大、调度重。它适合长期稳定运行的系统(如数据库主库),但不适合需要秒级伸缩的Web服务。

2.2 容器:共享内核的轻量级进程沙箱

容器(Container)不是新发明,而是对Linux内核已有能力的重新组合。早在2008年,LXC(Linux Containers)就利用cgroups(控制组)和namespaces(命名空间)实现了进程隔离。cgroups负责限制、记录、隔离进程组的资源使用(CPU、内存、磁盘IO、网络带宽),而namespaces则让每个进程“以为”自己独占整个系统——PID namespace让进程看不到其他容器的进程号,net namespace让它拥有独立的网络栈,mnt namespace让它挂载独立的文件系统。

你可以把容器理解成:一个被严格限制资源、拥有独立视图的普通Linux进程。它不包含内核,不包含系统服务,只打包应用本身及其直接依赖的二进制文件和库。一个Nginx容器镜像通常只有10~30MB,启动时间在毫秒级,内存占用可精确控制到几MB。更重要的是,所有容器共享宿主机的Linux内核,没有Hypervisor层的性能损耗。

但这里有个关键细节常被忽略:容器不是虚拟机,它无法运行Windows程序,也不能在macOS上原生运行Linux容器(因为内核不兼容)。Mac用户用Docker Desktop,本质是后台起了一个轻量级Linux VM,再在其中运行容器——这恰恰说明,容器的“轻量”是建立在“同内核”前提下的。

注意:容器解决了“启动快、体积小、资源可控”问题,但它本身不提供镜像分发、版本管理、跨主机部署能力。它只是一个运行时环境,就像Java的JVM,你需要配套的“构建-分发-运行”工具链。

2.3 Docker:让容器从实验室走向工业生产的标准化引擎

2013年Docker横空出世,它没发明容器技术,而是干了三件让容器真正落地的事:

第一,统一镜像格式。Docker Image采用分层存储(Layered Filesystem),每一行Dockerfile指令生成一层只读镜像层,底层共用基础镜像(如ubuntu:22.04),上层叠加应用代码。这样100个Java服务如果都基于openjdk:17-jre-slim,实际磁盘只存一份基础层,极大节省存储和传输带宽。

第二,定义标准构建流程。Dockerfile把环境配置、依赖安装、服务启动全部声明化。FROM → RUN → COPY → EXPOSE → CMD这一套语法,让“在我机器上能跑”变成“在任何装了Docker的机器上都能跑”。某金融公司曾用Dockerfile统一了测试、预发、生产三套环境的JDK版本、GC参数、时区设置,上线故障率下降67%。

第三,提供开箱即用的运行时。docker run命令封装了cgroups/namespaces的复杂调用,自动处理端口映射、卷挂载、网络桥接。开发者不用再手动写unshare命令或配置lxc-start,一句命令就能拉起一个隔离环境。

但Docker也有明显局限:它默认只管理单机上的容器。当你有10台服务器,要部署一个由Web、API、Redis、MySQL组成的四层应用,就得在每台机器上分别执行docker run,手动维护IP地址、端口冲突、健康检查逻辑——这显然不可持续。

实操心得:Docker是容器时代的“Makefile”,它解决了“怎么打包”和“怎么运行”的问题,但没解决“怎么协同运行”的问题。就像你有了100个乐高积木块,Docker告诉你每块怎么拼,但没告诉你100块怎么搭成一座桥。

2.4 Kubernetes:面向终态的分布式系统协调器

Kubernetes(K8S)的诞生,直接源于Google内部Borg系统的工程实践。它不试图替代Docker,而是站在更高维度思考:如何让成百上千个容器,在动态变化的硬件环境中,始终维持用户期望的状态?

它的设计哲学是“声明式API”(Declarative API):你告诉K8S“我要3个Nginx Pod,每个80端口暴露,CPU不超过500m”,K8S的控制器就会持续比对当前状态与目标状态,自动创建缺失的Pod、驱逐异常的Pod、重启崩溃的Pod、在节点故障时迁移Pod——你不用写一行“if-else”逻辑,所有决策由内置控制器完成。

K8S把复杂性封装在抽象层之下:

  • Pod是最小调度单元,一个Pod可含多个容器(如主应用+sidecar日志收集器),它们共享网络和存储,天生适合紧耦合组件;
  • Service解决服务发现与负载均衡,通过ClusterIP或DNS名屏蔽后端Pod的IP变化;
  • Deployment管理Pod副本集,支持滚动升级、回滚、金丝雀发布;
  • ConfigMap/Secret将配置与镜像解耦,避免敏感信息硬编码;
  • Ingress统一管理七层HTTP路由,替代Nginx反向代理的手动配置。

某物联网公司用K8S管理全国5万台边缘设备的固件升级服务。他们定义了一个Deployment,副本数设为50000,K8S自动将这些Pod调度到分布在全国的边缘节点上,并通过DaemonSet确保每台边缘机都运行一个本地代理。当某地断网时,K8S不会强行重试,而是等待网络恢复后自动同步状态——这种“最终一致性”模型,正是分布式系统的本质。

关键认知:K8S不是“容器编排工具”,而是“分布式系统协调器”。它协调的对象可以是容器、虚拟机(通过KubeVirt)、甚至无服务器函数(通过Knative)。它的核心价值在于:把“运维人员要时刻盯着系统是否正常”的被动模式,变成“定义好目标状态,系统自动维持”的主动模式。

3. 四层技术栈的实操对照:从一条命令看透全链路

3.1 启动一个Web服务:四层技术各自在做什么

我们以部署一个Python Flask应用为例,对比四层技术的实际操作和底层行为:

虚拟化方案(VM):

# 在VMware中新建一台CentOS 7虚拟机 # 手动安装Python 3.9、pip、Flask # 编写启动脚本 /opt/app/start.sh # 设置systemd服务并启用开机自启 # 配置iptables开放8000端口 # 最后执行:systemctl start flask-app
  • 耗时:约8分钟(含下载ISO、安装系统、配置网络)
  • 资源开销:1核2GB内存固定占用,即使Flask只用50MB
  • 隔离性:完全独立内核,可运行任意Linux软件
  • 问题:扩容需复制整台VM,配置难以复用,版本难统一

容器方案(裸容器):

# 编写Dockerfile FROM python:3.9-slim COPY requirements.txt . RUN pip install -r requirements.txt COPY . /app WORKDIR /app CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"] # 构建镜像 docker build -t my-flask-app . # 运行容器 docker run -d -p 8000:8000 --name flask-prod my-flask-app
  • 耗时:构建2分钟 + 启动0.3秒
  • 资源开销:内存按需分配(实测约80MB),CPU可限制为500m(0.5核)
  • 隔离性:共享宿主机内核,进程、网络、文件系统隔离
  • 问题:单机运行,无健康检查,容器退出后不自动重启

Docker Compose方案(单机多容器):

# docker-compose.yml version: '3.8' services: web: image: my-flask-app ports: ["8000:8000"] depends_on: [redis] restart: unless-stopped redis: image: redis:7-alpine volumes: ["redis-data:/data"] volumes: redis-data:
docker-compose up -d
  • 耗时:首次启动约15秒(拉取redis镜像+启动两个容器)
  • 资源开销:web容器80MB + redis容器15MB,总计<100MB
  • 优势:一键启停多容器,定义依赖关系,自动重启
  • 局限:仍局限于单台宿主机,无法跨机器调度,无服务发现

Kubernetes方案(生产级集群):

# flask-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: flask-web spec: replicas: 3 selector: matchLabels: app: flask-web template: metadata: labels: app: flask-web spec: containers: - name: web image: my-flask-app:1.2.0 ports: - containerPort: 8000 resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m" --- # flask-service.yaml apiVersion: v1 kind: Service metadata: name: flask-service spec: selector: app: flask-web ports: - protocol: TCP port: 80 targetPort: 8000 type: ClusterIP
kubectl apply -f flask-deployment.yaml kubectl apply -f flask-service.yaml # 查看状态 kubectl get pods -l app=flask-web kubectl get service flask-service
  • 耗时:应用部署约3秒(K8S内部调度+拉取镜像+启动容器)
  • 资源开销:3个Pod共用宿主机资源,内存按request分配(3×64Mi=192Mi),CPU可超卖
  • 核心能力:
    • 自动在3台Node上均匀分布Pod(亲和性策略可定制)
    • 某Pod Crash后,10秒内自动重建
    • 执行kubectl scale deployment/flask-web --replicas=5,瞬间扩容至5副本
    • 通过kubectl rollout history查看每次发布的镜像版本
    • Service自动更新Endpoints,无需改代码

实操对比表:同一应用在四层技术下的关键指标

维度虚拟化(VM)容器(Docker)Docker ComposeKubernetes
启动时间2~5分钟<1秒~10秒~3秒(含调度)
内存开销固定1GB+按需80MB80MB+15MB3×64Mi=192Mi(可压缩)
跨主机部署需手动迁移VM不支持不支持原生支持,自动调度
故障自愈无,需人工介入--restart仅限单机restart: always控制器持续巡检,自动修复
配置管理Ansible脚本分散管理ENV变量+CMD参数docker-compose.ymlConfigMap/Secret + Helm模板
适用场景数据库主库、ERP系统本地开发、CI构建小型项目、演示环境生产环境、微服务架构

3.2 镜像构建的本质:分层缓存与确定性交付

很多人以为docker build就是把文件打包成zip,其实它是一套精密的构建时依赖解析与缓存复用机制。我们拆解一个典型Dockerfile:

FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production # ← 关键:依赖安装单独成层 COPY . . EXPOSE 3000 CMD ["npm", "start"]

执行docker build时,Docker Engine会逐行解析:

  • 第1行:拉取node:18-alpine镜像作为基础层(ID: abc123)
  • 第2行:创建新的工作目录层(ID: def456)
  • 第3行:复制package.json和package-lock.json(ID: ghi789)
  • 第4行:执行npm ci,生成node_modules层(ID: jkl012)←这是最大层,约120MB
  • 第5行:复制全部源码(ID: mno345)

Docker的缓存机制是:只要某一行指令及之前所有层未变,后续层就直接复用缓存。这意味着:

  • 如果只改了app.js,第5行会生成新层,但第4行的node_modules层直接复用(节省120MB下载+解压时间);
  • 如果只改了package.json,第4行会重新执行npm ci,生成新node_modules层,第5行复用;
  • 如果基础镜像升级(node:18-alpine变为node:18.17-alpine),则从第1行开始全部失效,重新构建。

这就是为什么企业CI流水线要求:COPY package*.json必须放在COPY .之前。某团队曾因顺序写反,导致每次代码提交都重新安装全部NPM包,构建时间从23秒飙升到6分12秒。

Kubernetes进一步强化了镜像的确定性:它要求所有Pod必须通过ImagePullPolicy明确拉取策略。默认IfNotPresent(本地有则不拉),但在生产环境强烈建议设为Always,确保每次部署都使用Registry中确切的镜像摘要(如my-app@sha256:abc123...),而非模糊的tag(如my-app:latest),杜绝“在我机器上能跑”的陷阱。

注意:Docker镜像ID是内容哈希(Content Hash),相同Dockerfile+相同上下文生成的镜像ID必然一致。这是K8S实现“一次构建、处处运行”的基石。

3.3 K8S调度器的核心算法:不只是随机分配

当你执行kubectl run nginx --image=nginx,K8S调度器(Scheduler)会在几毫秒内决定这个Pod该落在哪台Node上。这个过程远非随机,而是经过严谨的过滤与打分:

阶段一:预选(Predicates)——硬性过滤

  • PodFitsResources:检查Node剩余CPU/Memory是否满足Pod的requests;
  • NoDiskConflict:确保Pod请求的Volume不与其他Pod冲突;
  • MatchNodeSelector:校验Node标签是否匹配Pod的nodeSelector;
  • HostName:确认Pod指定的hostNetwork或hostPort可用。

任何一项失败,该Node即被剔除。例如某Node剩余内存仅100Mi,而Pod requests为256Mi,则直接淘汰。

阶段二:优选(Priorities)——软性打分对通过预选的Node,按权重计算综合得分:

  • LeastRequestedPriority(权重1):剩余资源越多得分越高(避免资源倾斜);
  • BalancedResourceAllocation(权重1):CPU/内存使用率越均衡得分越高;
  • NodeAffinityPriority(权重2):匹配nodeAffinity规则的加分;
  • TaintTolerationPriority(权重1):容忍污点的Node加分。

最终选择得分最高的Node。某视频平台曾将NodeAffinity权重设为5,强制将GPU推理服务调度到装有NVIDIA显卡的Node上,避免普通Node被误分配。

阶段三:绑定(Binding)调度器向API Server发送Binding对象,API Server将Pod的nodeName字段更新为目标Node名称,Kubelet监听到变更后立即拉取镜像并启动容器。

你可以用kubectl describe node <node-name>查看Node的Allocatable资源,用kubectl get events --sort-by=.lastTimestamp观察调度全过程。当Pod卡在Pending状态,90%的原因是预选失败——此时kubectl describe pod <pod-name>的Events里会明确写出“0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.”。

实操技巧:用kubectl cordon <node>临时禁止调度,kubectl drain <node>安全驱逐现有Pod,是日常维护Node的标准流程。切勿直接reboot节点,否则K8S会认为它失联并触发大量Pod重建。

4. 真实生产环境中的典型问题与排查路径

4.1 问题现象:Pod反复重启(CrashLoopBackOff)

这是新手最常遇到的红字。表面看是容器启动失败,但根因可能分布在四层技术的任意环节:

排查路径:

  1. kubectl get pods查看状态:flask-web-5b8c9d7f4-abc12 0/1 CrashLoopBackOff 5 2m
  2. kubectl logs flask-web-5b8c9d7f4-abc12查看容器日志:Error: connect ECONNREFUSED 127.0.0.1:6379
  3. 发现应用试图连接Redis,但报错ECONNREFUSED,说明Redis服务不可达
  4. kubectl get svc查看Service:redis-service ClusterIP 10.96.123.45 <none> 6379/TCP 10m
  5. kubectl get endpoints redis-service显示<none>—— 关键线索!Endpoints为空,意味着没有Pod被Service选中
  6. kubectl get pods -l app=redis发现Redis Pod处于Pending状态
  7. kubectl describe pod <redis-pod>查看Events:0/3 nodes are available: 3 Insufficient memory.
  8. 原因定位:Redis Pod requests内存为512Mi,但所有Node剩余内存均不足,导致无法调度,进而使Service无后端,最终Web Pod启动失败

解决方案:

  • 降低Redis内存requests(如改为256Mi)
  • 或给Node扩容内存
  • 或添加容忍策略允许在内存紧张的Node上运行

注意:K8S的错误是链式传导的。CrashLoopBackOff只是表象,必须沿Pod → Container → Service → Endpoints → Target Pods → Node资源逐层下钻,不能只盯着第一个报错。

4.2 问题现象:服务间调用超时(Connection timed out)

某微服务A调用服务B的API,偶尔返回curl: (28) Connection timed out after 5000 milliseconds。网络看似通畅,但超时不稳定。

深度排查:

  • 首先确认Service类型:kubectl get svc b-service显示Type: ClusterIP,排除外部网络问题
  • 检查服务B的Pod是否健康:kubectl get pods -l app=b-service全部Running
  • kubectl exec -it <pod-a> -- sh进入A容器,手动curl http://b-service:8080/health—— 成功
  • 但A应用代码中用http://b-service.default.svc.cluster.local:8080调用仍超时
  • 关键发现:b-service的Service定义中clusterIP: None(Headless Service),而A应用代码硬编码了default.svc.cluster.local域名
  • Headless Service不分配ClusterIP,DNS返回的是Pod IP列表,客户端需自行负载均衡。但A应用未实现重试逻辑,当DNS返回的第一个Pod IP恰好是刚被删除的旧Pod时,连接即失败

根本原因:
服务发现方式与客户端实现不匹配。Headless Service适用于StatefulSet(如ZooKeeper),要求客户端具备DNS轮询和健康检查能力;而普通Deployment应使用ClusterIP Service,由K8S内置的kube-proxy(iptables或IPVS模式)做负载均衡。

修复方案:

  • 将b-service的clusterIP: None改为clusterIP: 10.96.123.45(或留空让K8S自动分配)
  • 或修改A应用代码,增加DNS解析重试和连接失败重试逻辑

实操心得:K8S的Service DNS解析有缓存(默认30秒),kubectl get endpoints看到的IP列表可能滞后于真实Pod状态。用nslookup b-service.default.svc.cluster.local可实时验证DNS解析结果。

4.3 问题现象:K8S集群节点NotReady,但服务器本身运行正常

kubectl get nodes显示node-01 NotReady <none> 5d v1.25.6,但SSH登录该服务器,top显示CPU空闲,docker ps看到Kubelet进程在运行。

系统级诊断:

  1. ssh node-01
  2. sudo systemctl status kubelet查看Kubelet状态:active (running),但日志有报错
  3. sudo journalctl -u kubelet -n 100 --no-pager | grep -i error发现关键错误:failed to run Kubelet: failed to create kubeconfig file: open /var/lib/kubelet/kubeconfig: permission denied
  4. 检查文件权限:ls -l /var/lib/kubelet/显示drwx------ 3 root root,但Kubelet进程以kubelet用户运行
  5. 根本原因:某次手动执行chown -R root:root /var/lib/kubelet,导致Kubelet无法写入其工作目录

修复步骤:

# 重置目录所有权 sudo chown -R kubelet:kubelet /var/lib/kubelet # 重启Kubelet sudo systemctl restart kubelet # 验证 sudo systemctl status kubelet # 应显示 active kubectl get nodes # 应变为 Ready

注意:Kubelet的--root-dir参数默认指向/var/lib/kubelet,这是它存储Pod状态、卷挂载点、证书的根目录。任何对该目录的权限修改都可能导致节点失联。生产环境严禁直接chown系统目录,应通过kubeadm reset或kubeadm init重置节点。

4.4 问题现象:Ingress 502 Bad Gateway,但后端Pod日志显示请求已到达

用户访问https://app.example.com返回502,但kubectl logs <pod>能看到Nginx成功打印了访问日志。

Ingress排障清单:

  • kubectl get ingress确认Ingress资源存在且ADDRESS字段有值(如192.168.1.100)
  • kubectl get svc -n ingress-nginx查看Ingress Controller的Service类型:必须是LoadBalancer或NodePort,不能是ClusterIP
  • kubectl get pods -n ingress-nginx确认Ingress Controller Pod处于Running状态
  • kubectl logs -n ingress-nginx <ingress-pod>查看Controller日志,常见错误:
    • upstream prematurely closed connection:后端Pod响应超时,检查Pod的readinessProbe是否配置过短
    • no backend found:Ingress规则中的serviceName与实际Service名称不匹配(注意大小写)
    • SSL certificate not found:TLS Secret未正确挂载,kubectl get secret -n default确认Secret存在

某教育平台曾因Ingress规则中写错serviceName: web-svc(实际为web-service),导致所有HTTPS请求502,而排查时只盯着Nginx日志,浪费4小时。

关键技巧:用kubectl port-forward svc/ingress-nginx-controller -n ingress-nginx 8080:80将本地8080端口转发到Ingress Controller,然后curl -H "Host: app.example.com" http://localhost:8080可绕过DNS和LB,直接测试Ingress规则是否生效。

5. 从零搭建可验证的本地K8S环境:minikube实战指南

5.1 为什么选择minikube而非Docker Desktop内置K8S

Docker Desktop自带K8S开关,但它是黑盒集成,无法查看etcd数据、无法调试kube-scheduler日志、无法模拟多节点故障。而minikube是Kubernetes官方推荐的本地开发环境,它:

  • 在单机上启动一个真实的K8S集群(Master+Worker合一)
  • 支持插件扩展(如metrics-server、ingress、dashboard)
  • 可自由切换K8S版本(minikube start --kubernetes-version=v1.25.6)
  • 所有组件以容器形式运行,docker ps可见k8s_kube-apiserver等进程

安装与初始化:

# macOS(Intel) brew install minikube # 启动(指定2核4GB内存,避免资源不足) minikube start --cpus=2 --memory=4096 --driver=docker # 验证 minikube status kubectl get nodes # 应显示 minikube Ready

启用关键插件:

# 启用Metrics Server(支持kubectl top命令) minikube addons enable metrics-server # 启用Ingress Controller(支持Ingress资源) minikube addons enable ingress # 启用Dashboard(可视化界面) minikube addons enable dashboard # 访问Dashboard minikube dashboard

5.2 部署一个真实可交互的Demo应用

我们部署一个带前端+后端+数据库的Todo应用,完整覆盖四层技术:

步骤1:准备容器镜像

# 后端(Python FastAPI) # Dockerfile.backend FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["uvicorn", "main:app", "--host", "0.0.0.0:8000", "--port", "8000"] # 构建并推送(需先登录Docker Hub) docker build -t yourname/todo-backend . docker push yourname/todo-backend

步骤2:编写K8S清单文件

# todo-namespace.yaml apiVersion: v1 kind: Namespace metadata: name: todo-app --- # todo-db.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: todo-db namespace: todo-app spec: serviceName: "todo-db" replicas: 1 selector: matchLabels: app: todo-db template: metadata: labels: app: todo-db spec: containers: - name: db image: postgres:14-alpine env: - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: db-secret key: password volumeMounts: - name: db-storage mountPath: /var/lib/postgresql/data volumeClaimTemplates: - metadata: name: db-storage spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 1Gi --- # todo-backend.yaml apiVersion: apps/v1 kind: Deployment metadata: name: todo-backend namespace: todo-app spec: replicas: 2 selector: matchLabels: app: todo-backend template: metadata: labels: app: todo-backend spec: containers: - name: backend image: yourname/todo-backend:latest ports: - containerPort: 8000 env: - name: DB_HOST value: "todo-db.todo-app.svc.cluster.local" - name: DB_NAME value: "todo" livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 8000 initialDelaySeconds: 5 periodSeconds: 5 --- # todo-frontend.yaml apiVersion: apps/v1 kind: Deployment metadata: name: todo-frontend namespace: todo-app spec: replicas: 2 selector: matchLabels: app: todo-frontend template: metadata: labels: app: todo-frontend spec: containers: - name: frontend image: nginx:alpine ports: - containerPort: 80 volumeMounts: - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf volumes: - name: nginx-config configMap: name: nginx-config --- # todo-ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: todo-ingress namespace: todo-app annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: todo.local http: paths: - path: / pathType: Prefix backend: service: name: todo-frontend port: number: 80

步骤3:一键部署与验证

# 创建命名空间 kubectl apply -f todo-namespace.yaml # 创建Secret(密码) kubectl create secret generic db

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询