Eli's Blog

1. K8S 资源

k8s中,所有的内容都被抽象为资源,资源实例化后,称为对象

集群资源分类:

  • 名称空间级别: 只在本名称空间下可见
  • 集群级别: role, 不管在什么名称空间小,均可见
  • 元数据级别: HPA(可以CPU利用率平滑扩展)

1.1 工作负载 (workload)

  • Pod: 最小资源,共享网络栈、存储卷等
  • ReplicaSet:调度器,管理Pod的创建,通过标签的选择去控制Pod的副本数
  • Deployment: 控制器,通过控制RS的创建,去创建Pod
  • StatefulSet:有状态服务管理器
  • DaemonSet:可在每个节点都运行一个Pod组件
  • Job: 批量工作
  • CronJob: 定时或轮训工作

1.2 服务发现及负载均衡

  • Service
  • Ingress

1.3 配置与存储

  • Volume: 存储卷

  • CSI: 容器存储接口,可扩展第三方存储设备

  • ConfigMap: 配置中心

  • Secret: 敏感数据保存

  • DownwardAPI: 外部环境中的信息输出给容器

1.4 集群

不指定名称空间,所有节点均能访问:role

  • Namespace
  • Node
  • Role
  • ClusterRole
  • RoleBinding
  • ClusterRoleBinding

1.5 元数据

  • HPA
  • PodTemplate
  • LimitRange

1.6 用法查询

1
2
3
kubectl explain pod

kubectl explain pod.spec

2. Pod 生命周期

img

1)kubectl –> apiserver –> CRI –> kubelet 环境初始化

2)启动Pause容器: 初始化网络和数据卷

3)init C初始化。多个initC时,必须串行执行,且每个必须执行成功才向下走

4)Main C,开始运行时,启动Start命令/脚本;结束时,执行Stop命令(做哪些清理操作等)

5)Readiness 就绪检测:若干秒后,进行是否就绪的探测。只有当Readiness成功后,Pod才会显示Ready状态

6)Liveness 生存检测:探测Main C中的进程是否正常,不正常则执行重启、删除等命令

2.1 InitC 容器

即 Infra 容器,其镜像是一个用汇编写成,永远处于“pause”状态的容器,解压后大小100~200KB,运行时占用极小的资源

作用

  • initC 容器可作为 Pod 中其他容器的初始化工具

  • 出于安全考虑,将应用容器中的某些实用工具(python, awk等)单独拆开放入initC容器

  • 应用容器的启动是并行的,而 initC 容器可阻塞应用容器的启动,直到满足一些先决条件

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
apiVersion: v1
kind: Pod
metadata:
name: test-initc
labels:
app: myapp
spec:
initContainers:
- name: init-myservice
image: busybox:1.28.4
command: ['sh','-c','until nslookup myservice;do echo waiting for myservice;sleep 2; done;']
- name: init-mydb
image: busybox:1.28.4
command: ['sh','-c','until nslookup mydb;do echo waiting for mydb;sleep 2;done;']
containers:
- name: mypod
image: busybox:1.28.4
command: ['sh','-c','echo The app is running!&& sleep 3600']
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9001
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9002

2.2 容器探针

健康状态探针:

  • ReadinessProbe: 判断容器服务是否可用,成功显示ready, 失败不触发重启
  • LivenessProbe: 判断容器是否存活,如果探测到不健康,将触发重启策略。如果未配置该探针,则永远返回成功.

探针的三种实现方式:

  • ExecAction: 容器内执行命令,返回码等于0则认为成功
  • TCPSocketAction: 在指定端口上的容器IP地址进行TCP检查,如果端口打开,则认为成功
  • HTTPGetAction: 在指定端口和路径的容器IP地址执行HTTP GET请求,状态码在 [200, 399] 表示成功

探测结果:

  • success
  • failed:失败,按策略处理
  • unknown:诊断失败,但不会采取任何行动

2.2.1 就绪检测

检测失败,状态非Ready,但不会重启容器

spec.containers[].readinessProbe.httpGet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# readiness-probe-httpget.yaml 
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
spec:
containers:
- name: readiness-httpget-container
image: nginx
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ kubectl create -f readiness-probe-httpget.yaml 

$ kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness-httpget-pod 0/1 Running 0 9s

$ kubectl describe pod readiness-httpget-pod
Warning Unhealthy 1s (x3 over 7s) kubelet, k8s-node01 Readiness probe failed: HTTP probe failed with statuscode: 404

# 进入容器
$ kubectl exec readiness-httpget-pod -it -- /bin/sh
# cd /usr/share/nginx/html
# ls -al
total 8
drwxr-xr-x 2 root root 40 Aug 14 00:36 .
drwxr-xr-x 3 root root 18 Aug 14 00:36 ..
-rw-r--r-- 1 root root 494 Aug 11 14:50 50x.html
-rw-r--r-- 1 root root 612 Aug 11 14:50 index.html
# echo "hello, i'am index1.html" > index1.html
# logout

$ kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness-httpget-pod 1/1 Running 0 2m57s

2.2.2 存活检测

检测失败,直接重启Pod

spec.containers[].livenessProbe.exec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# liveness-prob-exec.yaml 
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
spec:
containers:
- name: liveness-exec-container
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "touch /tmp/abc.txt; sleep 60; rm -f /tmp/abc.txt; sleep 3600"]
livenessProbe:
exec:
command: ["test", "-e", "/tmp/abc.txt"]
initialDelaySeconds: 1
periodSeconds: 3
1
2
3
4
5
6
7
8
9
$ kubectl create -f liveness-prob-exec.yaml 

# 失败,重启自动重启
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
liveness-exec-pod 1/1 Running 0 26s
liveness-exec-pod 1/1 Running 1 2m58s
liveness-exec-pod 1/1 Running 2 3m35s
liveness-exec-pod 1/1 Running 3 5m15s

spec.containers[].livenessProbe.httpGet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
spec:
containers:
- name: liveness-httpget
image: nginx
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
port: 80
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 10 # 超时处理

spec.containers[].livenessProbe.tcpSocket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
name: tcpsocket-pod
spec:
containers:
- name: redis
image: redis
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 5
periodSeconds: 3
timeoutSeconds: 1

2.3 启动 & 退出

spec.containers[].lifecycle.postStart|preStop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 postStart > /tmp/hello.txt"]
preStop:
exec:
command: ["/usr/sbin/nginx", "-s", "quit"]

2.4 Pod 状态值

  • Pending:Pod已被k8s系统接受,但有一个或多个镜像容器尚未创建。等待时间包括调度Pod的时间和通过网络下载镜像的时间。
  • Running: Pod已经绑定到一个节点上了,Pod中的所有容器已被创建
  • Succeeded: Pod中的所有容器都被成功终止,且不会再重启
  • Failed: Pod中的所有容器都已终止,但至少有一个容器以非0返回值退出
  • Unknown: 未知原因无法获取Pod状态,通常是因为与Pod所在主机的通信失败

3. 注入卷

Projected Volume: 不是为了存放容器里的数据,也不是用于容器和宿主机之间的数据交换,而是为了给容器提供预先定义好的数据

常见的 Projected Volume:

  • Secret
  • ConfigMap
  • Downward API
  • ServiceAccountToken

3.1 Secret

作用:把 Pod 想要访问的加密数据放入 etcd 中

类型:

内置类型 用法
Opaque 用户定义的任意数据
kubernetes.io/service-account-token 服务账号令牌
kubernetes.io/dockercfg ~/.dockercfg 文件的序列化形式
kubernetes.io/dockerconfigjson ~/.docker/config.json 文件的序列化形式
kubernetes.io/basic-auth 用于基本身份认证的凭据
kubernetes.io/ssh-auth 用于 SSH 身份认证的凭据
kubernetes.io/tls 用于 TLS 客户端或者服务器端的数据
bootstrap.kubernetes.io/token 启动引导令牌数据

3.1.1 Opaque

创建 secret:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 方法1:
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
stringData:
user: root
pass: "123456"
EOF

# 方法2:
$ kubectl create secret generic my-secret --from-literal=user=root --from-literal=pass=123456

使用 secret:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: Pod
metadata:
name: pod-secret-volume
spec:
containers:
- name: secret-volume
image: busybox
args:
- sleep
- "86400"
volumeMounts:
- name: mysql-cred
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: mysql-cred
secret:
secretName: mysecret
items:
- key: user
path: cred/user
- key: pass
path: cred/pass

检查数据挂载:

1
2
$ kubectl exec -it pod-secret-volume -- cat /projected-volume/cred/pass
123456

3.1.2 TLS

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Secret
metadata:
name: secret-tls
type: kubernetes.io/tls
data:
tls.crt: |
MIIC2DCCAcCgAwIBAgIBATANBgkqh ...
tls.key: |
MIIEpgIBAAKCAQEA7yn3bRHQ5FHMQ ...

3.2 ConfigMap

作用:保存无需加密的,应用所需的配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
data:
special.how: very
---
apiVersion: v1
kind: ConfigMap
metadata:
name: env-config
data:
log_level: INFO
---
apiVersion: v1
kind: Pod
metadata:
name: pod-configmap
spec:
containers:
- name: cm-container
image: busybox
command: ["/bin/sh", "-c", "env"]
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: env-config
key: log_level
restartPolicy: Never

3.3 DownwardAPI

作用:让 Pod 中的容器,能够直接获取该 Pod API 对象本身的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
apiVersion: v1
kind: Pod
metadata:
name: downward-api-volume
labels:
zone: china-standard-time
cluster: taos-1
rack: rack-22
annotations:
version: "1.2"
builder: gopher
spec:
containers:
- name: busybox
image: busybox
command: ["sh", "-c"]
args:
- while true; do
if [[ -e /etc/podinfo/labels ]]; then
echo -en '\n\n'; cat /etc/podinfo/labels; fi;
if [[ -e /etc/podinfo/annotations ]]; then
echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
sleep 5;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotations

环境变量方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: downward-api-env
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "env"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP

3.4 ServiceAccountToken

Service Account 是 k8s 的一种内置”服务账户“,它绑定了一个特殊的 Secret,即 ServiceAccountToken, 保存了授权信息等内容。任何在 k8s 集群上运行的应用,都必须使用 ServiceAccountToken 中的授权信息,才能合法访问 API Server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ kubectl describe pod nginx
Containers:
nginx:
...
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-stqtv (ro)
Volumes:
kube-api-access-stqtv:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true

$ kubectl exec -it nginx -- ls /run/secrets/kubernetes.io/serviceaccount
ca.crt namespace token