好的,我们来详细整理一下关于 Kubernetes Endpoints 资源的笔记。这份笔记将严格按照你的大纲,并力求概念通俗易懂、案例详尽、代码完整且包含输出解释。
Kubernetes Endpoints 资源深度解析
一、什么是 Endpoints 资源?
你可以把 Endpoints 资源理解为 Kubernetes 中的“服务地址簿”或“DNS 白名单”。它是一个核心的服务发现组件,充当了 Service (svc) 和 Pod 之间的桥梁。
具体来说:
Endpoints是一个 Kubernetes 资源对象:就像Pod,Service,Deployment一样,它也有自己的 YAML 定义和生命周期。- 核心功能是存储地址:它的主要数据是一个 IP 地址列表和对应的端口号列表。这些 IP 地址正是后端提供服务的
Pod的 IP。 - 由
Service管理和引用:每个Service资源都会自动关联一个同名的Endpoints资源(在大多数情况下)。Service收到请求后,会查看自己关联的Endpoints列表,然后将请求转发到列表中的一个或多个 IP 上。
工作流程简图:
外部请求 -> Service (例如:my-service) -> Endpoints (my-service) -> [Pod IP 1, Pod IP 2, ...]|v后端应用 Pods
通俗比喻:
Service就像是一家公司的总机电话。Endpoints就是这家公司的员工通讯录,上面有各个部门员工的分机号(Pod IP)。- 当客户(外部请求)拨打总机(Service),接线员(kube-proxy)会查看通讯录(Endpoints),然后把电话转接到具体的员工(Pod)那里。
二、核心案例:Endpoints 的自动与手动管理
Endpoints 的创建和维护主要有两种模式:自动模式和手动模式。
案例 1:自动创建 Endpoints (最常见)
当你创建一个 Service 并为其指定 标签选择器(selector) 时,Kubernetes 会自动为你完成以下工作:
- 创建一个与
Service同名的Endpoints资源。 - 持续监控集群中所有
Pod的标签。 - 当发现有
Pod的标签与Service的selector匹配时,就把这个Pod的 IP 地址和端口(从Pod的containerPort获取)添加到Endpoints列表中。 - 当
Pod被删除、更新或标签改变时,Kubernetes 会自动更新Endpoints列表。
详细实现过程:
第 1 步:创建一个 Deployment 来管理 Pod
我们先创建一些带有特定标签的 Pod。这里我们用 Deployment 来批量管理。
my-app-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:name: my-app-deployment
spec:replicas: 3 # 运行 3 个 Pod 副本selector:matchLabels:app: my-app # Deployment 管理带有此标签的 Podtemplate:metadata:labels:app: my-app # 给 Pod 打上标签env: productionspec:containers:- name: my-app-containerimage: nginx:alpine # 使用一个简单的 nginx 镜像作为示例ports:- containerPort: 80 # 容器在 80 端口提供服务
执行命令创建:
kubectl apply -f my-app-deployment.yaml
查看创建的 Pod:
kubectl get pods -o wide --show-labels
输出示例:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
my-app-deployment-7f98d7c6b4-2xqzk 1/1 Running 0 10s 10.244.1.10 minikube <none> <none> app=my-app,env=production,pod-template-hash=7f98d7c6b4
my-app-deployment-7f98d7c6b4-5bmd7 1/1 Running 0 10s 10.244.1.11 minikube <none> <none> app=my-app,env=production,pod-template-hash=7f98d7c6b4
my-app-deployment-7f98d7c6b4-8vgrx 1/1 Running 0 10s 10.244.1.12 minikube <none> <none> app=my-app,env=production,pod-template-hash=7f98d7c6b4
你可以看到 3 个 Pod 已经运行,并且都带有 app=my-app 标签,它们的 IP 地址分别是 10.244.1.10, 10.244.1.11, 10.244.1.12。
第 2 步:创建一个带有 selector 的 Service
现在,我们创建一个 Service,让它通过标签选择器找到上面的 Pod。
my-app-service.yaml:
apiVersion: v1
kind: Service
metadata:name: my-app-service
spec:selector:app: my-app # 关键!Service 将选择所有带有 app=my-app 标签的 Podports:- protocol: TCPport: 80 # Service 暴露的端口targetPort: 80 # 转发到 Pod 的端口type: ClusterIP # 这是默认类型,只在集群内部可访问
执行命令创建:
kubectl apply -f my-app-service.yaml
查看 Service:
kubectl get svc my-app-service
输出示例:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-app-service ClusterIP 10.100.235.147 <none> 80/TCP 5s
Service 被分配了一个集群内部的虚拟 IP 地址(CLUSTER-IP)。
第 3 步:验证自动创建的 Endpoints
此时,Kubernetes 已经自动为我们创建了一个名为 my-app-service 的 Endpoints 资源。
查看 Endpoints:
kubectl get endpoints my-app-service -o wide
输出示例:
NAME ENDPOINTS AGE
my-app-service 10.244.1.10:80,10.244.1.11:80,10.244.1.12:80 15s
奇迹发生了! Endpoints 列表中自动包含了我们刚才创建的 3 个 Pod 的 IP 地址和端口 80。
第 4 步:验证服务发现
我们可以在集群内的另一个 Pod 中(比如一个临时的 busybox Pod)来访问这个 Service,以验证它是否能正常工作。
kubectl run -it --rm --image=busybox:1.28 --restart=Never busybox-test sh
这会打开一个 busybox Pod 的 shell 终端。在终端中执行:
# 使用 wget 或 curl 访问 Service 的名字
wget -qO- my-app-service
输出示例(简化):
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</html>
你成功地通过 Service 名称 my-app-service 访问到了后端的 nginx Pod 提供的服务。这背后正是 Endpoints 在默默地工作。
案例 2:手动创建 Endpoints
当 Service 没有 selector 时,Kubernetes 不会自动创建和管理 Endpoints。这种情况通常用于:
- 服务集群外部的应用:例如,你有一个数据库运行在 Kubernetes 集群之外的物理机上,你想让集群内的
Pod也能通过Service的方式访问它。 - 精细控制服务 endpoints:在某些特殊场景下,你需要手动决定哪些 IP 被包含。
详细实现过程:
假设我们有一个数据库运行在集群外部,IP 地址是 192.168.1.100,端口是 5432。
第 1 步:创建一个不带 selector 的 Service
my-external-db-service.yaml:
apiVersion: v1
kind: Service
metadata:name: my-external-db-service
spec:ports:- protocol: TCPport: 5432 # Service 暴露的端口targetPort: 5432 # 转发到外部服务的端口# 注意:这里没有 selector 字段
执行命令创建:
kubectl apply -f my-external-db-service.yaml
查看 Service:
kubectl get svc my-external-db-service
输出示例:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-external-db-service ClusterIP 10.100.156.78 <none> 5432/TCP 10s
Service 创建成功,但此时如果你查看它的 Endpoints,会发现是空的。
kubectl get endpoints my-external-db-service
输出示例:
NAME ENDPOINTS AGE
my-external-db-service <none> 20s
第 2 步:手动创建对应的 Endpoints 资源
现在,我们需要手动创建一个 Endpoints 资源,将 Service 指向外部的数据库 IP。
my-external-db-endpoints.yaml:
apiVersion: v1
kind: Endpoints
metadata:name: my-external-db-service # 名称必须和 Service 完全一致
subsets:
- addresses:- ip: 192.168.1.100 # 外部数据库的 IP 地址ports:- port: 5432 # 外部数据库的端口
关键点:
metadata.name必须与Service的名字相同,这样Service才能找到并使用它。subsets.addresses.ip可以是任何可访问的 IP,不一定是集群内的PodIP。- 你可以在
addresses列表中添加多个 IP,实现对多个外部服务的负载均衡。
执行命令创建:
kubectl apply -f my-external-db-endpoints.yaml
第 3 步:验证手动创建的 Endpoints
再次查看 Endpoints:
kubectl get endpoints my-external-db-service
输出示例:
NAME ENDPOINTS AGE
my-external-db-service 192.168.1.100:5432 5s
现在 Endpoints 已经有值了!
第 4 步:验证服务访问
现在,集群内的 Pod 就可以通过 my-external-db-service:5432 这个地址来访问外部的数据库了。
# 再次使用 busybox 进行测试
kubectl run -it --rm --image=busybox:1.28 --restart=Never busybox-test sh
在 shell 中:
# 尝试 telnet 到 Service 地址和端口,验证连通性
telnet my-external-db-service 5432
如果外部数据库可达,你应该会看到类似下面的输出,表示连接成功:
Trying 10.100.156.78...
Connected to my-external-db-service.default.svc.cluster.local.
Escape character is '^]'.
(注意:10.100.156.78 是 Service 的 CLUSTER-IP)
三、Endpoints 的特殊行为与配置
默认行为:Service 如何筛选 Pod?
在案例 1 的自动模式下,Service 并不是把所有标签匹配的 Pod 都加入 Endpoints。它有一套默认的健康检查机制:
一个 Pod 必须满足以下所有条件,才会被 Service 选中并加入到 Endpoints 列表中:
- 标签匹配:
Pod的标签必须与Service的selector完全匹配。 - 状态为 "Running":
Pod的status.phase必须是Running。处于Pending,Succeeded,Failed状态的Pod会被忽略。 - 就绪状态为 "Ready":
Pod必须通过所有的就绪探针(Readiness Probe)检查。Pod的status.conditions中必须有一个类型为Ready,且status为True的条件。这表示Pod内部的应用已经启动并准备好接收请求。
示例:一个未就绪的 Pod
假设我们有一个 Pod,它的就绪探针检查一个需要 30 秒才能启动的服务。在启动后的前 30 秒内,kubectl describe pod <pod-name> 会显示:
Conditions:Type StatusReady False ...
在这 30 秒内,这个 Pod 虽然标签匹配且状态是 Running,但因为 Ready 状态是 False,所以它不会出现在 Endpoints 列表中。只有当 Ready 变为 True 后,kube-proxy 才会将其加入 Endpoints,并开始转发流量。
特殊配置:publishNotReadyAddresses
有时候,你可能希望 Service 也能将那些未就绪(Not Ready)的 Pod 暴露出来。例如:
- 进行健康检查或监控:你可能有一个监控系统,需要访问所有
Pod(包括未就绪的)来收集启动时的日志或指标。 - 服务网格(Service Mesh)场景:一些服务网格代理需要在
Pod完全就绪前就与之通信,以注入sidecar代理。
要实现这个需求,你可以在 Service 的定义中设置 publishNotReadyAddresses: true。
详细实现过程:
我们修改案例 1 中的 Service YAML。
my-app-service-include-not-ready.yaml:
apiVersion: v1
kind: Service
metadata:name: my-app-service-include-not-ready
spec:selector:app: my-appports:- protocol: TCPport: 80targetPort: 80publishNotReadyAddresses: true # 关键配置!
执行命令创建或更新:
kubectl apply -f my-app-service-include-not-ready.yaml
效果:
现在,假设我们有一个 Pod 因为就绪探针失败而处于 Not Ready 状态。
kubectl get pods
输出示例:
NAME READY STATUS RESTARTS AGE
my-app-deployment-7f98d7c6b4-2xqzk 1/1 Running 0 5m
my-app-deployment-7f98d7c6b4-5bmd7 0/1 Running 0 30s # 这个 Pod 未就绪
my-app-deployment-7f98d7c6b4-8vgrx 1/1 Running 0 5m
查看新 Service 对应的 Endpoints:
kubectl get endpoints my-app-service-include-not-ready
输出示例:
NAME ENDPOINTS AGE
my-app-service-include-not-ready 10.244.1.10:80,10.244.1.11:80,10.244.1.12:80 1m
(假设 10.244.1.11 是那个未就绪 Pod 的 IP)
你会发现,即使 Pod my-app-deployment-7f98d7c6b4-5bmd7 的 READY 状态是 0/1,它的 IP 地址依然被包含在了 Endpoints 列表中。
警告:启用 publishNotReadyAddresses 后,Service 会将流量转发到所有标签匹配且状态为 Running 的 Pod,无论其是否就绪。这意味着客户端可能会访问到一个无法正常提供服务的 Pod,导致请求失败。因此,在生产环境中使用此配置时,必须确保你的应用程序或客户端有足够的容错能力(如重试机制)。
总结
Endpoints是服务发现的基石,它存储了Service背后真实的Pod(或外部服务) 的网络地址。- 自动模式是常态:通过
Service的selector自动管理Endpoints,是 Kubernetes 服务发现的标准用法。 - 手动模式用于特殊场景:当需要将
Service指向集群外部服务或进行精细控制时,可以手动创建Endpoints。 - 默认行为是安全的:
Service只将流量转发到Running且Ready的Pod,确保了服务的可用性。 publishNotReadyAddresses是一个强大的逃逸阀:它允许你打破默认的安全检查,在特定场景下非常有用,但使用时需谨慎。
K8s存储笔记
存储分类
K8s中的存储主要用于解决容器数据持久化、配置管理、敏感信息存储等问题,常见的存储类型包括ConfigMap(配置存储)、Secret(敏感信息存储)、PV/PVC(持久化存储)、Downward API(Pod元数据注入)等。不同存储类型针对不同场景设计,满足多样化的存储需求。
ConfigMap
概念
ConfigMap是K8s专门用来存储非敏感配置信息的资源,比如应用的配置参数、环境变量配置、配置文件内容等。你可以把它理解成一个“配置字典”,里面装着键值对形式的配置数据。
- 它能被挂载到Pod里,要么当成环境变量用,要么当成配置文件用;
- 多个Pod可以共享同一个ConfigMap,实现配置的统一管理和复用;
- 注意:ConfigMap是在Pod第一次启动时注入的,后续如果ConfigMap改了,Pod不会自动更新,除非删掉Pod重新创建(不过用Volume挂载的方式支持热更新,后面会讲)。
创建
1. 通过YAML文件创建
新建一个my-config.yaml文件,内容如下:
apiVersion: v1
kind: ConfigMap
metadata:name: my-config # ConfigMap的名字namespace: default # 所属命名空间,默认default
data:app.conf: | # 配置文件形式的键值对server.port=8080log.level=infoenv: "prod" # 简单键值对max_connections: "1000"
执行创建命令:
kubectl apply -f my-config.yaml
输出:configmap/my-config created
2. 从文件创建
先创建一个本地配置文件app.properties,内容为:
db.url=jdbc:mysql://localhost:3306/mydb
db.username=root
然后执行命令创建ConfigMap:
kubectl create configmap my-config-from-file --from-file=app.properties
输出:configmap/my-config-from-file created
如果有多个文件,可多次指定--from-file,比如--from-file=file1 --from-file=file2。
3. 从字面量创建
直接通过命令行指定键值对创建:
kubectl create configmap my-config-from-literal --from-literal=key1=value1 --from-literal=key2=value2
输出:configmap/my-config-from-literal created
Pod如何使用ConfigMap?
1. 作为环境变量
创建Pod的YAML文件pod-env-config.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-env-config
spec:containers:- name: nginx-containerimage: nginx:alpineenv:- name: ENV_CONFIG # 容器内的环境变量名valueFrom:configMapKeyRef:name: my-config # 引用的ConfigMap名称key: env # 取ConfigMap里的env键- name: MAX_CONN # 另一个环境变量valueFrom:configMapKeyRef:name: my-configkey: max_connections
创建Pod:
kubectl apply -f pod-env-config.yaml
进入Pod查看环境变量:
kubectl exec -it pod-env-config -- sh
# 执行env命令查看
env | grep -E "ENV_CONFIG|MAX_CONN"
输出:
ENV_CONFIG=prod
MAX_CONN=1000
2. 作为启动参数
创建Pod的YAML文件pod-args-config.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-args-config
spec:containers:- name: busybox-containerimage: busybox:latestcommand: ["/bin/sh", "-c", "echo $(ENV_VAL) $(MAX_VAL)"] # 启动命令中使用环境变量env:- name: ENV_VALvalueFrom:configMapKeyRef:name: my-configkey: env- name: MAX_VALvalueFrom:configMapKeyRef:name: my-configkey: max_connections
创建Pod后查看日志:
kubectl apply -f pod-args-config.yaml
kubectl logs pod-args-config
输出:prod 1000
3. 作为配置文件(Volume挂载)
创建Pod的YAML文件pod-volume-config.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-volume-config
spec:containers:- name: nginx-containerimage: nginx:alpinevolumeMounts:- name: config-volume # 卷名,和下面volumes里的name对应mountPath: /etc/nginx/conf.d # 挂载到容器内的路径readOnly: true # 只读,ConfigMap挂载默认只读volumes:- name: config-volumeconfigMap:name: my-config # 引用的ConfigMap名称items: # 可选,指定要挂载的键,不指定则挂载所有键- key: app.conf # ConfigMap里的app.conf键path: app.conf # 挂载到容器内的文件名
创建Pod:
kubectl apply -f pod-volume-config.yaml
进入Pod查看挂载的文件:
kubectl exec -it pod-volume-config -- sh
cat /etc/nginx/conf.d/app.conf
输出:
server.port=8080
log.level=info
再查看目录下的文件:
ls -lh /etc/nginx/conf.d/
输出(类似):
total 0
lrwxrwxrwx 1 root root 13 Mar 20 10:00 app.conf -> ..data/app.conf
可以看到是符号链接,指向..data目录下的实际文件,这是为了支持热更新。
热更新
只有通过Volume挂载为文件的ConfigMap才支持热更新,环境变量方式不支持。下面用Nginx示例演示:
1. 准备Nginx配置文件和ConfigMap
创建nginx.conf文件:
server {listen 80;server_name localhost;location / {default_type text/plain;return 200 'Hello from original config!';}
}
创建ConfigMap:
kubectl create configmap nginx-config --from-file=nginx.conf
2. 创建Deployment挂载ConfigMap
创建nginx-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploy
spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:alpineports:- containerPort: 80volumeMounts:- name: nginx-config-volumemountPath: /etc/nginx/conf.d/default.conf # 直接挂载为Nginx默认配置文件subPath: nginx.conf # 指定挂载的文件,避免覆盖整个目录volumes:- name: nginx-config-volumeconfigMap:name: nginx-config
创建Deployment:
kubectl apply -f nginx-deployment.yaml
查看Pod:
kubectl get pods
输出(类似):
NAME READY STATUS RESTARTS AGE
nginx-deploy-7f9d6c8b7d-2xqzk 1/1 Running 0 10s
nginx-deploy-7f9d6c8b7d-5m7tl 1/1 Running 0 10s
nginx-deploy-7f9d6c8b7d-8k4zp 1/1 Running 0 10s
3. 测试初始配置
访问其中一个Pod:
kubectl exec -it nginx-deploy-7f9d6c8b7d-2xqzk -- curl localhost
输出:Hello from original config!
4. 修改ConfigMap
编辑ConfigMap:
kubectl edit configmap nginx-config
把nginx.conf内容改成:
server {listen 80;server_name localhost;location / {default_type text/plain;return 200 'Hello from updated config!';}
}
保存退出。
5. 查看ConfigMap更新后的Pod文件
等待10秒左右,进入Pod查看配置文件:
kubectl exec -it nginx-deploy-7f9d6c8b7d-2xqzk -- cat /etc/nginx/conf.d/default.conf
输出:
server {listen 80;server_name localhost;location / {default_type text/plain;return 200 'Hello from updated config!';}
}
说明ConfigMap已经热更新到Pod的文件里了。
6. 重启Nginx使配置生效
但此时访问Pod还是返回旧内容,因为Nginx没重新加载配置:
kubectl exec -it nginx-deploy-7f9d6c8b7d-2xqzk -- curl localhost
输出:Hello from original config!
需要进入Pod重启Nginx:
kubectl exec -it nginx-deploy-7f9d6c8b7d-2xqzk -- nginx -s reload
再访问:
kubectl exec -it nginx-deploy-7f9d6c8b7d-2xqzk -- curl localhost
输出:Hello from updated config!
7. 滚动重启Deployment(推荐)
手动重启每个Pod太麻烦,可通过修改Deployment的Annotation触发滚动重启:
kubectl patch deployment nginx-deploy -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"kubectl.kubernetes.io/restartedAt\":\"$(date +%Y-%m-%dT%H:%M:%S)\"}}}}}"
查看Pod,会发现旧Pod被删除,新Pod创建:
kubectl get pods
新Pod会自动加载最新的ConfigMap配置,无需手动重启Nginx。
不可改变
1. 配置ConfigMap为不可改变
创建ConfigMap时指定immutable: true,或编辑现有ConfigMap添加该字段:
apiVersion: v1
kind: ConfigMap
metadata:name: my-immutable-config
data:key1: value1
immutable: true # 设为不可改变
执行创建:
kubectl apply -f immutable-config.yaml
2. 特点
- 设为不可改变后,无法修改ConfigMap的内容,也不能删除后重建同名的ConfigMap(除非先删除这个不可改变的ConfigMap);
- 这个设置不允许回退,一旦设为
immutable: true,就不能改回false,只能删除重建; - 优点是能提高性能(K8s无需监控其变化),并防止误修改配置。
3. 验证不可改变
尝试编辑:
kubectl edit configmap my-immutable-config
修改key1的值后保存,会报错:
error: configmaps "my-immutable-config" is immutable
Secret
概念
Secret是K8s用来存储敏感信息的资源,比如密码、密钥、证书、Token等。它和ConfigMap结构类似,但会对数据进行Base64编码(注意:Base64不是加密,只是编码,仍需通过权限控制保证安全),且只有Pod内的容器能解密访问。
- 同样支持挂载为环境变量或文件;
- 可被多个Pod共享,统一管理敏感信息;
- 相比ConfigMap,Secret的访问权限更严格,默认不会被非授权用户查看。
类型
| 类型 | 用途 |
|---|---|
| Opaque | 默认类型,存储任意键值对的敏感信息(如密码、密钥) |
| kubernetes.io/dockerconfigjson | 存储Docker镜像仓库的认证信息(拉取私有镜像用) |
| kubernetes.io/service-account-token | 存储服务账号的Token,用于Pod访问K8s API Server |
| kubernetes.io/tls | 存储TLS证书和私钥(如HTTPS证书) |
| bootstrap.kubernetes.io/token | 用于K8s集群节点引导的Token |
Opaque类型Secret
Opaque是最常用的Secret类型,数据以Base64编码存储,下面详细讲创建和使用。
创建
1. 通过YAML文件创建
首先把要存储的内容转成Base64编码:
echo -n "my-password" | base64 # 输出:bXktcGFzc3dvcmQ=
echo -n "admin" | base64 # 输出:YWRtaW4=
创建my-secret.yaml文件:
apiVersion: v1
kind: Secret
metadata:name: my-secret
type: Opaque
data:username: YWRtaW4= # Base64编码后的adminpassword: bXktcGFzc3dvcmQ= # Base64编码后的my-password
执行创建:
kubectl apply -f my-secret.yaml
输出:secret/my-secret created
2. 从文件创建
创建db-pass.txt文件,内容为db-password-123,然后执行:
kubectl create secret generic my-secret-from-file --from-file=db-pass=db-pass.txt
输出:secret/my-secret-from-file created
这里db-pass是Secret里的键,db-pass.txt是本地文件。
3. 从字面量创建
直接指定键值对(K8s会自动转Base64):
kubectl create secret generic my-secret-from-literal --from-literal=redis-pass=redis-123456
输出:secret/my-secret-from-literal created
查看Secret
1. 查看Secret列表
kubectl get secrets
输出(类似):
NAME TYPE DATA AGE
my-secret Opaque 2 20s
my-secret-from-file Opaque 1 10s
my-secret-from-literal Opaque 1 5s
default-token-xxxx kubernetes.io/service-account-token 3 1d
2. 查看Secret详情
kubectl describe secret my-secret
输出(类似):
Name: my-secret
Namespace: default
Labels: <none>
Annotations: <none>Type: OpaqueData
====
password: 10 bytes
username: 5 bytes
注意:describe不会显示具体的Base64编码值,保证敏感信息不泄露。
3. 查看Secret的原始数据
如果需要查看具体内容,可执行:
kubectl get secret my-secret -o jsonpath='{.data}'
输出:{"password":"bXktcGFzc3dvcmQ=","username":"YWRtaW4="}
解码查看:
echo "bXktcGFzc3dvcmQ=" | base64 -d # 输出:my-password
echo "YWRtaW4=" | base64 -d # 输出:admin
使用
1. 作为环境变量
创建pod-secret-env.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-secret-env
spec:containers:- name: busybox-containerimage: busybox:latestcommand: ["/bin/sh", "-c", "env | grep DB_"]env:- name: DB_USERNAMEvalueFrom:secretKeyRef:name: my-secretkey: username- name: DB_PASSWORDvalueFrom:secretKeyRef:name: my-secretkey: password
创建Pod并查看日志:
kubectl apply -f pod-secret-env.yaml
kubectl logs pod-secret-env
输出:
DB_USERNAME=admin
DB_PASSWORD=my-password
2. 作为配置文件(Volume挂载)
创建pod-secret-volume.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-secret-volume
spec:containers:- name: nginx-containerimage: nginx:alpinevolumeMounts:- name: secret-volumemountPath: /etc/secrets # 挂载到容器内的目录readOnly: truevolumes:- name: secret-volumesecret:secretName: my-secret # 引用的Secret名称
创建Pod并查看挂载的文件:
kubectl apply -f pod-secret-volume.yaml
kubectl exec -it pod-secret-volume -- sh
ls /etc/secrets/
输出:password username
查看文件内容:
cat /etc/secrets/username # 输出:admin
cat /etc/secrets/password # 输出:my-password
注意:挂载的文件内容是Base64解码后的原始值,容器可直接使用。
3. 自定义文件权限
默认挂载的Secret文件权限是0644,可通过defaultMode修改:
volumes:
- name: secret-volumesecret:secretName: my-secretdefaultMode: 0400 # 只有所有者可读
创建Pod后查看权限:
kubectl exec -it pod-secret-volume -- ls -l /etc/secrets/
输出(类似):
-r-------- 1 root root 5 Mar 20 12:00 username
-r-------- 1 root root 10 Mar 20 12:00 password
4. 挂载指定的Key
如果只需要挂载Secret中的部分Key,可通过items指定:
volumes:
- name: secret-volumesecret:secretName: my-secretitems:- key: usernamepath: db/user # 挂载到/etc/secrets/db/usermode: 0600
创建Pod后查看:
kubectl exec -it pod-secret-volume -- ls /etc/secrets/db/
cat /etc/secrets/db/user # 输出:admin
注意:通过items指定Key挂载的方式不支持热更新,Secret修改后Pod不会自动更新,需重启Pod。
不可更改
和ConfigMap一样,Secret也可设置为不可改变:
apiVersion: v1
kind: Secret
metadata:name: my-immutable-secret
type: Opaque
data:key: dmFsdWU=
immutable: true
设为不可改变后,无法修改Secret内容,也不能删除重建同名Secret,除非先删除该Secret。
Downward API
概念
Downward API是K8s提供的一种机制,能把Pod自身的元数据(比如Pod名称、IP、标签、资源限制等)注入到容器内部。简单说,就是让容器能“知道”自己所在Pod的信息,无需通过K8s API Server查询(当然也可以通过API Server查,后面会讲)。
- 支持两种注入方式:环境变量和Volume挂载;
- 注入的是Pod启动时的元数据,部分数据(如Pod IP)会在Pod生命周期中保持不变,部分数据(如资源使用)不会实时更新。
使用案例
1. 作为环境变量注入
创建pod-downward-env.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-downward-envlabels:app: my-apptier: frontendannotations:author: "k8s-user"version: "1.0"
spec:containers:- name: busybox-containerimage: busybox:latestcommand: ["/bin/sh", "-c", "env | grep POD_"]resources:requests:cpu: "100m" # 0.1核memory: "128Mi"limits:cpu: "200m"memory: "256Mi"env:# Pod名称- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name# Pod命名空间- name: POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespace# Pod IP- name: POD_IPvalueFrom:fieldRef:fieldPath: status.podIP# Pod标签(JSON格式)- name: POD_LABELSvalueFrom:fieldRef:fieldPath: metadata.labels# CPU请求- name: CPU_REQUESTvalueFrom:resourceFieldRef:containerName: busybox-containerresource: requests.cpu# 内存限制- name: MEMORY_LIMITvalueFrom:resourceFieldRef:containerName: busybox-containerresource: limits.memory
创建Pod并查看日志:
kubectl apply -f pod-downward-env.yaml
kubectl logs pod-downward-env
输出(类似):
POD_NAME=pod-downward-env
POD_NAMESPACE=default
POD_IP=10.244.1.10
POD_LABELS={"app":"my-app","tier":"frontend"}
CPU_REQUEST=100m
MEMORY_LIMIT=268435456
2. 通过Volume挂载注入
创建pod-downward-volume.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-downward-volumelabels:app: my-apptier: frontendannotations:author: "k8s-user"version: "1.0"
spec:containers:- name: nginx-containerimage: nginx:alpinevolumeMounts:- name: downward-volumemountPath: /etc/pod-inforeadOnly: trueresources:requests:cpu: "100m"memory: "128Mi"limits:cpu: "200m"memory: "256Mi"volumes:- name: downward-volumedownwardAPI:items:# Pod名称- path: "pod_name"fieldRef:fieldPath: metadata.name# Pod注解- path: "pod_annotations"fieldRef:fieldPath: metadata.annotations# CPU限制- path: "cpu_limit"resourceFieldRef:containerName: nginx-containerresource: limits.cpu# 内存请求- path: "memory_request"resourceFieldRef:containerName: nginx-containerresource: requests.memory
创建Pod并查看挂载的文件:
kubectl apply -f pod-downward-volume.yaml
kubectl exec -it pod-downward-volume -- sh
ls /etc/pod-info/
输出:cpu_limit memory_request pod_annotations pod_name
查看文件内容:
cat /etc/pod-info/pod_name # 输出:pod-downward-volume
cat /etc/pod-info/cpu_limit # 输出:200m
cat /etc/pod-info/memory_request # 输出:134217728
cat /etc/pod-info/pod_annotations # 输出:{"author":"k8s-user","version":"1.0"}
通过API Server获取集群元数据
除了Downward API,也可以在Pod内通过K8s API Server获取集群的元数据(比如其他Pod、Service信息)。需要给Pod绑定Service Account并授权:
1. 创建Service Account和Role
创建sa-role.yaml:
apiVersion: v1
kind: ServiceAccount
metadata:name: pod-reader-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: pod-reader-role
rules:
- apiGroups: [""]resources: ["pods"]verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: pod-reader-binding
subjects:
- kind: ServiceAccountname: pod-reader-sa
roleRef:kind: Rolename: pod-reader-roleapiGroup: rbac.authorization.k8s.io
执行创建:
kubectl apply -f sa-role.yaml
2. 创建Pod使用Service Account
创建pod-api-server.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-api-server
spec:serviceAccountName: pod-reader-sa # 使用上面创建的SAcontainers:- name: curl-containerimage: curlimages/curl:latestcommand: ["/bin/sh", "-c", "while true; do curl -sS --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/api/v1/namespaces/default/pods; sleep 10; done"]
创建Pod并查看日志:
kubectl apply -f pod-api-server.yaml
kubectl logs -f pod-api-server
输出会包含default命名空间下所有Pod的详细信息(JSON格式)。
其中:
KUBERNETES_SERVICE_HOST和KUBERNETES_SERVICE_PORT是Pod内默认的环境变量,指向API Server;/var/run/secrets/kubernetes.io/serviceaccount/ca.crt是集群CA证书,用于验证API Server身份;/var/run/secrets/kubernetes.io/serviceaccount/token是Service Account的Token,用于认证(curl会自动使用?或需要在Header里指定:-H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)")。
补充:Pod访问Service的方式
- 同一命名空间:直接用Service名称访问(比如
curl my-service:80); - 不同命名空间:用全限定名访问(比如
curl my-service.my-namespace.svc.cluster.local:80)。