3. Kubernetes核心概念:Pod、Service、Deployment、Namespace、ConfigMap、Secret
好,咱们今天聊聊Kubernetes里最核心的几个概念。说实话,我刚接触K8s那会儿,面对一堆术语确实有点懵。Pod、Service、Deployment……这些到底都是干嘛的?它们之间怎么配合?
别急,我带你一个个捋清楚。搞懂了这些,你就能理解K8s是怎么运作的了。
3.1 Pod:最小的调度单元
Pod是Kubernetes里你能创建和管理的最小计算单元。说白了,它就是一组容器的集合。
为什么要有Pod?
你想想看,有时候两个进程需要共享网络栈、共享存储卷。比如一个应用容器和一个日志收集容器,它们需要紧密协作。如果各自独立调度,网络配置、存储挂载都得重复搞,太麻烦了。
Pod就是解决这个问题的。Pod里的所有容器:
- 共享同一个网络命名空间(IP和端口)
- 共享同一个存储卷(Volume)
- 通过localhost互相通信
核心要点:Pod是K8s调度的原子单位。你永远不会直接调度一个容器,你调度的是一个Pod。
我在项目中遇到过一个问题:有个同事把两个需要共享日志目录的容器放在了不同Pod里,结果日志采集一直对不上。后来改成同一个Pod,用emptyDir卷共享目录,问题就解决了。
来看一个最简单的Pod定义:
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
labels:
app: my-app
spec:
containers:
- name: app-container
image: nginx:1.21
ports:
- containerPort: 80
- name: sidecar-container
image: busybox
command: ["sh", "-c", "tail -f /var/log/nginx/access.log"]
volumeMounts:
- name: log-volume
mountPath: /var/log/nginx
volumes:
- name: log-volume
emptyDir: {}
嗯,这里要注意:Pod里的容器启动顺序是不保证的。如果你的应用容器依赖sidecar先启动,那得用initContainers来解决。
3.2 Deployment:声明式应用管理
直接管理Pod?别傻了。Pod挂了怎么办?版本升级怎么搞?回滚怎么弄?
Deployment就是来干这个的。它帮你管理Pod的声明式更新和扩缩容。
Deployment的核心能力:
- 滚动更新:逐步替换旧版本Pod,保证服务不中断
- 回滚:出问题了?一键回到上一个版本
- 扩缩容:调整副本数,应对流量变化
- 自愈:Pod挂了自动重建
我个人习惯,生产环境里所有无状态应用都用Deployment来管理。有状态应用?那是StatefulSet的事,后面再聊。
来看一个Deployment示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
小技巧:Deployment的滚动更新策略可以配置。maxSurge控制最多可以多出几个Pod,maxUnavailable控制最多可以有几个Pod不可用。生产环境我一般设maxSurge=25%,maxUnavailable=25%。
3.3 Service:稳定的网络入口
Pod是会飘的。今天在Node A,明天可能就在Node B了。IP也会变。那客户端怎么访问?
Service就是给Pod提供一个稳定的访问入口。它通过Label Selector找到对应的Pod,然后把流量转发过去。
Service的类型:
| 类型 | 说明 | 使用场景 |
|---|---|---|
| ClusterIP | 集群内部IP,外部无法访问 | 内部服务间调用 |
| NodePort | 在每个Node上开一个端口 | 开发测试、简单对外暴露 |
| LoadBalancer | 云厂商提供负载均衡器 | 生产环境对外暴露 |
| ExternalName | 映射到外部DNS名称 | 访问集群外服务 |
我曾经踩过一个坑:Service的selector写错了label,结果流量全打到了错误的Pod上。排查了半天才发现是label不匹配。所以,命名规范很重要。
Service定义示例:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
3.4 Namespace:逻辑隔离
一个K8s集群里可能有多个团队、多个项目。怎么隔离?Namespace就是干这个的。
Namespace提供了一种将集群资源划分为逻辑组的方式。不同Namespace里的资源可以重名,互不干扰。
常见的Namespace使用场景:
- 按环境分:dev、test、prod
- 按团队分:team-a、team-b
- 按项目分:project-x、project-y
注意:Namespace只提供逻辑隔离,不提供安全隔离。如果你需要真正的安全隔离,得用NetworkPolicy或者干脆建多个集群。
查看Namespace:
kubectl get namespaces
# 输出示例:
# NAME STATUS AGE
# default Active 30d
# kube-system Active 30d
# kube-public Active 30d
# kube-node-lease Active 30d
创建Namespace:
apiVersion: v1
kind: Namespace
metadata:
name: dev-environment
3.5 ConfigMap & Secret:配置管理
应用配置不应该硬编码在镜像里。环境不同,配置不同。ConfigMap和Secret就是用来解耦配置和镜像的。
ConfigMap:存明文配置,比如配置文件、环境变量。
Secret:存敏感信息,比如密码、Token、证书。数据会做Base64编码,但注意这只是编码,不是加密。
我建议:敏感信息一定要用Secret,别图省事放ConfigMap里。我曾经见过有人把数据库密码明文写在ConfigMap里……嗯,后果很严重。
ConfigMap示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
app.properties: |
database.url=jdbc:mysql://db:3306/app
database.username=app_user
log.level=INFO
Secret示例:
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
password: cGFzc3dvcmQxMjM= # 这是"password123"的Base64编码
最佳实践:Secret的Base64编码只是障眼法。真正生产环境,建议用SealedSecret、External Secrets Operator或者Vault来管理敏感信息。
在Pod里使用ConfigMap和Secret:
apiVersion: v1
kind: Pod
metadata:
name: config-pod
spec:
containers:
- name: app
image: my-app:latest
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config
3.6 它们是怎么配合的?
好,现在我们把所有概念串起来。一个典型的应用部署流程是这样的:
- Deployment 定义应用的期望状态(副本数、镜像版本等)
- Deployment通过 ReplicaSet 管理 Pod 的创建和更新
- Pod 里运行容器,引用 ConfigMap 和 Secret 获取配置
- Service 为Pod提供稳定的网络入口
- 所有资源都在某个 Namespace 下逻辑隔离
你想想看,这个组合拳打下来,应用部署、配置管理、网络访问、环境隔离,全搞定了。
最后说一句:这些概念是K8s的基石。搞懂了它们,后面学什么Volume、Ingress、HPA都会轻松很多。别急,咱们一章一章来。