嘿,朋友。是不是刚把 Kubernetes (K8s) 装好,信心满满地部署了第一个微服务,结果还没喝口咖啡,监控大屏就红了一片?Pod 起不来、服务连不通、日志满屏报错?别慌,你不是一个人。
我也曾经历过那种“我以为我懂了,其实我连门都没摸对”的绝望。今天,我想像老朋友聊天一样,跟你掏心窝子聊聊 K8s 入门那些最坑的误区,以及如何从一个 0 基础的状态,一步步搭建出一个真正稳定、能扛事儿的生产级集群。这不是教科书,这是血泪换来的实战经验。
误区一:“K8s 是 Docker 的替代品”
这是最经典的入门陷阱。很多新手(包括两年前的我)觉得,K8s 就是个高级版的 Docker Compose,用来管理容器的。
真相是: K8s 管理的是计算节点上的工作负载,而 Docker(现在叫 Containerd)只负责打包和运行单个容器。K8s 是 orchestrator(编排器),不是 runtime(运行时)。
避坑实战:
不要试图在 K8s 里用 docker 命令。你的镜像应该用 ctr 或 nerdctl,底层是 Containerd。你关注的是 Pod(最小的调度单元),而不是 Container。一个 Pod 里可以有多个容器(Sidecar 模式),它们共享网络和存储。
举个例子,如果你还在用 docker run -p 8080:80 的思维去理解 K8s,那你一定会困惑于 Service 和 Ingress 的区别。记住:
- Pod = 一组容器的逻辑集合。
- Deployment = 管理 Pod 的副本数和更新策略。
- Service = 给 Pod 提供稳定的访问入口(负载均衡)。
- Ingress = 管理外部到集群的 HTTP/HTTPS 流量(七层路由)。
误区二:“集群节点越多越稳定”
新手最爱干的事:买一堆便宜的云服务器,装个 K8s,觉得节点多就是高可用。
真相是: 节点多但配置不合理,集群会更难维护,网络故障率更高,资源碎片化严重。
避坑实战: 从小做起,但要规划好。
- 控制平面(Master 节点): 至少 3 个节点,奇数个,用于 etcd 数据一致性。不要用廉价的低配机器,etcd 对磁盘 I/O 要求极高。建议 4C8G 起步,SSD 磁盘。
- 工作节点(Worker 节点): 根据业务需求定。初期 2-3 个中等配置节点(8C16G)比 10 个 2C4G 的节点更稳定、更容易排查问题。
- 网络插件: 推荐 Calico 或 Cilium。它们性能更好,支持网络策略(Network Policy),这是安全的基础。Flannel 虽然简单,但在生产环境复杂网络策略下表现不佳。
误区三:“默认配置就能跑生产”
K8s 的默认配置是为了演示和开发设计的,离生产差着十万八千里。
核心坑点 1:资源限制(Requests/Limits) 很多新手不设置资源限制,或者随便设一个。结果呢?一个 Java 应用内存泄漏,把整个节点的内存吃光,导致其他 Pod 被 OOM Kill(内存溢出杀)。
正确做法:
resources:
requests:
memory: "256Mi" # 保证分配的最小资源
cpu: "250m" # 保证分配的最小 CPU
limits:
memory: "512Mi" # 最大不超过这个,否则被杀
cpu: "500m"
记住: requests 是调度依据,limits 是安全阀。没有 limits,你的集群随时可能被一个爆内存的应用拖垮。
核心坑点 2:探针(Probes)缺失 默认不配置健康检查,K8s 不知道你的应用是否真的就绪。结果:Service 把流量转发给了还没启动完的 Pod,导致请求 502 错误。
正确做法:
livenessProbe: # 活得不好了就重启
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe: # 准备好接受流量了才加进来
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
关键点: liveness 和 readiness 分开配置。很多应用启动慢,但启动后很健康。如果只用 liveness,启动中会被频繁重启,形成重启风暴。
误区四:“持久化存储随便搞”
微服务通常需要存数据(数据库、文件),新手容易踩两个坑:
- 用空壳的
emptyDir存数据,Pod 一重启数据没了。 - 直接用
HostPath,把数据写在节点本地盘上,节点挂了数据也丢了,而且无法迁移。
避坑实战: 使用 PV(Persistent Volume) 和 PVC(Persistent Volume Claim)。
- 云环境: 直接用云厂商提供的 StorageClass(如 AWS EBS、阿里云 PV、腾讯云 CDS)。一键挂载,自动备份。
- 自建环境: 部署一套分布式存储,如 Rook-Ceph 或 Longhorn。Longhorn 更轻量,适合中小型集群,支持副本和备份。
示例 YAML:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard # 对应你集群的 StorageClass
resources:
requests:
storage: 10Gi
然后在 Deployment 里挂载这个 PVC,数据就持久化了,Pod 重启、迁移都不会丢。
从 0 搭建稳定集群的实战步骤
好了,理念讲清楚了,我们来点干货。假设你有一台干净的 Linux 服务器(或者几台),我们从头开始。
第一步:基础环境准备
- 禁用 Swap: K8s 要求节点禁用 Swap,否则 kubelet 启动失败。
sudo swapoff -a sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab - 内核参数调整: 开启桥接流量,防止 iptables 干扰。
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.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sudo sysctl --system - 安装容器运行时: 推荐 Containerd(K8s 1.24+ 已移除 Docker shim)。
sudo apt-get update sudo apt-get install -y containerd sudo mkdir -p /etc/containerd sudo containerd config default | sudo tee /etc/containerd/config.toml sudo systemctl restart containerd
第二步:安装 K8s 组件
安装 kubelet、kubeadm、kubectl:
sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl # 防止自动升级搞坏集群初始化控制平面(只在 Master 节点执行):
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=<你的MasterIP>注意:
--pod-network-cidr必须和你后面选的 CNI 插件兼容。Calico 默认用这个网段,所以很稳妥。 初始化成功后,你会看到一段kubeadm join的命令,一定要保存下来,后面加节点要用。配置 kubectl 权限(普通用户):
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
第三步:部署网络插件(CNI)
这是很多新手卡住的地方。集群起来了,但 Pod 之间通不了信,因为没装网络插件。
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/calico.yaml
等几分钟,kubectl get pods -n kube-system,看到所有 Pod 都是 Running,说明网络通了。
第四步:加入 Worker 节点
在其他节点上执行初始化和复制出来的 kubeadm join 命令。
sudo kubeadm join <MasterIP>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>
回到 Master 节点,kubectl get nodes,应该能看到新节点状态为 Ready。
第五步:部署一个稳定的微服务应用
让我们部署一个典型的“用户服务”,它依赖 MySQL 和 Redis。
创建 Namespace 隔离:
apiVersion: v1 kind: Namespace metadata: name: user-service配置 ConfigMap 和 Secret: 不要把密码硬编码在 YAML 里。
apiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque data: password: bXlwYXNzd29yZA== # base64 编码的 "mypassword"部署 MySQL(使用 StatefulSet,保证稳定性和有序性): “`yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql namespace: user-service spec: serviceName: mysql replicas: 1 selector: matchLabels:
app: mysqltemplate: metadata:
labels: app: mysqlspec:
containers: - name: mysql image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: db-secret key: password ports: - containerPort: 3306 resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" volumeMounts: - name: mysql-data mountPath: /var/lib/mysqlvolumeClaimTemplates:
- metadata: name: mysql-data spec: accessModes: [“ReadWriteOnce”] storageClassName: standard resources: requests: storage: 10Gi
”` StatefulSet vs Deployment: 数据库用 StatefulSet,因为它需要稳定的网络标识和持久化存储。Deployment 适合无状态的应用。
部署用户服务(Deployment + Service): “`yaml apiVersion: apps/v1 kind: Deployment metadata: name: user-service namespace: user-service spec: replicas: 3 # 高可用,至少 2 个副本 selector: matchLabels:
app: user-servicetemplate: metadata:
labels: app: user-servicespec:
containers: - name: user-service image: myregistry/user-service:v1.2 ports: - containerPort: 8080 env: - name: DB_HOST value: "mysql" # 指向上面的 Service 名称 - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-secret key: password resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 10 periodSeconds: 5
apiVersion: v1 kind: Service metadata: name: mysql namespace: user-service spec: selector: app: mysql ports:
- port: 3306 targetPort: 3306 — apiVersion: v1 kind: Service metadata: name: user-service namespace: user-service spec: selector: app: user-service ports:
- port: 80 targetPort: 8080 type: ClusterIP # 集群内部访问
”`
暴露外部访问(Ingress): “`yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: user-ingress namespace: user-service annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules:
- host: api.example.com
http:
paths:
- path: / pathType: Prefix backend: service: name: user-service port: number: 80
别忘了部署 Ingress Controller(如 nginx-ingress): ```bash kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/deploy.yaml- host: api.example.com
http:
paths:
监控与日志:不要等到崩了才看
稳定集群离不开监控。手动 ssh 上去看日志是 2010 年的做法。
Metrics Server: 提供资源使用数据,支持 HPA(自动伸缩)。
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yamlPrometheus + Grafana: 行业标准监控栈。可以用 Helm 一键部署:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update helm install prometheus prometheus-community/kube-prometheus-stack部署后,访问 Grafana Dashboard,你可以看到集群的整体健康状况、每个 Pod 的 CPU/内存使用、网络流量等。
日志收集: 推荐 EFK(Elasticsearch + Fluentd + Kibana)或 Loki(更轻量,成本更低)。
helm install loki grafana/loki-stack这样,所有 Pod 的日志都会自动收集,你可以用 LogQL 查询任意时间段的错误日志。
最后的忠告:稳定性的核心是“可观测性”和“自动化”
搭建集群只是第一步。真正的稳定,来自于:
- 每一个请求都有日志追踪。
- 每一个异常都有告警通知(接入 Alertmanager,推送钉钉/企业微信/Slack)。
- 每一次变更都有备份和回滚方案。
不要害怕犯错。我在第一次生产环境变更时,误删了所有 Pod,花了 3 小时才恢复。但那 3 小时让我彻底理解了 K8s 的声明式 API 和自愈机制。现在,我会用 Argo CD 做 GitOps,所有配置提交 Git,自动同步到集群,变更可追溯,回滚只需一条命令。
K8s 不是魔法,它是一套复杂的系统。但只要你避开这些入门误区,打好基础,它一定会成为你最有价值的武器。
祝你集群稳定,报警安静。有问题随时来找我聊聊。
