Kubernetes里的DNS
K8s集群內有一個DNS服務:
kubectl get svc -n kube-system |grep dns
測試:
在tang3上安裝bind-utils,目的是安裝dig命令
yum install -y bind-utils
apt install dnsutils #ubuntu上
解析外網域名
dig @10.15.0.10 www.baidu.com
解析內部域名
dig @10.15.0.10 ngx-svc.default.svc.cluster.local
說明: ngx-svc為service name,service完整域名為service.namespace.svc.cluster.local
還可以解析Pod,Pod的域名有點特殊,格式為..pod.,例如10-18-206-93.default.pod.cluster.local
對應的Pod為coredns:
kubectl get po coredns -n kube-system
解釋:
- nameserver: 定義DNS服務器的IP,其實就是kube-dns那個service的IP。
- search: 定義域名的查找后綴規則,查找配置越多,說明域名解析查找匹配次數越多。集群匹配有 default.svc.cluster.local、svc.cluster.local、cluster.local 3個后綴,最多進行8次查詢 (IPV4和IPV6查詢各四次) 才能得到正確解析結果。不同命名空間,這個參數的值也不同。
- option: 定義域名解析配置文件選項,支持多個KV值。例如該參數設置成ndots:5,說明如果訪問的域名字符串內的點字符數量超過ndots值,則認為是完整域名,并被直接解析;如果不足ndots值,則追加search段后綴再進行查詢。
DNS配置
可以通過查看coredns的configmap來獲取DNS的配置信息:
[root@tang3 k8s]# kubectl describe cm coredns -n kube-system
Name: coredns
Namespace: kube-system
Labels: <none>
Annotations: <none>Data
====
Corefile:
----
.:53 {errorshealth {lameduck 5s}readykubernetes cluster.local in-addr.arpa ip6.arpa {pods insecurefallthrough in-addr.arpa ip6.arpattl 30}prometheus :9153forward . /etc/resolv.conf {max_concurrent 1000}cache 30loopreloadloadbalance
}
說明:
- errors:錯誤信息到標準輸出。
- health:CoreDNS自身健康狀態報告,默認監聽端口8080,一般用來做健康檢查。您可以通過http://10.18.206.207:8080/health獲取健康狀態。(10.18.206.207為coredns其中一個Pod的IP)
- ready:CoreDNS插件狀態報告,默認監聽端口8181,一般用來做可讀性檢查。可以通過http://10.18.206.207:8181/ready獲取可讀狀態。當所有插件都運行后,ready狀態為200。
- kubernetes:CoreDNS kubernetes插件,提供集群內服務解析能力。
- prometheus:CoreDNS自身metrics數據接口。可以通過http://10.15.0.10:9153/metrics獲取prometheus格式的監控數據。(10.15.0.10為kube-dns service的IP)
- forward(或proxy):將域名查詢請求轉到預定義的DNS服務器。默認配置中,當域名不在kubernetes域時,將請求轉發到預定義的解析器(宿主機的/etc/resolv.conf)中,這是默認配置。
- cache:DNS緩存時長,單位秒。
- loop:環路檢測,如果檢測到環路,則停止CoreDNS。
- reload:允許自動重新加載已更改的Corefile。編輯ConfigMap配置后,請等待兩分鐘以使更改生效。
- loadbalance:循環DNS負載均衡器,可以在答案中隨機A、AAAA、MX記錄的順序。
API資源對象ingress
有了Service之后,我們可以訪問這個Service的IP(clusterIP)來請求對應的Pod,但是這只能是在集群內部訪問。
要想讓外部用戶訪問此資源,可以使用NodePort,即在node節點上暴漏一個端口出來,但是這個非常不靈活。為了解決此問題,K8s引入了一個新的API資源對象Ingress,它是一個七層的負載均衡器,類似于Nginx。
三個概念:Ingress、Ingress Controller、IngressClass
- Ingress用來定義具體的路由規則,要實現什么樣的訪問效果;
- Ingress Controller是實現Ingress定義具體規則的工具或者叫做服務,在K8s里就是具體的Pod;
- IngressClass是介于Ingress和Ingress Controller之間的一個協調者,它存在的意義在于,當有多個Ingress Controller時,可以讓Ingress和Ingress Controller彼此獨立,不直接關聯,而是通過IngressClass實現關聯。
Ingress YAML示例:
vi tang.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: tang ##ingress名字spec:ingressClassName: tang ##定義關聯的IngressClassrules: ##定義具體的規則- host: tanglinux.com ##訪問的目標域名http:paths:- path: /pathType: Exactbackend: ##定義后端的service對象service:name: ngx-svcport:number: 80
查看ingress
kubectl get ing
kubectl describe ing tang
IngressClassYAML示例:
vi tangc.yaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:name: tangspec:controller: nginx.org/ingress-controller ##定義要使用哪個controller
查看ingressClass
kubectl get ingressclass
安裝ingress-controller(使用Nginx官方提供的 https://github.com/nginxinc/kubernetes-ingress)
首先做一下前置工作
curl -O 'https://gitee.com/aminglinux/linux_study/raw/master/k8s/ingress.tar.gz'
tar zxf ingress.tar.gz
cd ingress
./setup.sh ##說明,執行這個腳本會部署幾個ingress相關資源,包括namespace、configmap、secrect等
vi ingress-controller.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: ngx-ingnamespace: nginx-ingressspec:replicas: 1selector:matchLabels:app: ngx-ingtemplate:metadata:labels:app: ngx-ing#annotations:#prometheus.io/scrape: "true"#prometheus.io/port: "9113"#prometheus.io/scheme: httpspec:serviceAccountName: nginx-ingresscontainers:- image: nginx/nginx-ingress:2.2-alpineimagePullPolicy: IfNotPresentname: ngx-ingports:- name: httpcontainerPort: 80- name: httpscontainerPort: 443- name: readiness-portcontainerPort: 8081- name: prometheuscontainerPort: 9113readinessProbe:httpGet:path: /nginx-readyport: readiness-portperiodSeconds: 1securityContext:allowPrivilegeEscalation: truerunAsUser: 101 #nginxcapabilities:drop:- ALLadd:- NET_BIND_SERVICEenv:- name: POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespace- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.nameargs:- -ingress-class=tangc- -health-status- -ready-status- -nginx-status- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
應用YAML
kubectl apply -f ingress-controller.yaml
查看pod、deployment
kubectl get po -n nginx-ingress
kubectl get deploy -n nginx-ingress
將ingress對應的pod端口映射到master上臨時測試
kubectl port-forward -n nginx-ingress ngx-ing-547d6575c7-fhdtt 8888:80 &
測試前,可以修改ng-deploy對應的兩個pod里的/usr/share/nginx/html/index.html文件內容,用于區分兩個pod
測試
curl -x127.0.0.1:8888 aminglinux.com
或者:
curl -H 'Host:aminglinux.com' http://127.0.0.1:8888
上面對ingress做端口映射,然后通過其中一個節點的IP來訪問ingress只是一種臨時方案。那么正常如何做呢?有三種常用的方案:
1)Deployment+LoadBalancer模式的Service
如果要把ingress部署在公有云,那用這種方式比較合適。用Deployment部署ingress-controller,創建一個type為LoadBalancer的service關聯這組pod。
大部分公有云,都會為LoadBalancer的service自動創建一個負載均衡器,通常還綁定了公網地址。
只要把域名解析指向該地址,就實現了集群服務的對外暴露。
2)Deployment+NodePort模式的Service
同樣用deployment模式部署ingress-controller,并創建對應的服務,但是type為NodePort。這樣,ingress就會暴露在集群節點ip的特定端口上。
由于nodeport暴露的端口是隨機端口,一般會在前面再搭建一套負載均衡器來轉發請求。該方式一般用于宿主機是相對固定的環境ip地址不變的場景。
NodePort方式暴露ingress雖然簡單方便,但是NodePort多了一層NAT,在請求量級很大時可能對性能會有一定影響。
3)DaemonSet+HostNetwork+nodeSelector
用DaemonSet結合nodeselector來部署ingress-controller到特定的node上,然后使用HostNetwork直接把該pod與宿主機node的網絡打通(如,上面的臨時方案kubectl port-forward),直接使用宿主機的80/433端口就能訪問服務。
這時,ingress-controller所在的node機器就很類似傳統架構的邊緣節點,比如機房入口的nginx服務器。該方式整個請求鏈路最簡單,性能相對NodePort模式更好。
缺點是由于直接利用宿主機節點的網絡和端口,一個node只能部署一個ingress-controller pod。比較適合大并發的生產環境使用。
搞懂Kubernetes調度
K8S調度器Kube-schduler的主要作用是將新創建的Pod調度到集群中的合適節點上運行。kube-scheduler的調度算法非常靈活,可以根據不同的需求進行自定義配置,比如資源限制、親和性和反親和性等。
1)kube-scheduler的工作原理如下:
- 監聽API Server: kube-scheduler會監聽API Server上的Pod對象,以獲取需要被調度的Pod信息。它會通過API Server提供的REST API接口獲取Pod的信息,例如Pod的標簽、資源需求等信息。
- 篩選可用節點: kube-scheduler會根據Pod的資源需求和約束條件(例如Pod需要的特定節點標簽)篩選出可用的Node節點。它會從所有注冊到集群中的Node節點中選擇符合條件的節點。
- 計算分值: kube-scheduler會為每個可用的節點計算一個分值,以決定哪個節點是最合適的。分值的計算方式可以通過調度算法來指定,例如默認的算法是將節點資源利用率和距離Pod的網絡延遲等因素納入考慮。
- 選擇節點: kube-scheduler會選擇分值最高的節點作為最終的調度目標,并將Pod綁定到該節點上。如果有多個節點得分相等,kube-scheduler會隨機選擇一個節點。
- 更新API Server: kube-scheduler會更新API Server上的Pod對象,將選定的Node節點信息寫入Pod對象的spec字段中,然后通知Kubelet將Pod綁定到該節點上并啟動容器。
2)Kube-scheduler調度器內部流轉過程
- ① Scheduler通過注冊client-go的informer handler方法監聽api-server的pod和node變更事件,獲取pod和node信息緩存到Informer中
- ② 通過Informer的handler將事件更新到ActiveQ(ActiveQ、UnschedulableQ、PodBackoffQ為三個Scheduling隊列,ActiveQ是一個維護著Pod優先級的堆結構,調度器在調度循環中每次從堆中取出優先級最高的Pod進行調度)
- ③ 調度循環通過NextPod方法從ActiveQ中取出待調度隊列
- ④ 使用調度算法針對Node和Pod進行匹配和打分確定調度目標節點
- ⑤ 如果調度器出錯或失敗,會調用shed.Error將Pod寫入UnschedulableQ里
- ⑥ 當不可調度時間超過backoff的時間,Pod會由Unschedulable轉換到Podbackoff,也就是說Pod信息會寫入到PodbackoffQ里
- ⑦ Client-go向Api Server發送一個bind請求,實現異步綁定
調度器在執行綁定操作的時候是一個異步過程,調度器會先在緩存中創建一個和原來Pod一樣的Assume Pod對象用模擬完成節點的綁定,如將Assume Pod的Nodename設置成綁定節點名稱,同時通過異步執行綁定指令操作。在Pod和Node綁定之前,Scheduler需要確保Volume已經完成綁定操作,確認完所有綁定前準備工作,Scheduler會向Api Server 發送一個Bind 對象,對應節點的Kubelet將待綁定的Pod在節點運行起來。
3)為節點計算分值
節點分值計算是通過調度器算法實現的,而不是固定的。默認情況下,kube-scheduler采用的是DefaultPreemption算法,其計算分值的方式包括以下幾個方面:
- 節點的資源利用率 kube-scheduler會考慮每個節點的CPU和內存資源利用率,將其納入節點分值的計算中。資源利用率越低的節點得分越高。
- 節點上的Pod數目 kube-scheduler會考慮每個節點上已經存在的Pod數目,將其納入節點分值的計算中。如果節點上已經有大量的Pod,新的Pod可能會導致資源競爭和擁堵,因此節點得分會相應降低。
- Pod與節點的親和性和互斥性 kube-scheduler會考慮Pod與節點的親和性和互斥性,將其納入節點分值的計算中。如果Pod與節點存在親和性,例如Pod需要特定的節點標簽或節點與Pod在同一區域,節點得分會相應提高。如果Pod與節點存在互斥性,例如Pod不能與其他特定的Pod共存于同一節點,節點得分會相應降低。
- 節點之間的網絡延遲 kube-scheduler會考慮節點之間的網絡延遲,將其納入節點分值的計算中。如果節點之間的網絡延遲較低,節點得分會相應提高。
- Pod的優先級 kube-scheduler會考慮Pod的優先級,將其納入節點分值的計算中。如果Pod具有高優先級,例如是關鍵業務的部分,節點得分會相應提高。
這些因素的相對權重可以通過kube-scheduler的命令行參數或者調度器配置文件進行調整。需要注意的是,kube-scheduler的算法是可擴展的,可以根據需要編寫自定義的調度算法來計算節點分值。
4)調度策略
- 默認調度策略(DefaultPreemption): 默認調度策略是kube-scheduler的默認策略,其基本原則是為Pod選擇一個未滿足需求的最小代價節點。如果無法找到這樣的節點,就會考慮使用預選,即將一些已經調度的Pod驅逐出去來為新的Pod騰出空間。
- 帶優先級的調度策略(Priority): 帶優先級的調度策略基于Pod的優先級對節點進行排序,優先選擇優先級高的Pod。該策略可以通過設置Pod的PriorityClass來實現。
- 節點親和性調度策略(NodeAffinity): 節點親和性調度策略基于節點標簽或其他條件,選擇與Pod需要的條件相匹配的節點。這可以通過在Pod定義中使用NodeAffinity配置實現。
- Pod 親和性調度策略(PodAffinity): Pod 親和性調度策略根據Pod的標簽和其他條件,選擇與Pod相似的其他Pod所在的節點。這可以通過在Pod定義中使用PodAffinity配置實現。
- Pod 互斥性調度策略(PodAntiAffinity): Pod 互斥性調度策略選擇與Pod不相似的其他Pod所在的節點,以避免同一節點上運行相似的Pod。這可以通過在Pod定義中使用PodAntiAffinity配置實現。
- 資源限制調度策略(ResourceLimits): 資源限制調度策略選擇可用資源最多的節點,以滿足Pod的資源需求。這可以通過在Pod定義中使用ResourceLimits配置實現。
節點選擇器NodeSelector
NodeSelector會將Pod根據定義的標簽選定到匹配的Node上去。
示例:
cat > nodeselector.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:name: nginx-ssd
spec:containers:- name: nginx-ssdimage: nginx:1.23.2imagePullPolicy: IfNotPresentports:- containerPort: 80nodeSelector:disktype: ssd
EOF
應用YAML
kubectl apply -f nodeselector.yaml
查看Pod狀態
kubectl describe po nginx-ssd
給Node打標簽
kubectl label node k8s02 disktype=ssd
查看Node label
kubectl get node --show-labels
查看Pod信息
kubectl describe po nginx-ssd |grep -i node
節點親和性NodeAffinity
也是針對Node,目的是把Pod部署到符合要求的Node上。
關鍵詞:
① requiredDuringSchedulingIgnoredDuringExecution:表示強匹配,必須要滿足
② preferredDuringSchedulingIgnoredDuringExecution:表示弱匹配,盡可能滿足,但不保證
示例:
apiVersion: v1
kind: Pod
metadata:name: with-node-affinity
spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution: ##必須滿足下面匹配規則nodeSelectorTerms:- matchExpressions:- key: envoperator: In ##邏輯運算符支持:In,NotIn,Exists,DoesNotExist,Gt,Ltvalues:- test- devpreferredDuringSchedulingIgnoredDuringExecution: ##盡可能滿足,但不保證- weight: 1preference:matchExpressions:- key: projectoperator: Invalues:- aminglinuxcontainers:- name: with-node-affinityimage: redis:6.0.6
說明:
匹配邏輯:
① 同時指定Node Selector和Node Affinity,兩者必須同時滿足;
② Node Affinity中指定多組nodeSelectorTerms,只需要一組滿足就可以;
③ 當在nodeSelectorTerms中包含多組matchExpressions,必須全部滿足才可以;
演示示例:
編輯pod的yaml
cat > nodeAffinity.yaml << EOF
apiVersion: v1
kind: Pod
metadata:name: node-affinity
spec:containers:- name: my-containerimage: nginx:1.23.2affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: special-nodeoperator: Exists
EOF
給其中一個節點定義標簽
kubectl label nodes aminglinux03 special-node=true
生效Pod yaml
kubectl apply -f nodeAffinity.yaml
檢查Pod所在node
kubectl get po -o wide