简介:《Kubernetes in Action》英文原版电子书,是一本面向容器编排技术学习者的系统化参考手册,适合正在使用或计划采用Kubernetes的开发者、DevOps工程师与架构师。这本书聚焦Kubernetes在生产环境中的部署、扩展与管理,系统讲解了命名空间、Pod、工作负载、服务发现、配置管理、持久化存储与安全防护等核心知识,帮助读者从零构建对分布式应用编排的完整理解。资源包内仅有1个PDF文件,压缩后约11.1MB,轻量便于在电脑、平板等设备上阅读和检索。目前已有436人学习下载。书中内容按照集群级资源、部署、服务、配置、存储等模块展开,并提供一份Kubernetes资源速查表,列出各资源的API版本与用途;同时通过大量示例演示了如何声明式发布应用、暴露服务、配置密钥,以及使用持久卷保存数据。这些内容既适合作为入门阶段的系统教程,也可作为日常集群运维和认证备考的案头参考资料。
1. Kubernetes + In Action:这份英文原版 PDF 值不值得啃
几年前我还在用 docker-compose 一把梭,直到接手一个需要横向扩容的线上项目,才真正意识到 kubernetes 不是一个"会写 YAML 就行"的工具。当时手边就放着这份 Kubernetes in Action 英文原版 PDF,原以为是本劝退大部头,翻完才发现里面把 Pod、控制器、调度器这些概念讲得比网上绝大多数文章透。它解决的不只是"怎么装集群",而是"为什么 K8s 要这么设计"——这对排查问题特别关键。适合两类人:一类是被 K8s 面试题追着跑的新手,一类是想把集群调稳的运维或后端开发。
2. 先弄懂三个抽象:Pod、Service、控制器,工作负载是如何被编排的
2.1 Pod:为什么它是调度和资源分配的最小单位
很多人上手 K8s 时第一个疑惑是:既然有容器,为什么非要再包一层 Pod?其实把一个 Pod 理解成"进程组"就顺了。单容器场景下 Pod 只是壳,但多容器场景里,同一个 Pod 内的容器共享网络命名空间和存储卷,意味着它们可以通过 localhost 互相访问,也可以挂载同一块数据盘。最常见的搭档是业务容器加 sidecar 容器——比如日志采集器跟着业务容器一起跑,业务在哪个节点它就在哪个节点,不用单独维护一套映射。
实际排查时,你几乎所有的操作都围绕 Pod 展开。我一般先执行kubectl get pods -o wide看状态,再kubectl describe pod看事件。后者是排查的第一现场,PullImage 失败、探针失败、调度不满足条件,都会以事件的形式出现在末尾的 Events 里。
# 查看所有命名空间下的 Pod 及所在节点 kubectl get pods -A -o wide # 查看某个 Pod 的详细事件与状态 kubectl describe pod <pod-name> -n <namespace>第一行命令的-o wide会多输出节点 IP 和 Pod IP,方便你确认 Pod 到底被调度到了哪台机器;第二行是定位问题的关键步骤,描述信息里的 Events 区块记录了最近发生的所有调度与运行事件,比如镜像拉取失败、端口冲突、内存不足。大多数异常在这里都能直接看到原因,而不是靠猜。
2.2 Service:稳定接入入口,kube-proxy 到底做了什么
Pod 是会被创建和销毁的,IP 也随之变化,所以需要 Service 提供一个稳定的访问入口。Service 的底层机制不复杂:它是一组 iptables 规则,由 kube-proxy 维护,把发往 Service IP 的流量转发到后端某个 Pod 的 IP 上。种类主要有三种,选型看场景:
| Service 类型 | 访问范围 | 适用场景 | 备注 |
|---|---|---|---|
| ClusterIP | 集群内部 | 服务间调用 | 默认类型,外部无法直接访问 |
| NodePort | 集群外部通过节点 IP 访问 | 快速对外暴露端口 | 端口范围默认 30000-32767 |
| LoadBalancer | 公网/云环境 | 面向外部流量的生产服务 | 通常由云平台负载均衡器实现 |
一个常见的坑是把 Service 的 selector 写错,导致没有后端 Pod 被选中。kubectl get endpoints能立刻看出来——如果 Endpoints 列表为空,说明 Service 和 Pod 的标签没对上。这时候回头查 Deployment 里的metadata.labels和 Service 的spec.selector是否一致,多半是少写了或者值写错了。
2.3 控制器:Deployment、StatefulSet 与 ReplicaSet 的分工
ReplicaSet 负责维持指定数量的 Pod 副本,但日常你很少直接操作它,而是通过 Deployment 来管理。Deployment 在 ReplicaSet 之上加了滚动更新和回滚能力,适合无状态服务。StatefulSet 则给有状态服务提供稳定的网络标识和持久化存储绑定,比如数据库、消息队列,但复杂度也更高,节点故障时恢复步骤更繁琐。
apiVersion: apps/v1 kind: Deployment metadata: name: web-demo labels: app: web-demo spec: replicas: 3 selector: matchLabels: app: web-demo template: metadata: labels: app: web-demo spec: containers: - name: web image: registry.example.com/web-demo:v1.2 ports: - containerPort: 8080 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi这份 YAML 的关键点有三个。selector.matchLabels必须和template.metadata.labels一致,Deployment 靠这个标签管理它创建的 Pod;replicas声明期望副本数,控制器会保证集群里始终有这么多副本在运行;资源里的requests用于调度时的资源预留,limits则是运行时的硬上限,超过会被 OOM-Killed。注意requests不要设置过小,否则调度器可能把多个对 CPU 敏感的 Pod 堆到同一台机器上,运行时互相争抢。
3. 从零拉起一个集群:版本规划、初始化参数与应用落地
3.1 环境准备:先把容器运行时和内核参数搞对
不管你是用 minikube 在本机尝鲜,还是用 kubeadm 搭多节点集群,底层的容器运行时都是绕不开的。当前主流是 containerd,你的这份 PDF 覆盖面足够让你看懂这套架构。两个阶段要注意:一是节点上必须先装好 containerd,再装 kubelet、kubeadm、kubectl;二是节点必须关闭 swap,否则 kubelet 会拒绝启动。
# 加载内核模块并设置 IP 转发(Debian/Ubuntu 系) cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 EOF sudo sysctl --systemoverlay是容器镜像分层存储依赖的内核模块,br_netfilter让桥接流量也能通过 iptables 过滤,这两项不加载,后续 Pod 网络和数据包转发都会出现诡异问题。net.ipv4.ip_forward必须为 1,否则节点无法转发 Pod 之间的跨主机流量,Service 转发也会中断。做完这些,sysctl --system是让配置立即生效,别漏。
3.2 kubeadm 初始化:参数怎么给才不出幺蛾子
初始化时最值得注意的参数是--pod-network-cidr,它决定了 Pod 网络的地址段。这个段不能和节点网段、Service 网段重叠,否则路由会错乱。常见的做法是给 Pod 网段分配一个专用的私有段,比如 10.244.0.0/16,给 Service 网段用--service-cidr单独指定。如果你用的是 flannel,它对网段有默认预期,配合 10.244.0.0/16 可以减少配置量。
# 仅在控制平面节点执行 sudo kubeadm init \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --cri-socket=/run/containerd/containerd.sock初始化成功后,终端会输出两段关键信息:一段是让你在普通用户下执行的kubectl配置命令,也就是把管理员证书拷贝到当前用户目录下的操作;另一段是加入集群的kubeadm join命令,包含 token 和 ca 证书哈希,工作节点靠它加入集群。这两段建议先存到文本文件里,因为 token 有效期默认 24 小时,过期就得重新生成,到时候再去翻会很被动。
3.3 安装网络插件:把 Pod 网络打通
初始化完集群如果不装 CNI 插件,节点状态会一直停留在 NotReady,因为 kubelet 检查到网络未就绪。这个环节选 flannel 还是 Calico,主要看规模。小规模集群 flannel 够用,VXLAN 模式配置简单;需要 NetworkPolicy 做租户隔离时选 Calico,但它的资源占用和能力复杂度都更高。
| 对比项 | Flannel | Calico |
|---|---|---|
| 网络模型 | VXLAN 覆盖网络 | 纯三层 BGP 路由 |
| NetworkPolicy 支持 | 不支持 | 支持 |
| 性能开销 | 较高 | 较低 |
| 推荐场景 | 小型集群、快速验证 | 生产环境、政策隔离需求 |
# 以 flannel 为例,安装网络插件 kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml这里有个常见做法是先把 YAML 下载到本地,检查里面的镜像地址是否在你能访问的仓库里,再执行 apply。不同网络插件对--pod-network-cidr有约定,flannel 默认就是用 10.244.0.0/16,改来改去容易翻车。安装完成后用kubectl get nodes观察,节点从 NotReady 变成 Ready 通常需要一两分钟,如果长时间卡住,去看 kube-system 命名空间下 flannel Pod 的日志。
3.4 部署第一个无状态应用:从镜像到对外访问
集群就绪后,从部署到访问的完整链路走一遍,能验证前面所有配置是否真的通了。准备一个简单的 Nginx 镜像,建 Deployment 暴露 80 端口,再用 NodePort 类型的 Service 把端口映射出来。
# 创建一个测试 Deployment kubectl create deployment nginx-test --image=nginx:1.27 # 暴露为 NodePort 服务 kubectl expose deployment nginx-test \ --type=NodePort \ --port=80 \ --target-port=80 # 查看映射出的访问端口 kubectl get svc nginx-test--port是 Service 对外暴露的端口,--target-port是后端 Pod 实际监听的端口,两者可以不一样。kubectl get svc输出里PORT(S)一列如果显示80:31234/TCP,说明 Service 的 80 端口被映射到了节点的 31234 端口,通过任意节点的 IP 加 31234 就能访问到 Nginx。这个链路通了,说明 Pod 网络、kube-proxy 转发、节点端口都正常。
4. 避坑与排查:集群运行中最常翻车的五个现场
4.1 镜像拉取失败:ImagePullBackOff 和 ErrImagePull
现象:Pod 一直停留在 ImagePullBackOff 状态,describe看到事件里有Failed to pull image。
原因有三类:镜像地址写错或者 tag 不存在;镜像在私有仓库需要认证,但 YAML 里没有配置imagePullSecrets;节点无法访问镜像仓库的域名。最后这种情况在国内环境尤其常见,仓库域名解析被卡住后,kubelet 反复重试,事件里全是 429 或超时。
解决:先用crictl pull <镜像地址>在节点上手动拉一次,能区分是仓库认证问题还是网络问题。地址写错就改 Deployment 的 image 字段;私有仓库就创建 docker-registry 类型的 Secret,然后在 Deployment 的spec.template.spec下加imagePullSecrets。如果确认是仓库访问问题,把镜像地址换到能访问的镜像仓库前缀,比如某云厂商提供的镜像加速地址,再重新部署。
4.2 节点一直 NotReady:CNI 网络插件没有就绪
现象:kubectl get nodes看到某个节点状态是 NotReady,kubectl get pods -n kube-system里 coredns 一直 CrashLoopBackOff。
原因:最常见的是节点加入集群时没有安装 CNI 插件,或者插件的 DaemonSet 没有调度到该节点。另一个隐蔽原因是容器运行时和 CNI 插件的版本不兼容,比如 containerd 和 flannel 的 cni-plugin 版本相差过大,导致网络命名空间配置失败。
解决:在 NotReady 节点上执行ls /etc/cni/net.d/看是否有配置目录。没有就是 CNI 没装到位。删除插件 DaemonSet 再重新 apply,前提是确认该节点没有被污点拦截。遇到 containerd 与 CNI 插件版本不匹配时,把 flannel 的镜像版本升级一档,或者换用 Calico,一般能解决。
4.3 证书过期:集群突然无法认证
现象:某天kubectl执行任何命令都报证书无效或过期,kubelet 连接 apiserver 也报 TLS 握手失败。
原因:kubeadm 生成的证书有效期只有一年,官方推荐一年内做一次续期或者直接升级集群。很多集群建完就没人管证书,到期自然就翻车。
解决:在控制平面节点执行kubeadm certs renew all续期所有证书,然后重启 kubelet 和静态 Pod 组件。这个操作会覆盖掉旧的证书文件,建议续期前先把/etc/kubernetes/pki整个目录备份一次——这是你的后悔药。续期后kubectl如果还报证书错误,重新拷贝一次 admin 配置即可。
4.4 PVC 一直 Pending:没有可用的 StorageClass
现象:创建 PVC 后一直处于 Pending,describe pvc事件里提示waiting for a volume to be created, either by external provisioner or by storage class。
原因:集群里没有部署任何存储插件,比如 NFS Client Provisioner 或云平台自带的存储驱动。有些裸金属集群装完 K8s 就认为存储是自带的,其实 PVC 和 PV 不会自动绑定。
解决:先kubectl get storageclass确认有没有默认存储类。如果完全没有,最简单的方案是先部署一个 NFS 子目录供给器,把 NFS 服务端路径挂上去;本地测试场景不想搭 NFS,也可以直接在 PVC 里手动绑定一个 hostPath 类型的 PV。但要注意 hostPath 只适合单节点测试,多节点集群里 Pod 被调度到其他节点后数据就不在本地了。
4.5 Pod 频繁被驱逐或重启:内存 limit 设置不合理
现象:某个服务运行一段时间后容器被 OOM-Killed,kubectl get pods看到重启次数不断上涨,节点上/var/log/kern.log里也有 OOM 记录。
原因:YAML 里limits.memory设置得太小,或者存在内存泄漏。另一个常被忽略的因素是节点本身内存已经吃紧,kubelet 根据可分配资源量驱逐低优先级 Pod。
解决:先把内存 limit 调大一档观察,比如从 512Mi 调到 1Gi,看是否继续被 OOM;如果还会,就需要排查应用内存占用曲线,而非盲目加资源。如果节点确实内存不足,检查节点上是否有其他占用大户,或者干脆增加节点规格。总之,limits不是摆设,设置前至少要有压测数据支撑。
5. 进阶技巧:用滚动更新、标签选择器与亲和性把集群调稳
滚动更新是把新版本 Pod 逐步替换旧版本的过程,但很多人只改了镜像 tag 就完事,忽略了另外两个核心参数。
spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: spec: containers: - name: web image: registry.example.com/web-demo:v1.3 readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 10maxSurge: 1表示更新时允许额外多创建一个 Pod,maxUnavailable: 0表示更新期间不能有旧 Pod 下线。两者的配合决定了发布速度和可用性:追求快速发布就把 maxUnavailable 设成 1,允许旧副本先下线;追求零中断就把 maxUnavailable 设成 0,靠 maxSurge 先拉起新副本再淘汰旧副本。但注意,maxUnavailable: 0依赖 readinessProbe 正常工作——如果探针一直不通过,新 Pod 永远不会进入 Ready,发布就直接卡死,这也是很多人改完参数后发布"莫名其妙"不动的常见原因。
readinessProbe 是做滚动更新的前置条件。它告诉 kubelet 什么时候可以认为容器已经就绪、可以接收流量。initialDelaySeconds是容器启动后等多久才开始探测,给应用留出加载配置、建立连接的时间;periodSeconds是探测间隔。生产里我一般会额外加timeoutSeconds和failureThreshold,避免网络抖动导致探针误判。
标签选择器和节点亲和性解决的是"把 Pod 放在哪"的问题。节点亲和性比 nodeSelector 更灵活,可以用 In 或 NotIn 表达复杂条件。
affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disktype operator: In values: - ssd这段配置的意思是:Pod 只能调度到带有disktype=ssd标签的节点上。requiredDuringSchedulingIgnoredDuringExecution这种硬亲和力适合数据库这类对存储介质有要求的服务。还有一类软亲和力preferredDuringSchedulingIgnoredDuringExecution,只是调度时优先,找不到也能跑在别处,适合把有网络依赖的服务尽量聚拢。官方文档里把这两者的区别写得清楚,但你手头这份 PDF 的价值在于,它能帮你把"亲和性"和"污点容忍"放在同一个调度链路里理解——先过滤掉不满足硬性条件的节点,再按软性偏好打分排序。理解这个顺序,你才能灵活组合出合理的拓扑分布。
现在每当我搭一个新的生产集群,都会强制走一遍这套流程:从确认容器运行时版本,到 CNI 插件事前选型,再到给关键服务补全 readinessProbe,最后检查证书余量。特别是证书,我在日历上标了到期日,每年提前一个月续期,已经成了条件反射。这份 PDF 里的英文原版内容虽然对新手有阅读门槛,但它跳过了一堆翻译失真和不准确的二手内容,值得花时间啃下来。希望帮到你。
本文还有配套的精品资源,点击获取