恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
虚拟化→容器→Docker→Kubernetes:云原生技术演进全链路解析
首页
资讯中心
/
虚拟化→容器→Docker→Kubernetes:云原生技术演进全链路解析
虚拟化→容器→Docker→Kubernetes:云原生技术演进全链路解析
发布时间:2026/10/13 9:31:16
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年LXCLinux 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面向终态的分布式系统协调器KubernetesK8S的诞生直接源于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副本数设为50000K8S自动将这些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秒资源开销内存按需分配实测约80MBCPU可限制为500m0.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: ClusterIPkubectl apply -f flask-deployment.yaml kubectl apply -f flask-service.yaml # 查看状态 kubectl get pods -l appflask-web kubectl get service flask-service耗时应用部署约3秒K8S内部调度拉取镜像启动容器资源开销3个Pod共用宿主机资源内存按request分配3×64Mi192MiCPU可超卖核心能力自动在3台Node上均匀分布Pod亲和性策略可定制某Pod Crash后10秒内自动重建执行kubectl scale deployment/flask-web --replicas5瞬间扩容至5副本通过kubectl rollout history查看每次发布的镜像版本Service自动更新Endpoints无需改代码实操对比表同一应用在四层技术下的关键指标维度虚拟化VM容器DockerDocker ComposeKubernetes启动时间2~5分钟1秒~10秒~3秒含调度内存开销固定1GB按需80MB80MB15MB3×64Mi192Mi可压缩跨主机部署需手动迁移VM不支持不支持原生支持自动调度故障自愈无需人工介入--restart仅限单机restart: always控制器持续巡检自动修复配置管理Ansible脚本分散管理ENV变量CMD参数docker-compose.ymlConfigMap/Secret Helm模板适用场景数据库主库、ERP系统本地开发、CI构建小型项目、演示环境生产环境、微服务架构3.2 镜像构建的本质分层缓存与确定性交付很多人以为docker build就是把文件打包成zip其实它是一套精密的构建时依赖解析与缓存复用机制。我们拆解一个典型DockerfileFROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction # ← 关键依赖安装单独成层 COPY . . EXPOSE 3000 CMD [npm, start]执行docker build时Docker Engine会逐行解析第1行拉取node:18-alpine镜像作为基础层ID: abc123第2行创建新的工作目录层ID: def456第3行复制package.json和package-lock.jsonID: ghi789第4行执行npm ci生成node_modules层ID: jkl012←这是最大层约120MB第5行复制全部源码ID: mno345Docker的缓存机制是只要某一行指令及之前所有层未变后续层就直接复用缓存。这意味着如果只改了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-appsha256:abc123...而非模糊的tag如my-app:latest杜绝“在我机器上能跑”的陷阱。注意Docker镜像ID是内容哈希Content Hash相同Dockerfile相同上下文生成的镜像ID必然一致。这是K8S实现“一次构建、处处运行”的基石。3.3 K8S调度器的核心算法不只是随机分配当你执行kubectl run nginx --imagenginxK8S调度器Scheduler会在几毫秒内决定这个Pod该落在哪台Node上。这个过程远非随机而是经过严谨的过滤与打分阶段一预选Predicates——硬性过滤PodFitsResources检查Node剩余CPU/Memory是否满足Pod的requestsNoDiskConflict确保Pod请求的Volume不与其他Pod冲突MatchNodeSelector校验Node标签是否匹配Pod的nodeSelectorHostName确认Pod指定的hostNetwork或hostPort可用。任何一项失败该Node即被剔除。例如某Node剩余内存仅100Mi而Pod requests为256Mi则直接淘汰。阶段二优选Priorities——软性打分对通过预选的Node按权重计算综合得分LeastRequestedPriority权重1剩余资源越多得分越高避免资源倾斜BalancedResourceAllocation权重1CPU/内存使用率越均衡得分越高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 didnt tolerate.”。实操技巧用kubectl cordon node临时禁止调度kubectl drain node安全驱逐现有Pod是日常维护Node的标准流程。切勿直接reboot节点否则K8S会认为它失联并触发大量Pod重建。4. 真实生产环境中的典型问题与排查路径4.1 问题现象Pod反复重启CrashLoopBackOff这是新手最常遇到的红字。表面看是容器启动失败但根因可能分布在四层技术的任意环节排查路径kubectl get pods查看状态flask-web-5b8c9d7f4-abc12 0/1 CrashLoopBackOff 5 2mkubectl logs flask-web-5b8c9d7f4-abc12查看容器日志Error: connect ECONNREFUSED 127.0.0.1:6379发现应用试图连接Redis但报错ECONNREFUSED说明Redis服务不可达kubectl get svc查看Serviceredis-service ClusterIP 10.96.123.45 none 6379/TCP 10mkubectl get endpoints redis-service显示none—— 关键线索Endpoints为空意味着没有Pod被Service选中kubectl get pods -l appredis发现Redis Pod处于Pending状态kubectl describe pod redis-pod查看Events0/3 nodes are available: 3 Insufficient memory.原因定位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 appb-service全部Runningkubectl 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: NoneHeadless Service而A应用代码硬编码了default.svc.cluster.local域名Headless Service不分配ClusterIPDNS返回的是Pod IP列表客户端需自行负载均衡。但A应用未实现重试逻辑当DNS返回的第一个Pod IP恰好是刚被删除的旧Pod时连接即失败根本原因服务发现方式与客户端实现不匹配。Headless Service适用于StatefulSet如ZooKeeper要求客户端具备DNS轮询和健康检查能力而普通Deployment应使用ClusterIP Service由K8S内置的kube-proxyiptables或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进程在运行。系统级诊断ssh node-01sudo systemctl status kubelet查看Kubelet状态active (running)但日志有报错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检查文件权限ls -l /var/lib/kubelet/显示drwx------ 3 root root但Kubelet进程以kubelet用户运行根本原因某次手动执行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.100kubectl get svc -n ingress-nginx查看Ingress Controller的Service类型必须是LoadBalancer或NodePort不能是ClusterIPkubectl 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 foundIngress规则中的serviceName与实际Service名称不匹配注意大小写SSL certificate not foundTLS 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内置K8SDocker Desktop自带K8S开关但它是黑盒集成无法查看etcd数据、无法调试kube-scheduler日志、无法模拟多节点故障。而minikube是Kubernetes官方推荐的本地开发环境它在单机上启动一个真实的K8S集群MasterWorker合一支持插件扩展如metrics-server、ingress、dashboard可自由切换K8S版本minikube start --kubernetes-versionv1.25.6所有组件以容器形式运行docker ps可见k8s_kube-apiserver等进程安装与初始化# macOSIntel brew install minikube # 启动指定2核4GB内存避免资源不足 minikube start --cpus2 --memory4096 --driverdocker # 验证 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 dashboard5.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