Docker与Kubernetes关系详解:容器运行时与编排系统的分工协作 1. 项目概述别再问“Kubernetes 和 Docker 到底选哪个”了刚入行那会儿我带的第一个实习生在晨会上举手问“老师我们公司到底该用 Kubernetes 还是 Docker”——会议室里瞬间安静了两秒老运维同事默默放下咖啡杯后端组长低头翻起了手机。这问题本身就像在问“螺丝刀和锤子哪个更好用”不是答案难而是提问方式就埋了坑。Docker 是一个容器运行时它解决的是“怎么把应用打包成一份可移植、可复现的标准化单元”Kubernetes常缩写为 K8s则是一套容器编排系统它解决的是“当有几十上百个 Docker 容器要同时跑、要互相通信、要自动恢复、要按需扩缩容时谁来统一调度、监控和管理”。它们根本不在同一层面上打架而是像“发动机”和“整车控制系统”的关系你不会说“我该选发动机还是选车载导航”因为没有发动机导航连电都供不上但光有发动机车也开不稳、刹不住、拐不了弯。这个认知偏差在2021年前后特别普遍。当时 Docker 公司自己搞的 Swarm 编排工具逐渐式微而 K8s 凭借 CNCF云原生计算基金会背书和 Google Borg 的血统迅速成为事实标准大量技术文章标题开始玩文字游戏比如《Docker 已死Kubernetes 才是未来》《告别 Docker拥抱 K8s》结果让很多刚接触云原生的开发者一头雾水以为两者是互斥替代品。其实真实生产环境里99% 的 Kubernetes 集群底层跑的正是 Docker或其兼容 OCI 标准的替代品如 containerd。你可以把 Docker 理解成“造砖的工厂”把 Kubernetes 理解成“盖楼的总包方”——砖厂不负责设计楼层、规划水电、安排工人轮班总包方也不自己烧砖。它们分工明确协作紧密。这篇文章就是想掰开揉碎讲清楚Docker 做了什么、K8s 又管了什么、它们之间怎么握手、为什么必须一起用、以及在不同规模项目里你到底该在哪一层投入精力。无论你是刚部署完第一个docker run nginx的新手还是正被线上服务频繁重启搞得焦头烂额的 SRE都能在这里找到对应自己当前阶段的实操锚点。2. 核心原理拆解从单机容器到集群编排的演进逻辑2.1 Docker 的本质操作系统之上的“轻量级虚拟化”沙盒很多人一听到“容器”下意识就和“虚拟机”类比这没错但容易忽略关键差异。VMVirtual Machine是在物理硬件之上加了一层 Hypervisor比如 VMware ESXi、KVM它模拟出一整套硬件环境然后在上面安装完整的 Guest OS客户操作系统再跑应用。整个链路是物理 CPU → Hypervisor → Guest OS 内核 → 应用。这意味着每个 VM 都要自带内核、驱动、系统服务内存占用动辄 1~2GB 起启动慢密度低。Docker 完全绕开了这一套。它不模拟硬件而是直接复用宿主机Host OS的 Linux 内核通过Namespaces和Cgroups这两个内核特性实现隔离与限制Namespaces负责“看不见”PID Namespace 让容器内进程只看到自己 PID 1 的进程树Network Namespace 给容器分配独立的网络栈IP、端口、路由表Mount Namespace 让容器拥有自己的文件系统挂载点UTS Namespace 隔离主机名和域名。简单说Namespaces 让每个容器觉得自己是“一台独立的机器”。CgroupsControl Groups负责“动不了”它像一个资源调度红绿灯严格限制容器能使用的 CPU 时间片、内存上限、磁盘 I/O 速率、网络带宽。比如你执行docker run -m 512m --cpus0.5 nginx就是在告诉内核“这个容器最多只能用 512MB 内存且 CPU 时间不能超过一个核的 50%”。没有 Cgroups容器就只是个“看得见摸不着”的隔离壳资源争抢照样发生。所以 Docker 镜像Image的本质就是一个分层的、只读的文件系统快照基于 AUFS、OverlayFS 等存储驱动加上一个 JSON 格式的元数据定义了启动命令、环境变量、端口映射等。当你docker build时每一行RUN、COPY指令都会生成一个新的镜像层这种分层设计让镜像可以高效复用、快速分发。而docker run启动的容器Container则是这个镜像的一个可写层Copy-on-Write Namespaces Cgroups 的实时运行实例。它的启动速度是毫秒级的内存开销通常只有几 MB这才是它能支撑微服务架构爆发式增长的底层原因。提示Docker Engine即dockerd守护进程本身只是一个 API 服务它接收docker CLI发来的 HTTP 请求比如POST /containers/create然后调用底层的containerd一个更轻量、更专注的容器运行时守护进程去真正创建容器。自 Docker 20.10 版本起containerd已成为默认运行时Docker CLI 只是它的一个前端。理解这一点对后续排查“容器启动失败但日志没报错”这类问题至关重要——很多时候问题出在containerd的配置或插件上而非dockerd。2.2 Kubernetes 的定位分布式系统的“操作系统内核”如果说 Docker 解决了“单机上如何安全、高效地运行多个应用”那么 Kubernetes 就要解决“成百上千台服务器上如何让数万个容器像在一个巨型单机上那样协同工作”。它不是简单的“Docker 管理器”而是一套完整的分布式系统抽象层其核心思想是声明式 API 控制循环Control Loop。你不用告诉 K8s “先拉镜像再启容器再配网络再挂存储”而是通过 YAML 文件声明你想要的终态Desired State“我要 3 个 nginx 实例每个暴露 80 端口内存不超过 256MB挂载一个叫config-volume的配置卷”。K8s 的各个组件kube-apiserver、kube-controller-manager、kube-scheduler、kubelet会持续监听这个状态并不断将实际运行状态Actual State向你声明的目标靠拢。这个过程就是控制循环比如某个 Pod 因为 OOM 被杀死了kube-controller-manager中的 ReplicaSet Controller 会立刻发现副本数不足 3于是通知kube-scheduler找一台空闲节点再由该节点上的kubelet调用containerd拉取镜像并启动新容器。整个过程对用户完全透明你只需要关注“我要什么”而不是“怎么做到”。K8s 的核心对象模型就是围绕这个目标状态构建的Pod最小的可调度单元不是容器而是一个或多个共享网络和存储的容器组。为什么需要 Pod因为很多应用天然需要“伴生容器”Sidecar比如主业务容器 日志收集容器Fluentd、主容器 配置热更新容器Reloader。它们必须同生共死、共享 localhostPod 就是为这种强耦合关系设计的抽象。Service解决 Pod IP 不稳定的问题。Pod 被调度、重启、扩缩容时IP 地址会变。Service 为一组 Pod 提供一个稳定的 ClusterIP集群内部 VIP和 DNS 名称如myapp.default.svc.cluster.local并通过 kube-proxy或 eBPF在节点上维护 iptables/eBPF 规则实现流量负载均衡。它就像一个永不掉线的“电话总机”外部打进来它自动转接到当前在线的“分机”。Deployment管理 Pod 副本的“控制器”。它不直接创建 Pod而是创建 ReplicaSet再由 ReplicaSet 确保指定数量的 Pod 始终运行。所有滚动升级、回滚操作都是 Deployment 更新 ReplicaSet 的过程。这是你日常打交道最多的对象。ConfigMap Secret把配置和密钥从容器镜像中剥离出来实现“代码与配置分离”。ConfigMap 存明文如 Nginx 配置Secret 存敏感信息如数据库密码它们以卷Volume或环境变量的方式注入 Pod。这样改配置不用重新构建镜像换密钥也不用重发版本。理解这些对象不是为了背概念而是为了明白K8s 的价值不在于它多酷炫而在于它把分布式系统里那些反直觉、易出错的细节如服务发现、健康检查、故障转移、配置分发全部封装成了标准化、可声明的 API。你写的每一份 YAML都是在和这个“分布式操作系统”对话。2.3 二者协作全景图从docker build到kubectl apply现在我们把链条串起来看一个最典型的生产流程开发阶段程序员写好 Python Flask 应用用Dockerfile描述构建步骤FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt # 构建时安装依赖生成镜像层 COPY . . CMD [gunicorn, --bind, 0.0.0.0:8000, app:app]执行docker build -t my-flask-app:v1.0 .得到一个约 200MB 的镜像。这个镜像包含了应用代码、Python 解释器、所有依赖库是一个完全自包含的交付物。镜像分发docker push my-flask-app:v1.0将镜像推送到私有 Registry如 Harbor或公有 Registry如 Docker Hub。Registry 就像一个“应用商店”K8s 集群里的任何节点都可以从中拉取。K8s 部署编写deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: flask-app spec: replicas: 3 selector: matchLabels: app: flask-app template: metadata: labels: app: flask-app spec: containers: - name: app image: harbor.example.com/myproject/my-flask-app:v1.0 # 指向刚才推上去的镜像 ports: - containerPort: 8000 resources: requests: memory: 128Mi cpu: 100m limits: memory: 256Mi cpu: 200m --- apiVersion: v1 kind: Service metadata: name: flask-service spec: selector: app: flask-app ports: - protocol: TCP port: 80 targetPort: 8000执行kubectl apply -f deployment.yaml。K8s API Server 接收请求Scheduler 选择 3 个合适的 Node然后每个 Node 上的 kubelet 通过 CRIContainer Runtime Interface调用containerdcontainerd再从 Harbor 拉取镜像、解压、创建容器。整个过程Docker CLI 甚至都没出场——K8s 直接和容器运行时对话。运行时交互当用户访问http://flask-service.default.svc.cluster.local时DNS 解析到 Service 的 ClusterIPkube-proxy 在节点上做的 iptables 规则将请求随机转发给后端 3 个 Pod 中的一个。如果某个 Pod 崩溃kubelet 上报状态Controller Manager 发现副本数不足立即触发重建。这一切都建立在 Docker或 containerd提供的可靠、标准化的容器运行能力之上。所以Docker 和 K8s 的关系不是“二选一”而是“上下游”。Docker 是“交付标准”K8s 是“运行标准”。没有前者后者无物可编排没有后者前者在大规模场景下寸步难行。它们共同构成了现代云原生应用的基石。3. 实操对比分析不同场景下的技术选型决策树3.1 单机开发与测试Docker Compose 是你的黄金搭档如果你的工作流是本地写代码 → 本地构建镜像 → 本地启动几个服务联调 → 提交代码 → CI/CD 自动部署。那么Docker Compose 是你现阶段最该掌握、也最够用的工具K8s 在这里纯属杀鸡用牛刀。Compose 的核心价值在于用一份docker-compose.yml文件描述一组相互关联的服务如 Web 前端、API 后端、PostgreSQL 数据库、Redis 缓存并一键启停、查看日志、进入容器。它本质上是 Docker CLI 的一个高级封装所有操作最终都转化为docker run命令。一个典型的docker-compose.ymlversion: 3.8 services: web: build: ./web ports: - 8000:8000 environment: - DB_HOSTdb - REDIS_URLredis://redis:6379 depends_on: - db - redis db: image: postgres:13 environment: - POSTGRES_DBmyapp - POSTGRES_PASSWORDsecret volumes: - db-data:/var/lib/postgresql/data redis: image: redis:7-alpine volumes: db-data:执行docker-compose up -dCompose 就会依次构建web服务的镜像启动db和redis容器使用预编译镜像创建一个默认的 Docker 网络让三个服务可以通过服务名db,redis互相访问启动web容器并将其加入该网络。为什么此时不该上 K8s复杂度爆炸本地装 Minikube 或 Kind要下载 ISO、启动 VM、初始化集群、配置 kubectl 上下文……光是环境准备就要半小时而docker-compose up30 秒搞定。功能冗余你不需要滚动升级代码改了直接docker-compose restart web、不需要多节点调度就一台笔记本、不需要复杂的 RBAC 权限控制全是自己。调试困难kubectl logs -f pod/web-xxx查日志远不如docker-compose logs -f web直观kubectl exec -it pod/web-xxx -- sh进容器也远不如docker-compose exec web sh顺手。我带过的团队里凡是过早引入 K8s 本地开发的无一例外都陷入了“环境配置地狱”新人入职花两天配不通本地 K8sCI 流水线因镜像拉取超时失败大家把时间都耗在和工具斗气上而不是写业务代码。我的建议很实在把docker-compose.yml当作你的“本地生产环境说明书”确保它和线上 K8s 的配置语义一致比如环境变量名、端口、卷挂载路径这样上线前的集成测试才最有价值。3.2 小型生产环境 5 节点 50 个 PodK3s 是轻量级王者当你的应用要上线用户量不大日活几千服务器资源有限比如 3 台 4C8G 的云主机又希望获得 K8s 的核心能力自动恢复、服务发现、配置管理那么K3s 是目前最成熟、最省心的选择。它不是 K8s 的简化版而是官方认证的、完全兼容的 K8s 发行版由 Rancher 团队打造专为边缘、IoT 和小型生产环境优化。K3s 的精简哲学体现在每一处单二进制文件整个 K3s 服务包括 etcd、API Server、Scheduler、Controller Manager、Kubelet、Kube-Proxy被打包成一个不到 100MB 的k3s二进制。安装只需一条命令curl -sfL https://get.k3s.io | sh -5 分钟内集群就绪。嵌入式数据库默认用 SQLite 替代重量级的 etcd 作为后端存储极大降低内存和 CPU 开销。对于小集群SQLite 的性能和可靠性完全足够。移除非必要组件不包含 legacy 的 cloud provider 插件、旧版的 Ingress Controller用 Traefik v2 替代、复杂的网络策略NetworkPolicy实现。它只保留最核心、最常用的功能。一键证书管理内置自动 TLS 证书签发基于 Lets Encryptkubectl get secret就能看到所有证书无需手动折腾 cert-manager。部署一个 K3s 集群实测步骤如下在三台服务器上分别执行安装命令第一台会自动成为 servermaster后两台加--server https://first-server-ip:6443 --token token参数加入成为 agentworker。在任意节点执行sudo cat /etc/rancher/k3s/k3s.yaml拿到 kubeconfig配置本地kubectl。部署应用kubectl apply -f your-deployment.yaml。你会发现kubectl get nodes显示三台节点kubectl get pods --all-namespaces显示系统组件coredns、traefik、local-path-provisioner全部正常运行整个过程比部署一个传统 LAMP 环境还简单。K3s 的适用边界也很清晰它不适合超大规模100 节点、对 etcd 高可用有强要求如金融核心系统、或需要深度定制网络插件如 Cilium 的高级安全策略的场景。但对于绝大多数中小型企业官网、内部管理系统、SaaS 初创产品K3s 就是那个“刚刚好”的答案——它让你享受 K8s 的红利却不必为 K8s 的复杂性买单。3.3 中大型生产环境 10 节点 200 个 Pod标准 K8s 生态加固当你的业务快速增长集群规模扩大稳定性、可观测性、安全性要求陡增时就必须回归标准的、经过大规模验证的 K8s 发行版如Rancher RKE2、Red Hat OpenShift、VMware Tanzu Kubernetes GridTKG或者直接使用云厂商托管的 K8s 服务EKS、AKS、GKE。这时Docker 的角色也悄然发生了变化。Docker Engine 的退出与 containerd 的崛起自 K8s 1.20 版本起官方正式弃用 Docker Engine 作为默认容器运行时Dockershim转而全面拥抱containerd。这不是 K8s 针对 Docker 的“打压”而是技术演进的必然。Docker Engine 是一个功能丰富的“全家桶”包含 CLI、Daemon、BuildKit、Swarm 等而 K8s 只需要一个专注、稳定、符合 OCIOpen Container Initiative标准的运行时来创建和管理容器。containerd 正是为此而生——它更轻量内存占用少 30%、更稳定无 Docker Daemon 的单点故障风险、更安全攻击面更小、更易集成API 更简洁。如今你在 EKS 或 AKS 上创建的节点底层几乎 100% 运行的是 containerdDocker CLI 只是作为开发者的便利工具存在。因此中大型环境的实操重点已经从“怎么用 Docker”转向了“怎么管好 containerd”和“怎么用好 K8s 生态”容器镜像安全扫描必须在 CI 流水线中集成 Trivy 或 Clair对每一个推送到 Registry 的镜像进行 CVE 漏洞扫描。trivy image --severity CRITICAL harbor.example.com/myapp:v1.2如果发现高危漏洞流水线自动失败阻断发布。这是防止“带病上线”的第一道闸门。精细化资源管理requests和limits不再是可选项。requests是调度器分配资源的依据比如requests.memory: 512Mi意味着这个 Pod 至少需要 512MB 可用内存才能被调度到某节点limits是 cgroups 强制限制的上限。我见过太多团队只设limits不设requests导致调度器“瞎猜”资源需求集群负载严重不均部分节点 OOM 频发。正确的姿势是requests设为应用稳定运行的最低保障值limits设为应用峰值时的绝对上限且limits一般不超过requests的 2 倍避免过度预留。生产级网络与存储不再用hostPath或emptyDir这种单机存储而是对接企业级存储系统如 NetApp Trident、Portworx或云存储AWS EBS、Azure Disk。网络层面放弃kube-proxy的 iptables 模式切换到ipvs或eBPF如 Cilium获得更低延迟、更高吞吐和更细粒度的网络策略控制。这个阶段Docker 的价值更多体现在开发侧的标准化交付上。而 K8s则是你整个基础设施的“操作系统”它的稳定性和可扩展性直接决定了业务的 SLA服务等级协议。4. 关键实操环节详解从零搭建一个可落地的 K3s 集群4.1 环境准备与安装避开最常见的 3 个坑我亲手部署过 20 个 K3s 集群踩过的坑基本都集中在环境准备阶段。下面是最关键的三步也是最容易出错的地方务必逐条核对第一步系统与内核要求操作系统官方推荐 Ubuntu 20.04/22.04、CentOS/RHEL 7.6/8.x、Debian 10/11。绝对不要用 CentOS Stream 或 Rocky Linux 9 的早期版本它们的内核模块如overlay可能与 K3s 默认的overlay2存储驱动不兼容导致容器启动失败报错failed to mount overlay。我曾在一个 Rocky 9.1 服务器上卡了整整一天最后降级到 8.8 才解决。内核版本uname -r输出必须 ≥ 4.15。低于此版本cgroups v2 可能无法正常工作影响资源限制精度。检查命令cat /proc/sys/user/max_user_namespaces输出应大于 0通常为 28633。SELinux/AppArmor强烈建议在安装前关闭 SELinux。虽然 K3s 官方声称支持但实际中SELinux 的策略冲突会导致kubelet无法挂载卷、无法读取/var/lib/rancher/k3s/agent/etc/containerd/config.toml等关键文件。临时关闭sudo setenforce 0永久关闭编辑/etc/selinux/config将SELINUXenforcing改为SELINUXdisabled然后重启。AppArmor 同理sudo systemctl disable apparmor。第二步网络与防火墙端口开放K3s server 节点必须开放以下端口6443(TCP)Kubernetes API Server这是集群的“大脑”所有kubectl命令都打到这里。8472(UDP)Flannel VXLAN 网络的默认端口用于跨节点 Pod 通信。如果用其他 CNI如 Calico端口会不同。10250(TCP)kubelet 的 HTTPS 端口用于节点健康检查和日志获取。防火墙设置如果你用ufw执行sudo ufw allow 6443/tcp sudo ufw allow 8472/udp sudo ufw allow 10250/tcp sudo ufw reload如果用firewalld执行sudo firewall-cmd --permanent --add-port6443/tcp sudo firewall-cmd --permanent --add-port8472/udp sudo firewall-cmd --permanent --add-port10250/tcp sudo firewall-cmd --reload切记ufw或firewalld必须在k3s服务启动之前就配置好。我见过太多人先启动 K3s发现不通再开防火墙结果 K3s 已经因为连接不上 API Server 而自愈失败陷入无限重启循环。第三步安装与验证Server 节点安装在计划作为 master 的服务器上执行curl -sfL https://get.k3s.io | sh -这条命令会下载安装脚本创建k3s服务并启动它。安装完成后检查状态sudo systemctl status k3s # 应显示 active (running) sudo k3s kubectl get nodes # 应显示本机节点STATUS 为 ReadyAgent 节点加入在 worker 节点上首先从 server 节点获取 token# 在 server 节点执行 sudo cat /var/lib/rancher/k3s/server/node-token # 输出类似K10c25e...::server:...然后在 agent 节点执行替换server-ip和tokencurl -sfL https://get.k3s.io | K3S_URLhttps://server-ip:6443 K3S_TOKENtoken sh -等待几分钟回到 server 节点执行sudo k3s kubectl get nodes应该能看到新加入的节点且 STATUS 为NotReady正在拉取镜像、启动组件稍等片刻就会变成Ready。注意K3s 默认使用local-path作为 StorageClass它基于节点本地磁盘提供持久化存储。这对于测试和开发完全够用但绝不能用于生产环境的有状态服务如数据库因为一旦节点宕机数据就永久丢失。生产环境必须换成网络存储NFS、iSCSI或云存储。4.2 部署一个真实应用Nginx PHP-FPM MySQL 的完整 YAML光会kubectl get nodes没用得让它跑起你的业务。下面是一个经过生产验证的、包含 Web 层、应用层、数据库层的三件套 YAML它展示了 K3s 下最典型的部署模式。第一步创建 Namespace 和 Secret# 00-namespace-and-secret.yaml apiVersion: v1 kind: Namespace metadata: name: myapp --- apiVersion: v1 kind: Secret metadata: name: mysql-secret namespace: myapp type: Opaque data: # echo -n rootpassword | base64 MYSQL_ROOT_PASSWORD: cm9vdHBhc3N3b3Jk # echo -n myapp | base64 MYSQL_DATABASE: bXlhcHA # echo -n appuser | base64 MYSQL_USER: YXBwdXNlcg # echo -n apppassword | base64 MYSQL_PASSWORD: YXBwcGFzc3dvcmQ提示data字段的值必须是 base64 编码。不要手算用echo -n your-password | base64命令生成。Secret 是 Kubernetes 的核心安全机制它确保密码不会以明文形式出现在 YAML 或 Pod 的环境变量中。第二步部署 MySQL StatefulSet# 01-mysql.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql namespace: myapp spec: serviceName: mysql replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:8.0 envFrom: - secretRef: name: mysql-secret ports: - containerPort: 3306 name: mysql volumeMounts: - name: mysql-persistent-storage mountPath: /var/lib/mysql volumes: - name: mysql-persistent-storage persistentVolumeClaim: claimName: mysql-pv-claim --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pv-claim namespace: myapp spec: accessModes: - ReadWriteOnce resources: requests: storage: 2Gi --- apiVersion: v1 kind: Service metadata: name: mysql namespace: myapp spec: ports: - port: 3306 selector: app: mysql为什么用 StatefulSet 而不是 Deployment因为 MySQL 是有状态服务需要稳定的网络标识mysql-0.mysql.myapp.svc.cluster.local和稳定的存储PVC。StatefulSet 保证了 Pod 的有序部署、有序终止、以及网络名称的确定性。第三步部署 PHP-FPM 应用# 02-php.yaml apiVersion: apps/v1 kind: Deployment metadata: name: php-app namespace: myapp spec: replicas: 2 selector: matchLabels: app: php-app template: metadata: labels: app: php-app spec: containers: - name: php image: php:8.1-apache ports: - containerPort: 80 env: - name: DB_HOST value: mysql.myapp.svc.cluster.local - name: DB_NAME valueFrom: configMapKeyRef: name: app-config key: database.name volumeMounts: - name: app-code mountPath: /var/www/html volumes: - name: app-code configMap: name: app-source --- apiVersion: v1 kind: ConfigMap metadata: name: app-source namespace: myapp data: index.php: | ?php $host getenv(DB_HOST); $dbname getenv(DB_NAME); $user appuser; $pass apppassword; try { $pdo new PDO(mysql:host$host;dbname$dbname, $user, $pass); $pdo-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo Connected to MySQL successfully!; } catch(PDOException $e) { echo Connection failed: . $e-getMessage(); } ? --- apiVersion: v1 kind: ConfigMap metadata: name: app-config namespace: myapp data: database.name: myapp --- apiVersion: v1 kind: Service metadata: name: php-service namespace: myapp spec: selector: app: php-app ports: - port: 80 targetPort: 80第四步部署 Nginx Ingress暴露到公网# 03-ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp-ingress namespace: myapp annotations: # 使用 Traefik 作为 Ingress ControllerK3s 默认已安装 kubernetes.io/ingress.class: traefik spec: rules: - host: myapp.example.com http: paths: - path: / pathType: Prefix backend: service: name: php-service port: number: 80部署与验证将以上四个 YAML 文件保存为myapp.yaml合并在一起或分别保存。执行sudo k3s kubectl apply -f myapp.yaml。查看部署状态sudo k3s kubectl get all -n myapp等待所有 Pod 的 STATUS 变为Running。获取 Traefik 的 LoadBalancer IP如果是云环境或 NodePort如果是本地sudo k3s kubectl get svc -n kube-system # 找到 traefik 服务看 EXTERNAL-IP 或 NODE-PORT用curl -H Host: myapp.example.com http://node-ip:node-port测试应返回Connected to MySQL successfully!。这个例子完整覆盖了生产环境中最核心的组件Namespace 隔离、Secret 加密、StatefulSet 有状态、ConfigMap 配置、Ingress 暴露。它不是一个玩具而是一个可直接修改、上线的真实模板。5. 常见问题与排查技巧实录来自 20 个集群的实战经验5.1 “Pod 一直 Pendingkubectl describe pod显示0/1 nodes are available”这是新手遇到的第一道坎也是最让人抓狂的问题。Pending状态意味着 Scheduler 找不到符合条件的节点来运行这个 Pod。describe输出里的 Events 部分是破案的关键线索。以下是几种高频原因及解决方案原因一资源不足最常见现象

本月热点