docker 和 Kubernetes 已经成为后端开发和运维绕不开的两个词。我见过太多人单独学 docker 时觉得挺简单,不就是 build、run、push 嘛;单独看 K8s 文档时也觉得自己懂了,Pod、Deployment、Service 概念能背得滚瓜烂熟。可真到要把一个项目从 docker 搬到 K8s 上跑起来,或者排查一个“容器启动失败”的问题时,就卡住了。这篇总结我打算换个思路,不讲那些谁都能查到的命令大全,而是把 docker 和 Kubernetes 这条学习路径上的核心痛点、常见误区和落地经验串起来讲,重点解决“为什么这么用”和“出问题了怎么查”这两个更实际的问题。不管你是刚接触容器化的新手,还是已经写了 Dockerfile 但还没搞懂 K8s 的开发者,这篇应该都能帮你把知识体系补完整。
1. Docker:搞懂镜像、容器与数据,才算真正入门
很多人学 docker 第一个动作就是执行 docker run nginx,看到能访问页面就觉得入门了。这其实只是“会用”,离“理解”还差得远。Docker 的底层逻辑其实就三件事:镜像怎么构建、容器怎么运行、数据怎么持久化。把这三点串起来,你才能理解为什么后来 K8s 里那么多设计都是围绕这些来做的。
1.1 镜像与容器:不是一回事
先说镜像和容器的关系。我经常拿“程序与进程”来打比方:镜像就是一个静态的、只读的模板,它包含了运行某个应用所需的操作系统层、依赖库、环境变量和启动命令;容器则是这个模板运行起来后的实例。同一个镜像可以同时跑出多个容器,每个容器之间相互隔离,但又共享同一个宿主机内核。
这个设计带来的好处是巨大的。以前部署一个 Java 应用,需要先在服务器上装 JDK、配环境变量、处理依赖冲突,换一台机器就要重复一遍;现在只需要把镜像 build 好,任何装了 Docker 的机器上都能 docker run 直接跑起来,行为完全一致。这也是为什么容器化能成为交付标准——它把“环境一致性”这个老大难问题直接解决了。
这里有一个新手很容易踩的坑:不要在容器里跑“需要长期保持状态的进程”却不做任何持久化处理。比如你用 docker 跑一个 MySQL,如果没挂载数据卷,容器一删,数据全部灰飞烟灭。后面专门有一节讲数据卷,这里先记住这句话:容器是“牲口”,不是“宠物”,随时可以杀掉重建,数据必须放外面。
1.2 数据卷与持久化:容器重启后数据还在吗
Docker 里面的数据持久化主要靠 Volume(数据卷)和 Bind Mount(绑定挂载)两种方式。Volume 是 Docker 管理的目录,适合存放数据库文件、应用上传的附件等;Bind Mount 则是把宿主机的一个目录直接映射进容器,适合开发环境改代码实时生效。
拿实际场景来说,用 Docker 跑 Redis 主从复制时,主节点和从节点都要挂载数据卷,否则容器重建后内存里的数据就丢了。我见过有人配置 redis 主从,明明命令都没写错,但每次重启容器后数据就没,折腾了半天才发现是没挂载持久化目录。如果你用 docker-compose 编排,volume 段的写法就是类似下面这样的:
yaml复制services:
redis-master:
image: redis:7
volumes:
- redis-master-data:/data
redis-slave:
image: redis:7
command: redis-server --slaveof redis-master 6379
volumes:
- redis-slave-data:/data
volumes:
redis-master-data:
redis-slave-data:
关键点在于:容器本身是无状态的,所有需要保留的数据必须写到挂载的卷里。这个思维转变特别重要,因为你一旦上到 Kubernetes,Pod 里的容器更是随时可能被重新调度到别的节点上,如果数据没有放到外部存储,Pod 重建后数据就没了。
1.3 容器网络:端口映射与通信
网络是 docker 里很容易让人懵的一部分。容器默认跑在一个隔离的网络命名空间里,宿主机直接访问不到容器的 IP。你需要用 -p 宿主机端口:容器端口 做端口映射,外部才能访问到容器内的服务。
比如 docker run -p 8080:80 nginx,意思就是访问宿主机的 8080 端口,流量会被转发到容器内的 80 端口。这个映射关系在单机环境下很直观,但到了 Kubernetes 里就会变成一个更复杂的网络模型。K8s 里每个 Pod 有自己的 IP,并且集群内所有 Pod 之间默认可以互相通信,不需要额外的端口映射。这个思路上的转变,是理解 K8s 网络的一个分水岭。
再说一个常见问题:如果同一个宿主机上要跑多个容器,它们之间怎么通信?最简单的办法是用 docker-compose 创建一个自定义网络,服务名就是域名,比如服务名叫 db,其他容器里直接用 db:3306 就能连上。这个设计到了 K8s 里变成了 Service 和 DNS 解析,原理相通,但抽象层次更高。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Docker 的日常操作:从镜像构建到部署的完整链路
上节讲了核心概念,这节我挑几个日常高频操作展开,包括提取镜像、构建镜像、青龙面板这类脚本依赖管理场景,以及用 IDEA 打包 Docker 镜像。这些都是我在实操中反复用到、且容易出错的地方。
2.1 镜像获取与加速:docker pull 也有讲究
docker pull 看起来只是拉取镜像,但很多人没意识到它背后有一整套缓存和分层机制。镜像是由多个只读层叠加而成的,pull 的时候只会拉取本地不存在的层,所以多次拉取同一镜像的不同版本时,公共层可以直接复用,省时省流量。
实操中比较棘手的是镜像源不稳定。如果你在国内网络环境下直接 docker pull,经常会出现超时或者下载缓慢的情况。解决办法是配置镜像加速器,在 Docker Desktop 或 /etc/docker/daemon.json 里设置 registry-mirrors。至于哪个加速器可用,这个变化比较快,建议自己试,原则就是选延迟低、能稳定拉到的源。配好之后记得重启 Docker 服务,否则不生效。
还有一个容易忽略的小技巧:docker pull 时可以指定平台架构。比如你的开发机是 Apple Silicon(ARM 架构),但线上服务器是 x86 架构,拉镜像时可以直接拉对应平台的版本,避免运行时报格式错误。命令是这样:
bash复制docker pull --platform linux/amd64 nginx:latest
如果你在 x86 机器上拉了一个 ARM 镜像,运行时通常会报 exec format error,这时候第一反应就应该是检查平台架构是否匹配。
2.2 构建自己的镜像:编写高效的 Dockerfile
说到 Dockerfile,很多人的第一版就是“能用就行”,但“能用”和“好用”差距非常大。一个典型的 Java 后端项目 Dockerfile,新手可能这么写:
dockerfile复制FROM ubuntu:22.04
RUN apt-get update && apt-get install -y openjdk-17-jdk
COPY target/app.jar /app.jar
CMD ["java", "-jar", "/app.jar"]
这个写法的最大问题是镜像体积巨大,而且每次代码变更,依赖层都要重新安装。更合理的做法是分层构建、利用缓存、多阶段构建。例如使用 eclipse-temurin 这种官方精简 JDK 镜像作为基础,再配合多阶段构建把编译环境和运行环境分离:
dockerfile复制# 构建阶段
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# 运行阶段
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=builder /build/target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
这样最终镜像里只保留了 JRE 和 jar 包,体积能小一大截。而且 Maven 依赖层单独放在一层,只要 pom.xml 不变,后续构建就能命中缓存,构建速度快很多。这是我在实际项目里验证过非常有效的写法。
再举一个经常被提到的场景:青龙面板的依赖管理。很多人用 docker 跑青龙面板(一个定时任务管理工具),会遇到依赖库装不进容器的问题。核心原因是容器内是独立的环境,你在宿主机装的依赖跟容器毫无关系。正确的做法是在青龙面板的容器里使用它提供的“依赖管理”功能,或者在 Dockerfile 里提前装好依赖。这种问题本质上就是“不要把宿主机环境想当然地带入容器”,理解了这个,很多疑问都会迎刃而解。
2.3 使用 IDEA 高效打包 Docker 镜像
对于 Java 开发者来说,用 IDEA 直接打包 Docker 镜像是非常顺手的事。常见做法是在 pom.xml 里配置 dockerfile-maven-plugin 或者 docker-maven-plugin,这样执行一条 mvn package 就能自动构建镜像并推送到远程仓库。
我个人更推荐在 IDEA 里配置 Docker 插件,连接本地 Docker Desktop,然后右键 Dockerfile 直接 Run。这种方式的好处是可以在 IDEA 里直接看到构建日志,定位问题比较直观。配置要点是:Docker 插件要能连上 Docker Engine,Windows 下需要让 Docker Desktop 开放 tcp 端口,并配置 IDEA 的连接地址。
另外提醒一句,打包镜像之前一定要确认 target 目录下的 jar 包是最新的,经常有人忘记先执行 mvn package,结果镜像里跑的还是旧代码。排查这个问题时,可以进容器里看 jar 包的时间戳:
bash复制docker exec -it my-container ls -l /app.jar
2.4 Docker Compose:单机编排的利器
当你需要同时启动多个容器时(比如后端 + 数据库 + 缓存),一条条 docker run 显然不是好办法。Docker Compose 是官方提供的单机编排工具,通过一个 docker-compose.yml 文件就能定义整个服务栈。
一个典型的例子是用 docker 安装 GitLab。GitLab 依赖 PostgreSQL 和 Redis,但在官方镜像中已经内置了这些依赖,所以直接用 docker run 也能跑,只是参数比较多。用 Compose 写更清晰,同时可以方便地定义卷、网络、环境变量。实际使用中常见的坑是端口冲突,比如本地已经占用了 80 或 443 端口,GitLab 的 Web 服务会起不来。解决办法是映射到其他端口,或者在配置里关闭不需要的组件。
Compose 文件里 depends_on 字段很多人会用错,它默认只保证服务启动顺序,并不保证依赖服务已经“就绪”。比如数据库容器虽然起来了,但 MySQL 还没初始化完,后端连接就会被拒绝。实际做法是要么在应用里加重试逻辑,要么在依赖服务里做健康检查,这个意识和 K8s 里的 readinessProbe 是一脉相承的。
3. Kubernetes:从单机容器到集群编排的飞跃
docker 解决了“怎么装、怎么跑”的问题,但当你的服务多了、机器多了、流量大了,就会发现光靠 docker 远远不够。你需要知道容器挂了几台机器上、有没有挂掉、挂掉后谁来重启、流量分给谁,这时候 Kubernetes 就该登场了。
3.1 核心架构与组件:API Server、Scheduler、kubelet
Kubernetes 最核心的设计是“声明式 API”。你用 YAML 描述期望状态(比如“我要 3 个副本”),K8s 的控制平面会不断调谐实际状态,让真实状态向期望状态靠拢。这个思路跟 docker 的“命令式”操作有本质区别,很多人从 docker 转向 K8s 时最大的思维转变就在这里。
集群里主要节点分为 Master(控制平面)和 Node(工作节点)。控制平面核心组件包括:
- kube-apiserver:所有组件交互的入口,也是唯一直接操作 etcd 的组件
- etcd:保存整个集群的状态数据,键值对数据库
- kube-scheduler:决定 Pod 调度到哪个节点
- kube-controller-manager:运行各种控制器,比如副本控制器、节点控制器
每个工作节点上运行的是 kubelet(负责管理本节点的 Pod 和容器)、kube-proxy(负责网络代理和负载均衡)和容器运行时(比如 containerd 或 docker)。
这个架构理解起来其实不复杂,关键是“所有组件之间不直接通信,都通过 API Server 中转”。比如你执行 kubectl apply -f deployment.yaml,命令发给 API Server,API Server 存到 etcd,Scheduler 看到新 Pod 后分配节点,kubelet 收到指令后调容器运行时启动容器。这个过程环环相扣,每层职责单一,这也是 K8s 能承载大规模集群的原因。
3.2 工作负载:Pod、Deployment、StatefulSet、DaemonSet
Pod 是 K8s 最小调度单位。这跟 docker 的直接差异是:你不直接运行容器,而是运行一个 Pod,Pod 里面可以有一个或多个容器。同一个 Pod 里的容器共享网络和存储,所以通常把关系紧密的进程放在同一个 Pod 里,比如一个 Web 容器配一个日志收集容器。
Deployment 是最常用的工作负载,专门用于管理无状态应用。你只需要定义镜像版本和副本数,Deployment 会负责滚动更新、回滚、自动恢复。比如设置 replicas: 3,某个 Pod 挂了,控制器会自动拉起一个新的,这个过程是不需要人为干预的。
有状态应用(如数据库)则要用 StatefulSet。它跟 Deployment 最大的区别是每个 Pod 有稳定的网络标识(如 pod-0、pod-1)和独立的存储卷,Pod 重新调度后身份不变、数据不丢。这也是为什么在 K8s 里跑 MySQL、Redis 集群会优先考虑 StatefulSet。
DaemonSet 比较特殊,它保证每个节点上恰好运行一个 Pod,比如日志采集器、监控 agent 这种需要覆盖所有节点的组件就适合用 DaemonSet。理解了这几个工作负载的区别,你就基本拿到了 K8s 的钥匙。
3.3 Service 与网络模型:Pod 的“固定入口”
Pod 的 IP 是动态的,每次重建都会变。如果让前端直接访问 Pod IP,那简直是噩梦中的噩梦。Service 就是解决这个问题的抽象层:它提供一个稳定的访问入口(ClusterIP),后端挂载一组标签选择器选中的 Pod,流量会自动负载均衡到这些 Pod 上。
Service 的几种类型要分清:
- ClusterIP:集群内部访问,默认类型
- NodePort:通过每个节点的 IP + 端口访问,适合外部简单访问场景
- LoadBalancer:云平台提供的负载均衡器,适合生产环境对外暴露服务
除此之外还有 Ingress。简单理解,Service 是四层负载均衡,Ingress 是七层(HTTP/HTTPS)的入口,支持域名、路径路由、TLS 终止等规则。在本地开发环境,很多人直接用 NodePort 或者 kubectl port-forward 测试服务;生产环境则通常用 Ingress Controller(如 Nginx Ingress、Traefik)作为统一入口。
值得提一嘴的是,K8s 里的 Service 自带 DNS 解析。你在集群内只要知道 Service 名字就能访问,不用关心它后面 Pod 的 IP 怎么变。比如有一个 Service 叫 order-service,命名空间是 default,另一个服务里直接用 http://order-service:8080 就能连上。这个体验比 Compose 里用服务名通信还顺手。
3.4 存储与配置管理:ConfigMap、Secret、PV/PVC
K8s 里的配置管理也是一个核心话题。以前我们改配置要重新打包、重启容器,在 K8s 里可以优雅得多。ConfigMap 用来存普通配置,Secret 用来存敏感信息(比如密码、Token),它们都可以通过环境变量方式注入容器,也可以挂载成文件。
使用 ConfigMap 时有个小坑:如果 ConfigMap 作为环境变量注入,Pod 创建后改了 ConfigMap,环境变量不会自动更新;如果是以文件方式挂载,更新 ConfigMap 后文件内容会热更新(不过进程是否需要重启还得看应用自己)。这个特性在排查配置变更不生效时特别关键。
持久化存储这块,K8s 抽象出了 PV(持久卷)和 PVC(持久卷声明)。PV 是管理员预先分配的存储资源,PVC 是用户对存储的请求。比如一个 StatefulSet 需要 10GB 存储,它声明一个 PVC,K8s 会找符合条件的 PV 绑定上去。这样做的好处是应用不用关心底层存储到底是什么(NFS、云盘、本地盘都行),只管声明“我要多大空间”。
4. 从 Docker 到 K8s 的落地迁移:常见坑与最佳实践
这一节重点聊一个实际的问题:一个已经在 docker 里跑得好好的项目,怎么平稳迁到 K8s?如果直接把 docker run 命令改写成 Deployment YAML,大概率会踩坑。我总结了一套实践下来比较稳的迁移路线。
4.1 镜像层面的准备与优化
上 K8s 之前,镜像必须满足几个硬性条件。第一,应用要以非 root 用户运行。容器里默认可能是 root,这在 K8s 集群里往往会被安全策略拦住,或者存在安全隐患。建议在 Dockerfile 里显式声明 USER。第二,镜像要尽量精简,不光是体积问题,精简镜像意味着攻击面更小,拉取也更快。第三,日志要打到 stdout/stdout/stderr 而不是文件里。K8s 的 kubelet 会收集容器日志,如果应用把日志写到文件,收集起来会很麻烦。
这里特别说一下“镜像标签”的问题。很多人在本地习惯了用 latest 标签,但生产环境里 latest 是灾难性的。因为 Deployment 的镜像版本是声明式绑定的,你无法确切知道当前运行的是哪个版本,回滚也无从谈起。迁移到 K8s 前,应该建立一套镜像版本规范,比如用 Git commit hash 或语义化版本号作为标签,每次发布都是唯一的、可追溯的。
4.2 从 docker run 到 Deployment YAML 的思维转换
docker run 里的参数,几乎每一条都能在 K8s 里找到对应配置。我用一个表格来对照:
| docker run 参数 | K8s Deployment 配置 |
|---|---|
-p 8080:80 |
Service NodePort / LoadBalancer |
-v /data:/var/lib/data |
PVC 挂载 |
-e MYSQL_ROOT_PASSWORD=xxx |
env / Secret |
--network my-net |
同命名空间下通过 Service 互相访问 |
--restart always |
由 Deployment 控制器自动保证 |
-m 512m |
resources.limits.memory |
--cpus 1 |
resources.limits.cpu |
初看可能会觉得繁琐,但仔细想就会发现,K8s 是把 docker 原本比较“随意”的部分规范化、显式化了。你不再靠十几个命令参数拼出一个运行环境,而是把这个运行环境完整地、可重复地用 YAML 描述出来。
迁移后最重要的第一步是看 Pod 是否正常运行:
bash复制kubectl get pods
kubectl describe pod <pod-name>
describe 几乎是排查问题时最先该执行的命令,它能帮你看到事件列表、拉了哪个镜像、挂载了哪些卷、为什么中途失败等。很多人习惯直接 kubectl logs,但如果容器一直 CrashLoopBackOff,看 logs 之前先 describe 往往更能定位根因。
4.3 资源限制与健康检查:不设置等于裸奔
在 docker 时代,很多人跑容器时完全无视资源限制,反正宿主机有多大就能用多少。但在 K8s 里,如果你不设置 resources.requests 和 resources.limits,调度器就无法估算节点资源,极端情况下可能把一个节点打挂。
requests 是给调度器看的“我要占用多少”,limits 是运行时强制的上限。CPU 是压缩资源,超过 limit 会被限流;内存不是压缩资源,超过 limit 会被直接杀掉(OOMKilled)。所以内存 limit 一定要设置,而且要留出余量,避免 JVM 这类有自身堆内存管理的应用频繁被杀。
健康检查在 K8s 里分成 livenessProbe(存活探针,失败就重启容器)和 readinessProbe(就绪探针,失败就摘流量)。比如一个 Web 服务,readinessProbe 可以设置为检查 /actuator/health 接口,只有接口返回 200 才把流量打进去。这个机制在滚动更新时尤为重要,新版本启动慢的话,如果不配置就绪探针,流量可能打在还没就绪的 Pod 上,造成短暂的 502。
5. 常见问题与排查技巧实录
这一节我把实际操作中遇到的典型问题和排查思路整理出来,方便你以后出了问题按图索骥。以下都是实际场景,不是照抄文档的。
5.1 镜像拉取与构建类问题
现象一:docker pull 卡住不动或者超时。 排查思路:先看是否是网络问题,检查 DNS、换镜像加速器。如果是在公司内网,确认是否需要配置代理。很多镜像仓库还区分公有和私有,私有仓库需要先 docker login。
现象二:构建镜像时提示 no space left on device。 这是 Docker 磁盘空间满了。可以执行 docker system prune -a 清理所有未使用的镜像、容器和缓存,但注意这个命令会把本地没用到的镜像都清掉,如果之后还要用就得重新拉。更温和的方式是 docker image prune 只清理 dangling 镜像,或者把 Docker 的数据目录迁移到大分区。
现象三:Docker Desktop 在 Windows 上启动失败,提示 virtualization support wasn't detected。 这个非常常见。重点检查三处:BIOS/UEFI 里是否开启了虚拟化(Intel VT-x/AMD-V)、Windows 的 Hyper-V 或 WSL2 功能是否已启用、以及是否有其他虚拟机软件占用了 Hyper-V。顺便说个非技术原因:Docker Desktop 安装后必须重启系统,很多人忽略这一步导致后续问题不断。
5.2 容器运行类问题
现象一:容器启动后立刻退出。 先执行 docker logs <container-id> 看输出。常见原因包括:启动命令写错、环境变量缺失、依赖服务(比如数据库)没就绪。如果是 docker run 加上 -d 后立刻退出,多半是前台进程结束了,检查启动命令是不是没有保持前台运行,比如 nginx 可以,但某些自定义脚本执行完就退出了,需要用 tail -f /dev/null 这类方式保持容器存活。
现象二:容器内服务能访问,但宿主机访问不了。 很大概率是端口映射没做对,或者防火墙拦截。先用 docker ps 看端口映射状态,再在本机 curl 127.0.0.1:映射端口 测试。如果是跨机器访问,还要检查安全组是否放通了端口。这里给个经验:与其猜网络问题,不如直接 docker exec 进容器里测试,能快速区分是容器内部问题还是网络问题。
现象三:Pod 一直 CrashLoopBackOff。 这个在 K8s 环境非常常见。第一步 kubectl describe pod,查看 Events 段的原因,比如 Back-off restarting failed container。基本可以确定是容器启动后立即退出。然后 kubectl logs <pod-name> --previous 看上次退出的日志,很多人不知道有 --previous 这个参数,导致看到的是当前空日志。日志里如果报了连接数据库失败,检查是否是应用启动早于数据库就绪,解决办法是给数据库服务加 readinessProbe,或者应用层做重试。
5.3 集群调度与服务发现类问题
现象一:Pod 一直处于 Pending 状态。 先 kubectl describe pod,看到 0/3 nodes are available 大概率是资源不足,比如 CPU/Memory 请求超出节点剩余量,或者 PVC 没有可绑定的存储。通过 kubectl get nodes 和 kubectl describe node 查看节点资源水位,定位是 resources.requests 调得过高,还是缺少标签选择器。
现象二:服务之间访问不通。 先确认 Service 的 selector 能选中目标 Pod:kubectl get endpoints <service-name>。如果 Endpoints 列表为空,说明 Pod 的 label 和 Service 的 selector 对不上。这是新人最常踩的一个坑,标签写错一个单词就导致服务发现完全失效。其次检查 Service 类型,如果是 ClusterIP,外部访问肯定不通,需要换成 NodePort 或 LoadBalancer。
现象三:滚动更新后服务短暂不可用。 大多数情况是缺少 readinessProbe。新 Pod 还没就绪就开始接收流量,导致部分请求失败。另外,maxUnavailable 和 maxSurge 参数也值得调,滚动更新时必须保证有足够多的 Pod 可用,这几个参数直接影响更新期间的服务稳定性。
5.4 资源与性能类问题
现象一:Pod 被 OOMKilled。 这说明内存使用超过了 limit。执行 kubectl describe pod 看 Last State 里的 Reason 是否为 OOMKilled,同时看 exit code 是否为 137。排查时不能只加 limit,要分析应用为什么内存涨上去。比如 Java 应用在容器里如果没正确识别到容器内存限制,堆会按宿主机内存来分配,导致 Pod 被提前杀死。这样修复:在容器里通过 -XX:MaxRAMPercentage=75 限制 JVM 堆大小,或者设置 -Xmx 显式指定。
现象二:CPU 使用率飙升但容器限流严重。 如果 Pod 配置了很低的 CPU limit,应用一忙就会被限流,表现是响应变慢但 CPU 占用不高(因为在等待调度配额)。这时候需要调高 limit,或者考虑水平扩容,让多个副本分担压力。
实操总结:从零搭一套单机 K8s 环境的建议
很多人在“要不要自己搭一套 K8s 环境”这件事上犹豫再三,觉得太重。我的建议是:有条件一定要亲手在本地完整地装一次 K8s,即使是最简化的单机模式,也能让你把前面讲的所有概念串起来。
如果你是 Windows 用户,最轻量的方案是启用 Docker Desktop 自带的 Kubernetes 集群。这个方案的好处是跟 Docker 打通,不用额外装工具,直接在 Docker Desktop 设置里勾上 Kubernetes 就能用。缺点是版本升级慢,不适合模拟多节点场景。如果你是 Mac 或 Linux 用户,推荐用 kind(Kubernetes in Docker)或 minikube。kind 是直接在 Docker 容器里跑 K8s 集群,一条命令创建一个集群;minikube 更适合需要本地卷和部分云资源模拟的场景。
我个人的经验是:先玩转 Docker 再玩 K8s,顺序不要反。容器不熟练的人直接上 K8s,等于还没学会走就想跑,排查问题时会非常痛苦,因为你不知道问题是出在容器层还是编排层。先在 docker 里把一个项目完整跑起来,再用 Compose 编排一套 Web + 后端 + 数据库,然后把 Compose 转成一个 Deployment,加上 Service,访问通了再去研究滚动更新和探针。这样一步步走下来,比直接背 K8s 文档要牢靠得多。
最后再分享一个小技巧:遇到 K8s 问题不要慌,记住“先 describe,再 logs,再看 events,最后看资源状态”这个顺序。80% 的问题都能通过这个流程定位到。容器化和云原生这条路走进来之后,你会慢慢发现,docker 和 K8s 只是工具,真正难的是怎么把一个应用以可维护、可扩展的方式跑起来。这个能力需要踩坑积累,希望我这篇总结能帮你少走一些弯路。
