为什么需要 Kubernetes?
当我们从单体应用转向微服务架构时,容器化解决了环境一致性问题,但随之而来的是容器编排的挑战:如何管理成百上千个容器?如何保证服务的高可用?如何实现自动扩缩容?Kubernetes(简称 K8s)正是为了解决这些问题而生的容器编排平台。
对于初学者来说,K8s 的概念众多,但最核心的莫过于 Pod、Deployment 和 Service。理解这三者,你就掌握了 K8s 的骨架。本文将带你从零开始,通过一个实际示例,彻底搞懂它们。
前置准备
在开始之前,你需要一个可用的 Kubernetes 集群。推荐使用 Minikube 或 Kind 在本地搭建,也可以使用云厂商的托管集群(如 AKS、EKS、GKE)。本文示例使用 Minikube 演示。
安装完成后,验证集群状态:
kubectl cluster-info
如果看到类似 Kubernetes control plane is running at ... 的输出,说明集群已就绪。
核心概念初识
- Pod:Kubernetes 的最小调度单元,一个 Pod 可以包含一个或多个容器(通常一个)。Pod 内的容器共享网络命名空间和存储卷,它们总是被调度到同一个节点上。
- Deployment:负责管理无状态应用的副本(ReplicaSet),提供声明式更新、滚动升级、回滚等功能。你只需要声明期望的状态,Deployment 会确保实际状态与期望一致。
- Service:为 Pod 提供稳定的网络访问入口。由于 Pod 的生命周期是短暂的,IP 会变化,Service 通过标签选择器将流量转发到后端的 Pod 集合,并提供负载均衡和服务发现。
第一步:创建 Deployment
我们先创建一个 Nginx 的 Deployment,它负责运行 3 个 Nginx Pod 副本。
创建文件 nginx-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
解释:
apiVersion:使用的 API 版本,apps/v1是 Deployment 的稳定版本。metadata.name:Deployment 的名称。spec.replicas:期望的副本数。spec.selector.matchLabels:选择器,用于匹配它管理的 Pod。这里选择标签为app: nginx的 Pod。spec.template:Pod 模板,定义了 Pod 的配置。注意template.metadata.labels必须与selector匹配,否则 Deployment 无法管理这些 Pod。
应用这个清单:
kubectl apply -f nginx-deployment.yaml
查看 Deployment 和 Pod 的状态:
kubectl get deployments
kubectl get pods
你会看到 3 个 Pod 正在运行,它们的名字以 Deployment 名称为前缀,后面跟着一个随机字符串。
> 💡 注意:Pod 的 IP 是动态分配的,不能直接用于外部访问。如果你尝试 kubectl get pods -o wide 查看 IP,然后用 curl 访问,你会发现无法从集群外部访问。
第二步:创建 Service
现在,我们需要一个稳定的入口来访问这些 Pod。创建 Service 对象,类型为 ClusterIP(默认),它会在集群内部提供一个虚拟 IP,并将请求负载均衡到后端的 3 个 Pod。
创建文件 nginx-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80 # Service 暴露的端口
targetPort: 80 # 转发到 Pod 的容器端口
应用并查看:
kubectl apply -f nginx-service.yaml
kubectl get services
你会看到 nginx-service 的 CLUSTER-IP,例如 10.108.123.45。在集群内,你可以通过这个 IP 访问 Nginx。
验证服务发现与负载均衡:
在集群内运行一个临时 Pod,使用 curl 访问 Service:
kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -- sh
进入容器后:
curl http://nginx-service
你应该能看到 Nginx 的欢迎页。多次执行 curl,你会注意到响应来自不同的 Pod(可以通过查看 Nginx 的访问日志验证)。
第三步:暴露服务到集群外部
ClusterIP 类型的 Service 只能在集群内部访问。如果你希望从外部访问,需要将 Service 类型改为 NodePort 或 LoadBalancer。对于本地开发,NodePort 最简单。
修改 nginx-service.yaml,添加 type: NodePort:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080 # 可选,指定节点端口,不指定则随机分配 30000-32767
重新应用:
kubectl apply -f nginx-service.yaml
现在,你可以通过 http://<节点IP>:30080 访问 Nginx。如果使用 Minikube,可以运行 minikube service nginx-service 自动打开浏览器。
> 💡 注意:NodePort 的端口范围默认是 30000-32767,确保你指定的端口没有被占用。
进阶:滚动更新与回滚
Deployment 的强大之处在于声明式更新。假设你想升级 Nginx 版本到 1.26,只需修改镜像标签并重新 apply:
kubectl set image deployment/nginx-deployment nginx=nginx:1.26
或者编辑 YAML 文件后重新 apply。Kubernetes 会执行滚动更新,逐个替换 Pod,确保服务不中断。
查看更新状态:
kubectl rollout status deployment/nginx-deployment
如果更新出现问题,可以回滚到之前的版本:
kubectl rollout undo deployment/nginx-deployment
常见坑与最佳实践
1. 选择器与标签必须匹配
如果 selector 与 Pod 模板的标签不一致,Deployment 将无法管理这些 Pod,甚至可能创建出孤立的 Pod。
2. 不要直接创建 Pod
在大多数情况下,你应该通过 Deployment 等控制器来管理 Pod,而不是直接创建 Pod。直接创建的 Pod 不会被自动恢复,如果节点故障,Pod 将丢失。
3. 服务发现:使用 DNS
Kubernetes 集群内置 DNS 服务(如 CoreDNS),你可以通过 Service 名称直接访问,而不是依赖 IP。例如,在集群内,nginx-service 会被解析为 Service 的 ClusterIP。如果跨命名空间,可以使用 service.namespace 的格式。
4. 资源请求与限制
在生产环境中,务必为容器设置资源请求(requests)和限制(limits)。这有助于调度器做出更好的决策,并防止单个 Pod 耗尽节点资源。示例:
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
5. 使用命名空间隔离环境
对于不同环境(开发、测试、生产),建议使用命名空间(Namespace)进行隔离。创建命名空间:
kubectl create namespace dev
然后可以在 YAML 中指定 metadata.namespace。
总结
本文通过一个 Nginx 示例,带你实践了 Kubernetes 中最核心的三个概念:
- Pod:最小部署单元,运行容器。
- Deployment:管理 Pod 的副本和更新。
- Service:为 Pod 提供稳定的网络入口和负载均衡。
掌握这些,你已经迈入了 Kubernetes 的大门。接下来,你可以深入学习 ConfigMap、Secret、Ingress、PersistentVolume 等更高级的主题,逐步构建生产级的应用。
延伸阅读
希望这篇文章对你有所帮助,欢迎在评论区留言交流你的实践心得!