k8s 深入篇———— pod 深入实战[七]
前言
深入一下pod 实战。
正文
在 Kubernetes 中,有几种特殊的 Volume,它们存在的意义不是为了存放容器里的数据,也不是用来进行容器和宿主机之间的数据交换。
这些特殊 Volume 的作用,是为容器提供预先定义好的数据。
所以,从容器的角度来看,这些 Volume 里的信息就是仿佛是被Kubernetes“投射”(Project)进入容器当中的。这正是 Projected Volume 的含义。
到目前为止,Kubernetes 支持的 Projected Volume 一共有四种:
- Secret;
- ConfigMap;
- Downward API;
- ServiceAccountToken。
在今天这篇文章中,我首先和你分享的是 Secret。它的作用,是帮你把 Pod 想要访问的加密数据,存放到 Etcd 中。
然后,你就可以通过在 Pod 的容器里挂载 Volume 的方式,访问到这些 Secret 里保存的信息了。
Secret 最典型的使用场景,莫过于存放数据库的 Credential 信息,比如下面这个例子:
例如:
apiVersion: v1
kind: Pod
metadata:
name: test-projected-volume
spec:
containers:
- name: test-secret-volume
image: busybox
args:
- sleep
- "86400"
volumeMounts:
- name: mysql-cred
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: mysql-cred
projected:
sources:
- secret:
name: user
- secret:
name: pass
这个启动会报错,因为现在还没有创建secret/user资源和 secret/pass 资源。
那么创建一下:
kubectl create secret generic user --from-file=./username.txt
kubectl create secret generic pass --from-file=./password.txt
里面可以随便写一些东西。


那么我们看下挂载的是什么吧。

可以看到,将映射后的内容到了里面。
来看下docker的情况吧。

其实就是创建了一个volume,然后将两个文件放入进去了,然后容器再进行映射。
在这个 Pod 中,我定义了一个简单的容器。它声明挂载的 Volume,并不是常见的emptyDir 或者 hostPath 类型,
而是 projected 类型。而这个 Volume 的数据来源(sources),则是名为 user 和 pass 的 Secret 对象,分别对应的是数据库的用户名和密码。
还可以通过yaml的方式声明,一般都是这种方式哈:

可以看到,通过编写 YAML 文件创建出来的 Secret 对象只有一个。但它的 data 字段,却以 Key-Value 的格式保存了两份 Secret 数据。其中,“user”就是第一份数据的Key,“pass”是第二份数据的 Key。
需要注意的是,Secret 对象要求这些数据必须是经过 Base64 转码的,以免出现明文密码
的安全隐患。这个转码操作也很简单,比如:

这里需要注意的是,像这样创建的 Secret 对象,它里面的内容仅仅是经过了转码,而并没有被加密。
在真正的生产环境中,你需要在 Kubernetes 中开启 Secret 的加密插件,增强数据的安全性。
从返回结果中,我们可以看到,保存在 Etcd 里的用户名和密码信息,已经以文件的形式出
现在了容器的 Volume 目录里。而这个文件的名字,就是 kubectl create secret 指定的
Key,或者说是 Secret 对象的 data 字段指定的 Key。
更重要的是,像这样通过挂载方式进入到容器里的 Secret,一旦其对应的 Etcd 里的数据被
更新,这些 Volume 里的文件内容,同样也会被更新。其实,这是 kubelet 组件在定时维
护这些 Volume。
需要注意的是,这个更新可能会有一定的延时。所以在编写应用程序时,在发起数据库连接的代码处写好重试和超时的逻辑,绝对是个好习惯。
与 Secret 类似的是 ConfigMap,它与 Secret 的区别在于,ConfigMap 保存的是不需要加密的、应用所需的配置信息。
而 ConfigMap 的用法几乎与 Secret 完全相同:你可以使用 kubectl create configmap 从文件或者目录创建 ConfigMap,也可以直接编写ConfigMap 对象的 YAML 文件。


备注:kubectl get -o yaml 这样的参数,会将指定的 Pod API 对象以YAML 的方式展示出来。
接下来是 Downward API,它的作用是:让 Pod 里的容器能够直接获取到这个 Pod API对象本身的信息。
下面是downward api:
apiVersion: v1
kind: Pod
metadata:
name: test-downwardapi-volume
labels:
zone: us-est-coast
cluster: test-cluster1
rack: rack-22
spec:
containers:
- name: client-container
image: busybox
imagePullPolicy: IfNotPresent
command: ["sh", "-c"]
args:
- while true; do
if [[ -e /etc/podinfo/labels ]]; then
echo -en '\n\n'; cat /etc/podinfo/labels; fi;
sleep 500;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
readOnly: false
volumes:
- name: podinfo
projected:
sources:
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
在这个 Pod 的 YAML 文件中,我定义了一个简单的容器,声明了一个 projected 类型的
Volume。只不过这次 Volume 的数据来源,变成了 Downward API。而这个 Downward
API Volume,则声明了要暴露 Pod 的 metadata.labels 信息给容器。
通过这样的声明方式,当前 Pod 的 Labels 字段的值,就会被 Kubernetes 自动挂载成为
容器里的 /etc/podinfo/labels 文件。
而这个容器的启动命令,则是不断打印出 /etc/podinfo/labels 里的内容。所以,当我创建
了这个 Pod 之后,就可以通过 kubectl logs 指令,查看到这些 Labels 字段被打印出来,
如下所示:

其实就是把label的信息挂载到了labels文件下面。

具体可查看官方文档。
其实,Secret、ConfigMap,以及 Downward API 这三种 Projected Volume 定义的信息,大多还可以通过环境变量的方式出现在容器里。
但是,通过环境变量获取这些信息的方式,不具备自动更新的能力。所以,一般情况下,我都建议你使用 Volume 文件的方式获取这些信息。
相信你一定有过这样的想法:我现在有了一个 Pod,我能不能在这个 Pod 里安装一个
Kubernetes 的 Client,这样就可以从容器里直接访问并且操作这个 Kubernetes 的 API 了
呢?
这当然是可以的。
不过,你首先要解决 API Server 的授权问题。
Service Account 对象的作用,就是 Kubernetes 系统内置的一种“服务账户”,它是
Kubernetes 进行权限分配的对象。比如,Service Account A,可以只被允许对
Kubernetes API 进行 GET 操作,而 Service Account B,则可以有 Kubernetes API 的所
有操作的权限。
像这样的 Service Account 的授权信息和文件,实际上保存在它所绑定的一个特殊的
Secret 对象里的。这个特殊的 Secret 对象,就叫作ServiceAccountToken。任何运行在
Kubernetes 集群上的应用,都必须使用这个 ServiceAccountToken 里保存的授权信息,
也就是 Token,才可以合法地访问 API Server。
所以说,Kubernetes 项目的 Projected Volume 其实只有三种,因为第四种
ServiceAccountToken,只是一种特殊的 Secret 而已。
另外,为了方便使用,Kubernetes 已经为你提供了一个的默认“服务账户”(default
Service Account)。并且,任何一个运行在 Kubernetes 里的 Pod,都可以直接使用这个
默认的 Service Account,而无需显示地声明挂载它。
这是如何做到的呢?
当然还是靠 Projected Volume 机制。
如果你查看一下任意一个运行在 Kubernetes 集群里的 Pod,就会发现,每一个 Pod,都
已经自动声明一个类型是 Secret、名为 default-token-xxxx 的 Volume,然后 自动挂载
在每个容器的一个固定目录上。比如:

默认挂载了这两个。
这个 Secret 类型的 Volume,正是默认 Service Account 对应的
ServiceAccountToken。所以说,Kubernetes 其实在每个 Pod 创建的时候,自动在它的
spec.volumes 部分添加上了默认 ServiceAccountToken 的定义,然后自动给每个容器加
上了对应的 volumeMounts 字段。这个过程对于用户来说是完全透明的。
这样,一旦 Pod 创建完成,容器里的应用就可以直接从这个默认 ServiceAccountToken
的挂载目录里访问到授权信息和文件。这个容器内的路径在 Kubernetes 里是固定的,
即:/var/run/secrets/kubernetes.io/serviceaccount ,而这个 Secret 类型的 Volume
里面的内容如下所示:

所以,你的应用程序只要直接加载这些授权文件,就可以访问并操作 Kubernetes API 了。
而且,如果你使用的是 Kubernetes 官方的 Client 包(k8s.io/client-go)的话,它还
可以自动加载这个目录下的文件,你不需要做任何配置或者编码操作。
这种把 Kubernetes 客户端以容器的方式运行在集群里,然后使用 default Service
Account 自动授权的方式,被称作“InClusterConfig”。
当然,考虑到自动挂载默认 ServiceAccountToken 的潜在风险,Kubernetes 允许你设置
默认不为 Pod 里的容器自动挂载这个 Volume。
除了这个默认的 Service Account 外,我们很多时候还需要创建一些我们自己定义的
Service Account,来对应不同的权限设置。这样,我们的 Pod 里的容器就可以通过挂载
这些 Service Account 对应的 ServiceAccountToken,来使用这些自定义的授权信息。
在 Kubernetes 中,你可以为 Pod 里的容器定义一个健康检查“探针”(Probe)。这样,kubelet 就会根据这个 Probe 的返回值决定这个容器的状态,而不是直接以容器进行是否运行(来自 Docker 返回的信息)作为依据。
这种机制,是生产环境中保证应用健康存活的重要手段。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: test-liveness-exec
spec:
containers:
- name: liveness
image: daocloud.io/library/nginx
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 50
livenessProbe: #探针,健康检查
exec: #类型
command: #命令
- cat
- /tmp/healthy
initialDelaySeconds: 5 #健康检查,在容器启动 5 s 后开始执行
periodSeconds: 5 #每 5 s 执行一次
然后这个监控检查不通过之后,那么就会出现一个东西,叫做pod的恢复机制,也叫restartPolicy。
。它是 Pod 的 Spec 部
分的一个标准字段(pod.spec.restartPolicy),默认值是 Always,即:任何时候这个容
器发生了异常,它一定会被重新创建。
但一定要强调的是,Pod 的恢复过程,永远都是发生在当前节点上,而不会跑到别的节点
上去。事实上,一旦一个 Pod 与一个节点(Node)绑定,除非这个绑定发生了变化
(pod.spec.node 字段被修改),否则它永远都不会离开这个节点。这也就意味着,如果
这个宿主机宕机了,这个 Pod 也不会主动迁移到其他节点上去。
而如果你想让 Pod 出现在其他的可用节点上,就必须使用 Deployment 这样的“控制器”来管理 Pod,哪怕你只需要一个 Pod 副本。
而作为用户,你还可以通过设置 restartPolicy,改变 Pod 的恢复策略。除了 Always,它
还有 OnFailure 和 Never 两种情况:
Always:在任何情况下,只要容器不在运行状态,就自动重启容器;
OnFailure: 只在容器 异常时才自动重启容器;
Never: 从来不重启容器。
- 只要 Pod 的 restartPolicy 指定的策略允许重启异常的容器(比如:Always),那么
这个 Pod 就会保持 Running 状态,并进行容器重启。否则,Pod 就会进入 Failed 状
态 。 - 对于包含多个容器的 Pod,只有它里面所有的容器都进入异常状态后,Pod 才会进入
Failed 状态。在此之前,Pod 都是 Running 状态。此时,Pod 的 READY 字段会显示
正常容器的个数,
所以,假如一个 Pod 里只有一个容器,然后这个容器异常退出了。那么,只有当
restartPolicy=Never 时,这个 Pod 才会进入 Failed 状态。而其他情况下,由于
Kubernetes 都可以重启这个容器,所以 Pod 的状态保持 Running 不变。
而如果这个 Pod 有多个容器,仅有一个容器异常退出,它就始终保持 Running 状态,哪
怕即使 restartPolicy=Never。只有当所有容器也异常退出之后,这个 Pod 才会进入
Failed 状态
在 Kubernetes 的 Pod 中,还有一个叫 readinessProbe 的字段。虽然它的用法与
livenessProbe 类似,但作用却大不一样。readinessProbe 检查结果的成功与否,决定的
这个 Pod 是不是能被通过 Service 的方式访问到,而并不影响 Pod 的生命周期。
下面是Pod Preset例子:
kind: PodPreset
apiVersion: settings.k8s.io/v1alpha1
metadata:
name: allow-database
namespace: myns
spec:
selector:
matchLabels:
role: frontend
env:
- name: DB_PORT
value: "6379"
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
然后编写pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
spec:
containers:
- name: website
image: ecorp/website
ports:
- containerPort: 80
然后最终生成的yaml为:
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
annotations:
podpreset.admission.kubernetes.io/allow-database: "resource version"
spec:
containers:
- name: website
image: ecorp/website
volumeMounts:
- mountPath: /cache
name: cache-volume
ports:
- containerPort: 80
env:
- name: DB_PORT
value: "6379"
volumes:
- name: cache-volume
emptyDir: {}
比如,我们现在提交的是一个 nginx-deployment,那么这个 Deployment 对象本身是永
远不会被 PodPreset 改变的,被修改的只是这个 Deployment 创建出来的所有 Pod。这
一点请务必区分清楚。
这里有一个问题:如果你定义了同时作用于一个 Pod 对象的多个 PodPreset,会发生什么
呢?
实际上,Kubernetes 项目会帮你合并(Merge)这两个 PodPreset 要做的修改。而如果
它们要做的修改有冲突的话,这些冲突字段就不会被修改。
PodPreset 这个被弃用了。
结
下一节,编排。
k8s 深入篇———— pod 深入实战[七]的更多相关文章
- C# Redis实战(七)
七.修改数据 在上一篇 C# Redis实战(六)中介绍了如何查询Redis中数据,本篇将介绍如何修改Redis中相关数据.大家都知道Redis是key-value型存储系统,所以应该可以修改key, ...
- Python爬虫实战七之计算大学本学期绩点
大家好,本次为大家带来的项目是计算大学本学期绩点.首先说明的是,博主来自山东大学,有属于个人的学生成绩管理系统,需要学号密码才可以登录,不过可能广大读者没有这个学号密码,不能实际进行操作,所以最主要的 ...
- k8s学习 - 概念 - Pod
k8s学习 - 概念 - Pod 这篇继续看概念,主要是 Pod 这个概念,这个概念非常重要,是 k8s 集群的最小单位. 怎么才算是理解好 pod 了呢,基本上把 pod 的所有 describe ...
- docker+k8s基础篇五
Docker+K8s基础篇(五) service资源介绍 A:service资源的工作特性 service的使用 A:service字段介绍 B:ClusterIP的简单使用 C:NodePort的简 ...
- docker+k8s基础篇四
Docker+K8s基础篇(四) pod控制器 A:pod控制器类型 ReplicaSet控制器 A:ReplicaSet控制器介绍 B:ReplicaSet控制器的使用 Deployment控制器 ...
- docker+k8s基础篇三
Docker+K8s基础篇(三) kubernetes上的资源 A:k8s上的常用资源 Pod的配置清单 A:Pod上的清单定义 B:Pod创建资源的方法 C:spec下其它字段的介绍 Pod的生命周 ...
- docker+k8s基础篇二
Docker+K8s基础篇(二) docker的资源控制 A:docker的资源限制 Kubernetes的基础篇 A:DevOps的介绍 B:Kubernetes的架构概述 C:Kubernetes ...
- docker+k8s基础篇一
Docker+K8s基础篇(一) docker的介绍 A:为什么是docker B:k8s介绍 docker的使用 A:docker的安装 B:docker的常用命令 C:docker容器的启动和操作 ...
- SpringSecurity权限管理系统实战—七、处理一些问题
目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...
- Longhorn+K8S+KubeSphere云端数据管理,实战 Sentry PostgreSQL 数据卷增量快照/备份与还原
云端实验环境配置 VKE K8S Cluster Vultr 托管集群 https://vultr.com/ 3 个 worker 节点,kubectl get nodes. k8s-paas-71a ...
随机推荐
- 7、mysql的缓存优化
概述 开启Mysql的查询缓存,当执行完全相同的SQL语句的时候,服务器就会直接从缓存中读取结果,当数据被修改,之前的缓存会失效,修改比较频繁的表不适合做查询缓存. 操作流程 客户端发送一条查询给服务 ...
- 5、zookeeper应用场景-配置中心原理
配置中心 使用 zookeeper的特性watcher监听器 工作中有这样的一个场景:数据库用户名和密码信息放在一个配置文件中,应用读取该配置文件,配置文件信息放入缓存 若数据库的用户名和密码改变时候 ...
- Apollo获取配置异常:Load config failed, will retry in 1 SECONDS
一.现象 apollo开启秘钥,服务获取配置参数需要启动参数中添加:jvm参数-Dapollo.accesskey.secret=XXX.日志如下: 二.解决方案 应用服务器时间异常,重置应用服务器时 ...
- xxl-job的基本使用
xxl-job的基本使用 xxl-job是分布式的调度平台调度执行器执行任务,使用的是DB锁(for update)来保证集群分布式调用的一致性,学习简单,操作容易,成本不高. 准备阶段 服务端配置 ...
- .NET Aspire Preview 4 发布!
.NET Aspire是一个有态度的云原生应用开发框架,旨在改善生成.NET云原生应用的体验,并提供一组强大的工具来帮助你生成和运行分布式应用.它允许开发者快速创建属于自己的云原生应用,或改造已有的项 ...
- 阿里云centos Linux系统挂载数据盘操作流程
这里假设的是已经在阿里云管理后台购买完了新增的硬盘.我们只讲在Linux系统里操作挂载硬盘过程. 一. 环境: 操作系统: Centos 7 二.查看云服务下所有硬盘 使用root账号登录服务器,执行 ...
- Performance Improvements in .NET 8 -- Native AOT & VM & GC & Mono【翻译】
原生 AOT 原生 AOT 在 .NET 7 中发布.它使 .NET 程序在构建时被编译成一个完全由原生代码组成的自包含可执行文件或库:在执行时不需要 JIT 来编译任何东西,实际上,编译的程序中没有 ...
- 视野修炼第71期 | Rspack 家族新成员 Rsdoctor
欢迎来到第 71 期的[视野修炼 - 技术周刊],下面是本期的精选内容简 强烈推荐 Rspack 新成员:Rsdoctor Bun Shell DCloud:App跨平台框架对比2023版 开源工具& ...
- 设计模式之二:Builder模式
设计模式之二:Builder模式 目录介绍 0.关于Builder模式案例下载 1.Builder模式介绍 2.Builder模式使用场景 3.Builder模式简单案例 3.1 Builder模式U ...
- 记录--为什么 export 导出一个字面量会报错,而使用 export default 就不会报错?
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 核心 其实总的来说就是 export 导出的是变量的句柄(或者说符号绑定.近似于 C 语言里面的指针,C++里面的变量别名),而 expo ...