文章目录
容器
容器的状态
容器的状态 | —— |
---|---|
Waiting | 处于Waiting状态的容器仍在运行它完成启动所需要的操作:例如, 从某个容器镜像仓库拉取容器镜像,或者向容器应用Secret数据等等。 |
Running | Running状态表明容器正在执行状态并且没有问题发生。 如果配置了postStart回调,那么该回调已经执行且已完成。 |
Terminating | 处于Terminated状态的容器开始执行后,或者运行至正常结束或者因为某些原因失败。 |
CrashBackOff | 这一状态表明,对于一个给定的、处于崩溃循环、反复失效并重启的容器, 回退延迟机制目前正在生效。 |
pod实例
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
namespace: demo
labels:
app: nginx
spec:
containers:
imagePullSecrets: # Pull镜像时使用的仓库名称
- name: aliyun
- name: nginx
image: nginx:1.20.2# 镜像名称
imagePullPolicy: Always# 镜像拉取策略:IfNotPresent(默认)、Always、Never
env: # 环境变量 相当于 docker run -e xxx = xxx
- name: MYSQL_ROOT_PASSWORD
value: "123456"
- name: MYSQL_DATABASE
value: ssm
commend: ["/bin/sh","-c","Eouch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]
args:
- "--lower_case_table_names=1"
- "--character-set-server=utf8mb4"
- "--collation-server=utf8mb4_general_ci"
- "--default-authentication-plugin=mysql_native_password"
ports:
- name: nginx-port
containerPort: 80
portocol: TCP
resources: # 资源限制
limits:
cpu: 2
memory: 1500M
requests:
cpu: 1
memory: 1024M
volumeMounts: # 卷挂载
- name: localtime
mountPath: /etc/localtime
livenessProbe: # 探针
exeс:
command: ["/bin/ca]","/tmp/hello.txt"] #执行一个查看文件的命令
initialDelaySeconds: 1# 探测延迟,RUNNING 后指定 20 秒后才执行探测,默认为 0 秒。
periodSeconds: 5 # 执行探测的时间间隔(单位是秒),默认为 10 秒。
successThreshold: 1 # 成功阈值:连续几次成功才算成功。探测器在失败后,被视为成功的最小连续成功数。默认值是 1 ,存活和启动探针的这个值必须是 1。最小值是 1。
failureThreshold: 3 # 失败阈值:连续几次失败才算失败。当探测失败时,Kubernetes 的重试次数。 存活探测情况下的放弃就意味着重新启动容器。 就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。
timeoutSeconds: 5 # 探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
- name: arcgis
image: registry.cn-shanghai.aliyuncs.com/xudaxian/arcgis/v1.0
imagePullPolicy: Always
restartPolicy: Always
配置镜像仓库
# -n demo :表示该密钥将只在指定的名称空间 demo 中生效
# docker-registry aliyun :指定 Docker 镜像仓库的名称
# --docker-server:Docker 镜像仓库的地址
# --docker-username:Docker 镜像仓库的用户名
# --docker-password:Docker 镜像仓库的密码
kubectl create secret -n demo docker-registry aliyun \
--docker-server=registry.cn-shanghai.aliyuncs.com \
--docker-username=xudaxian \
--docker-password=123456
拉镜像默认值
- 如果镜像tag为具体版本号,默认策略是: IfNotPresent
- 如果镜像tag为: latest(最终版本),默认策略是always
特别说明
- 通过上面发现command已经可以完成启动命令和传递参数的功能,为什么这里还要提供一个args选项,用于传递参数呢?
- 这其实跟docker类似,kubernetes中的command, args两项其实是实现覆盖Dockerfile中ENTRYPoINT的功能。
- 如果command和args均没有写,那么用Dockerfile的配置。
- 如果command写了,但args没有写,那么Dockerfile默认的配置会被忽略,执行输入的command
- 如果command没写,但args写了,那么Dockerfile中配置的ENTRYPOINT的命令会被执行,使用当前args的参数
- 如果command和args都写了,那么Dockerfile的配置被忽略,执行command并追加上args参数
容器重启策略
Pod 的spec中包含一个restartPolicy字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。
- Always:只要容器终止就自动重启容器。
- OnFailure:只有在容器错误退出(退出状态非零)时才重新启动容器。
- Never:不会自动重启已终止的容器。
Init 容器
Init 容器与普通的容器非常像,除了如下两点:
- 它们总是运行到完成。
- 每个都必须在下一个启动之前成功完成。(初始化容器是串行化过程)
如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的 restartPolicy 值为 “Never”,Kubernetes 不会重新启动 Pod。并且 Pod 的 Init 容器失败, 则 Kubernetes 会将整个 Pod 状态设置为失败。
Init容器优势
- Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。 例如,没有必要仅为了在安装过程中使用类似sed、awk、python或dig这样的工具而去FROM一个镜像来生成一个新的镜像。
- 应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。
- 与同一 Pod 中的多个应用容器相比,Init 容器能以不同的文件系统视图运行。因此,Init 容器可以被赋予访问应用容器不能访问的Secret的权限。
- 由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。 一旦前置条件满足,Pod 内的所有的应用容器会并行启动。
- Init 容器可以安全地运行实用程序或自定义代码,而在其他方式下运行这些实用程序或自定义代码可能会降低应用容器镜像的安全性。 通过将不必要的工具分开,你可以限制应用容器镜像的被攻击范围。
Init容器实例
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
特殊说明
- 在 Pod 启动过程中,每个 Init 容器会在网络和数据卷初始化之后按顺序启动。 每个 Init 容器成功退出后才会启动下一个 Init 容器。 如果某容器因为容器运行时的原因无法启动,或以错误状态退出,kubelet 会根据 Pod 的 restartPolicy 策略进行重试。 然而,如果 Pod 的 restartPolicy 设置为 “Always”,Init 容器失败时会使用 restartPolicy 的 “OnFailure” 策略。
- 在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。 Init 容器的端口将不会在 Service 中进行聚集。正在初始化中的 Pod 处于 Pending 状态, 但会将状况 Initializing 设置为 true。
- 如果 Pod 重启,所有 Init 容器必须重新执行。
- 对 Init 容器规约的修改仅限于容器的 image 字段。 更改 Init 容器的 image 字段,等同于重启该 Pod。
- 因为 Init 容器可能会被重启、重试或者重新执行,所以 Init 容器的代码应该是幂等的。 特别地,基于 emptyDirs 写文件的代码,应该对输出文件可能已经存在做好准备。
- Init容器只有完成状态,所以不需要、也没有就绪检测、存活检测。
- 在 Pod 中的每个应用容器和 Init 容器的名称必须唯一, 与任何其它容器共享同一个名称,会在校验时抛出错误。
临时容器
- 临时容器是一种特殊的容器,该容器可以在现有的 Pod 中临时运行,以便完成我们发起的操作,比如故障排查。我们应该使用临时容器来检查服务,而不是用临时容器来构建应用程序。
- 临时容器和其他容器的不同之处在于,它们缺少对资源或执行的保证,并且永远不会自动重启,因此不适合用来构建应用程序。临时容器使用和常规容器相同的
ContainerSpec
来描述,但是许多字段是不兼容或者不允许的。
临时容器没有端口配置,因此像ports
、livenessProbe
、readinessProbe
这样的字段是没有的。 - Pod 的资源分配是不可变的,因此
resources
这样的配置临时容器也是没有的。 - 临时容器是使用
ephemeralcontainers
来进行创建的,而不是直接添加到pod.spec
中,所以是无法使用kubectl edit
来添加一个临时容器。 - 当由于容器奔溃或容器镜像不包含调试工具而导致
kubectl exec
无用的时候,临时容器对于交互式故障排查非常有用。
hook 钩子函数
- 钩子函数能够感知自身生命周期中的事件,
并在相应的时刻到来时运行用户指定的程序代码。
- Kubernetes 在主容器启动之后和停止之前提供了两个钩子函数:
- postStart:容器创建之后执行,如果失败会重启容器。
- preStop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作。
- 钩子处理器支持使用下面的三种方式定义动作:
- exec 命令:在容器内执行一次命令。
- tcpSocket:在当前容器尝试访问指定的 socket。
- httpGet:在当前容器中向某 url 发起 HTTP 请求(应用程序使用此种方式最多)
容器探针
- 探针是kubelet检测容器的方式,有四种探测方式:exec(执行命令成功)、grpc(调用成功)、http(调用成功)、tcp(调用成功)。
- 有三种探针:
- livenessProbe(存活探针):指示容器是否正在运行。
周期性执行。
如果存活态探测失败,则 kubelet 会杀死容器,
并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success。 - readinessProbe(就绪探针):指示容器是否准备好为请求提供服务。
周期性执行。
如果就绪态探测失败,端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址
。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。 - startupProbe(启动探针): 指示容器中的应用是否已经启动。
仅在容器启动的时候执行,不像其他两类指针一样一直执行。
如果提供了启动探针,则所有其他探针都会被禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器
,而容器依其 重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success。
- livenessProbe(存活探针):指示容器是否正在运行。
何时该使用启动探针
- 对于所包含的容器需要较长时间才能启动就绪的 Pod 而言,启动探针是有用的。
- 你不再需要配置一个较长的存活态探测时间间隔,只需要设置另一个独立的配置选定,对启动期间的容器执行探测,从而允许使用远远超出存活态时间间隔所允许的时长。
Pause(Infra) 容器
背景
- Pod 需要让里面的多个容器之间最高效的共享某些资源和数据。
- 因为容器之间原本是被 Linux Namespace 和 cgroups 隔开的,所以现在实际要解决的是
怎么去打破这个隔离,然后共享某些事情和某些信息。
- 所以说具体的解法分为两个部分:网络和存储。
Pause 容器就是为解决 Pod 中的网络问题而生的。
实现
- Infra container 是一个非常小的镜像,大概 700KB 左右,是一个 C 语言写的、永远处于“暂停”状态的容器。由于有了这样一个 Infra container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。
- 所以说一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP 地址、Mac 地址等等,跟网络相关的信息,其实全是一份,这一份都来自于 Pod 第一次创建的这个 Infra container。在 Pod 里面有一个 IP 地址,是这个 Pod 的 Network Namespace 对应的地址,也是这个 Infra container 的 IP 地址。
- 由于需要有一个相当于说中间的容器存在,所以整个 Pod 里面,必然是 Infra container 第一个启动。并且整个 Pod 的生命周期是等同于 Infra container 的生命周期的,与容器 A 和 B 是无关的。
Pause 容器的作用
kubernetes 中的 pause 容器主要为每个业务容器提供以下功能:
- 在 pod 中担任 Linux 命名空间共享的基础;
- 启用 pid 命名空间,开启 init 进程。
我们首先在节点上运行一个 pause 容器。
docker run -d --name pause -p 8880:80 --ipc=shareable jimmysong/pause-amd64:3.0
然后再运行一个 nginx 容器,nginx 将为localhost:2368创建一个代理。
$ cat <<EOF >> nginx.conferror_log stderr;events { worker_connections 1024; }http { access_log /dev/stdout combined; server { listen 80 default_server; server_name example.com www.example.com; location / { proxy_pass http://127.0.0.1:2368; } }}EOF$ docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause nginx
然后再为ghost创建一个应用容器,这是一款博客软件。
$ docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause ghost
现在访问 http://localhost:8880/ 就可以看到 ghost 博客的界面了。
解析
- pause 容器将内部的 80 端口映射到宿主机的 8880 端口,pause 容器在宿主机上设置好了网络 namespace 。
- nginx 容器加入到该网络 namespace 中,我们看到 nginx 容器启动的时候指定了–net=container:pause,ghost 容器同样加入到了该网络 namespace 中。
- 这样三个容器就共享了网络,互相之间就可以使用localhost直接通信,–ipc=contianer:pause --pid=container:pause就是三个容器处于同一个 namespace 中,init 进程为pause,这时我们进入到 ghost 容器中查看进程情况。
Pod Preset
- 有时候想要让一批容器在启动的时候就注入一些信息,比如 secret、volume、volume mount 和环境变量,而又不想一个一个的改这些 Pod 的 template,这时候就可以用到 Pod Preset 这个资源对象了。
- Pod Preset是用来在 Pod 被创建的时候向其中注入额外的运行时需求的 API 资源。
- 您可以使用label selector来指定为哪些 Pod 应用 Pod Preset。
- 使用 Pod Preset 使得 pod 模板的作者可以不必为每个 Pod 明确提供所有信息。这样一来,pod 模板的作者就不需要知道关于该服务的所有细节。
如何工作
- Kubernetes 提供了一个准入控制器(PodPreset),当其启用时,Pod Preset 会将应用创建请求传入到该控制器上。当有 Pod 创建请求发生时,系统将执行以下操作:
- 检索所有可用的PodPresets。
- 检查 PodPreset 标签选择器上的标签,看看其是否能够匹配正在创建的 Pod 上的标签。
- 尝试将由PodPreset定义的各种资源合并到正在创建的 Pod 中。
- 出现错误时,在该 Pod 上引发记录合并错误的事件,PodPreset不会注入任何资源到创建的 Pod 中。
- 注释刚生成的修改过的 Pod spec,以表明它已被 PodPreset 修改过。
- 每个 Pod 可以匹配零个或多个 Pod Prestet;并且每个PodPreset可以应用于零个或多个 Pod。PodPreset应用于一个或多个 Pod 时,Kubernetes 会修改 Pod Spec。对于Env、EnvFrom和VolumeMounts的更改,Kubernetes 修改 Pod 中所有容器的容器 spec;对于Volume的更改,Kubernetes 修改 Pod Spec。
- 注意:Pod Preset 可以在适当的时候修改 Pod spec 中的spec.containers字段。Pod Preset 中的资源定义将不会应用于initContainers字段。