5-1 创建Nginx Pod
Kubectl 创建 Nginx Pod
编写
nginx.yaml
文件apiVersion: v1
kind: Pod
metadata:
name: my-nginx
labels:
name: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
创建Pod
kubectl create -f nginx.yaml
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
my-nginx 1/1 Running 0 54s
[root@master ~]#
[root@master ~]# kubectl describe pod my-nginx
登录到kubernetes-dashboard就能看到创建的Nginx Pod的状态
5-2 Pod实现原理
什么是 Pod?
Pod的共享上下文包括一组Linux命名空间、控制组(cgroup)和可能一些其他的隔离方面,即用来隔离 Docker 容器的技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。
就 Docker 概念的术语而言,Pod 类似于共享命名空间和文件系统卷的一组 Docker 容器。
说明:除了Docker之外,Kubernetes支持很多其他容器运行时,Docker是最有名的运行时, 使用 Docker 的术语来描述 Pod 会很有帮助。
Pod 生命期
和一个个独立的应用容器一样,Pod 也被认为是相对临时性(而不是长期存在)的实体。Pod会被创建、赋予一个唯一的ID(UID) , 并被调度到节点,并在终止(根据重启策略)或删除之前一直
运行在该节点。
如果一个节点死掉了,调度到该节点 的 Pod 也被计划在预定超时期限结束后删除。
Pod 结构图例
一个包含多个容器的 Pod 中包含一个用来拉取文件的程序和一个Web 服务器, 均使用持久卷作为容器间共享的存储。
使用Pod
通常你不需要直接创建 Pod,甚至单实例 Pod。相反,你会使用诸如 Deployment 或 Job 这类工作负载资源 来创建 Pod。如果 Pod 需要跟踪状态,可以考虑StatefulSet 资源。
Kubernetes 集群中的 Pod 主要有两种用法:
- 运行单个容器的 Pod。”每个Pod一个容器” 模型是最常见的Kubernetes 用例;在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。
- 运行多个协同工作的容器的 Pod。Pod 可能封装由多个紧密耦合且需要共享资源的共处容器组成的应用程序。这些位于同一位置的容器可能形成单个内聚的服务单元一个容器将文件从共享卷提供给公众,而另一个单独的“边车”(sidecar) 容器则刷新或更新这些文件。Pod 将这些容器和存储资源打包为一个可管理的实体。
- 演示:使用Job 创建一个 Pod,打印信息后会暂停。
hello-job.yaml 文件
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
completions: 5
template:
# 这里是pod模板
spec:
containers:
- name: hello
image: nginx
command: ['sh', '-c', 'echo "Hello, Kubernetes! " && sleep 1']
restartPolicy: OnFailure
# 以上为Pod模板
- 创建job
kubectl create -f hello-job.yaml
查看pod和job状态,使用
-w
即watch参数实时观察pod状态[root@node1 ~]# kubectl get job
NAME COMPLETIONS DURATION AGE
hello 5/5 75s 2m44s
[root@node1 ~]#
[root@node1 ~]# kubectl get po -w
NAME READY STATUS RESTARTS AGE
hello-f4nt8 0/1 Completed 0 2m26s
hello-jrhwx 0/1 Completed 0 111s
hello-nlzv4 0/1 Completed 0 2m44s
hello-vqsnf 0/1 Completed 0 2m8s
hello-zfslx 0/1 Completed 0 2m48s
[root@node1 ~]# kubectl logs -f hello-f4nt8
Hello, Kubernetes!
删除job
[root@node1 ~]# kubectl delete job hello
job.batch "hello" deleted
[root@node1 ~]#
[root@node1 ~]# kubectl get job
No resources found in default namespace.
Pod 网络
每个Pod都在每个地址族中获得一个唯一的IP 地址。Pod中的每个容器共享网络名字空间,包括 IP 地址和网络端口。 Pod 内的容器可以使用 localhost 互相通信。 当 Pod 中的容器与 Pod 之外的实体通信时,它们必须协调如何使用共享的网络资源(例如端口)。
5-3 Pod 的生命周期
容器阶段 Phase
- Pending(挂起)Pod已被Kubernetes系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。
- Running(运行中) Pod已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。
- Succeeded(成功)Pod 中的所有容器都已成功终止,并且不会再重启。
- Failed(失败)Pod中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。
- Unknown(未知)因为某些原因无法取得Pod的状态。这种情况通常是因为与Pod所在主机通信失败。
容器状态 Status
一旦调度器将 Pod 分派给某个节点,kubelet 就通过容器运行时开始为 Pod 创建容器。 容器的状态有三种:Waiting(等待)、Running(运行中)和Terminated(已终止)kubectl describe pod <pod 名称>
[root@node1 ~]# kubectl describe pod hello-hdm2p
Name: hello-hdm2p
Namespace: default
Priority: 0
Node: node1/192.168.56.102
Start Time: Fri, 21 Jan 2022 23:28:29 +0800
Labels: controller-uid=0d4d7a48-ff45-4b24-b737-56ebe0123ec2
job-name=hello
Annotations: <none>
Status: Succeeded
IP: 10.244.1.9
IPs:
IP: 10.244.1.9
Controlled By: Job/hello
Containers:
hello:
Container ID: docker://b83e3e4634b180fa64a19f17cee8dc908e3c8430fa22419ed85838f1a12b1519
Image: nginx
Image ID: docker-pullable://nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Port: <none>
Host Port: <none>
Command:
sh
-c
echo "Hello, Kubernetes! " && sleep 1
State: Terminated
Reason: Completed
Exit Code: 0
Started: Fri, 21 Jan 2022 23:28:46 +0800
Finished: Fri, 21 Jan 2022 23:28:47 +0800
Ready: False
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-pp7dx (ro)
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
kube-api-access-pp7dx:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <invalid> default-scheduler Successfully assigned default/hello-hdm2p to node1
Normal Pulling 3m14s kubelet Pulling image "nginx"
Normal Pulled 2m58s kubelet Successfully pulled image "nginx" in 15.628833599s
Normal Created 2m58s kubelet Created container hello
Normal Started 2m58s kubelet Started container hello
- Waiting(等待)
如果容器并不处在Running或Terminated状态之一,它就处在Waiting状态。处于Waiting状态的容器仍在运行它完成启动所需要的操作:例如,从某个容器镜像仓库拉取容器镜像,或者向容器应用Secret 数据等等。 当你使用 kubectl 来查询包含 Waiting 状态的容器的 Pod 时,你也会看到一个Reason 字段,其中给出了容器处于等待状态的原因。
- Running (运行中)
Running 状态表明容器正在执行状态并且没有问题发生。 如果配置了 postStart 回调,那么该回调已经执行完成。如果你使用 kubectl 来查询包含 Running 状态的容器的 Pod 时,你也会看到 关于容器进入Running状态的信息。
- Terminated(已终止)
处于 Terminated 状态的容器已经开始执行并且或者正常结束或者因为某些原因失败。 如果你使用kubectl来查询包含 Terminated 状态的容器的 Pod 时,你会看到容器进入此状态的原因、退出代码以及容器执行期间的起止时间。
5-4 为容器的生命周期事件设置处理函数
定义 postStart 和 preStop 处理函数
在本练习中,你将创建一个包含一个容器的 Pod,该容器为 postStart 和 preStop 事件提供对应的处理函数。
lifecycle.yaml
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
在上述配置文件中,可以看到 postStart 命令在容器的/usr/share
目录下写入文件 message。
命令preStop 负责优雅地终止 nginx 服务。当因为失效而导致容器终止时,这一处理方式很有用。
创建pod
[root@node1 ~]# kubectl create -f lifecycle.yaml
pod/lifecycle-demo created
[root@node1 ~]# kubectl get po -w
NAME READY STATUS RESTARTS AGE
lifecycle-demo 0/1 ContainerCreating 0 16s
lifecycle-demo 1/1 Running 0 18s
使用shell进入pod容器内
[root@node1 ~]# kubectl exec -it lifecycle-demo bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@lifecycle-demo:/#
在shell中,验证portStart处理函数创建了message文件,输出为处理函数所写入的内容
root@lifecycle-demo:/# cat /usr/share/message
Hello from the postStart handler
5-5 创建包含Init 容器的 Pod
理解 Init 容器
每个 Pod 中可以包含多个容器,应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。
Init 容器与普通的容器非常像,除了如下两点:
- 它们总是运行到完成。
- 每个都必须在下一个启动之前成功完成。
如果Pod的Init容器失败,Kubernetes会不断地重启该Pod,直到Init容器成功为止。然而,如果Pod 对应的 restartPolicy 值为 Never,Kubernetes 不会重新启动 Pod。
与普通容器的不同之处
Init 容器支持应用容器的全部属性和特性,包括资源限制、数据卷和安全设置。
同时Init 容器不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe,因为它们必须在Pod 就绪之前运行完成。
实战 Init Pod
下面的例子定义了一个具有2个Init容器的简单Pod。第一个等待 myservice 启动, 第二个等待mydb 启动。一旦这两个 Init容器 都启动完成,Pod 将启动 spec 节中的应用容器。
initPod.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
containers:
- name: myapp
image: busybox:1.28
command: ['sh', '-c', 'date && sleep 3600']
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
initContainers:
- name: init-container
image: busybox:1.28
command: ['sh', '-c', 'date && sleep 10']
在init容器启动后10s才启动myapp容器
- 创建容器
[root@node1 ~]# kubectl create -f initPod.yaml
pod/myapp created
[root@node1 ~]#
[root@node1 ~]# kubectl get po -w
NAME READY STATUS RESTARTS AGE
lifecycle-demo 1/1 Running 0 19m
myapp 0/1 Init:0/1 0 6s
myapp 0/1 PodInitializing 0 14s
myapp 1/1 Running 0 15s
^C[root@node1 ~]#
[root@node1 ~]# kubectl logs myapp
Fri Jan 21 17:13:37 UTC 2022
[root@node1 ~]#
[root@node1 ~]# kubectl logs myapp -c init-container
Fri Jan 21 17:13:27 UTC 2022
5-6 用探针检查 Pod 的健康性
探针的作用
探针是由kubelet 对容器执行的定期诊断。 要执行诊断,kubelet 调用由容器实现的 Handler(处理程序)。有三种类型的处理程序:
- ExecAction:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
- TCPSocketAction:对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成
功的。
- HTTPGetAction: 对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求。如果响应的状态码大于等于200且小于400,则诊断被认为是成功的。
每次探测都将获得以下三种结果之一:
- Success(成功):容器通过了诊断。
- Failure(失败):容器未通过诊断。
- Unknown(未知):诊断失败,因此不会采取任何行动。
何时该使用启动探针?
对于所包含的容器需要较长时间才能启动就绪的 Pod 而言,启动探针是有用的。 你不再需要配置一个较长的存活态探测时间间隔,只需要设置另一个独立的配置选定,对启动期间的容器执行探测,从而允许使用远远超出存活态时间间隔所允许的时长。
如果你的容器启动时间通常超出 initialDelaySeconds + failureThreshold x periodSeconds
总值,你应该设置一个启动探测,对存活态探针所使用的同一端点执行检查。periodSeconds 的默认值是 30秒。你应该将其 failureThreshold 设置得足够高,以便容器有充足的时间完成启动,并且避免更改存活态探针所使用的默认值。这一设置有助于减少死锁状况的发生。
实战 HTTP 请求探针
下面是一个 Pod 的配置文件:liveness.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: mirrorgooglecontainers/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
在这个配置文件中,可以看到 Pod 也只有一个容器。periodSeconds
字段指定了 kubelet 每隔 3 秒执
行一次存活探测。initiaLDelaySeconds
字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。 如果服
务器上 /healthz
路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。
任何大于或等于 200 并且小于 400 的返回代码标示成功,其它返回代码都标示失败。
可以在这里看服务的源码server.go
。
http.HandleFunc("/healthz",func(w http.Responsewriter, r *http.Requests)
duration := time.Now().Sub(started)
if duration.Seconds() > 10 {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
} else {
w.WriteHeader(200)
w.Write([]byte("ok"))
}
)}
容器存活的最开始 10 秒中, /healthz
处理程序返回一个 200 的状态码。之后处理程序返回 500 的状态码。
kubelet 在容器启动之后 3 秒开始执行健康检测。所以前几次健康检查都是成功的。但是 10 秒之后,健康检查会失败,并且 kubelet 会杀死容器再重新启动容器。
创建一个 Pod 来测试 HTTP 的存活检测:
kubectl apply -f liveness.yaml
# 或者
kubect1 apply -f https://k8s.io/examples/pods/probe/http-liveness.yaml
10秒之后,过看Pod事件来检测存活探经失败了并且容器被重新启动了。
[root@node1 ~]# kubectl get po -w
NAME READY STATUS RESTARTS AGE
liveness-http 0/1 ContainerCreating 0 8s
liveness-http 1/1 Running 0 20s
liveness-http 1/1 Running 1 (27m ago) 54s
liveness-http 1/1 Running 2 (27m ago) 86s
liveness-http 1/1 Running 3 (27m ago) 118s
liveness-http 1/1 Running 4 (27m ago) 2m17s
[root@node1 ~]#
[root@node1 ~]# kubectl describe pod liveness-http
... ...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <invalid> default-scheduler Successfully assigned default/liveness-http to node1
Normal Pulled 2m27s kubelet Successfully pulled image "mirrorgooglecontainers/liveness" in 17.895838646s
Normal Pulled 114s kubelet Successfully pulled image "mirrorgooglecontainers/liveness" in 15.605885143s
Normal Started 81s (x3 over 2m26s) kubelet Started container liveness
Normal Pulled 81s kubelet Successfully pulled image "mirrorgooglecontainers/liveness" in 15.652596078s
Normal Created 81s (x3 over 2m27s) kubelet Created container liveness
Warning Unhealthy 63s (x9 over 2m15s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500
Normal Killing 63s (x3 over 2m9s) kubelet Container liveness failed liveness probe, will be restarted
Normal Pulling 63s (x4 over 2m44s) kubelet Pulling image "mirrorgooglecontainers/liveness"
[root@node1 ~]#
[root@node1 ~]# kubectl get po
NAME READY STATUS RESTARTS AGE
liveness-http 0/1 CrashLoopBackOff 7 (27m ago) 9m55s
5-7 为容器设置启动时要执行的命令和参数
创建 Pod 时设置命令及参数
创建 Pod 时,可以为其下的容器设置启动时要执行的命令及其参数。如果要设置命令,就填写在配置文件的 command
字段下,如果要设置命令的参数,就填写在配置文件的 args 字段下。一旦 Pod 创建完成,该命令及其参数就无法再进行更改了。
如果在配置文件中设置了容器启动时要执行的命令及其参数,那么容器镜像中自带的命令与参数将会被覆盖而不再执行。如果配置文件中只是设置了参数,却没有设置其对应的命令,那么容器镜像中自带的命令会使用该新参数作为其执行时的参数。
说明:在有些容器运行时中,
command
字段对应entrypoint
。
本示例commands.yaml
中,将创建一个只包含单个容器的 Pod。在 Pod 配置文件中设置了一个命令与两个参数:
apiVersion: v1
kind: Pod
metadata:
name: command-demo
labels:
purpose: demonstrate-command
spec:
containers:
- name: command-demo-container
image: debian
command: ["printenv"]
args: ["HOSTNAME", "KUBERNETES_PORT"]
restartPolicy: OnFailure
另一示例args.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
containers:
- name: myapp
image: debian
command: ["printenv"]
args: ["HOSTNAME","KUBERNATES_PORT"]
restartPolicy: OnFailure
基于YAML文件创建一个Pod:
kubectl apply -f commands.yaml
获取正在运行的Pods:
kubectl get pods
查询结果显示在 command-demo 这个Pod运行的容器已经启动完成。
如果要获取容器启动时执行命令的输出结果,可以通过Pod的日志进行查看:
kubectl logs command-demo
日志中显示了 HOSTNAME 和 KUBERNETES_PORT 这两个环境变量的值:
command-demo
tcp://10.3.240.1:443
使用环境变量来设置参数
在上面的示例中,我们直接将一串字符作为命令的参数。除此之外,我们还可以将环境变量作为命令的参数。 ```yaml env:
name: MESSAGE value: “hello world” command: [“/bin/echo”] args:[“$(MESSAGE)”]
comands.yaml文件就变成:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: command-demo
labels:
purpose: demonstrate-command
spec:
containers:
- name: command-demo-container
image: debian
env:
- name: MESSAGE
value: "hello world"
command: ["/bin/echo"]
args: ["$(MESSAGE)"]
restartPolicy: OnFailure
再次创建查看结果 ```bash
通过YAML文件删除pod
[root@node1 ~]# kubectl delete -f commands.yaml pod “command-demo” deleted
添加env参数后重新构建pod
[root@node1 ~]# kubectl apply -f commands.yaml pod/command-demo created [root@node1 ~]# [root@node1 ~]# kubectl get po -w NAME READY STATUS RESTARTS AGE command-demo 0/1 ContainerCreating 0 6s command-demo 0/1 Completed 0 29s ^C[root@node1 ~]# [root@node1 ~]# kubectl logs command-demo hello world
这意味着你可以将那些用来设置环境变量的方法应用于设置命令的参数,其中包括了ConfigMaps与Secrets
> 说明:环境变量需要加上括号,类似于`"$(VAR)"`。这是在 `command` 或 `args` 字段使用变量的格式要求。
<a name="2413e318"></a>
# 5-8 为容器定义相互依赖的环境变量
---
当创建一个 Pod 时,你可以为运行在 Pod 中的容器设置相互依赖的环境变量。 设置相互依赖的环境变量,你就可以在配置清单文件的 `env` 的 `value` 中使用 `$(VAR_NAME)`。
在本练习中,你会创建一个单容器的 Pod。 此 Pod 的配置文件定义了一个已定义常用用法的相互依赖的环境变量。 下面是 Pod 的配置清单:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: dependent-envars-demo
spec:
containers:
- name: dependent-envars-demo
args:
- while true; do echo -en '\n'; printf UNCHANGED_REFERENCE=$UNCHANGED_REFERENCE'\n'; printf SERVICE_ADDRESS=$SERVICE_ADDRESS'\n';printf ESCAPED_REFERENCE=$ESCAPED_REFERENCE'\n'; sleep 30; done;
command:
- sh
- -c
image: busybox
env:
- name: SERVICE_PORT
value: "80"
- name: SERVICE_IP
value: "172.17.0.1"
- name: UNCHANGED_REFERENCE
value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
- name: PROTOCOL
value: "https"
- name: SERVICE_ADDRESS
value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
- name: ESCAPED_REFERENCE
value: "$$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
依据清单创建 Pod:
kubectl apply -f https://k8s.io/examples/pods/inject/dependent-envars.yaml
pod/dependent-envars-demo created
列出运行的 Pod:
kubectl get pods dependent-envars-demo
NAME READY STATUS RESTARTS AGE
dependent-envars-demo 1/1 Running 0 9s
检查 Pod 中运行容器的日志:
kubectl logs pod/dependent-envars-demo
UNCHANGED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
SERVICE_ADDRESS=https://172.17.0.1:80
ESCAPED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
如上所示,你已经定义了 SERVICE_ADDRESS
的正确依赖引用, UNCHANGED_REFERENCE
的错误依赖引用, 并跳过了 ESCAPED_REFERENCE
的依赖引用。
如果环境变量被引用时已事先定义,则引用可以正确解析, 比如 SERVICE_ADDRESS
的例子。
当环境变量未定义或仅包含部分变量时,未定义的变量会被当做普通字符串对待, 比如 UNCHANGED_REFERENCE
的例子。 注意,解析不正确的环境变量通常不会阻止容器启动。
$(VAR_NAME)
这样的语法可以用两个 $
转义,既:$$(VAR_NAME)
。 无论引用的变量是否定义,转义的引用永远不会展开。 这一点可以从上面 ESCAPED_REFERENCE
的例子得到印证。
5-9 为容器和 Pods 分配 CPU 资源
创建一个命名空间
创建一个命名空间,以便将 本练习中创建的资源与集群的其余部分资源隔离。
kubectl create namespace cpu-example
指定 CPU 请求和 CPU 限制
要为容器指定 CPU 请求,请在容器资源清单中包含resources:requests
字段。要指定CPU限制,请包含resources:limits
。
在本练习中,你将创建一个具有一个容器的 Pod。这是 Pod 的配置文件 cpu-request-limit.yaml
:
apiVersion: v1
kind: Pod
metadata:
name: cpu-demo
namespace: cpu-example
spec:
containers:
- name: cpu-demo-crt
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "1"
requests:
cpu: "0.5"
args:
- -cpus
- "2"
配置文件的args
部分提供了容器启动的参数。-cpus "2"
参数告诉容器尝试使用2个CPU。
创建Pod:
kubectl apply -f cpu-request-limit.yaml --namespace=cpu-example
为命令取别名
alias kc="kubectl"
。 输出pod的yaml文件kc get po cpu-demo -o yaml
验证所创建的pod处于running状态
kubectl get pod cpu-demo --namespace=cpu-example
查看显示关于pod的详细信息
kubectl get pod cpu-demo --output=yaml --namespace=cpu-example
输出显示Pod中的一个容器的CPU请求为 500 milli CPU,并且CPU 显示为1个
resources:
limits:
cpu: "1"
requests:
cpu: 500m
5-10 用节点亲和性把 Pods 分配到节点
本页展示在Kubernetes集群中,如何使用节点亲和性把Kubernetes Pod 分配到特定节点。
给节点添加标签
列出集群中的节点及其标签:
kubectl get node --show-labels
在前面的输出中,可以看到node2节点有个disktype=ssd
的标签选择一个节点,给它添加一个标签:
kubectl label nodes <your-node-name> disktype=ssd
依据强制的节点亲和性调度Pod
下面清单描述了一个 Pod,它有一个节点亲和性配置 requiredDuringSchedulingIgnoredDuringExecution
,disktype=ssd
。这意味着pod只会被调度到具有 disktype=ssd
标签的节点上。
affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoreDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
执行(Apply)此清单来创建一个调度到所选节点上的pod:
kubectl apply -f affinity.yaml
查看结果:
kubectl describe pod nginx
kubectl get pods --output=wide
5-11 将ConfigMap 中的键值对配置为容器环境变量
创建一个包含多个键值对的ConfigMap。以下是:configmap-multikeys.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
SPECIAL_LEVEL: very
SPECIAL_TYPE: charm
创建ConfigMap:
kubectl create -f configmap-multikeys.yaml
使用
envFrom
将所有ConfigMap的数据定义为容器环境变量,ConfigMap中的键成为Pod中的环
境变量名称。创建文件:pod-configmap-envFrom.yaml
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- conifigMapRef:
name: special-config
restartPolicy: Never
- 将configmap与pod结合的文件:configmap.yaml ```yaml apiVersion: v1 kind: ConfigMap metadata: name: my-db-config data: db-url: localhost
apiVersion: v1 kind: Pod metadata: name: myapp labels: name: myapp spec: containers:
- name: myapp
image: busybox
command: [“sh”,”-c”,”env”]
envFrom:
- configMapRef: name: my-db-config resources: limits: memory: “128Mi” cpu: “500m” ```
- 查看configmap命令:
kubectl get configmap
5-12 容器root用户vs privileged(特权用户)
以root用户运行
Docker允许隔离其主机OS上的进程,功能和文件系统,并且出于实际原因,大多数容器默认实际上以root用户身份运行。
[root@master ~]# docker run -it busybox sh
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
5cc84ad355aa: Pull complete
Digest: sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
Status: Downloaded newer image for busybox:latest
/ # whoami
root # Notice here, we are still root!
/ # id -u
0
/ # hostname
e0c1bb5f966f
/ # sysctl kernel.hostname=Attacker
sysctl: error setting key 'kernel.hostname': Read-only file system
启动privileged
[root@master ~]# docker run -it --privileged busybox sh
/ # whoami
root # Root again!
/ # id -u
0
/ # hostname
0974f256c8b7
/ # sysctl kernel.hostname=Attacker
kernel.hostname = Attacker # Except now we are privileged.
/ # hostname
Attacker
Kubernetes通过Security Context提供了相同的功能:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true
5-13 为 Pod 创建非 root 用户运行
要为 Pod 设置安全性设置,可在 Pod 规约中包含securityContext
字段。securityContext
字段值是一个PodSecurityContext
对象。你为 Pod 所设置的安全性配置会应用到 Pod 中所有 Container 上。
下面是一个 Pod的配置文件,该Pod定义了securityContext
和一个emptyDir
卷。创建security-context.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
volumes:
- name: security
emptyDir: {}
containers:
- name: myapp
image: busybox
command: ["sh","-c","sleep 1h"]
volumeMounts:
- name: security
mountPath: /data/demo
securityContext:
allowPrivilegeEscalation: false
resources:
limits:
memory: "128Mi"
cpu: "500m"
在配置文件中,runAsUser
字段指定 Pod 中的所有容器内的进程都使用用户 ID 1000 来运行。runAsGroup
字段指定所有容器中的进程都以主组 ID 3000 来运行。 如果忽略此字段,则容器的主组 ID 将是root(0)
。 当 runAsGroup
被设置时,所有创建的文件也会划归用户 1000 和组 3000。由于fsGroup
被设置,容器中所有进程也会是附组 ID 2000 的一部分。卷/data/demo
及在该卷中创建的任何文件的属主都会是组 ID 2000。