本文由 CNCF + Alibaba 云原生技术公开课 整理而来
背景
- 背景问题:
如果想通过一个镜像来启动容器,有时不仅仅只需要一个镜像,其实还有些问题需要解决:
1. 应用的可变配置写到镜像里面是无法接受的,如何加载可变配置?
2. 应用的敏感信息如何存储和使用?
3. 容器如何通过身份认证来访问集群自身,如访问 API Server ?
4. 容器在节点上运行之后,如何配置资源需求?
5. 节点上的容器由于共享内核,如何对它们进行安全管控?
6. 如何在容器启动之前进行前置条件校验?
- Kubernetes 解决:
在 Kubernetes 里面,这样来解决上面的问题:
可变配置:ConfigMap
敏感信息:Secret
身份认证:ServiceAccount
资源配置:Resources
安全管控:SecurityContext
前置校验:InitContainer
ConfigMap
ConfigMap
是 Kubernetes 的资源对象之一,主要管理容器运行所需的配置文件、环境变量、命令行参数等可变配置。用于解耦容器镜像和可变配置,从而保障工作负载 Pod
的可移植性。
ConfigMap
语法:
flannel ConfigMap
yaml 文件示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: flannel
namespace: kube-system
labels:
app: flannel
tier: node
data:
cni-conf.json: |
{
"name": "cbr0",
"type": "flannel",
"delegate": {
"isDefaultGateway": true
}
}
net-conf.json: |
{
"Network": "172.27.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
文件解析:
apiVersion: v1 表示 ConfigMap 的 API 版本是 v1
kind 表示 Kubernetes 资源类型是 ConfigMap
metadata 表示 ConfigMap 的元数据,元数据通常包含 name、namespace、labels、annotations 等
data 表示 ConfigMap 管理的配置文件,每个配置文件是 key:value,其中 key 是文件名,value 是文件内容
- 使用
ConfigMap
:
可以通过命令 kubectl apply -f <yaml file>
创建一个 ConfigMap
资源对象。ConfigMap
主要被 Pod
使用,一般用于挂载 Pod
用的配置文件、环境变量、命令行参数等。
Pod
使用 ConfigMap
:
1. 配置环境变量,通过 valueFrom 指定,其下 ConfigMapKeyRef.name 指定 ConfigMap 的名称,ConfigMapKeyRef.key 是 ConfigMap 中 data 下的 key
2. 配置命令行参数,在 Pod yaml 文件中,containers 下 command 中可以使用 ConfigMap 定义的环境变量
3. 挂载配置文件,ConfigMap 可以被 Pod 挂载,volumes.configMap.name 字段指定 ConfigMap 的名称
ConfigMap
注意点:
1. ConfigMap 文件大小。ConfigMap 文件没有大小限制,但是在 etcd 里面,数据的写入是有大小限制的,现在是限制在 1MB 以内;
2. Pod 使用 ConfigMap 时,必须是相同 Namespace 下的 ConfigMap,而且 ConfigMap 需要在 Pod 之前创建好;
3. 当使用 enfFrom 配置环境变量时,如果 ConfigMap 中有些 key 是无效的,则环境变量时不会注入容器,它会被忽略;
4. 只有通过 API Server 创建的 Pod 才能使用 ConfigMap,kubelet 通过 manifest 创建的静态 Pod 无法使用 ConfigMap。
Secret
Secret
是 Kubernetes 的资源对象之一,主要用来存储密码、token等一些敏感信息。其中,敏感信息采用 base64 编码保存。
Secret
语法:
mysecret Secret
yaml 文件示例:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
namespace: kube-system
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMwU2N2Rm
文件解析:
apiVersion: v1 表示 Secret 的 API 版本是 v1
kind 表示 Kubernetes 资源类型是 Secret
metadata 表示 Secret 的元数据,元数据通常包含 name、namespace 等
type 表示 Secret 的类型,默认类型是 Opaque
data 表示 Secret 管理的敏感信息,每条敏感信息是 key:value
Secret
常用的类型有 4 种:
Opaque,默认类型,即普通的 Secret 文件
service-account-token,用于 ServiceAccount 身份认证用的 Secret
dockerconfigjson,用于拉取私有仓库镜像
bootstrap.token,用于节点接入集群校验使用
- 使用
Secret
:
可以通过命令 kubectl apply -f <yaml file>
创建一个 Secret
资源对象。Secret
主要被 Pod
使用,一般是通过 Volume
形式挂载到容器里的指定目录,然后容器里的业务进程到目录下读取 Secret
。在需要访问私有镜像仓库时也是通过 Secret
来实现。
Pod
使用 Secret
是通过 Volume
形式挂载到容器里的指定目录,volumes.secert.secretName
字段指定 Secret
的名称
- 访问私有镜像库:
在 Secret
中配置私有镜像仓库的账号密码,创建好之后,将 Pod
的 template.spec
部分的 imagePullSecrets.name
指定为 Secret
的名称。
Secret
注意点:
1. Secret 文件大小。Secret 文件也没有大小限制,但是由于 etcd 的原因,数据限制在 1MB 以内;
2. Pod 使用 Secret 时,必须是相同 Namespace 下的 Secret,而且 Secret 需要在 Pod 之前创建好;
3. 当读取 Secret 时,不建议使用 list/watch,推荐使用 GET 方法获取需要的 Secret
ServiceAccount
ServiceAccount
用于解决 Pod
在集群里面的身份认证问题,身份认证信息是存在于 Secret
中。
ServiceAccount
语法:
default ServiceAccount
yaml 文件示例:
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
resourceVersion: "419220122"
selflink: /api/v1/namespaces/default/serviceaccounts/default
uid: 320061cb-85da-11e9-909b-06b1d3b258c8
secrets:
- name: default-token-6fn7q
Secret
由 ServiceAccount Controller
自动创建:
apiVersion: v1
data:
ca.crt: LS0tLS1...
namespace: ZGVmYXVsdA==
token: ZXlKaGjHY2..
kind: Secret
metadata:
annotations:
kubernetes.io/service-account.name: default
kubernetes.io/service-account.uid: 320061cb-85da-11e9-909b-06b1d3b258c8
creationTimestamp: 2019-06-03T08:32:59Z
name: default-token-6fn7q
namespace: default
resourceVersion: "419220122"
selflink: /api/v1/namespaces/default/serviceaccounts/default
uid: 320061cb-85da-11e9-909b-06b1d3b258c8
type: Opaque
文件解析:
apiVersion: v1 表示 ServiceAccount 的 API 版本是 v1
kind 表示 Kubernetes 资源类型是 ServiceAccount
metadata 表示 ServiceAccount 的元数据,元数据通常包含 name、namespace 等
data 表示 ServiceAccount 使用的 Secret
ServiceAccount
对应的 Secret
中,data
有两块数据,分别是 ca.crt
和 token
。ca.crt
用于对服务端的校验,token
用于 Pod
的身份认证,它们都是用 base64 编码过的。然后可以看到 metadata
即元信息里,其实是有关联 ServiceAccount
信息的(该 Secret
被哪个 ServiceAccount
使用)。
Pod
访问所属集群的实现原理:
1. Pod 创建时 Admission Controller 会根据指定的 ServiceAccount(默认为 default)把对应的 Secret 挂载到容器中指定的目录(/var/run/secrets/kubernetes.io/serviceaccount)下,这由 Kubernetes 自动实现
2. 当 Pod 访问集群时,可以默认利用 Secret 其中的 token 文件来认证 Pod 的身份,ca.crt 用于校验服务端
3. 默认 token 的认证信息为:
- Group: system:serviceaccounts:[namespace-name]
- User: system:serviceaccounts:[namespace-name][pod-name]
假如 RBAC
没有配置的话,默认的 Pod
具有资源的 GET 权限,就是可以从所属的 Kubernetes
集群里 GET 数据。如果想要更多的权限,那么就需要自行配置 RBAC
。
Resource
- 容器资源配置:
Resource
是对容器的资源配额管理。目前 Kubernetes 内部支持的类型有三种:CPU、内存,以及临时存储。资源配置有 request
和 limit
两种类型,分别指定了资源分配的下限和上限。CPU、内存以及临时存储都是在 containers
下的 resources
字段里进行一个声明。
示例:
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: wp
image: wordpress
resources:
requests:
memory: "64Mi"
cpu: "250m"
ephemeral-storage: "2Gi"
limits:
memory: "128Mi"
cpu: "500m"
ephemeral-storage: "4Gi"
Pod
服务质量QoS
配置:
根据 容器对 CPU 、内存资源的需求,可以对 Pod
的服务质量 QoS
进行一个分类,分别是 Guaranteed
、Burstable
和 BestEffort
。
Guaranteed:Pod 里面的每个容器都必须有 CPU 、内存的 request 和 limit 的声明,而且 request 和 limit 必须一致
Burstable:Pod 里面至少有一个容器存在 CPU 、内存的一个 request
BestEffort:只要不是 Guaranteed 和 Burstable,就属于 BestEffort
服务质量是 Guaranteed
的 Pod
优先级最高,Burstable
次之,BestEffort
最低。如果节点上资源紧缺,kubelet
会根据服务质量来依次驱逐低优先级的 Pod
。
SecurityContext
SecurityContext
主要是用于限制容器的行为,保证系统和其它容器的安全。SecurityContext
主要分为 3 个级别:
容器级别,仅对容器生效
Pod 级别,对 Pod 里所有容器生效
集群级别,对集群内所有 Pod 生效
权限和访问控制设置项,目前一共有 7 项:
1. 通过用户 ID 和组 ID 来控制文件访问权限
2. SElinux,通过策略配置来控制用户或进程对文件的访问控制
3. 特权容器
4. Capablilities,它是给特定进程配置一个 privileged 能力
5. AppArmor,通过一些配置文件来控制可执行文件的访问权限,如端口的读写权限
6. 对系统调用的控制
7. 对子进程能否获取比父进程更多权限的限制
这些权限和访问控制设置项最终都是通过内核来达到目的。示例:
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: sec-ctx-demo
image: busybox
command: ["sh", "-c", "sleep 1h"]
volumeMounts:
- name: sec-ctx-vol
mountPath: /data/demo
securityContext:
allowPrivilegeEacalation: false
volumes:
- name: sec-ctx-vol
emptyDir: {
}
InitContainer
InitContainer
用于完成主业务容器所需要的一些辅助工作,将辅助功能从主业务容器解耦。
InitContainer
和 Container
的区别:
1. InitContainer 会比普通 Container 先启动,并且直到所有的 InitContainer 执行成功后,普通 Container 才会被启动
2. InitContainer 之间是按定义的次序去启动执行的,执行成功一个之后再执行第二个,而普通 Container 是并发启动的
3. InitContainer 执行成功后就结束退出,而普通 Container 可能会一直在执行