——————————————————————————————————————————————
博主介紹:Java領域優質創作者,博客之星城市賽道TOP20、專注于前端流行技術框架、Java后端技術領域、項目實戰運維以及GIS地理信息領域。
🍅文末獲取源碼下載地址🍅
👇🏻 精彩專欄推薦訂閱👇🏻 歡迎點贊收藏評論拍磚…
【Docker Swarm總結】《容器技術 Docker+K8S專欄》?
【uniapp+uinicloud多用戶社區博客實戰項目】《完整開發文檔-從零到完整項目》?
【Springcloud Alibaba微服務分布式架構 | Spring Cloud】《系列教程-更新完畢》?
【SpringSecurity-從入門到精通】《學習完整筆記-附(完整demo源碼)》?
【從零開始Vue項目中使用MapboxGL開發三維地圖教程】《系列教程-不定時更新》?
【Vue.js學習詳細課程系列】《共32節專欄收錄內容》?
感興趣的可以先收藏起來相關問題都可以給我留言咨詢,希望幫助更多的人。
——————————————————————————————————————————————
文章目錄
- 1. Kubernetes介紹
- 1.1 應用部署方式演變
- 1.2 kubernetes簡介
- 1.3 kubernetes組件
- 1.4 kubernetes概念
- 2. kubernetes集群環境搭建
- 2.1 前置知識點
- 2.2 kubeadm 部署方式介紹
- 2.3 安裝要求
- 2.4 最終目標
- 2.5 準備環境
- 2.6 環境初始化
- 2.6.1 檢查操作系統的版本
- 2.6.2 主機名解析
- 2.6.3 時間同步
- 2.6.4 禁用iptable和firewalld服務
- 2.6.5 禁用selinux
- 2.6.6 禁用swap分區
- 2.6.7 修改linux的內核參數
- 2.6.8 配置ipvs功能
- 2.6.9 安裝docker
- 2.6.10 安裝Kubernetes組件
- 2.6.11 準備集群鏡像
- 2.6.11 集群初始化
- 2.6.13 安裝網絡插件,只在master節點操作即可
- 2.6.14 使用kubeadm reset重置集群
- 2.6.15 重啟kubelet和docker
- 2.6.16 kubeadm中的命令(如果token失效了,一般是24h)
- 2.7 集群測試
- 2.7.1 創建一個nginx服務
- 2.7.2 暴露端口
- 2.7.3 查看服務
- 2.7.4 查看pod
- 2.8 解決在工作節點不能使用kubectl查看節點信息
- 3. 資源管理
- 3.1 資源管理介紹
- 3.2 YAML語言介紹
- 3.3 資源管理方式
- 3.3.1 命令式對象管理
- 3.3.2 命令式對象配置
- 3.3.3 聲明式對象配置
- 4. 實戰入門
- 4.1 Namespace
- 4.1.1 **查看**
- 4.1.2 **創建**
- 4.1.3 **刪除**
- 4.1.4 **配置方式**
- 4.2 Pod
- 4.2.1 創建并運行
- 4.2.2 查看pod信息
- 4.2.3 訪問Pod
- 4.2.4 刪除指定Pod
- 4.2.5 配置操作
- 4.3 Label
- 4.3.1 命令方式
- 4.3.2 配置方式
- 4.4 Deployment
- 4.4.1 命令操作
- 4.4.2 配置操作
- 4.5 Service
- 4.5.1 創建集群內部可訪問的Service
- 4.5.2 創建集群外部也可訪問的Service
- 4.5.3 刪除Service
- 4.5.4 配置方式
- 5. Pod詳解
- 5.1 Pod介紹
- 5.1.1 Pod結構
- 5.1.2 Pod定義
- 5.2 Pod配置
- 5.2.1 基本配置
- 5.2.2 鏡像拉取
- 5.2.3 啟動命令
- 5.2.4 環境變量
- 5.2.5 端口設置
- 5.2.6 資源配額
- 5.3 Pod生命周期
- 5.3.1 創建和終止
- 5.3.2 初始化容器
- 5.3.3 鉤子函數
- 5.3.4 容器探測
- 5.3.5 重啟策略
- 5.4 Pod調度
- 5.4.1 定向調度
- 5.4.2 親和性調度
- 5.4.3 污點和容忍
?
1. Kubernetes介紹
1.1 應用部署方式演變
在部署應用程序的方式上,主要經歷了三個時代:
-
傳統部署:互聯網早期,會直接將應用程序部署在物理機上
優點:簡單,不需要其它技術的參與
缺點:不能為應用程序定義資源使用邊界,很難合理地分配計算資源,而且程序之間容易產生影響
-
虛擬化部署:可以在一臺物理機上運行多個虛擬機,每個虛擬機都是獨立的一個環境
優點:程序環境不會相互產生影響,提供了一定程度的安全性
缺點:增加了操作系統,浪費了部分資源
-
容器化部署:與虛擬化類似,但是共享了操作系統
優點:
可以保證每個容器擁有自己的文件系統、CPU、內存、進程空間等
運行應用程序所需要的資源都被容器包裝,并和底層基礎架構解耦
容器化的應用程序可以跨云服務商、跨Linux操作系統發行版進行部署
容器化部署方式給帶來很多的便利,但是也會出現一些問題,比如說:
- 一個容器故障停機了,怎么樣讓另外一個容器立刻啟動去替補停機的容器
- 當并發訪問量變大的時候,怎么樣做到橫向擴展容器數量
這些容器管理的問題統稱為容器編排問題,為了解決這些容器編排問題,就產生了一些容器編排的軟件:
- Swarm:Docker自己的容器編排工具
- Mesos:Apache的一個資源統一管控的工具,需要和Marathon結合使用
- Kubernetes:Google開源的的容器編排工具
1.2 kubernetes簡介
kubernetes,是一個全新的基于容器技術的分布式架構領先方案,是谷歌嚴格保密十幾年的秘密武器----Borg系統的一個開源版本,于2014年9月發布第一個版本,2015年7月發布第一個正式版本。
kubernetes的本質是一組服務器集群,它可以在集群的每個節點上運行特定的程序,來對節點中的容器進行管理。目的是實現資源管理的自動化,主要提供了如下的主要功能:
- 自我修復:一旦某一個容器崩潰,能夠在1秒中左右迅速啟動新的容器
- 彈性伸縮:可以根據需要,自動對集群中正在運行的容器數量進行調整
- 服務發現:服務可以通過自動發現的形式找到它所依賴的服務
- 負載均衡:如果一個服務起動了多個容器,能夠自動實現請求的負載均衡
- 版本回退:如果發現新發布的程序版本有問題,可以立即回退到原來的版本
- 存儲編排:可以根據容器自身的需求自動創建存儲卷
1.3 kubernetes組件
一個kubernetes集群主要是由控制節點(master)、**工作節點(node)**構成,每個節點上都會安裝不同的組件。
master:集群的控制平面,負責集群的決策 ( 管理 )
ApiServer : 資源操作的唯一入口,接收用戶輸入的命令,提供認證、授權、API注冊和發現等機制
Scheduler : 負責集群資源調度,按照預定的調度策略將Pod調度到相應的node節點上
ControllerManager : 負責維護集群的狀態,比如程序部署安排、故障檢測、自動擴展、滾動更新等
Etcd :負責存儲集群中各種資源對象的信息
node:集群的數據平面,負責為容器提供運行環境 ( 干活 )
Kubelet : 負責維護容器的生命周期,即通過控制docker,來創建、更新、銷毀容器
KubeProxy : 負責提供集群內部的服務發現和負載均衡
Docker : 負責節點上容器的各種操作
下面,以部署一個nginx服務來說明kubernetes系統各個組件調用關系:
-
首先要明確,一旦kubernetes環境啟動之后,master和node都會將自身的信息存儲到etcd數據庫中
-
一個nginx服務的安裝請求會首先被發送到master節點的apiServer組件
-
apiServer組件會調用scheduler組件來決定到底應該把這個服務安裝到哪個node節點上
在此時,它會從etcd中讀取各個node節點的信息,然后按照一定的算法進行選擇,并將結果告知apiServer
-
apiServer調用controller-manager去調度Node節點安裝nginx服務
-
kubelet接收到指令后,會通知docker,然后由docker來啟動一個nginx的pod
pod是kubernetes的最小操作單元,容器必須跑在pod中至此,
-
一個nginx服務就運行了,如果需要訪問nginx,就需要通過kube-proxy來對pod產生訪問的代理
這樣,外界用戶就可以訪問集群中的nginx服務了
1.4 kubernetes概念
Master:集群控制節點,每個集群需要至少一個master節點負責集群的管控
Node:工作負載節點,由master分配容器到這些node工作節點上,然后node節點上的docker負責容器的運行
Pod:kubernetes的最小控制單元,容器都是運行在pod中的,一個pod中可以有1個或者多個容器
Controller:控制器,通過它來實現對pod的管理,比如啟動pod、停止pod、伸縮pod的數量等等
Service:pod對外服務的統一入口,下面可以維護者同一類的多個pod
Label:標簽,用于對pod進行分類,同一類pod會擁有相同的標簽
NameSpace:命名空間,用來隔離pod的運行環境
2. kubernetes集群環境搭建
2.1 前置知識點
目前生產部署Kubernetes 集群主要有兩種方式:
kubeadm
Kubeadm 是一個K8s 部署工具,提供kubeadm init 和kubeadm join,用于快速部署Kubernetes 集群。
官方地址:https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/
二進制包
從github 下載發行版的二進制包,手動部署每個組件,組成Kubernetes 集群。
Kubeadm 降低部署門檻,但屏蔽了很多細節,遇到問題很難排查。如果想更容易可控,推薦使用二進制包部署Kubernetes 集群,雖然手動部署麻煩點,期間可以學習很多工作原理,也利于后期維護。
2.2 kubeadm 部署方式介紹
kubeadm 是官方社區推出的一個用于快速部署kubernetes 集群的工具,這個工具能通過兩條指令完成一個kubernetes 集群的部署:
- 創建一個Master 節點kubeadm init
- 將Node 節點加入到當前集群中$ kubeadm join <Master 節點的IP 和端口>
2.3 安裝要求
在開始之前,部署Kubernetes 集群機器需要滿足以下幾個條件:
- 一臺或多臺機器,操作系統CentOS7.x-86_x64
- 硬件配置:2GB 或更多RAM,2 個CPU 或更多CPU,硬盤30GB 或更多
- 集群中所有機器之間網絡互通
- 可以訪問外網,需要拉取鏡像
- 禁止swap 分區
2.4 最終目標
- 在所有節點上安裝Docker 和kubeadm
- 部署Kubernetes Master
- 部署容器網絡插件
- 部署Kubernetes Node,將節點加入Kubernetes 集群中
- 部署Dashboard Web 頁面,可視化查看Kubernetes 資源
2.5 準備環境
角色 | IP地址 | 組件 |
---|---|---|
tigerhhzz-master01-31 | 192.168.162.31 | docker,kubectl,kubeadm,kubelet |
tigerhhzz-node01-41 | 192.168.162.41 | docker,kubectl,kubeadm,kubelet |
tigerhhzz-node02-42 | 192.168.162.42 | docker,kubectl,kubeadm,kubelet |
tigerhhzz-node03-43 | 192.168.162.43 | docker,kubectl,kubeadm,kubelet |
2.6 環境初始化
2.6.1 檢查操作系統的版本
# 此方式下安裝kubernetes集群要求Centos版本要在7.5或之上
[root@master ~]# cat /etc/redhat-release
Centos Linux 7.5.1804 (Core)
2.6.2 主機名解析
為了方便集群節點間的直接調用,在這個配置一下主機名解析,企業中推薦使用內部DNS服務器
# 主機名成解析 編輯三臺服務器的/etc/hosts文件,添加下面內容
192.168.162.31 k8s-master01
192.168.162.41 k8s-node01
192.168.162.42 k8s-node02
192.168.162.43 k8s-node03
2.6.3 時間同步
kubernetes要求集群中的節點時間必須精確一直,這里使用chronyd服務從網絡同步時間
企業中建議配置內部的會見同步服務器
# 啟動chronyd服務
[root@master ~]# systemctl start chronyd
[root@master ~]# systemctl enable chronyd
[root@master ~]# date
2.6.4 禁用iptable和firewalld服務
kubernetes和docker 在運行的中會產生大量的iptables規則,為了不讓系統規則跟它們混淆,直接關閉系統的規則
# 1 關閉firewalld服務
[root@master ~]# systemctl stop firewalld
[root@master ~]# systemctl disable firewalld
# 2 關閉iptables服務
[root@master ~]# systemctl stop iptables
[root@master ~]# systemctl disable iptables
2.6.5 禁用selinux
selinux是linux系統下的一個安全服務,如果不關閉它,在安裝集群中會產生各種各樣的奇葩問題
# 編輯 /etc/selinux/config 文件,修改SELINUX的值為disable
# 注意修改完畢之后需要重啟linux服務
SELINUX=disabled
2.6.6 禁用swap分區
swap分區指的是虛擬內存分區,它的作用是物理內存使用完,之后將磁盤空間虛擬成內存來使用,啟用swap設備會對系統的性能產生非常負面的影響,因此kubernetes要求每個節點都要禁用swap設備,但是如果因為某些原因確實不能關閉swap分區,就需要在集群安裝過程中通過明確的參數進行配置說明
# 編輯分區配置文件/etc/fstab,注釋掉swap分區一行
# 注意修改完畢之后需要重啟linux服務
vim /etc/fstab
注釋掉 /dev/mapper/centos-swap swap
# /dev/mapper/centos-swap swap
2.6.7 修改linux的內核參數
# 修改linux的內核采納數,添加網橋過濾和地址轉發功能
# 編輯/etc/sysctl.d/kubernetes.conf文件,添加如下配置:
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1# 重新加載配置
[root@master ~]# sysctl -p
# 加載網橋過濾模塊
[root@master ~]# modprobe br_netfilter
# 查看網橋過濾模塊是否加載成功
[root@master ~]# lsmod | grep br_netfilter
2.6.8 配置ipvs功能
在Kubernetes中Service有兩種帶來模型,一種是基于iptables的,一種是基于ipvs的兩者比較的話,ipvs的性能明顯要高一些,但是如果要使用它,需要手動載入ipvs模塊
# 1.安裝ipset和ipvsadm
[root@master ~]# yum install ipset ipvsadm -y
# 2.添加需要加載的模塊寫入腳本文件
[root@master ~]# cat <<EOF> /etc/sysconfig/modules/ipvs.modules
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF
# 3.為腳本添加執行權限
[root@master ~]# chmod +x /etc/sysconfig/modules/ipvs.modules
# 4.執行腳本文件
[root@master ~]# /bin/bash /etc/sysconfig/modules/ipvs.modules
# 5.查看對應的模塊是否加載成功
[root@master ~]# lsmod | grep -e ip_vs -e nf_conntrack_ipv4
2.6.9 安裝docker
# 1、切換鏡像源
[root@master ~]# wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo# 2、查看當前鏡像源中支持的docker版本
[root@master ~]# yum list docker-ce --showduplicates# 3、安裝特定版本的docker-ce
# 必須制定--setopt=obsoletes=0,否則yum會自動安裝更高版本
[root@master ~]# yum install --setopt=obsoletes=0 docker-ce-18.06.3.ce-3.el7 -y# 4、添加一個配置文件
#Docker 在默認情況下使用Vgroup Driver為cgroupfs,而Kubernetes推薦使用systemd來替代cgroupfs
[root@master ~]# mkdir /etc/docker
[root@master ~]# cat <<EOF> /etc/docker/daemon.json
{"exec-opts": ["native.cgroupdriver=systemd"],"registry-mirrors": ["https://kn0t2bca.mirror.aliyuncs.com"]
}
EOF# 5、啟動dokcer
[root@master ~]# systemctl restart docker
[root@master ~]# systemctl enable docker
2.6.10 安裝Kubernetes組件
# 1、由于kubernetes的鏡像在國外,速度比較慢,這里切換成國內的鏡像源
# 2、編輯/etc/yum.repos.d/kubernetes.repo,添加下面的配置
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgchech=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpghttp://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg# 3、安裝kubeadm、kubelet和kubectl
[root@master ~]# yum install --setopt=obsoletes=0 kubeadm-1.17.4-0 kubelet-1.17.4-0 kubectl-1.17.4-0 -y# 4、配置kubelet的cgroup
#編輯/etc/sysconfig/kubelet, 添加下面的配置
KUBELET_CGROUP_ARGS="--cgroup-driver=systemd"
KUBE_PROXY_MODE="ipvs"# 5、設置kubelet開機自啟
[root@master ~]# systemctl enable kubelet
2.6.11 準備集群鏡像
# 在安裝kubernetes集群之前,必須要提前準備好集群需要的鏡像,所需鏡像可以通過下面命令查看
[root@master ~]# kubeadm config images list# 下載鏡像
# 此鏡像kubernetes的倉庫中,由于網絡原因,無法連接,下面提供了一種替換方案
images=(kube-apiserver:v1.17.17kube-controller-manager:v1.17.17kube-scheduler:v1.17.17kube-proxy:v1.17.17pause:3.1etcd:3.4.3-0coredns:1.6.5
)for imageName in ${images[@]};dodocker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageNamedocker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageNamedocker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
done
2.6.11 集群初始化
下面的操作只需要在master節點上執行即可
# 創建集群
[root@master ~]# kubeadm init \--apiserver-advertise-address=192.168.162.31 \--image-repository registry.aliyuncs.com/google_containers \--kubernetes-version=v1.17.17 \--service-cidr=10.96.0.0/12 \--pod-network-cidr=10.244.0.0/16
# 創建必要文件[root@master ~]# mkdir -p $HOME/.kube[root@master ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config[root@master ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
下面的操作只需要在node節點上執行即可
kubeadm join 192.168.162.31:6443 --token 92oqey.88ma9jlx0vcwc5fh \--discovery-token-ca-cert-hash sha256:967bbc3b30871241bbfd61e42ae5fa836e08111a5a43d63b319f028fdbc2241a
在master上查看節點信息
[root@master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master NotReady master 6m v1.17.4
node1 NotReady <none> 22s v1.17.4
node2 NotReady <none> 19s v1.17.4
2.6.13 安裝網絡插件,只在master節點操作即可
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
由于外網不好訪問,如果出現無法訪問的情況,可以直接用下面的 記得文件名是kube-flannel.yml,位置:/root/kube-flannel.yml內容:
https://github.com/flannel-io/flannel/tree/master/Documentation/kube-flannel.yml
也可手動拉取指定版本
docker pull quay.io/coreos/flannel:v0.14.0 #拉取flannel網絡,三臺主機
docker images #查看倉庫是否拉去下來
個人筆記
若是集群狀態一直是 notready,用下面語句查看原因,
journalctl -f -u kubelet.service
若原因是: cni.go:237] Unable to update cni config: no networks found in /etc/cni/net.d
mkdir -p /etc/cni/net.d #創建目錄給flannel做配置文件
vim /etc/cni/net.d/10-flannel.conf #編寫配置文件
{"name":"cbr0","cniVersion":"0.3.1","type":"flannel","deledate":{"hairpinMode":true,"isDefaultGateway":true}}
2.6.14 使用kubeadm reset重置集群
#在master節點之外的節點進行操作
kubeadm reset
systemctl stop kubelet
systemctl stop docker
rm -rf /var/lib/cni/
rm -rf /var/lib/kubelet/*
rm -rf /etc/cni/
ifconfig cni0 down
ifconfig flannel.1 down
ifconfig docker0 down
ip link delete cni0
ip link delete flannel.1
##重啟kubelet
systemctl restart kubelet
##重啟docker
systemctl restart docker
2.6.15 重啟kubelet和docker
# 重啟kubelet
systemctl restart kubelet
# 重啟docker
systemctl restart docker
使用配置文件啟動fannel
kubectl apply -f kube-flannel.yml
等待它安裝完畢 發現已經是 集群的狀態已經是Ready
2.6.16 kubeadm中的命令(如果token失效了,一般是24h)
# 生成 新的token
[root@master ~]# kubeadm token create --print-join-command
2.7 集群測試
2.7.1 創建一個nginx服務
kubectl create deployment nginx --image=nginx:1.14-alpine
2.7.2 暴露端口
kubectl expose deploy nginx --port=80 --target-port=80 --type=NodePort
2.7.3 查看服務
kubectl get pod,svc
2.7.4 查看pod
瀏覽器測試結果:
http://k8s-master01:31433/
http://k8s-node01:31433/
http://k8s-node02:31433/
http://k8s-node03:31433/
2.8 解決在工作節點不能使用kubectl查看節點信息
1.將master節點中的/etc/kubernetes/admin.conf拷貝到要運行的工作服務器的/etc/kubernetes目錄中;
2.在對應的服務器上配置環境變量
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
3.讓環境變量生效
source ~/.bash_profile
4.測試
kubectl get nodes
3. 資源管理
3.1 資源管理介紹
在kubernetes中,所有的內容都抽象為資源,用戶需要通過操作資源來管理kubernetes。
kubernetes的本質上就是一個集群系統,用戶可以在集群中部署各種服務,所謂的部署服務,其實就是在kubernetes集群中運行一個個的容器,并將指定的程序跑在容器中。
kubernetes的最小管理單元是pod而不是容器,所以只能將容器放在
Pod
中,而kubernetes一般也不會直接管理Pod,而是通過Pod控制器
來管理Pod的。Pod可以提供服務之后,就要考慮如何訪問Pod中服務,kubernetes提供了
Service
資源實現這個功能。當然,如果Pod中程序的數據需要持久化,kubernetes還提供了各種
存儲
系統。
學習kubernetes的核心,就是學習如何對集群上的
Pod、Pod控制器、Service、存儲
等各種資源進行操作
3.2 YAML語言介紹
YAML是一個類似 XML、JSON 的標記性語言。它強調以數據為中心,并不是以標識語言為重點。因而YAML本身的定義比較簡單,號稱"一種人性化的數據格式語言"。
<heima><age>15</age><address>Beijing</address>
</heima>
heima:age: 15address: Beijing
YAML的語法比較簡單,主要有下面幾個:
- 大小寫敏感
- 使用縮進表示層級關系
- 縮進不允許使用tab,只允許空格( 低版本限制 )
- 縮進的空格數不重要,只要相同層級的元素左對齊即可
- '#'表示注釋
YAML支持以下幾種數據類型:
- 純量:單個的、不可再分的值
- 對象:鍵值對的集合,又稱為映射(mapping)/ 哈希(hash) / 字典(dictionary)
- 數組:一組按次序排列的值,又稱為序列(sequence) / 列表(list)
# 純量, 就是指的一個簡單的值,字符串、布爾值、整數、浮點數、Null、時間、日期
# 1 布爾類型
c1: true (或者True)
# 2 整型
c2: 234
# 3 浮點型
c3: 3.14
# 4 null類型
c4: ~ # 使用~表示null
# 5 日期類型
c5: 2018-02-17 # 日期必須使用ISO 8601格式,即yyyy-MM-dd
# 6 時間類型
c6: 2018-02-17T15:02:31+08:00 # 時間使用ISO 8601格式,時間和日期之間使用T連接,最后使用+代表時區
# 7 字符串類型
c7: heima # 簡單寫法,直接寫值 , 如果字符串中間有特殊字符,必須使用雙引號或者單引號包裹
c8: line1line2 # 字符串過多的情況可以拆成多行,每一行會被轉化成一個空格
# 對象
# 形式一(推薦):
heima:age: 15address: Beijing
# 形式二(了解):
heima: {age: 15,address: Beijing}
# 數組
# 形式一(推薦):
address:- 順義- 昌平
# 形式二(了解):
address: [順義,昌平]
小提示:
1 書寫yaml切記
:
后面要加一個空格2 如果需要將多段yaml配置放在一個文件中,中間要使用
---
分隔3 下面是一個yaml轉json的網站,可以通過它驗證yaml是否書寫正確
https://www.json2yaml.com/convert-yaml-to-json
3.3 資源管理方式
-
命令式對象管理:直接使用命令去操作kubernetes資源
kubectl run nginx-pod --image=nginx:1.17.1 --port=80
-
命令式對象配置:通過命令配置和配置文件去操作kubernetes資源
kubectl create/patch -f nginx-pod.yaml
-
聲明式對象配置:通過apply命令和配置文件去操作kubernetes資源
kubectl apply -f nginx-pod.yaml
類型 | 操作對象 | 適用環境 | 優點 | 缺點 |
---|---|---|---|---|
命令式對象管理 | 對象 | 測試 | 簡單 | 只能操作活動對象,無法審計、跟蹤 |
命令式對象配置 | 文件 | 開發 | 可以審計、跟蹤 | 項目大時,配置文件多,操作麻煩 |
聲明式對象配置 | 目錄 | 開發 | 支持目錄操作 | 意外情況下難以調試 |
3.3.1 命令式對象管理
kubectl命令
kubectl是kubernetes集群的命令行工具,通過它能夠對集群本身進行管理,并能夠在集群上進行容器化應用的安裝部署。kubectl命令的語法如下:
kubectl [command] [type] [name] [flags]
comand:指定要對資源執行的操作,例如create、get、delete
type:指定資源類型,比如deployment、pod、service
name:指定資源的名稱,名稱大小寫敏感
flags:指定額外的可選參數
# 查看所有pod
kubectl get pod # 查看某個pod
kubectl get pod pod_name# 查看某個pod,以yaml格式展示結果
kubectl get pod pod_name -o yaml
資源類型
kubernetes中所有的內容都抽象為資源,可以通過下面的命令進行查看:
kubectl api-resources
經常使用的資源有下面這些:
資源分類 | 資源名稱 | 縮寫 | 資源作用 |
---|---|---|---|
集群級別資源 | nodes | no | 集群組成部分 |
namespaces | ns | 隔離Pod | |
pod資源 | pods | po | 裝載容器 |
pod資源控制器 | replicationcontrollers | rc | 控制pod資源 |
replicasets | rs | 控制pod資源 | |
deployments | deploy | 控制pod資源 | |
daemonsets | ds | 控制pod資源 | |
jobs | 控制pod資源 | ||
cronjobs | cj | 控制pod資源 | |
horizontalpodautoscalers | hpa | 控制pod資源 | |
statefulsets | sts | 控制pod資源 | |
服務發現資源 | services | svc | 統一pod對外接口 |
ingress | ing | 統一pod對外接口 | |
存儲資源 | volumeattachments | 存儲 | |
persistentvolumes | pv | 存儲 | |
persistentvolumeclaims | pvc | 存儲 | |
配置資源 | configmaps | cm | 配置 |
secrets | 配置 |
操作
kubernetes允許對資源進行多種操作,可以通過–help查看詳細的操作命令
kubectl --help
經常使用的操作有下面這些:
命令分類 | 命令 | 翻譯 | 命令作用 |
---|---|---|---|
基本命令 | create | 創建 | 創建一個資源 |
edit | 編輯 | 編輯一個資源 | |
get | 獲取 | 獲取一個資源 | |
patch | 更新 | 更新一個資源 | |
delete | 刪除 | 刪除一個資源 | |
explain | 解釋 | 展示資源文檔 | |
運行和調試 | run | 運行 | 在集群中運行一個指定的鏡像 |
expose | 暴露 | 暴露資源為Service | |
describe | 描述 | 顯示資源內部信息 | |
logs | 日志輸出容器在 pod 中的日志 | 輸出容器在 pod 中的日志 | |
attach | 纏繞進入運行中的容器 | 進入運行中的容器 | |
exec | 執行容器中的一個命令 | 執行容器中的一個命令 | |
cp | 復制 | 在Pod內外復制文件 | |
rollout | 首次展示 | 管理資源的發布 | |
scale | 規模 | 擴(縮)容Pod的數量 | |
autoscale | 自動調整 | 自動調整Pod的數量 | |
高級命令 | apply | rc | 通過文件對資源進行配置 |
label | 標簽 | 更新資源上的標簽 | |
其他命令 | cluster-info | 集群信息 | 顯示集群信息 |
version | 版本 | 顯示當前Server和Client的版本 |
下面以一個namespace / pod的創建和刪除簡單演示下命令的使用:
# 創建一個namespace
[root@master ~]# kubectl create namespace dev
namespace/dev created# 獲取namespace
[root@master ~]# kubectl get ns
NAME STATUS AGE
default Active 21h
dev Active 21s
kube-node-lease Active 21h
kube-public Active 21h
kube-system Active 21h# 在此namespace下創建并運行一個nginx的Pod
[root@master ~]# kubectl run pod --image=nginx:latest -n dev
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/pod created# 查看新創建的pod
[root@master ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pod 1/1 Running 0 21s# 刪除指定的pod
[root@master ~]# kubectl delete pod pod-864f9875b9-pcw7x
pod "pod" deleted# 刪除指定的namespace
[root@master ~]# kubectl delete ns dev
namespace "dev" deleted
3.3.2 命令式對象配置
命令式對象配置就是使用命令配合配置文件一起來操作kubernetes資源。
1) 創建一個nginxpod.yaml,內容如下:
apiVersion: v1
kind: Namespace
metadata:name: dev---apiVersion: v1
kind: Pod
metadata:name: nginxpodnamespace: dev
spec:containers:- name: nginx-containersimage: nginx:latest
2)執行create命令,創建資源:
[root@master ~]# kubectl create -f nginxpod.yaml
namespace/dev created
pod/nginxpod created
此時發現創建了兩個資源對象,分別是namespace和pod
3)執行get命令,查看資源:
[root@master ~]# kubectl get -f nginxpod.yaml
NAME STATUS AGE
namespace/dev Active 18sNAME READY STATUS RESTARTS AGE
pod/nginxpod 1/1 Running 0 17s
這樣就顯示了兩個資源對象的信息
4)執行delete命令,刪除資源:
[root@master ~]# kubectl delete -f nginxpod.yaml
namespace "dev" deleted
pod "nginxpod" deleted
此時發現兩個資源對象被刪除了
總結:命令式對象配置的方式操作資源,可以簡單的認為:命令 + yaml配置文件(里面是命令需要的各種參數)
3.3.3 聲明式對象配置
聲明式對象配置跟命令式對象配置很相似,但是它只有一個命令apply。
# 首先執行一次kubectl apply -f yaml文件,發現創建了資源
[root@master ~]# kubectl apply -f nginxpod.yaml
namespace/dev created
pod/nginxpod created# 再次執行一次kubectl apply -f yaml文件,發現說資源沒有變動
[root@master ~]# kubectl apply -f nginxpod.yaml
namespace/dev unchanged
pod/nginxpod unchanged
總結:
其實聲明式對象配置就是使用apply描述一個資源最終的狀態(在yaml中定義狀態)
使用apply操作資源:
如果資源不存在,就創建,相當于 kubectl create
如果資源已存在,就更新,相當于 kubectl patch
擴展:kubectl可以在node節點上運行嗎 ?
kubectl的運行是需要進行配置的,它的配置文件是$HOME/.kube,如果想要在node節點運行此命令,需要將master上的.kube文件復制到node節點上,即在master節點上執行下面操作:
scp -r HOME/.kube node1: HOME/
使用推薦: 三種方式應該怎么用 ?
創建/更新資源 使用聲明式對象配置 kubectl apply -f XXX.yaml
刪除資源 使用命令式對象配置 kubectl delete -f XXX.yaml
查詢資源 使用命令式對象管理 kubectl get(describe) 資源名稱
4. 實戰入門
本章節將介紹如何在kubernetes集群中部署一個nginx服務,并且能夠對其進行訪問。
4.1 Namespace
Namespace是kubernetes系統中的一種非常重要資源,它的主要作用是用來實現多套環境的資源隔離或者多租戶的資源隔離。
默認情況下,kubernetes集群中的所有的Pod都是可以相互訪問的。但是在實際中,可能不想讓兩個Pod之間進行互相的訪問,那此時就可以將兩個Pod劃分到不同的namespace下。kubernetes通過將集群內部的資源分配到不同的Namespace中,可以形成邏輯上的"組",以方便不同的組的資源進行隔離使用和管理。
可以通過kubernetes的授權機制,將不同的namespace交給不同租戶進行管理,這樣就實現了多租戶的資源隔離。此時還能結合kubernetes的資源配額機制,限定不同租戶能占用的資源,例如CPU使用量、內存使用量等等,來實現租戶可用資源的管理。
kubernetes在集群啟動之后,會默認創建幾個namespace
[root@master ~]# kubectl get namespace
NAME STATUS AGE
default Active 45h # 所有未指定Namespace的對象都會被分配在default命名空間
kube-node-lease Active 45h # 集群節點之間的心跳維護,v1.13開始引入
kube-public Active 45h # 此命名空間下的資源可以被所有人訪問(包括未認證用戶)
kube-system Active 45h # 所有由Kubernetes系統創建的資源都處于這個命名空間
下面來看namespace資源的具體操作:
4.1.1 查看
# 1 查看所有的ns 命令:kubectl get ns
[root@master ~]# kubectl get ns
NAME STATUS AGE
default Active 45h
kube-node-lease Active 45h
kube-public Active 45h
kube-system Active 45h # 2 查看指定的ns 命令:kubectl get ns ns名稱
[root@master ~]# kubectl get ns default
NAME STATUS AGE
default Active 45h# 3 指定輸出格式 命令:kubectl get ns ns名稱 -o 格式參數
# kubernetes支持的格式有很多,比較常見的是wide、json、yaml
[root@master ~]# kubectl get ns default -o yaml
apiVersion: v1
kind: Namespace
metadata:creationTimestamp: "2021-05-08T04:44:16Z"name: defaultresourceVersion: "151"selfLink: /api/v1/namespaces/defaultuid: 7405f73a-e486-43d4-9db6-145f1409f090
spec:finalizers:- kubernetes
status:phase: Active# 4 查看ns詳情 命令:kubectl describe ns ns名稱
[root@master ~]# kubectl describe ns default
Name: default
Labels: <none>
Annotations: <none>
Status: Active # Active 命名空間正在使用中 Terminating 正在刪除命名空間# ResourceQuota 針對namespace做的資源限制
# LimitRange針對namespace中的每個組件做的資源限制
No resource quota.
No LimitRange resource.
4.1.2 創建
# 創建namespace
[root@master ~]# kubectl create ns dev
namespace/dev created
4.1.3 刪除
# 刪除namespace
[root@master ~]# kubectl delete ns dev
namespace "dev" deleted
4.1.4 配置方式
首先準備一個yaml文件:ns-dev.yaml
apiVersion: v1
kind: Namespace
metadata:name: dev
然后就可以執行對應的創建和刪除命令了:
創建:kubectl create -f ns-dev.yaml
刪除:kubectl delete -f ns-dev.yaml
4.2 Pod
Pod是kubernetes集群進行管理的最小單元,程序要運行必須部署在容器中,而容器必須存在于Pod中。
Pod可以認為是容器的封裝,一個Pod中可以存在一個或者多個容器。
kubernetes在集群啟動之后,集群中的各個組件也都是以Pod方式運行的。可以通過下面命令查看:
[root@master ~]# kubectl get pod -n kube-system
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-6955765f44-68g6v 1/1 Running 0 2d1h
kube-system coredns-6955765f44-cs5r8 1/1 Running 0 2d1h
kube-system etcd-master 1/1 Running 0 2d1h
kube-system kube-apiserver-master 1/1 Running 0 2d1h
kube-system kube-controller-manager-master 1/1 Running 0 2d1h
kube-system kube-flannel-ds-amd64-47r25 1/1 Running 0 2d1h
kube-system kube-flannel-ds-amd64-ls5lh 1/1 Running 0 2d1h
kube-system kube-proxy-685tk 1/1 Running 0 2d1h
kube-system kube-proxy-87spt 1/1 Running 0 2d1h
kube-system kube-scheduler-master 1/1 Running 0 2d1h
4.2.1 創建并運行
kubernetes沒有提供單獨運行Pod的命令,都是通過Pod控制器來實現的
# 命令格式: kubectl run (pod控制器名稱) [參數]
# --image 指定Pod的鏡像
# --port 指定端口
# --namespace 指定namespace
[root@master ~]# kubectl run nginx --image=nginx:latest --port=80 --namespace dev
deployment.apps/nginx created
4.2.2 查看pod信息
# 查看Pod基本信息
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 43s# 查看Pod的詳細信息
[root@master ~]# kubectl describe pod nginx -n dev
Name: nginx
Namespace: dev
Priority: 0
Node: node1/192.168.5.4
Start Time: Wed, 08 May 2021 09:29:24 +0800
Labels: pod-template-hash=5ff7956ff6run=nginx
Annotations: <none>
Status: Running
IP: 10.244.1.23
IPs:IP: 10.244.1.23
Controlled By: ReplicaSet/nginx
Containers:nginx:Container ID: docker://4c62b8c0648d2512380f4ffa5da2c99d16e05634979973449c98e9b829f6253cImage: nginx:latestImage ID: docker-pullable://nginx@sha256:485b610fefec7ff6c463ced9623314a04ed67e3945b9c08d7e53a47f6d108dc7Port: 80/TCPHost Port: 0/TCPState: RunningStarted: Wed, 08 May 2021 09:30:01 +0800Ready: TrueRestart Count: 0Environment: <none>Mounts:/var/run/secrets/kubernetes.io/serviceaccount from default-token-hwvvw (ro)
Conditions:Type StatusInitialized TrueReady TrueContainersReady TruePodScheduled True
Volumes:default-token-hwvvw:Type: Secret (a volume populated by a Secret)SecretName: default-token-hwvvwOptional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300snode.kubernetes.io/unreachable:NoExecute for 300s
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled <unknown> default-scheduler Successfully assigned dev/nginx-5ff7956ff6-fg2db to node1Normal Pulling 4m11s kubelet, node1 Pulling image "nginx:latest"Normal Pulled 3m36s kubelet, node1 Successfully pulled image "nginx:latest"Normal Created 3m36s kubelet, node1 Created container nginxNormal Started 3m36s kubelet, node1 Started container nginx
4.2.3 訪問Pod
# 獲取podIP
[root@master ~]# kubectl get pods -n dev -o wideapiVersion: v1
kind: Service
metadata:name: service-nodeportnamespace: dev
spec:selector:app: nginx-podtype: NodePort # service類型ports:- port: 80nodePort: 30002 # 指定綁定的node的端口(默認的取值范圍是:30000-32767), 如果不指定,會默認分配targetPort: 80
# 創建service
[root@k8s-master01 ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created# 查看service
[root@k8s-master01 ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) SELECTOR
service-nodeport NodePort 10.105.64.191 <none> 80:30002/TCP app=nginx-pod# 接下來可以通過電腦主機的瀏覽器去訪問集群中任意一個nodeip的30002端口,即可訪問到pod
4.2.4 刪除指定Pod
# 刪除指定Pod
[root@master ~]# kubectl delete pod nginx -n dev
pod "nginx" deleted# 此時,顯示刪除Pod成功,但是再查詢,發現又新產生了一個
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 21s# 這是因為當前Pod是由Pod控制器創建的,控制器會監控Pod狀況,一旦發現Pod死亡,會立即重建
# 此時要想刪除Pod,必須刪除Pod控制器# 先來查詢一下當前namespace下的Pod控制器
[root@master ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 9m7s# 接下來,刪除此PodPod控制器
[root@master ~]# kubectl delete deploy nginx -n dev
deployment.apps "nginx" deleted# 稍等片刻,再查詢Pod,發現Pod被刪除了
[root@master ~]# kubectl get pods -n dev
No resources found in dev namespace.
4.2.5 配置操作
創建一個pod-nginx.yaml,內容如下:
apiVersion: v1
kind: Pod
metadata:name: nginxnamespace: dev
spec:containers:- image: nginx:latestname: podports:- name: nginx-portcontainerPort: 80protocol: TCP
然后就可以執行對應的創建和刪除命令了:
創建:kubectl create -f pod-nginx.yaml
刪除:kubectl delete -f pod-nginx.yaml
4.3 Label
Label是kubernetes系統中的一個重要概念。它的作用就是在資源上添加標識,用來對它們進行區分和選擇。
Label的特點:
- 一個Label會以key/value鍵值對的形式附加到各種對象上,如Node、Pod、Service等等
- 一個資源對象可以定義任意數量的Label ,同一個Label也可以被添加到任意數量的資源對象上去
- Label通常在資源對象定義時確定,當然也可以在對象創建后動態添加或者刪除
可以通過Label實現資源的多維度分組,以便靈活、方便地進行資源分配、調度、配置、部署等管理工作。
一些常用的Label 示例如下:
- 版本標簽:“version”:“release”, “version”:“stable”…
- 環境標簽:“environment”:“dev”,“environment”:“test”,“environment”:“pro”
- 架構標簽:“tier”:“frontend”,“tier”:“backend”
標簽定義完畢之后,還要考慮到標簽的選擇,這就要使用到Label Selector,即:
Label用于給某個資源對象定義標識
Label Selector用于查詢和篩選擁有某些標簽的資源對象
當前有兩種Label Selector:
-
基于等式的Label Selector
name = slave: 選擇所有包含Label中key="name"且value="slave"的對象
env != production: 選擇所有包括Label中的key="env"且value不等于"production"的對象
-
基于集合的Label Selector
name in (master, slave): 選擇所有包含Label中的key="name"且value="master"或"slave"的對象
name not in (frontend): 選擇所有包含Label中的key="name"且value不等于"frontend"的對象
標簽的選擇條件可以使用多個,此時將多個Label Selector進行組合,使用逗號","進行分隔即可。例如:
name=slave,env!=production
name not in (frontend),env!=production
4.3.1 命令方式
# 為pod資源打標簽
[root@master ~]# kubectl label pod nginx-pod version=1.0 -n dev
pod/nginx-pod labeled# 為pod資源更新標簽
[root@master ~]# kubectl label pod nginx-pod version=2.0 -n dev --overwrite
pod/nginx-pod labeled# 查看標簽
[root@master ~]# kubectl get pod nginx-pod -n dev --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-pod 1/1 Running 0 10m version=2.0# 篩選標簽
[root@master ~]# kubectl get pod -n dev -l version=2.0 --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-pod 1/1 Running 0 17m version=2.0
[root@master ~]# kubectl get pod -n dev -l version!=2.0 --show-labels
No resources found in dev namespace.#刪除標簽
[root@master ~]# kubectl label pod nginx-pod -n dev tier-
pod/nginx unlabeled
4.3.2 配置方式
apiVersion: v1
kind: Pod
metadata:name: nginxnamespace: devlabels:version: "3.0" env: "test"
spec:containers:- image: nginx:latestname: podports:- name: nginx-portcontainerPort: 80protocol: TCP
然后就可以執行對應的更新命令了:kubectl apply -f pod-nginx.yaml
4.4 Deployment
在kubernetes中,Pod是最小的控制單元,但是kubernetes很少直接控制Pod,一般都是通過Pod控制器來完成的。Pod控制器用于pod的管理,確保pod資源符合預期的狀態,當pod的資源出現故障時,會嘗試進行重啟或重建pod。
在kubernetes中Pod控制器的種類有很多,本章節只介紹一種:Deployment。
4.4.1 命令操作
# 命令格式: kubectl create deployment 名稱 [參數]
# --image 指定pod的鏡像
# --port 指定端口
# --replicas 指定創建pod數量
# --namespace 指定namespace
[root@master ~]# kubectl run nginx --image=nginx:latest --port=80 --replicas=3 -n dev
deployment.apps/nginx created# 查看創建的Pod
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
nginx-5ff7956ff6-6k8cb 1/1 Running 0 19s
nginx-5ff7956ff6-jxfjt 1/1 Running 0 19s
nginx-5ff7956ff6-v6jqw 1/1 Running 0 19s# 查看deployment的信息
[root@master ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 3/3 3 3 2m42s# UP-TO-DATE:成功升級的副本數量
# AVAILABLE:可用副本的數量
[root@master ~]# kubectl get deploy -n dev -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx 3/3 3 3 2m51s nginx nginx:latest run=nginx# 查看deployment的詳細信息
[root@master ~]# kubectl describe deploy nginx -n dev
Name: nginx
Namespace: dev
CreationTimestamp: Wed, 08 May 2021 11:14:14 +0800
Labels: run=nginx
Annotations: deployment.kubernetes.io/revision: 1
Selector: run=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max 違規詞匯
Pod Template:Labels: run=nginxContainers:nginx:Image: nginx:latestPort: 80/TCPHost Port: 0/TCPEnvironment: <none>Mounts: <none>Volumes: <none>
Conditions:Type Status Reason---- ------ ------Available True MinimumReplicasAvailableProgressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-5ff7956ff6 (3/3 replicas created)
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal ScalingReplicaSet 5m43s deployment-controller Scaled up replicaset nginx-5ff7956ff6 to 3# 刪除
[root@master ~]# kubectl delete deploy nginx -n dev
deployment.apps "nginx" deleted
4.4.2 配置操作
創建一個deploy-nginx.yaml,內容如下:
apiVersion: apps/v1
kind: Deployment
metadata:name: nginxnamespace: dev
spec:replicas: 3selector:matchLabels:run: nginxtemplate:metadata:labels:run: nginxspec:containers:- image: nginx:latestname: nginxports:- containerPort: 80protocol: TCP
然后就可以執行對應的創建和刪除命令了:
創建:kubectl create -f deploy-nginx.yaml
刪除:kubectl delete -f deploy-nginx.yaml
4.5 Service
通過上節課的學習,已經能夠利用Deployment來創建一組Pod來提供具有高可用性的服務。
雖然每個Pod都會分配一個單獨的Pod IP,然而卻存在如下兩問題:
- Pod IP 會隨著Pod的重建產生變化
- Pod IP 僅僅是集群內可見的虛擬IP,外部無法訪問
這樣對于訪問這個服務帶來了難度。因此,kubernetes設計了Service來解決這個問題。
Service可以看作是一組同類Pod對外的訪問接口。借助Service,應用可以方便地實現服務發現和負載均衡。
4.5.1 創建集群內部可訪問的Service
# 暴露Service
[root@master ~]# kubectl expose deploy nginx --name=svc-nginx1 --type=ClusterIP --port=80 --target-port=80 -n dev
service/svc-nginx1 exposed# 查看service
[root@master ~]# kubectl get svc svc-nginx1 -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx1 ClusterIP 10.109.179.231 <none> 80/TCP 3m51s run=nginx# 這里產生了一個CLUSTER-IP,這就是service的IP,在Service的生命周期中,這個地址是不會變動的
# 可以通過這個IP訪問當前service對應的POD
[root@master ~]# curl 10.109.179.231:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<h1>Welcome to nginx!</h1>
.......
</body>
</html>
4.5.2 創建集群外部也可訪問的Service
# 上面創建的Service的type類型為ClusterIP,這個ip地址只用集群內部可訪問
# 如果需要創建外部也可以訪問的Service,需要修改type為NodePort
[root@master ~]# kubectl expose deploy nginx --name=svc-nginx2 --type=NodePort --port=80 --target-port=80 -n dev
service/svc-nginx2 exposed# 此時查看,會發現出現了NodePort類型的Service,而且有一對Port(80:31928/TC)
[root@master ~]# kubectl get svc svc-nginx2 -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx2 NodePort 10.100.94.0 <none> 80:31928/TCP 9s run=nginx# 接下來就可以通過集群外的主機訪問 節點IP:31928訪問服務了
# 例如在的電腦主機上通過瀏覽器訪問下面的地址
http://192.168.90.100:31928/
4.5.3 刪除Service
[root@master ~]# kubectl delete svc svc-nginx-1 -n dev
service "svc-nginx-1" deleted
4.5.4 配置方式
創建一個svc-nginx.yaml,內容如下:
apiVersion: v1
kind: Service
metadata:name: svc-nginxnamespace: dev
spec:clusterIP: 10.109.179.231 #固定svc的內網ipports:- port: 80protocol: TCPtargetPort: 80selector:run: nginxtype: ClusterIP
然后就可以執行對應的創建和刪除命令了:
創建:kubectl create -f svc-nginx.yaml
刪除:kubectl delete -f svc-nginx.yaml
小結
至此,已經掌握了Namespace、Pod、Deployment、Service資源的基本操作,有了這些操作,就可以在kubernetes集群中實現一個服務的簡單部署和訪問了,但是如果想要更好的使用kubernetes,就需要深入學習這幾種資源的細節和原理。
5. Pod詳解
5.1 Pod介紹
5.1.1 Pod結構
每個Pod中都可以包含一個或者多個容器,這些容器可以分為兩類:
-
用戶程序所在的容器,數量可多可少
-
Pause容器,這是每個Pod都會有的一個根容器,它的作用有兩個:
-
可以以它為依據,評估整個Pod的健康狀態
-
可以在根容器上設置Ip地址,其它容器都此Ip(Pod IP),以實現Pod內部的網路通信
這里是Pod內部的通訊,Pod的之間的通訊采用虛擬二層網絡技術來實現,我們當前環境用的是Flannel
-
5.1.2 Pod定義
下面是Pod的資源清單:
apiVersion: v1 #必選,版本號,例如v1
kind: Pod #必選,資源類型,例如 Pod
metadata: #必選,元數據name: string #必選,Pod名稱namespace: string #Pod所屬的命名空間,默認為"default"labels: #自定義標簽列表- name: string
spec: #必選,Pod中容器的詳細定義containers: #必選,Pod中容器列表- name: string #必選,容器名稱image: string #必選,容器的鏡像名稱imagePullPolicy: [ Always|Never|IfNotPresent ] #獲取鏡像的策略 command: [string] #容器的啟動命令列表,如不指定,使用打包時使用的啟動命令args: [string] #容器的啟動命令參數列表workingDir: string #容器的工作目錄volumeMounts: #掛載到容器內部的存儲卷配置- name: string #引用pod定義的共享存儲卷的名稱,需用volumes[]部分定義的的卷名mountPath: string #存儲卷在容器內mount的絕對路徑,應少于512字符readOnly: boolean #是否為只讀模式ports: #需要暴露的端口庫號列表- name: string #端口的名稱containerPort: int #容器需要監聽的端口號hostPort: int #容器所在主機需要監聽的端口號,默認與Container相同protocol: string #端口協議,支持TCP和UDP,默認TCPenv: #容器運行前需設置的環境變量列表- name: string #環境變量名稱value: string #環境變量的值resources: #資源限制和請求的設置limits: #資源限制的設置cpu: string #Cpu的限制,單位為core數,將用于docker run --cpu-shares參數memory: string #內存限制,單位可以為Mib/Gib,將用于docker run --memory參數requests: #資源請求的設置cpu: string #Cpu請求,容器啟動的初始可用數量memory: string #內存請求,容器啟動的初始可用數量lifecycle: #生命周期鉤子postStart: #容器啟動后立即執行此鉤子,如果執行失敗,會根據重啟策略進行重啟preStop: #容器終止前執行此鉤子,無論結果如何,容器都會終止livenessProbe: #對Pod內各容器健康檢查的設置,當探測無響應幾次后將自動重啟該容器exec: #對Pod容器內檢查方式設置為exec方式command: [string] #exec方式需要制定的命令或腳本httpGet: #對Pod內個容器健康檢查方法設置為HttpGet,需要制定Path、portpath: stringport: numberhost: stringscheme: stringHttpHeaders:- name: stringvalue: stringtcpSocket: #對Pod內個容器健康檢查方式設置為tcpSocket方式port: numberinitialDelaySeconds: 0 #容器啟動完成后首次探測的時間,單位為秒timeoutSeconds: 0 #對容器健康檢查探測等待響應的超時時間,單位秒,默認1秒periodSeconds: 0 #對容器監控檢查的定期探測時間設置,單位秒,默認10秒一次successThreshold: 0failureThreshold: 0securityContext:privileged: falserestartPolicy: [Always | Never | OnFailure] #Pod的重啟策略nodeName: <string> #設置NodeName表示將該Pod調度到指定到名稱的node節點上nodeSelector: obeject #設置NodeSelector表示將該Pod調度到包含這個label的node上imagePullSecrets: #Pull鏡像時使用的secret名稱,以key:secretkey格式指定- name: stringhostNetwork: false #是否使用主機網絡模式,默認為false,如果設置為true,表示使用宿主機網絡volumes: #在該pod上定義共享存儲卷列表- name: string #共享存儲卷名稱 (volumes類型有很多種)emptyDir: {} #類型為emtyDir的存儲卷,與Pod同生命周期的一個臨時目錄。為空值hostPath: string #類型為hostPath的存儲卷,表示掛載Pod所在宿主機的目錄path: string #Pod所在宿主機的目錄,將被用于同期中mount的目錄secret: #類型為secret的存儲卷,掛載集群與定義的secret對象到容器內部scretname: string items: - key: stringpath: stringconfigMap: #類型為configMap的存儲卷,掛載預定義的configMap對象到容器內部name: stringitems:- key: stringpath: string
#小提示:
# 在這里,可通過一個命令來查看每種資源的可配置項
# kubectl explain 資源類型 查看某種資源可以配置的一級屬性
# kubectl explain 資源類型.屬性 查看屬性的子屬性
[root@k8s-master01 ~]# kubectl explain pod
KIND: Pod
VERSION: v1
FIELDS:apiVersion <string>kind <string>metadata <Object>spec <Object>status <Object>[root@k8s-master01 ~]# kubectl explain pod.metadata
KIND: Pod
VERSION: v1
RESOURCE: metadata <Object>
FIELDS:annotations <map[string]string>clusterName <string>creationTimestamp <string>deletionGracePeriodSeconds <integer>deletionTimestamp <string>finalizers <[]string>generateName <string>generation <integer>labels <map[string]string>managedFields <[]Object>name <string>namespace <string>ownerReferences <[]Object>resourceVersion <string>selfLink <string>uid <string>
5.2 Pod配置
本小節主要來研究pod.spec.containers屬性,這也是pod配置中最為關鍵的一項配置。
5.2.1 基本配置
創建pod-base.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-basenamespace: devlabels:user: heima
spec:containers:- name: nginximage: nginx:1.17.1- name: busyboximage: busybox:1.30
kubectl get pods -n dev
上面定義了一個比較簡單Pod的配置,里面有兩個容器:
- nginx:用1.17.1版本的nginx鏡像創建,(nginx是一個輕量級web容器)
- busybox:用1.30版本的busybox鏡像創建,(busybox是一個小巧的linux命令集合)
# 創建Pod
[root@k8s-master01 pod]# kubectl apply -f pod-base.yaml
pod/pod-base created# 查看Pod狀況
# READY 1/2 : 表示當前Pod中有2個容器,其中1個準備就緒,1個未就緒
# RESTARTS : 重啟次數,因為有1個容器故障了,Pod一直在重啟試圖恢復它
[root@k8s-master01 pod]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pod-base 1/2 Running 4 95s# 可以通過describe查看內部的詳情
# 此時已經運行起來了一個基本的Pod,雖然它暫時有問題
[root@k8s-master01 pod]# kubectl describe pod pod-base -n dev
5.2.2 鏡像拉取
創建pod-imagepullpolicy.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-imagepullpolicynamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1imagePullPolicy: Never # 用于設置鏡像拉取策略- name: busyboximage: busybox:1.30
imagePullPolicy,用于設置鏡像拉取策略,kubernetes支持配置三種拉取策略:
- Always:總是從遠程倉庫拉取鏡像(一直遠程下載)
- IfNotPresent:本地有則使用本地鏡像,本地沒有則從遠程倉庫拉取鏡像(本地有就本地 本地沒遠程下載)
- Never:只使用本地鏡像,從不去遠程倉庫拉取,本地沒有就報錯 (一直使用本地)
默認值說明:
如果鏡像tag為具體版本號, 默認策略是:IfNotPresent
如果鏡像tag為:latest(最終版本) ,默認策略是always
# 創建Pod
[root@k8s-master01 pod]# kubectl create -f pod-imagepullpolicy.yaml
pod/pod-imagepullpolicy created# 查看Pod詳情
# 此時明顯可以看到nginx鏡像有一步Pulling image "nginx:1.17.1"的過程
[root@k8s-master01 pod]# kubectl describe pod pod-imagepullpolicy -n dev
......
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled <unknown> default-scheduler Successfully assigned dev/pod-imagePullPolicy to node1Normal Pulling 32s kubelet, node1 Pulling image "nginx:1.17.1"Normal Pulled 26s kubelet, node1 Successfully pulled image "nginx:1.17.1"Normal Created 26s kubelet, node1 Created container nginxNormal Started 25s kubelet, node1 Started container nginxNormal Pulled 7s (x3 over 25s) kubelet, node1 Container image "busybox:1.30" already present on machineNormal Created 7s (x3 over 25s) kubelet, node1 Created container busyboxNormal Started 7s (x3 over 25s) kubelet, node1 Started container busybox
5.2.3 啟動命令
在前面的案例中,一直有一個問題沒有解決,就是的busybox容器一直沒有成功運行,那么到底是什么原因導致這個容器的故障呢?
原來busybox并不是一個程序,而是類似于一個工具類的集合,kubernetes集群啟動管理后,它會自動關閉。解決方法就是讓其一直在運行,這就用到了command配置。
創建pod-command.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-commandnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]
command,用于在pod中的容器初始化完畢之后運行一個命令。
稍微解釋下上面命令的意思:
“/bin/sh”,“-c”, 使用sh執行命令 touch /tmp/hello.txt; 創建一個/tmp/hello.txt 文件 while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done; 每隔3秒向文件中寫入當前時間
# 創建Pod
[root@k8s-master01 pod]# kubectl create -f pod-command.yaml
pod/pod-command created# 查看Pod狀態
# 此時發現兩個pod都正常運行了
[root@k8s-master01 pod]# kubectl get pods pod-command -n dev
NAME READY STATUS RESTARTS AGE
pod-command 2/2 Runing 0 2s# 進入pod中的busybox容器,查看文件內容
# 補充一個命令: kubectl exec pod名稱 -n 命名空間 -it -c 容器名稱 /bin/sh 在容器內部執行命令
# 使用這個命令就可以進入某個容器的內部,然后進行相關操作了
# 比如,可以查看txt文件的內容
[root@k8s-master01 pod]# kubectl exec pod-command -n dev -it -c busybox /bin/sh
/ # tail -f /tmp/hello.txt
14:44:19
14:44:22
14:44:25
特別說明:
通過上面發現command已經可以完成啟動命令和傳遞參數的功能,為什么這里還要提供一個args選項,用于傳遞參數呢?這其實跟docker有點關系,kubernetes中的command、args兩項其實是實現覆蓋Dockerfile中ENTRYPOINT的功能。
1.如果command和args均沒有寫,那么用Dockerfile的配置。
2.如果command寫了,但args沒有寫,那么Dockerfile默認的配置會被忽略,執行輸入的command
3.如果command沒寫,但args寫了,那么Dockerfile中配置的ENTRYPOINT的命令會被執行,使用當前args的參數
4.如果command和args都寫了,那么Dockerfile的配置被忽略,執行command并追加上args參數
5.2.4 環境變量
創建pod-env.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-envnamespace: dev
spec:containers:- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]env: # 設置環境變量列表- name: "username"value: "admin"- name: "password"value: "123456"
env,環境變量,用于在pod中的容器設置環境變量。
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-env.yaml
pod/pod-env created# 進入容器,輸出環境變量
[root@k8s-master01 ~]# kubectl exec pod-env -n dev -c busybox -it /bin/sh
/ # echo $username
admin
/ # echo $password
123456
這種方式不是很推薦,推薦將這些配置單獨存儲在配置文件中,這種方式將在后面介紹。
5.2.5 端口設置
本小節來介紹容器的端口設置,也就是containers的ports選項。
首先看下ports支持的子選項:
[root@k8s-master01 ~]# kubectl explain pod.spec.containers.ports
KIND: Pod
VERSION: v1
RESOURCE: ports <[]Object>
FIELDS:name <string> # 端口名稱,如果指定,必須保證name在pod中是唯一的 containerPort<integer> # 容器要監聽的端口(0<x<65536)hostPort <integer> # 容器要在主機上公開的端口,如果設置,主機上只能運行容器的一個副本(一般省略) hostIP <string> # 要將外部端口綁定到的主機IP(一般省略)protocol <string> # 端口協議。必須是UDP、TCP或SCTP。默認為“TCP”。
接下來,編寫一個測試案例,創建pod-ports.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-portsnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: # 設置容器暴露的端口列表- name: nginx-portcontainerPort: 80protocol: TCP
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-ports.yaml
pod/pod-ports created# 查看pod
# 在下面可以明顯看到配置信息
[root@k8s-master01 ~]# kubectl get pod pod-ports -n dev -o yaml
......
spec:containers:- image: nginx:1.17.1imagePullPolicy: IfNotPresentname: nginxports:- containerPort: 80name: nginx-portprotocol: TCP
......
訪問容器中的程序需要使用的是Podip:containerPort
5.2.6 資源配額
容器中的程序要運行,肯定是要占用一定資源的,比如cpu和內存等,如果不對某個容器的資源做限制,那么它就可能吃掉大量資源,導致其它容器無法運行。針對這種情況,kubernetes提供了對內存和cpu的資源進行配額的機制,這種機制主要通過resources選項實現,他有兩個子選項:
- limits:用于限制運行時容器的最大占用資源,當容器占用資源超過limits時會被終止,并進行重啟
- requests :用于設置容器需要的最小資源,如果環境資源不夠,容器將無法啟動
可以通過上面兩個選項設置資源的上下限。
接下來,編寫一個測試案例,創建pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-resourcesnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1resources: # 資源配額limits: # 限制資源(上限)cpu: "2" # CPU限制,單位是core數memory: "10Gi" # 內存限制requests: # 請求資源(下限)cpu: "1" # CPU限制,單位是core數memory: "10Mi" # 內存限制
在這對cpu和memory的單位做一個說明:
- cpu:core數,可以為整數或小數
- memory: 內存大小,可以使用Gi、Mi、G、M等形式
# 運行Pod
[root@k8s-master01 ~]# kubectl create -f pod-resources.yaml
pod/pod-resources created# 查看發現pod運行正常
[root@k8s-master01 ~]# kubectl get pod pod-resources -n dev
NAME READY STATUS RESTARTS AGE
pod-resources 1/1 Running 0 39s # 接下來,停止Pod
[root@k8s-master01 ~]# kubectl delete -f pod-resources.yaml
pod "pod-resources" deleted# 編輯pod,修改resources.requests.memory的值為10Gi
[root@k8s-master01 ~]# vim pod-resources.yaml# 再次啟動pod
[root@k8s-master01 ~]# kubectl create -f pod-resources.yaml
pod/pod-resources created# 查看Pod狀態,發現Pod啟動失敗
[root@k8s-master01 ~]# kubectl get pod pod-resources -n dev -o wide
NAME READY STATUS RESTARTS AGE
pod-resources 0/1 Pending 0 20s # 查看pod詳情會發現,如下提示
[root@k8s-master01 ~]# kubectl describe pod pod-resources -n dev
......
Warning FailedScheduling 35s default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 Insufficient memory.(內存不足)
5.3 Pod生命周期
我們一般將pod對象從創建至終的這段時間范圍稱為pod的生命周期,它主要包含下面的過程:
-
pod創建過程
-
運行初始化容器(init container)過程
-
運行主容器(main container)
? - 容器啟動后鉤子(post start)、容器終止前鉤子(pre stop)
- 容器的存活性探測(liveness probe)、就緒性探測(readiness probe)
-
pod終止過程
在整個生命周期中,Pod會出現5種狀態(相位),分別如下:
- 掛起(Pending):apiserver已經創建了pod資源對象,但它尚未被調度完成或者仍處于下載鏡像的過程中
- 運行中(Running):pod已經被調度至某節點,并且所有容器都已經被kubelet創建完成
- 成功(Succeeded):pod中的所有容器都已經成功終止并且不會被重啟
- 失敗(Failed):所有容器都已經終止,但至少有一個容器終止失敗,即容器返回了非0值的退出狀態
- 未知(Unknown):apiserver無法正常獲取到pod對象的狀態信息,通常由網絡通信失敗所導致
5.3.1 創建和終止
pod的創建過程
- 用戶通過kubectl或其他api客戶端提交需要創建的pod信息給apiServer
- apiServer開始生成pod對象的信息,并將信息存入etcd,然后返回確認信息至客戶端
- apiServer開始反映etcd中的pod對象的變化,其它組件使用watch機制來跟蹤檢查apiServer上的變動
- scheduler發現有新的pod對象要創建,開始為Pod分配主機并將結果信息更新至apiServer
- node節點上的kubelet發現有pod調度過來,嘗試調用docker啟動容器,并將結果回送至apiServer
- apiServer將接收到的pod狀態信息存入etcd中
pod的終止過程
-
用戶向apiServer發送刪除pod對象的命令
-
apiServcer中的pod對象信息會隨著時間的推移而更新,在寬限期內(默認30s),pod被視為dead
-
將pod標記為terminating狀態
-
kubelet在監控到pod對象轉為terminating狀態的同時啟動pod關閉過程
-
端點控制器監控到pod對象的關閉行為時將其從所有匹配到此端點的service資源的端點列表中移除
-
如果當前pod對象定義了preStop鉤子處理器,則在其標記為terminating后即會以同步的方式啟動執行
-
pod對象中的容器進程收到停止信號
-
寬限期結束后,若pod中還存在仍在運行的進程,那么pod對象會收到立即終止的信號
-
kubelet請求apiServer將此pod資源的寬限期設置為0從而完成刪除操作,此時pod對于用戶已不可見
5.3.2 初始化容器
初始化容器是在pod的主容器啟動之前要運行的容器,主要是做一些主容器的前置工作,它具有兩大特征:
-
初始化容器必須運行完成直至結束,若某初始化容器運行失敗,那么kubernetes需要重啟它直到成功完成
-
初始化容器必須按照定義的順序執行,當且僅當前一個成功之后,后面的一個才能運行
初始化容器有很多的應用場景,下面列出的是最常見的幾個:
- 提供主容器鏡像中不具備的工具程序或自定義代碼
- 初始化容器要先于應用容器串行啟動并運行完成,因此可用于延后應用容器的啟動直至其依賴的條件得到滿足
接下來做一個案例,模擬下面這個需求:
假設要以主容器來運行nginx,但是要求在運行nginx之前先要能夠連接上mysql和redis所在服務器
為了簡化測試,事先規定好mysql(192.168.90.14)和redis(192.168.90.15)服務器的地址
創建pod-initcontainer.yaml,內容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-initcontainernamespace: dev
spec:containers:- name: main-containerimage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80initContainers:- name: test-mysqlimage: busybox:1.30command: ['sh', '-c', 'until ping 192.168.90.14 -c 1 ; do echo waiting for mysql...; sleep 2; done;']- name: test-redisimage: busybox:1.30command: ['sh', '-c', 'until ping 192.168.90.15 -c 1 ; do echo waiting for reids...; sleep 2; done;']
# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-initcontainer.yaml
pod/pod-initcontainer created# 查看pod狀態
# 發現pod卡在啟動第一個初始化容器過程中,后面的容器不會運行
root@k8s-master01 ~]# kubectl describe pod pod-initcontainer -n dev
........
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 49s default-scheduler Successfully assigned dev/pod-initcontainer to node1Normal Pulled 48s kubelet, node1 Container image "busybox:1.30" already present on machineNormal Created 48s kubelet, node1 Created container test-mysqlNormal Started 48s kubelet, node1 Started container test-mysql# 動態查看pod
[root@k8s-master01 ~]# kubectl get pods pod-initcontainer -n dev -w
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Init:0/2 0 15s
pod-initcontainer 0/1 Init:1/2 0 52s
pod-initcontainer 0/1 Init:1/2 0 53s
pod-initcontainer 0/1 PodInitializing 0 89s
pod-initcontainer 1/1 Running 0 90s# 接下來新開一個shell,為當前服務器新增兩個ip,觀察pod的變化
[root@k8s-master01 ~]# ifconfig ens33:1 192.168.90.14 netmask 255.255.255.0 up
[root@k8s-master01 ~]# ifconfig ens33:2 192.168.90.15 netmask 255.255.255.0 up
5.3.3 鉤子函數
鉤子函數能夠感知自身生命周期中的事件,并在相應的時刻到來時運行用戶指定的程序代碼。
kubernetes在主容器的啟動之后和停止之前提供了兩個鉤子函數:
- post start:容器創建之后執行,如果失敗了會重啟容器
- pre stop :容器終止之前執行,執行完成之后容器將成功終止,在其完成之前會阻塞刪除容器的操作
鉤子處理器支持使用下面三種方式定義動作:
- Exec命令:在容器內執行一次命令
……lifecycle:postStart: exec:command:- cat- /tmp/healthy
……
TCPSocket:在當前容器嘗試訪問指定的socket
…… lifecycle:postStart:tcpSocket:port: 8080
……
……lifecycle:postStart:httpGet:path: / #URI地址port: 80 #端口號host: 192.168.5.3 #主機地址scheme: HTTP #支持的協議,http或者https
……
接下來,以exec方式為例,演示下鉤子函數的使用,創建pod-hook-exec.yaml文件,內容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-hook-execnamespace: dev
spec:containers:- name: main-containerimage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80lifecycle:postStart: exec: # 在容器啟動的時候執行一個命令,修改掉nginx的默認首頁內容command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]preStop:exec: # 在容器停止之前停止nginx服務command: ["/usr/sbin/nginx","-s","quit"]
# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-hook-exec.yaml
pod/pod-hook-exec created# 查看pod
[root@k8s-master01 ~]# kubectl get pods pod-hook-exec -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-hook-exec 1/1 Running 0 29s 10.244.2.48 node2 # 訪問pod
[root@k8s-master01 ~]# curl 10.244.2.48
postStart...
5.3.4 容器探測
容器探測用于檢測容器中的應用實例是否正常工作,是保障業務可用性的一種傳統機制。如果經過探測,實例的狀態不符合預期,那么kubernetes就會把該問題實例" 摘除 ",不承擔業務流量。kubernetes提供了兩種探針來實現容器探測,分別是:
- liveness probes:存活性探針,用于檢測應用實例當前是否處于正常運行狀態,如果不是,k8s會重啟容器
- readiness probes:就緒性探針,用于檢測應用實例當前是否可以接收請求,如果不能,k8s不會轉發流量
livenessProbe 決定是否重啟容器,readinessProbe 決定是否將請求轉發給容器。
上面兩種探針目前均支持三種探測方式:
- Exec命令:在容器內執行一次命令,如果命令執行的退出碼為0,則認為程序正常,否則不正常
……livenessProbe:exec:command:- cat- /tmp/healthy
……
- TCPSocket:將會嘗試訪問一個用戶容器的端口,如果能夠建立這條連接,則認為程序正常,否則不正常
…… livenessProbe:tcpSocket:port: 8080
……
- HTTPGet:調用容器內Web應用的URL,如果返回的狀態碼在200和399之間,則認為程序正常,否則不正常
……livenessProbe:httpGet:path: / #URI地址port: 80 #端口號host: 127.0.0.1 #主機地址scheme: HTTP #支持的協議,http或者https
……
下面以liveness probes為例,做幾個演示:
方式一:Exec
創建pod-liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-execnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80livenessProbe:exec:command: ["/bin/cat","/tmp/hello.txt"] # 執行一個查看文件的命令
創建pod,觀察效果
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-exec.yaml
pod/pod-liveness-exec created# 查看Pod詳情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-exec -n dev
......Normal Created 20s (x2 over 50s) kubelet, node1 Created container nginxNormal Started 20s (x2 over 50s) kubelet, node1 Started container nginxNormal Killing 20s kubelet, node1 Container nginx failed liveness probe, will be restartedWarning Unhealthy 0s (x5 over 40s) kubelet, node1 Liveness probe failed: cat: can't open '/tmp/hello11.txt': No such file or directory# 觀察上面的信息就會發現nginx容器啟動之后就進行了健康檢查
# 檢查失敗之后,容器被kill掉,然后嘗試進行重啟(這是重啟策略的作用,后面講解)
# 稍等一會之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~]# kubectl get pods pod-liveness-exec -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 0/1 CrashLoopBackOff 2 3m19s# 當然接下來,可以修改成一個存在的文件,比如/tmp/hello.txt,再試,結果就正常了......
方式二:TCPSocket
創建pod-liveness-tcpsocket.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-tcpsocketnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80livenessProbe:tcpSocket:port: 8080 # 嘗試訪問8080端口
創建pod,觀察效果
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created# 查看Pod詳情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-tcpsocket -n dev
......Normal Scheduled 31s default-scheduler Successfully assigned dev/pod-liveness-tcpsocket to node2Normal Pulled <invalid> kubelet, node2 Container image "nginx:1.17.1" already present on machineNormal Created <invalid> kubelet, node2 Created container nginxNormal Started <invalid> kubelet, node2 Started container nginxWarning Unhealthy <invalid> (x2 over <invalid>) kubelet, node2 Liveness probe failed: dial tcp 10.244.2.44:8080: connect: connection refused# 觀察上面的信息,發現嘗試訪問8080端口,但是失敗了
# 稍等一會之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~]# kubectl get pods pod-liveness-tcpsocket -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 0/1 CrashLoopBackOff 2 3m19s# 當然接下來,可以修改成一個可以訪問的端口,比如80,再試,結果就正常了......
方式三:HTTPGet
創建pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet: # 其實就是訪問http://127.0.0.1:80/hello scheme: HTTP #支持的協議,http或者httpsport: 80 #端口號path: /hello #URI地址
創建pod,觀察效果
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created# 查看Pod詳情
[root@k8s-master01 ~]# kubectl describe pod pod-liveness-httpget -n dev
.......Normal Pulled 6s (x3 over 64s) kubelet, node1 Container image "nginx:1.17.1" already present on machineNormal Created 6s (x3 over 64s) kubelet, node1 Created container nginxNormal Started 6s (x3 over 63s) kubelet, node1 Started container nginxWarning Unhealthy 6s (x6 over 56s) kubelet, node1 Liveness probe failed: HTTP probe failed with statuscode: 404Normal Killing 6s (x2 over 36s) kubelet, node1 Container nginx failed liveness probe, will be restarted# 觀察上面信息,嘗試訪問路徑,但是未找到,出現404錯誤
# 稍等一會之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~]# kubectl get pod pod-liveness-httpget -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 5 3m17s# 當然接下來,可以修改成一個可以訪問的路徑path,比如/,再試,結果就正常了......
至此,已經使用liveness Probe演示了三種探測方式,但是查看livenessProbe的子屬性,會發現除了這三種方式,還有一些其他的配置,在這里一并解釋下:
[root@k8s-master01 ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:exec <Object> tcpSocket <Object>httpGet <Object>initialDelaySeconds <integer> # 容器啟動后等待多少秒執行第一次探測timeoutSeconds <integer> # 探測超時時間。默認1秒,最小1秒periodSeconds <integer> # 執行探測的頻率。默認是10秒,最小1秒failureThreshold <integer> # 連續探測失敗多少次才被認定為失敗。默認是3。最小值是1successThreshold <integer> # 連續探測成功多少次才被認定為成功。默認是1
下面稍微配置兩個,演示下效果即可:
[root@k8s-master01 ~]# more pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80 path: /initialDelaySeconds: 30 # 容器啟動后30s開始探測timeoutSeconds: 5 # 探測超時時間為5s
5.3.5 重啟策略
在上一節中,一旦容器探測出現了問題,kubernetes就會對容器所在的Pod進行重啟,其實這是由pod的重啟策略決定的,pod的重啟策略有 3 種,分別如下:
- Always :容器失效時,自動重啟該容器,這也是默認值。
- OnFailure : 容器終止運行且退出碼不為0時重啟
- Never : 不論狀態為何,都不重啟該容器
重啟策略適用于pod對象中的所有容器,首次需要重啟的容器,將在其需要時立即進行重啟,隨后再次需要重啟的操作將由kubelet延遲一段時間后進行,且反復的重啟操作的延遲時長以此為10s、20s、40s、80s、160s和300s,300s是最大延遲時長。
創建pod-restartpolicy.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-restartpolicynamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80path: /hellorestartPolicy: Never # 設置重啟策略為Never
運行Pod測試
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-restartpolicy.yaml
pod/pod-restartpolicy created# 查看Pod詳情,發現nginx容器失敗
[root@k8s-master01 ~]# kubectl describe pods pod-restartpolicy -n dev
......Warning Unhealthy 15s (x3 over 35s) kubelet, node1 Liveness probe failed: HTTP probe failed with statuscode: 404Normal Killing 15s kubelet, node1 Container nginx failed liveness probe# 多等一會,再觀察pod的重啟次數,發現一直是0,并未重啟
[root@k8s-master01 ~]# kubectl get pods pod-restartpolicy -n dev
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Running 0 5min42s
5.4 Pod調度
在默認情況下,一個Pod在哪個Node節點上運行,是由Scheduler組件采用相應的算法計算出來的,這個過程是不受人工控制的。但是在實際使用中,這并不滿足的需求,因為很多情況下,我們想控制某些Pod到達某些節點上,那么應該怎么做呢?這就要求了解kubernetes對Pod的調度規則,kubernetes提供了四大類調度方式:
- 自動調度:運行在哪個節點上完全由Scheduler經過一系列的算法計算得出
- 定向調度:NodeName、NodeSelector
- 親和性調度:NodeAffinity、PodAffinity、PodAntiAffinity
- 污點(容忍)調度:Taints、Toleration
5.4.1 定向調度
定向調度,指的是利用在pod上聲明nodeName或者nodeSelector,以此將Pod調度到期望的node節點上。注意,這里的調度是強制的,這就意味著即使要調度的目標Node不存在,也會向上面進行調度,只不過pod運行失敗而已。
NodeName
NodeName用于強制約束將Pod調度到指定的Name的Node節點上。這種方式,其實是直接跳過Scheduler的調度邏輯,直接將Pod調度到指定名稱的節點。
接下來,實驗一下:創建一個pod-nodename.yaml文件
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeName: node1 # 指定調度到node1節點上
#創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created#查看Pod調度到NODE屬性,確實是調度到了node1節點上
[root@k8s-master01 ~]# kubectl get pods pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodename 1/1 Running 0 56s 10.244.1.87 node1 ...... # 接下來,刪除pod,修改nodeName的值為node3(并沒有node3節點)
[root@k8s-master01 ~]# kubectl delete -f pod-nodename.yaml
pod "pod-nodename" deleted
[root@k8s-master01 ~]# vim pod-nodename.yaml
[root@k8s-master01 ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created#再次查看,發現已經向Node3節點調度,但是由于不存在node3節點,所以pod無法正常運行
[root@k8s-master01 ~]# kubectl get pods pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodename 0/1 Pending 0 6s <none> node3 ......
NodeSelector
NodeSelector用于將pod調度到添加了指定標簽的node節點上。它是通過kubernetes的label-selector機制實現的,也就是說,在pod創建之前,會由scheduler使用MatchNodeSelector調度策略進行label匹配,找出目標node,然后將pod調度到目標節點,該匹配規則是強制約束。
接下來,實驗一下:
1 首先分別為node節點添加標簽
[root@k8s-master01 ~]# kubectl label nodes node1 nodeenv=pro
node/node2 labeled
[root@k8s-master01 ~]# kubectl label nodes node2 nodeenv=test
node/node2 labeled
2 創建一個pod-nodeselector.yaml文件,并使用它創建Pod
apiVersion: v1
kind: Pod
metadata:name: pod-nodeselectornamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeSelector: nodeenv: pro # 指定調度到具有nodeenv=pro標簽的節點上
#創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created#查看Pod調度到NODE屬性,確實是調度到了node1節點上
[root@k8s-master01 ~]# kubectl get pods pod-nodeselector -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodeselector 1/1 Running 0 47s 10.244.1.87 node1 ......# 接下來,刪除pod,修改nodeSelector的值為nodeenv: xxxx(不存在打有此標簽的節點)
[root@k8s-master01 ~]# kubectl delete -f pod-nodeselector.yaml
pod "pod-nodeselector" deleted
[root@k8s-master01 ~]# vim pod-nodeselector.yaml
[root@k8s-master01 ~]# kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created#再次查看,發現pod無法正常運行,Node的值為none
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-nodeselector 0/1 Pending 0 2m20s <none> <none># 查看詳情,發現node selector匹配失敗的提示
[root@k8s-master01 ~]# kubectl describe pods pod-nodeselector -n dev
.......
Events:Type Reason Age From Message---- ------ ---- ---- -------Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
5.4.2 親和性調度
上一節,介紹了兩種定向調度的方式,使用起來非常方便,但是也有一定的問題,那就是如果沒有滿足條件的Node,那么Pod將不會被運行,即使在集群中還有可用Node列表也不行,這就限制了它的使用場景。
基于上面的問題,kubernetes還提供了一種親和性調度(Affinity)。它在NodeSelector的基礎之上的進行了擴展,可以通過配置的形式,實現優先選擇滿足條件的Node進行調度,如果沒有,也可以調度到不滿足條件的節點上,使調度更加靈活。
Affinity主要分為三類:
- nodeAffinity(node親和性): 以node為目標,解決pod可以調度到哪些node的問題
- podAffinity(pod親和性) : 以pod為目標,解決pod可以和哪些已存在的pod部署在同一個拓撲域中的問題
- podAntiAffinity(pod反親和性) : 以pod為目標,解決pod不能和哪些已存在pod部署在同一個拓撲域中的問題
關于親和性(反親和性)使用場景的說明:
- 親和性:如果兩個應用頻繁交互,那就有必要利用親和性讓兩個應用的盡可能的靠近,這樣可以減少因網絡通信而帶來的性能損耗。
- 反親和性:當應用的采用多副本部署時,有必要采用反親和性讓各個應用實例打散分布在各個node上,這樣可以提高服務的高可用性。
NodeAffinity
首先來看一下NodeAffinity的可配置項:
pod.spec.affinity.nodeAffinityrequiredDuringSchedulingIgnoredDuringExecution Node節點必須滿足指定的所有規則才可以,相當于硬限制nodeSelectorTerms 節點選擇列表matchFields 按節點字段列出的節點選擇器要求列表matchExpressions 按節點標簽列出的節點選擇器要求列表(推薦)key 鍵values 值operat or 關系符 支持Exists, DoesNotExist, In, NotIn, Gt, LtpreferredDuringSchedulingIgnoredDuringExecution 優先調度到滿足指定的規則的Node,相當于軟限制 (傾向)preference 一個節點選擇器項,與相應的權重相關聯matchFields 按節點字段列出的節點選擇器要求列表matchExpressions 按節點標簽列出的節點選擇器要求列表(推薦)key 鍵values 值operator 關系符 支持In, NotIn, Exists, DoesNotExist, Gt, Ltweight 傾向權重,在范圍1-100。
關系符的使用說明:- matchExpressions:- key: nodeenv # 匹配存在標簽的key為nodeenv的節點operator: Exists- key: nodeenv # 匹配標簽的key為nodeenv,且value是"xxx"或"yyy"的節點operator: Invalues: ["xxx","yyy"]- key: nodeenv # 匹配標簽的key為nodeenv,且value大于"xxx"的節點operator: Gtvalues: "xxx"
接下來首先演示一下requiredDuringSchedulingIgnoredDuringExecution ,
創建pod-nodeaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: #親和性設置nodeAffinity: #設置node親和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制nodeSelectorTerms:- matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽- key: nodeenvoperator: Invalues: ["xxx","yyy"]
# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created# 查看pod狀態 (運行失敗)
[root@k8s-master01 ~]# kubectl get pods pod-nodeaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodeaffinity-required 0/1 Pending 0 16s <none> <none> ......# 查看Pod的詳情
# 發現調度失敗,提示node選擇失敗
[root@k8s-master01 ~]# kubectl describe pod pod-nodeaffinity-required -n dev
......Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.#接下來,停止pod
[root@k8s-master01 ~]# kubectl delete -f pod-nodeaffinity-required.yaml
pod "pod-nodeaffinity-required" deleted# 修改文件,將values: ["xxx","yyy"]------> ["pro","yyy"]
[root@k8s-master01 ~]# vim pod-nodeaffinity-required.yaml# 再次啟動
[root@k8s-master01 ~]# kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created# 此時查看,發現調度成功,已經將pod調度到了node1上
[root@k8s-master01 ~]# kubectl get pods pod-nodeaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodeaffinity-required 1/1 Running 0 11s 10.244.1.89 node1 ......
接下來再演示一下requiredDuringSchedulingIgnoredDuringExecution ,
創建pod-nodeaffinity-preferred.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-preferrednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: #親和性設置nodeAffinity: #設置node親和性preferredDuringSchedulingIgnoredDuringExecution: # 軟限制- weight: 1preference:matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽(當前環境沒有)- key: nodeenvoperator: Invalues: ["xxx","yyy"]
# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-nodeaffinity-preferred.yaml
pod/pod-nodeaffinity-preferred created# 查看pod狀態 (運行成功)
[root@k8s-master01 ~]# kubectl get pod pod-nodeaffinity-preferred -n dev
NAME READY STATUS RESTARTS AGE
pod-nodeaffinity-preferred 1/1 Running 0 40s
NodeAffinity規則設置的注意事項:1 如果同時定義了nodeSelector和nodeAffinity,那么必須兩個條件都得到滿足,Pod才能運行在指定的Node上2 如果nodeAffinity指定了多個nodeSelectorTerms,那么只需要其中一個能夠匹配成功即可3 如果一個nodeSelectorTerms中有多個matchExpressions ,則一個節點必須滿足所有的才能匹配成功4 如果一個pod所在的Node在Pod運行期間其標簽發生了改變,不再符合該Pod的節點親和性需求,則系統將忽略此變化
PodAffinity
PodAffinity主要實現以運行的Pod為參照,實現讓新創建的Pod跟參照pod在一個區域的功能。
首先來看一下PodAffinity的可配置項:
pod.spec.affinity.podAffinityrequiredDuringSchedulingIgnoredDuringExecution 硬限制namespaces 指定參照pod的namespacetopologyKey 指定調度作用域labelSelector 標簽選擇器matchExpressions 按節點標簽列出的節點選擇器要求列表(推薦)key 鍵values 值operator 關系符 支持In, NotIn, Exists, DoesNotExist.matchLabels 指多個matchExpressions映射的內容preferredDuringSchedulingIgnoredDuringExecution 軟限制podAffinityTerm 選項namespaces topologyKeylabelSelectormatchExpressions key 鍵values 值operatormatchLabels weight 傾向權重,在范圍1-100
topologyKey用于指定調度時作用域,例如:如果指定為kubernetes.io/hostname,那就是以Node節點為區分范圍如果指定為beta.kubernetes.io/os,則以Node節點的操作系統類型來區分
接下來,演示下requiredDuringSchedulingIgnoredDuringExecution,
1)首先創建一個參照Pod,pod-podaffinity-target.yaml:
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-targetnamespace: devlabels:podenv: pro #設置標簽
spec:containers:- name: nginximage: nginx:1.17.1nodeName: node1 # 將目標pod名確指定到node1上
# 啟動目標pod
[root@k8s-master01 ~]# kubectl create -f pod-podaffinity-target.yaml
pod/pod-podaffinity-target created# 查看pod狀況
[root@k8s-master01 ~]# kubectl get pods pod-podaffinity-target -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-target 1/1 Running 0 4s
2)創建pod-podaffinity-required.yaml,內容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: #親和性設置podAffinity: #設置pod親和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽- key: podenvoperator: Invalues: ["xxx","yyy"]topologyKey: kubernetes.io/hostname
上面配置表達的意思是:新Pod必須要與擁有標簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上,顯然現在沒有這樣pod,接下來,運行測試一下。
# 啟動pod
[root@k8s-master01 ~]# kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created# 查看pod狀態,發現未運行
[root@k8s-master01 ~]# kubectl get pods pod-podaffinity-required -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-required 0/1 Pending 0 9s# 查看詳細信息
[root@k8s-master01 ~]# kubectl describe pods pod-podaffinity-required -n dev
......
Events:Type Reason Age From Message---- ------ ---- ---- -------Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 2 node(s) didn't match pod affinity rules, 1 node(s) had taints that the pod didn't tolerate.# 接下來修改 values: ["xxx","yyy"]----->values:["pro","yyy"]
# 意思是:新Pod必須要與擁有標簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
[root@k8s-master01 ~]# vim pod-podaffinity-required.yaml# 然后重新創建pod,查看效果
[root@k8s-master01 ~]# kubectl delete -f pod-podaffinity-required.yaml
pod "pod-podaffinity-required" de leted
[root@k8s-master01 ~]# kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created# 發現此時Pod運行正常
[root@k8s-master01 ~]# kubectl get pods pod-podaffinity-required -n dev
NAME READY STATUS RESTARTS AGE LABELS
pod-podaffinity-required 1/1 Running 0 6s <none>
關于PodAffinity的 preferredDuringSchedulingIgnoredDuringExecution,這里不再演示。
PodAntiAffinity
PodAntiAffinity主要實現以運行的Pod為參照,讓新創建的Pod跟參照pod不在一個區域中的功能。
它的配置方式和選項跟PodAffinty是一樣的,這里不再做詳細解釋,直接做一個測試案例。
1)繼續使用上個案例中目標pod
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE LABELS
pod-podaffinity-required 1/1 Running 0 3m29s 10.244.1.38 node1 <none>
pod-podaffinity-target 1/1 Running 0 9m25s 10.244.1.37 node1 podenv=pro
2)創建pod-podantiaffinity-required.yaml,內容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-podantiaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: #親和性設置podAntiAffinity: #設置pod親和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配podenv的值在["pro"]中的標簽- key: podenvoperator: Invalues: ["pro"]topologyKey: kubernetes.io/hostname
上面配置表達的意思是:新Pod必須要與擁有標簽nodeenv=pro的pod不在同一Node上,運行測試一下。
# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-podantiaffinity-required.yaml
pod/pod-podantiaffinity-required created# 查看pod
# 發現調度到了node2上
[root@k8s-master01 ~]# kubectl get pods pod-podantiaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ..
pod-podantiaffinity-required 1/1 Running 0 30s 10.244.1.96 node2 ..
5.4.3 污點和容忍
污點(Taints)
前面的調度方式都是站在Pod的角度上,通過在Pod上添加屬性,來確定Pod是否要調度到指定的Node上,其實我們也可以站在Node的角度上,通過在Node上添加污點屬性,來決定是否允許Pod調度過來。
Node被設置上污點之后就和Pod之間存在了一種相斥的關系,進而拒絕Pod調度進來,甚至可以將已經存在的Pod驅逐出去。
污點的格式為:key=value:effect, key和value是污點的標簽,effect描述污點的作用,支持如下三個選項:
- PreferNoSchedule:kubernetes將盡量避免把Pod調度到具有該污點的Node上,除非沒有其他節點可調度
- NoSchedule:kubernetes將不會把Pod調度到具有該污點的Node上,但不會影響當前Node上已存在的Pod
- NoExecute:kubernetes將不會把Pod調度到具有該污點的Node上,同時也會將Node上已存在的Pod驅離
使用kubectl設置和去除污點的命令示例如下:
# 設置污點
kubectl taint nodes node1 key=value:effect# 去除污點
kubectl taint nodes node1 key:effect-# 去除所有污點
kubectl taint nodes node1 key-
接下來,演示下污點的效果:
- 準備節點node1(為了演示效果更加明顯,暫時停止node2節點)
- 為node1節點設置一個污點: tag=heima:PreferNoSchedule;然后創建pod1( pod1 可以 )
- 修改為node1節點設置一個污點: tag=heima:NoSchedule;然后創建pod2( pod1 正常 pod2 失敗 )
- 修改為node1節點設置一個污點: tag=heima:NoExecute;然后創建pod3 ( 3個pod都失敗 )
# 為node1設置污點(PreferNoSchedule)
[root@k8s-master01 ~]# kubectl taint nodes node1 tag=heima:PreferNoSchedule# 創建pod1
[root@k8s-master01 ~]# kubectl run taint1 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint1-7665f7fd85-574h4 1/1 Running 0 2m24s 10.244.1.59 node1 # 為node1設置污點(取消PreferNoSchedule,設置NoSchedule)
[root@k8s-master01 ~]# kubectl taint nodes node1 tag:PreferNoSchedule-
[root@k8s-master01 ~]# kubectl taint nodes node1 tag=heima:NoSchedule# 創建pod2
[root@k8s-master01 ~]# kubectl run taint2 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~]# kubectl get pods taint2 -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint1-7665f7fd85-574h4 1/1 Running 0 2m24s 10.244.1.59 node1
taint2-544694789-6zmlf 0/1 Pending 0 21s <none> <none> # 為node1設置污點(取消NoSchedule,設置NoExecute)
[root@k8s-master01 ~]# kubectl taint nodes node1 tag:NoSchedule-
[root@k8s-master01 ~]# kubectl taint nodes node1 tag=heima:NoExecute# 創建pod3
[root@k8s-master01 ~]# kubectl run taint3 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
taint1-7665f7fd85-htkmp 0/1 Pending 0 35s <none> <none> <none>
taint2-544694789-bn7wb 0/1 Pending 0 35s <none> <none> <none>
taint3-6d78dbd749-tktkq 0/1 Pending 0 6s <none> <none> <none>
小提示:使用kubeadm搭建的集群,默認就會給master節點添加一個污點標記,所以pod就不會調度到master節點上.
容忍(Toleration)
上面介紹了污點的作用,我們可以在node上添加污點用于拒絕pod調度上來,但是如果就是想將一個pod調度到一個有污點的node上去,這時候應該怎么做呢?這就要使用到容忍。
下面先通過一個案例看下效果:
-
上一小節,已經在node1節點上打上了NoExecute的污點,此時pod是調度不上去的
-
本小節,可以通過給pod添加容忍,然后將其調度上去
創建pod-toleration.yaml,內容如下
apiVersion: v1
kind: Pod
metadata:name: pod-tolerationnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1tolerations: # 添加容忍- key: "tag" # 要容忍的污點的keyoperator: "Equal" # 操作符value: "heima" # 容忍的污點的valueeffect: "NoExecute" # 添加容忍的規則,這里必須和標記的污點規則相同
# 添加容忍之前的pod
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
pod-toleration 0/1 Pending 0 3s <none> <none> <none> # 添加容忍之后的pod
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
pod-toleration 1/1 Running 0 3s 10.244.1.62 node1 <none>
下面看一下容忍的詳細配置:
[root@k8s-master01 ~]# kubectl explain pod.spec.tolerations
......
FIELDS:key # 對應著要容忍的污點的鍵,空意味著匹配所有的鍵value # 對應著要容忍的污點的值operator # key-value的運算符,支持Equal和Exists(默認)effect # 對應污點的effect,空意味著匹配所有影響tolerationSeconds # 容忍時間, 當effect為NoExecute時生效,表示pod在Node上的停留時間
dule# 創建pod1
[root@k8s-master01 ~]# kubectl run taint1 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint1-7665f7fd85-574h4 1/1 Running 0 2m24s 10.244.1.59 node1 # 為node1設置污點(取消PreferNoSchedule,設置NoSchedule)
[root@k8s-master01 ~]# kubectl taint nodes node1 tag:PreferNoSchedule-
[root@k8s-master01 ~]# kubectl taint nodes node1 tag=heima:NoSchedule# 創建pod2
[root@k8s-master01 ~]# kubectl run taint2 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~]# kubectl get pods taint2 -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint1-7665f7fd85-574h4 1/1 Running 0 2m24s 10.244.1.59 node1
taint2-544694789-6zmlf 0/1 Pending 0 21s <none> <none> # 為node1設置污點(取消NoSchedule,設置NoExecute)
[root@k8s-master01 ~]# kubectl taint nodes node1 tag:NoSchedule-
[root@k8s-master01 ~]# kubectl taint nodes node1 tag=heima:NoExecute# 創建pod3
[root@k8s-master01 ~]# kubectl run taint3 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
taint1-7665f7fd85-htkmp 0/1 Pending 0 35s <none> <none> <none>
taint2-544694789-bn7wb 0/1 Pending 0 35s <none> <none> <none>
taint3-6d78dbd749-tktkq 0/1 Pending 0 6s <none> <none> <none>
小提示:使用kubeadm搭建的集群,默認就會給master節點添加一個污點標記,所以pod就不會調度到master節點上.
容忍(Toleration)
上面介紹了污點的作用,我們可以在node上添加污點用于拒絕pod調度上來,但是如果就是想將一個pod調度到一個有污點的node上去,這時候應該怎么做呢?這就要使用到容忍。
下面先通過一個案例看下效果:
-
上一小節,已經在node1節點上打上了NoExecute的污點,此時pod是調度不上去的
-
本小節,可以通過給pod添加容忍,然后將其調度上去
創建pod-toleration.yaml,內容如下
apiVersion: v1
kind: Pod
metadata:name: pod-tolerationnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1tolerations: # 添加容忍- key: "tag" # 要容忍的污點的keyoperator: "Equal" # 操作符value: "heima" # 容忍的污點的valueeffect: "NoExecute" # 添加容忍的規則,這里必須和標記的污點規則相同
# 添加容忍之前的pod
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
pod-toleration 0/1 Pending 0 3s <none> <none> <none> # 添加容忍之后的pod
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
pod-toleration 1/1 Running 0 3s 10.244.1.62 node1 <none>
下面看一下容忍的詳細配置:
[root@k8s-master01 ~]# kubectl explain pod.spec.tolerations
......
FIELDS:key # 對應著要容忍的污點的鍵,空意味著匹配所有的鍵value # 對應著要容忍的污點的值operator # key-value的運算符,支持Equal和Exists(默認)effect # 對應污點的effect,空意味著匹配所有影響tolerationSeconds # 容忍時間, 當effect為NoExecute時生效,表示pod在Node上的停留時間