創(chuàng)建第一個(gè) pod
創(chuàng)建 nginx pod
編寫 yaml 文件
cd ~
vim 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
創(chuàng)建 pod
kubectl apply -f nginx.yaml
查看 pod
kubectl get po
可以看到
NAME READY STATUS RESTARTS AGE
my-nginx 1/1 Running 0 57s
查看詳細(xì)信息
kubectl describe pod my-nginx
使用 job 創(chuàng)建 pod
cd ~
vim hellojob.yaml
編寫 job 向胡,hello 這個(gè) job 會(huì)跑 5 次 pod
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 2']
restartPolicy: OnFailure
創(chuàng)建 job
kubectl create -f hellojob.yaml
kubectl get job
kubectl get po
kubectl get po -w
kubectl get po -w
kubectl delete job hello
為容器的生命周期事件設(shè)置處理函數(shù)
創(chuàng)建一個(gè)包含一個(gè)容器的 Pod 厌均,該容器為 postStart 和 preStop 事件提供對應(yīng)的處理函數(shù)
在從節(jié)點(diǎn) node1 :
cd ~
vim 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"]
kubectl create -f lifecycle.yaml
kubectl get po
kubectl exec -it lifecycle-demo sh
ls
cat /usr/share/message
可以看到
Hello from the postStart handler
創(chuàng)建包含 Init 容器的 Pod
Init 容器
每個(gè) pod 中可以包含多個(gè)容器艾杏,應(yīng)用運(yùn)行在這些容器里面殿托,同時(shí) Pod 也可以有一個(gè)或多個(gè)先于應(yīng)用容器啟動(dòng)的 Init 容器
Init 容器與普通的容器非常像褪秀,除了如下兩點(diǎn):
它們總是運(yùn)行到完成
每個(gè)都必須在下一個(gè)啟動(dòng)之前成功完成
如果 pod 的 Init 容器失敗三幻,Kubernetes 會(huì)不斷重啟該 pod 碍侦,知道 Init 容器成功為止邢隧。如果 pod 對應(yīng)的 restartPolicy 值為 Never 店印,Kubernetes 不會(huì)重新啟動(dòng) pod
與普通容器的不同之處
Init 容器支持應(yīng)用容器的全部屬性和特性,包括資源限制倒慧、數(shù)據(jù)卷和安全設(shè)置按摘。
同時(shí) Init 容器不支持 lifecycle, livenessProbe, readinessProbe 和 startupProbe, 因?yàn)樗鼈儽仨氃?pod 就緒之前運(yùn)行完成。
定義一個(gè)具有兩個(gè) Init 容器的 pod 纫谅。第一個(gè)等待 myservice 啟動(dòng)炫贤,第二個(gè)等待 mydb 啟動(dòng)。一旦這兩個(gè) Init 容器都啟動(dòng)完成付秕,pod 將啟動(dòng) spec 節(jié)中的應(yīng)用容器兰珍。
cd ~
vim initPod.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'date && sleep 3600']
initContainers:
- name: init-container
image: busybox:1.28
command: ['sh', '-c', "date && sleep 10"]
啟動(dòng) pod
kubectl apply -f initPod.yaml
kubectl get po
查看 pod 日志
kubectl logs myapp-pod
Thu Mar 18 08:52:08 UTC 2021
查看 pod 內(nèi) init 容器的日志
kubectl logs myapp-pod -c init-container
Thu Mar 18 08:51:57 UTC 2021
用探針檢查 pod 的健康性
探針是 kubelet 對容器執(zhí)行的定期診斷,監(jiān)測 pod 里面的容器是不是正常啟動(dòng)了询吴。kubelet 調(diào)用由容器實(shí)現(xiàn)的 Handler 進(jìn)行診斷掠河,Handler 的類型有三種:
- ExecAction:在容器內(nèi)執(zhí)行指定命令。如果命令退出時(shí)返回碼為 0 則認(rèn)為診斷成功
- TCPSocketAction:對容器的 ip 地址上的指定端口進(jìn)行 TCP 檢查猛计,如果端口打開唠摹,則認(rèn)為診斷成功。
- HTTPGetAction:對容器的 ip 地址上的指定端口的路徑執(zhí)行 HTTP Get 請求有滑。如果響應(yīng)的狀態(tài)碼大于等于 200 并且小于 400跃闹,則認(rèn)為診斷是成功的。
每次診斷都將獲得以下三種結(jié)果之一:
- Success(成功):容器通過了診斷
- Failure(失敗):容器未通過診斷
- Unknown(未知):診斷失敗,因此不會(huì)采取任何行動(dòng)
什么時(shí)候使用探針
對于所包含的容器需要比較長的時(shí)間才能啟動(dòng)就緒的 pod 望艺,啟動(dòng)探針是非常有用的苛秕。
HTTP 請求探針
cd ~
vim 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
上面官方提供的 liveness 容器,10秒之內(nèi)找默,服務(wù)會(huì)給 /healthz 請求返回 200艇劫,10秒之后,會(huì)返回 500:
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
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"))
}
})
initialDelaySeconds: 3
字段告訴 kubelet 在執(zhí)行第一次探測之前應(yīng)該等待 3 秒
periodSeconds: 3
字段指定了 kubelet 每隔 3 秒執(zhí)行一次存活探測
啟動(dòng) pod
kubectl apply -f liveness.yaml
觀察 pod
kubectl get po -w
kubelet 在容器啟動(dòng) 3 秒后惩激,開始健康檢測店煞,啟動(dòng)超過 10 秒之后,健康檢查會(huì)失敗风钻,kubelet 會(huì)重啟容器顷蟀。
為容器設(shè)置啟動(dòng)時(shí)要執(zhí)行的命令和參數(shù)
創(chuàng)建 pod 時(shí),可以為其下的容器設(shè)置啟動(dòng)時(shí)要執(zhí)行的命令及參數(shù)骡技。通過 command 字段設(shè)置命令鸣个,通過 args 字段設(shè)置命令的參數(shù)。
cd ~
vim args.yaml
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
以上布朦,設(shè)置了一個(gè)命令囤萤,兩個(gè)參數(shù)。命令會(huì)打印兩個(gè)參數(shù)是趴。
restartPolicy: OnFailure
字段的意思是:失敗了才會(huì)重啟
創(chuàng)建 pod
kubectl apply -f args.yaml
kubectl get po
kubectl logs command-demo
可以看到涛舍,打印了兩個(gè)參數(shù):
command-demo
tcp://10.1.0.1:443
使用環(huán)境變量設(shè)置參數(shù)
kubectl delete -f args.yaml
vim args.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
kubectl apply -f args.yaml
kubectl get po -w
kubectl logs command-demo
可以看到:
hello world
為容器定義相互依賴的環(huán)境變量
可以為運(yùn)行在 pod 中的容器設(shè)置相互依賴的環(huán)境變量
vim dependency-var.yaml
apiVersion: v1
kind: Pod
metadata:
name: dependent-envars-demo
spec:
containers:
- name: dependent-envars-demo
args:
- printf UNCHANGED_REFERENCE=$UNCHANGED_REFERENCE'\n'; printf SERVICE_ADDRESS=$SERVICE_ADDRESS'\n';printf ESCAPED_REFERENCE=$ESCAPED_REFERENCE'\n';
command:
- sh
- -c
image: busybox
env:
- name: SERVICE_PORT
value: "80"
- name: SERVICE_IP
value: "192.168.190.131"
- 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)"
kubectl apply -f dependency-var.yaml
kubectl get po
kubectl logs dependent-envars-demo
可以看到:
UNCHANGED_REFERENCE=$(PROTOCOL)://192.168.190.131:80
SERVICE_ADDRESS=https://192.168.190.131:80
ESCAPED_REFERENCE=$(PROTOCOL)://192.168.190.131:80
在變量 PROTOCOL
定義之前打印 $(PROTOCOL)
,會(huì)打铀敉尽:$(PROTOCOL)
打印 $$(PROTOCOL)
會(huì)打痈谎拧:$(PROTOCOL)
為容器和 Pods 分配 CPU 資源
創(chuàng)建一個(gè)命名空間,以便將本次練習(xí)中創(chuàng)建的資源與集群的其余部分資源隔離
kubectl create namespace cpu-example
為容器指定 CPU 請求窘哈,使用字段:
resources: requests
指定 CPU 限制吹榴,使用字段:
resources: limits
創(chuàng)建一個(gè) pod , pod 內(nèi)有一個(gè)容器滚婉,容器請求 0.5 個(gè) CPU 图筹,并且限制最多使用 1 個(gè) CPU
vim cpu-request-limit.yaml
apiVersion: v1
kind: Pod
metadata:
name: cpu-demo
namespace: cpu-example
spec:
containers:
- name: cpu-demo-ctr
image: nginx
resources:
limits:
cpu: "1"
requests:
cpu: "0.5"
args:
- -cpus
- "2"
arg
提供了容器啟動(dòng)時(shí)的參數(shù),優(yōu)先級高于默認(rèn)的參數(shù)让腹,這里 -cpu
"2"
告訴容器嘗試使用 2 個(gè) CPU 远剩,但是結(jié)果并不會(huì)使用 2 個(gè) CPU ,因?yàn)?resources: limits
限制了 CPU 個(gè)數(shù)是 1 骇窍。
安裝 work2
克隆 work1 虛擬機(jī)瓜晤,鏈接克隆或者完整克隆都可以,命名為 work2 腹纳,克隆完成后痢掠,啟動(dòng) work2 虛擬機(jī)
-
修改 ip :
vi /etc/sysconfig/network-scripts/ifcfg-ens33
IPADDR=192.168.190.133
重啟網(wǎng)絡(luò):
systemctl restart network
-
修改 hostname
hostnamectl set-hostname node2
- 查看 hostname :
hostname
- 查看 hostname :
-
重置 kubeadm
kubeadm reset
- 輸入:
y
-
配置端口轉(zhuǎn)發(fā):
echo 1 > /proc/sys/net/ipv4/ip_forward echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
-
將 master 節(jié)點(diǎn)的 admin.conf 復(fù)制到 work2 節(jié)點(diǎn)驱犹,在 master 節(jié)點(diǎn)執(zhí)行:
scp /etc/kubernetes/admin.conf root@node2:/etc/kubernetes/
-
清理環(huán)境
systemctl stop kubelet && systemctl stop docker && rm -rf /var/lib/cni/ && rm -rf /var/lib/kubelet/* && rm -rf /etc/cni/ && ifconfig flannel.1 down && ifconfig docker0 down && ip link delete flannel.1
-
啟動(dòng) docker 和 kubernetes
systemctl start docker && systemctl start kubelet
-
在 master 節(jié)點(diǎn)生成 token 給 work2 節(jié)點(diǎn),在 master 節(jié)點(diǎn)執(zhí)行:
kubeadm token create --print-join-command
-
生成的 token
kubeadm join 192.168.190.131:6443 --token hjyu47.u4ezlgt0yuw8jwrf --discovery-token-ca-cert-hash sha256:1271d25165cffe9623cc85980a5ac950eba7ff066149b590a509d3e09594a09d
復(fù)制到 work2 節(jié)點(diǎn)
等待半分鐘至1分鐘足画,執(zhí)行:
kubectl get nodes
-
可以看到:
NAME STATUS ROLES AGE VERSION master Ready master 30h v1.19.3 node1 Ready <none> 27h v1.19.3 node2 Ready <none> 78s v1.19.3
用節(jié)點(diǎn)親和性把 Pods 分配到節(jié)點(diǎn)
列出集群中的節(jié)點(diǎn)和節(jié)點(diǎn)的標(biāo)簽
kubectl get nodes --show-labels
給節(jié)點(diǎn)添加標(biāo)簽雄驹,這里區(qū)分一下:work1 是虛擬機(jī)的名字;node1 是節(jié)點(diǎn)的名字
kubectl label nodes <your-node-name> disktype=ssd
看一下這個(gè) pod 淹辞,通過 affinity
指定了 disktype
是 ssd
的節(jié)點(diǎn)
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
可以通過 kubectl get pods --output=wide
查看 pod 運(yùn)行在哪個(gè)節(jié)點(diǎn)
將 ConfigMap 中的兼職對配置為容器環(huán)境變量
vim config.yaml
下面的內(nèi)容医舆,包含 configmap 和 pod
# configmap
apiVersion: v1
kind: ConfigMap
metadata:
# configmap 的名字
name: my-db-config
# configmap 的數(shù)據(jù)
data:
db-url: localhost
---
# pod
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
containers:
- name: myapp
image: busybox
# 命令:寫入環(huán)境變量
command: ["sh","-c","env"]
# 環(huán)境變量來自于:
envFrom:
# 指定 configmap
- configMapRef:
name: my-db-config
resources:
limits:
memory: "128Mi"
cpu: "500m"
一次創(chuàng)建 configmap 和 pod 兩個(gè)對象
kubectl create -f configmap.yaml
可以看到:
configmap/my-db-config created
pod/myapp created
查看日志
kubectl logs myapp
可以看到 db-url=localhost
已經(jīng)寫入環(huán)境變量
容器 Root 用戶 VS privileged
大多數(shù)容器默認(rèn)以 root 身份運(yùn)行,不過為了安全象缀,默認(rèn)的 root 用戶實(shí)際上是映射的 root 用戶蔬将,并不具備 root 用戶的全部功能,如果需要更大的權(quán)限央星,需要使用特權(quán)用戶霞怀,在運(yùn)行容器的時(shí)候,加上 --privileged
參數(shù)莉给。
[root@node1 ~]# docker run -it busybox sh
/ # whoami
root
/ # id
uid=0(root) gid=0(root) groups=10(wheel)
/ # hostname
af29e1748ec7
/ # sysctl kernel.hostname=Attacker
sysctl: error setting key 'kernel.hostname': Read-only file system
/ # exit
[root@node1 ~]#
上面的例子里烦,以默認(rèn)的 root 用戶運(yùn)行容器,在修改 hostname 的時(shí)候禁谦,提示沒有權(quán)限。
下面的例子废封,在運(yùn)行容器的時(shí)候州泊,加上 --privileged
參數(shù),以特權(quán)用戶運(yùn)行
[root@node1 ~]# docker run -it --privileged busybox sh
/ # whoami
root
/ # id
uid=0(root) gid=0(root) groups=10(wheel)
/ # hostname
c21cf0211dc1
/ # sysctl kernel.hostname=Attacker
kernel.hostname = Attacker
/ # hostname
Attacker
/ # exit
[root@node1 ~]#
可以看到漂洋,hostname 修改成功了遥皂。
Kubernetes 通過 Security Context
提供了相同的功能
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true
為 Pod 創(chuàng)建非 Root 用戶運(yùn)行
創(chuàng)建一個(gè)設(shè)置 Security Context
的 pod
vim security.yaml
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo
spec:
securityContext:
# 容器內(nèi)所有進(jìn)程都以 1000 這個(gè)用戶id來運(yùn)行
runAsUser: 1000
# 進(jìn)程都以主組id 3000 來運(yùn)行,所有創(chuàng)建的文件刽漂,也劃歸用戶1000和主組3000演训;如果忽略此字段,則容器的主組id將是root(0)
runAsGroup: 3000
# 容器中所有進(jìn)程也會(huì)是附組id2000的一部分贝咙。卷/data/demo及在該卷中創(chuàng)建的任何文件的屬主都會(huì)是組id 2000
fsGroup: 2000
# 在宿主機(jī)上掛載一個(gè)空目錄样悟,存放這個(gè) pod 里面的內(nèi)容
volumes:
- name: sec-ctx-vol
emptyDir: {}
containers:
- name: sec-ctx-demo
image: busybox
command: [ "sh", "-c", "sleep 1h" ]
# 引用掛載的目錄
volumeMounts:
- name: sec-ctx-vol
mountPath: /data/demo
# 是否允許越出權(quán)限
securityContext:
allowPrivilegeEscalation: false
kubectl create -f security.yaml
[root@node1 ~]# kubectl exec -it security-context-demo sh
/ $ id
uid=1000 gid=3000 groups=2000
/ $ ps
PID USER TIME COMMAND
1 1000 0:00 sleep 1h
6 1000 0:00 sh
12 1000 0:00 ps
/ $ ls
bin data dev etc home proc root sys tmp usr var
/ $ cd data/demo/
/data/demo $ ls -l
total 0
drwxrwsrwx 2 root 2000 6 Mar 19 03:35 .
/data/demo $ exit
[root@node1 ~]#