A. 二十五 網路策略
網路策略(NetworkPolicy)是一種關於pod間及pod與其他網路端點間所允許的通信規則的規范。NetworkPolicy 資源使用標如知簽選擇pod,並定義選定pod所允許的通信規則。
前提
網路策略通過網路插件來實現,所以用戶必須使用支持 NetworkPolicy 的網路解決方案 - 簡單地創建資源對象,而沒有控制器來使它生效的話,是沒有任何作用的。
網路插件:
https://kubernetes.io/docs/concepts/services-networking/network-policies/
默認情況下,Pod是非隔離的,它們接受任何來源的流量。Pod可以通過相關的網路策略進行隔離。一旦命名空間中 NetworkPolicy 配置選擇了特定的Pod,該Pod會拒絕網路策略所不允許的連接。 (命名空間下其他未被網路策略所選擇的Pod會繼續接收所有的流量)
必填欄位: 與所有其他的Kubernetes配置一樣,NetworkPolicy 需要 apiVersion、 kind和 metadata 。
spec: NetworkPolicy spec 中包含了在一個命名空間中定義特定網路策略所需的所有信息.
podSelector: 每個 NetworkPolicy 都包括一個 podSelector ,它對該策略所應用的一組Pod進行選擇。因為 NetworkPolicy 目前只支持定義 ingress 規則,這里的 podSelector 本質上是為該策略定義 "目標pod" 。示例中的策略選擇帶有 "role=db" 標簽的pod。空的 podSelector 選擇命名空間下的所有pod。
policyTypes: 每個 NetworkPolicy 都包含一個 policyTypes 列表,其中包含 Ingress 或 Egress 或兩者兼具。policyTypes 欄位表示給定的策略是應用於 進入所選 Pod 的入站流量還是來自所選 Pod 的出站流量,或兩者兼有。 如果 NetworkPolicy 未指定 policyTypes 則默認情況下始終設置 Ingress; 如果 NetworkPolicy 有任何出口規則的話則設置 Egress。
ingress: 每個 NetworkPolicy 包含一個 ingress 規則的白名單列表。(其中的)規則允許同時匹配 from 和 ports 部分的流派碰量。示例策略中包含一條簡單的規則: 它匹配一個單一的埠,來自兩個來源中的一個, 第一個通過 namespaceSelector 指定,第二個通過 podSelector 指定。
egress: 每個 NetworkPolicy 包含一個 egress 規則的白名單列表。每個規則都允許匹配 to 和 port 部分的流量。該示例策略包含一條規則,該規則將單個埠上的流量匹配到 10.0.0.0/24 中的任何目的地。
所以,示例網路策略:
1.隔離 "default" 命名空間下標簽是 "role=db" 的pod (如果它們不是已經被隔離的話)。
2.(Ingress 規則)允許以下 Pod 連接到 "default" 名字空間下的帶有 "role=db" 標簽的所有 Pod 的 6379 TCP 埠:
注意作用域
「-」是同級,或的關系
注意ipBlock中目的地址或者源地址的NAT轉換
選擇器 to 和 from 的行為
可以在 ingress from 部分或 egress to 部分中指定四種選擇器:
podSelector: 這將在與 NetworkPolicy 相同的命名空塵橡談間中選擇特定的 Pod,應將其允許作為入口源或出口目的地。
namespaceSelector: 這將選擇特定的命名空間,應將所有 Pod 用作其輸入源或輸出目的地。
namespaceSelector 和 podSelector: 一個指定 namespaceSelector 和 podSelector 的 to/from 條目選擇特定命名空間中的特定 Pod。注意使用正確的YAML語法;
下面的策略,在 from 數組中僅包含一個元素,只允許來自標有 role = client 的 Pod 且該 Pod 所在的命名空間中標有user=alice的連接。
這項策略,在 from 數組中包含兩個元素,允許來自本地命名空間中標有 role = client 的 Pod 的連接,或來自任何命名空間中標有user = alice的任何Pod的連接。
ipBlock: 這將選擇特定的 IP CIDR 范圍以用作入口源或出口目的地。 這些應該是群集外部 IP,因為 Pod IP 存在時間短暫的且隨機產生。
群集的入口和出口機制通常需要重寫數據包的源 IP 或目標 IP。在發生這種情況的情況下,不確定在 NetworkPolicy 處理之前還是之後發生,並且對於網路插件,雲提供商,Service 實現等的不同組合,其行為可能會有所不同。
在進入的情況下,這意味著在某些情況下,您可以根據實際的原始源 IP 過濾傳入的數據包,而在其他情況下,NetworkPolicy 所作用的 源IP 則可能是 LoadBalancer 或 Pod的節點等。
對於出口,這意味著從 Pod 到被重寫為集群外部 IP 的 Service IP 的連接可能會或可能不會受到基於 ipBlock 的策略的約束。
默認策略
默認情況下,如果命名空間中不存在任何策略,則所有進出該命名空間中的Pod的流量都被允許。以下示例使您可以更改該命名空間中的默認行為。
默認拒絕所有入口流量
創建選擇所有容器但不允許任何進入這些容器的入口流量的 NetworkPolicy 來為命名空間創建 "default" 隔離策略。
這樣可以確保即使容器沒有選擇其他任何 NetworkPolicy,也仍然可以被隔離。此策略不會更改默認的出口隔離行為。
默認允許所有入口流量
如果要允許所有流量進入某個命名空間中的所有 Pod(即使添加了導致某些 Pod 被視為「隔離」的策略),則可以創建一個策略來明確允許該命名空間中的所有流量。
默認拒絕所有出口流量
您可以通過創建選擇所有容器但不允許來自這些容器的任何出口流量的 NetworkPolicy 來為命名空間創建 "default" egress 隔離策略。
這樣可以確保即使沒有被其他任何 NetworkPolicy 選擇的 Pod 也不會被允許流出流量。此策略不會更改默認的 ingress 隔離行為。
默認允許所有出口流量
如果要允許來自命名空間中所有 Pod 的所有流量(即使添加了導致某些 Pod 被視為「隔離」的策`略),則可以創建一個策略,該策略明確允許該命名空間中的所有出口流量。
默認拒絕所有入口和所有出口流量
可以為命名空間創建 "default" 策略,以通過在該命名空間中創建以下 NetworkPolicy 來阻止所有入站和出站流量.
這樣可以確保即使沒有被其他任何 NetworkPolicy 選擇的 Pod 也不會被允許進入或流出流量。
SCTP支持
Kubernetes 支持 SCTP 作為 NetworkPolicy 定義中的協議值作為 alpha 功能提供。要啟用此功能,集群管理員需要在 apiserver 上啟用 SCTPSupport 功能門,例如 「--feature-gates=SCTPSupport=true,...」。啟用功能門後,用戶可以將 NetworkPolicy 的 protocol 欄位設置為 SCTP。 Kubernetes 相應地為 SCTP 關聯設置網路,就像為 TCP 連接一樣。
CNI插件必須在 NetworkPolicy 中將 SCTP 作為 protocol 值支持。
首先需要有一個支持網路策略的 Kubernetes 集群。已經有許多支持 NetworkPolicy 的網路提供商,包括:
Calico
Romana
Weave 網路
注意:以上列表是根據產品名稱按字母順序排序,而不是按推薦或偏好排序。下面示例對於使用了上面任何提供商的 Kubernetes 集群都是有效的
創建一個nginx deployment並且通過服務將其暴露
為了查看 Kubernetes 網路策略是怎樣工作的,可以從創建一個nginx deployment 並且通過服務將其暴露開始
在 default 命名空間下運行了兩個 nginx pod,而且通過一個名字為 nginx 的服務進行了暴露
測試服務能夠被其它的pod訪問
從其它的 pod 訪問這個新的 nginx 服務。為了驗證它,從 default 命名空間下的其它 pod 來訪問該服務。請您確保在該命名空間下沒有執行孤立動作。
啟動一個 busybox 容器,然後在容器中使用 wget 命令去訪問 nginx 服務:
限制訪問nginx服務
想限制 nginx 服務,只讓那些擁有標簽 access: true 的 pod 訪問它,那麼您可以創建一個只允許從那些 pod 連接的 NetworkPolicy:
為服務指定策略
使用 kubectl 工具根據上面的 nginx-policy.yaml 文件創建一個 NetworkPolicy:
當訪問標簽沒有定義時測試訪問服務
如果您嘗試從沒有設定正確標簽的 pod 中去訪問 nginx 服務,請求將會超時:
定義訪問標簽後再次測試
創建一個擁有正確標簽的 pod,您將看到請求是被允許的:
B. K8s創建Pod的不同姿勢,常用策略講解Demo
錢比你想像的重要得多,超過20歲了就別整天活在夢里了,對於平凡的你來講,錢就是你的尊嚴。
Pod 環境:Ansible+K8s集群
Ansible 環境准備
容器集群環境准備
kubectl explain --help
查看pod的語法結構
新建一個命名空間用於創建
kubectl config set-context context1 --namespace=liruilong-pod-create
查看當前集群信息乎皮
查看命名空間
設置剛才新建的命名空間為當前命名空間
kubectl run podcommon --image=nginx --image-pull-policy=IfNotPresent --labels="name=liruilong" --env="name=liruilong"
kubectl get pods -o wide
kubectl delete pod pod-demo --force
每個Pod都有一個pause鏡像
kubectl run pod-demo --image=nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml >pod-demo.yaml
yaml文件的獲取方法:-o yaml
yaml文件創建pod
pod-demo.yaml
yaml文件創建pod
刪除pod:delete pod
創建pod時指定伏慧運行命令。替換鏡像中CMD的命令缺頃答
kubectl delete -f comm-pod.yaml 刪除pod
批量創建pod
通過 sed 更改 pod名字的方式:sed 『s/demo/demo1/』 demo.yaml | kubectl apply -f -
容器共享pod的網路空間的。即使用同一個IP地址:pod IP
一個pod內創建多個容器
comm-pod.yaml 文件編寫
創建 多容器pod
--image-pull-policy
restartPolicy –單個容器正常退出
k8s中每個資源對象都有標簽
查看標簽
指定標簽過濾
C. k8s 網路三
k8s網路包括網路模型、CNI、Service、Ingress、DNS。
k8s的容器網路關注兩點:IP地址分配,路由。
k8s的網路依賴於docker,docker的網路需要linux內核特性的支持。
1 每個Pod除了創建時指定的容器外,都有一個kubelet啟動時指定的基礎容器。
2 kubelet創建基礎容器,慶兆生成network namespace。
3 kubelet調用網路CNI driver,由它根據配置調用具體的CNI 插件。
4 CNI 插件給基礎容器配置網路。
5 Pod 中其他的容器共享使用基礎容器的網路。
Ingress是HTTP方式的路由轉發機制,即是7層負載均衡器,由Ingress Controller和HTTP代理伺服器組成。Ingress Controller實時監控k8s API,實時更新HTTP代理伺服器的轉發規則。HTTP代理伺服器有GCE Load-Balancer、HaProxy、Nginx等方案。ingress controller在轉發客戶端請求到後端服務時,將跳過kube-proxy提供的4層負載均衡器的功能,直接轉發到service的後端pod(endpoint)。
對於需要為k8s集群外的客戶端提供服務的service,可以通過ingress將服務暴露出去。
網路策略是基於pod源ip(因此k8s網路不能隨便做SNAT)的訪問控制列表,限制pod之間的訪問。
網路策略作為pod網路隔離的一層抽象,用白名單實現了訪問控制列表,從label selector、namespace selector、端握爛口、CIDR這4個維度限制pod的流量進出。
ingress限制進入的流量,egress限制外出的流量。
完成從服務名到clusterIP的解析。
DNS服務經歷從skyDNS到kubeDNS再到coreDNS的過程。k8s通過Add-On增值包的方式引入DNS系統,把服務名作為dns域名。程序就可以直接使用服務名來建立通信連接。
從容器發出的數據包先到達br0,然後交給host機器的協議棧,由於目的IP是外網,且host主機開啟了IP forward功能,數據包會通過eth0發出。因容器分配的網段都不在物理網路網段內,所以一般發出去之前先做NAT轉換。(可譽皮租以用iptables進行轉換)
當涉及轉發的目的IP地址是其他機器時,需要確保啟用ip forward功能,即把linux當作交換機。
D. Kubernetes Pod 安全策略(PSP)配置
Kubernetes Pod 安全策略(PSP)配置
默認情況下,Kubernetes 允許創建一個有特權並帶州容器的 Pod,這些容器很可能會危機系統安全,而 Pod 安全策略(PSP)則通過確保請求者有許可權按配置來創建 Pod,從而來保護集群免受特權 Pod 的影響。
其他插件來自 Kubernetes 文檔中推薦的一些插件列表。
然後直接創建上面的 Deployment:
deployment.apps/nginx-deploy created
我們可以看到 Deployment 已經創建成功了,現在檢查下 default 命名空間下面的 pod、replicaset、deployment:
NAME READY STATUS RESTARTS AGE
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-deploy-77f7d4c6b4 1 0 0 40s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-deploy 0/1 0 0 40s
可行凳以看到 replicaset 和 deployment 都創建成功了,但是 replicaset 控制器卻並沒有創建 Pod,這個時候就需要使用 ServiceAccount 了。
attachdetach-controller
calico-kube-controller
certificate-controller
clusterrole-aggregation-controller
cronjob-controller
daemon-set-controller
deployment-controller
disruption-controller
endpoint-controller
expand-controller
job-controller
namespace-controller
node-controller
pv-protection-controller
pvc-protection-controller
replicaset-controller
replication-controller
resourcequota-controller
service-account-controller
service-controller
statefulset-controller
ttl-controller
這些 ServiceAccount 指定了哪個控制器可以解析哪些策略的配置。
podsecuritypolicy.policy/restrictive configured
雖然限制性的訪問對於大多數 Pod 創建是足夠的了,但是對於需要提升訪問權絕蔽限的 Pod 來說,就需要一些允許策略了,例如,kube-proxy 就需要啟用 hostNetwork:
NAME READY STATUS RESTARTS AGE
kube-proxy-4z4vf 1/1 Running 0 18d
$ kubectl get pods -n kube-system kube-proxy-4z4vf -o yaml |grep hostNetwork
hostNetwork: true
這就需要創建一個用於提升創建許可權的許可策略了:(psp-permissive.yaml)
podsecuritypolicy.policy/permissive configured
$ kubectl get psp
NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES
permissive true RunAsAny RunAsAny RunAsAny RunAsAny false *
restrictive false * RunAsAny RunAsAny RunAsAny RunAsAny false configMap,downwardAPI,emptyDir,persistentVolumeClaim,secret,projected
現在配置都已經就緒了,但是我們需要引入到 Kubernetes 授權,這樣才可以確定請求 Pod 創建的用戶或者 ServiceAccount 是否解決了限制性或許可性策略,這就需要用到 RBAC 了。
clusterrole.rbac.authorization.k8s.io/psp-restrictive created
clusterrolebinding.rbac.authorization.k8s.io/psp-default created
然後現在我們再重新創建上面我們的定義的 Deployment:
deployment.apps "nginx-deploy" deleted
$ kubectl apply -f nginx.yaml
deployment.apps/nginx-deploy created
創建完成後同樣查看下 default 命名空間下面我們創建的一些資源對象:
NAME READY STATUS RESTARTS AGE
pod/nginx-deploy-77f7d4c6b4-njfdl 1/1 Running 0 13s
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-deploy-77f7d4c6b4 1 1 1 13s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-deploy 1/1 1 1 13s
我們可以看到 Pods 被成功創建了,但是,如果我們嘗試做一些策略不允許的事情,正常來說就應該被拒絕了。首先刪除上面的這個 Deployment:
deployment.apps "nginx-deploy" deleted
現在我們在 nginx-deploy 基礎上添加hostNetwork: true來使用 hostNetwork 這個特權:(nginx-hostnetwork.yaml)
deployment.apps/nginx-hostnetwork-deploy created
創建完成後同樣查看 default 這個命名空間下面的一些資源對象:
NAME READY STATUS RESTARTS AGE
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-hostnetwork-deploy-74c8fbd687 1 0 0 44s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-hostnetwork-deploy 0/1 0 0 44s
現在我們發現 ReplicaSet 又沒有創建 Pod 了,可以使用kubectl describe命令去查看這里我們創建的 ReplicaSet 資源對象來了解更多的信息:
Name: nginx-hostnetwork-deploy-74c8fbd687
......
Events:
Type Reason Age From Message
-
Warning FailedCreate 80s (x15 over 2m42s) replicaset-controller Error creating: pods "nginx-hostnetwork-deploy-74c8fbd687-" is forbidden: unable to validate against any pod security policy: [spec.securityContext.hostNetwork: Invalid value: true: Host network is not allowed to be used]
我們可以看到很明顯 Hostnetwork 不被允許使用,但是在某些情況下,我們的確有在某個命名空間(比如 kube-system)下面創建使用 hostNetwork 的 Pod,這里就需要我們創建一個允許執行的 ClusterRole,然後為特定的命名空間創建一個 RoleBinding,將這里的 ClusterRole 和相關的控制器 ServiceAccount 進行綁定:(psp-permissive-rbac.yaml)
clusterrole.rbac.authorization.k8s.io/psp-permissive created
rolebinding.rbac.authorization.k8s.io/psp-permissive created
現在,我們就可以在 kube-system 這個命名空間下面使用 hostNetwork 來創建 Pod 了,將上面的 nginx 資源清單更改成 kube-system 命名空間下面:
重新創建這個 Deployment:
deployment.apps/nginx-hostnetwork-deploy created
創建完成後同樣查看下對應的資源對象創建情況:
NAME READY STATUS RESTARTS AGE
pod/nginx-hostnetwork-deploy-74c8fbd687-7x8px 1/1 Running 0 2m1s
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-hostnetwork-deploy-74c8fbd687 1 1 1 2m1s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-hostnetwork-deploy 1/1 1 1 2m1s
現在我們可以看到 Pod 在 kube-system 這個命名空間下面創建成功了。
serviceaccount/specialsa created
然後創建一個 RoleBinding 將 specialsa 綁定到上面的 psp-permissive 這個 CluterRole 上:(specialsa-psp.yaml)
創建上面的 RoleBinding 對象:
rolebinding.rbac.authorization.k8s.io/specialsa-psp-permissive created
然後為我們上面的 Deployment 添加上 serviceAccount 屬性:(nginx-hostnetwork-sa.yaml)
然後直接創建即可:
deployment.apps/nginx-hostnetwork-deploy configured
這個時候我們查看 default 這個命名空間下面帶有 hostNetwork 的 Pod 也創建成功了:
NAME READY STATUS RESTARTS AGE
pod/nginx-hostnetwork-deploy-6c85dfbf95-hqt8j 1/1 Running 0 65s
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-hostnetwork-deploy-6c85dfbf95 1 1 1 65s
replicaset.extensions/nginx-hostnetwork-deploy-74c8fbd687 0 0 0 31m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-hostnetwork-deploy 1/1 1 1 31m
上面我們描述了 Pod 安全策略是一種通過使用 PSP 授權策略來保護 k8s 集群中的 Pod 的創建過程的方法。
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#policy-reference
https://octetz.com/posts/setting-up-psps
https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#is-there-a-recommended-set-of-admission-controllers-to-use
E. Kubernetes之NetworkPolicy,Flannel和Calico
Pod是Kubernetes調度的最小單元。一個Pod可以包含一個或多個容器,因此它可以被看作是內部容器的邏輯宿主機。Pod的設計理念是為了支持多個容器在一個Pod中共享網路和文件系統。那麼為什麼Pod內的容器能夠共享網路,IPC和PID命名空間?
原因:Kubernetes在每個Pod啟動時,會自動創建一個鏡像為gcr.io/google_containers/pause:version的容器,所有處於該Pod中的容器在啟動時都會添加諸如--net=container:pause --ipc=contianer:pause --pid=container:pause的啟動參數,因此Pod內所有容器共用pause容器的network,IPC和PID命名空間。所有容器共享pause容器的IP地址,也被稱為Pod IP。因此處於同一個Pod內的容器,可以通過localhost進行相互訪問。
在講K8s Pod間通信前,我們先復習一下Docker容器間的網路通信。默認情況下,Docker使用一種名為bridge的網路模型。如下圖所示:
Docker引擎在啟動時,會在宿主機上創建一個名為docker0的虛擬網橋,這個虛擬網橋負責給所有容器分配不重復的ip地址以及容器間的網路通信。首先,Docker在創建一個容器時,會執行以下操作:
通過這個docker0網橋,同一宿主機上的容器可以互相通信。然而由於宿主機的IP地址與容器veth pair的 IP地址均不在同一個網段,故僅僅依靠veth pair和namespace的技術,還不足以使宿主機以外的網路主動發現容器的存在。為了使外界可以訪問容器中的進程,docker採用了埠綁定的方式,也就是通過iptables的NAT,將宿主機上的埠埠流量轉發到容器內的埠上。
K8s 默認不提供網路功能,所有Pod的網路功能,都依賴於宿主機上的Docker。因此,Pod IP即是依靠docker0網橋分配給pause容器的虛擬IP。
同一個Node內,不同的Pod都有一個docker0網橋分配的IP,可以直接通過這個IP進行通信。Pod IP和docker0在同一個網段。因此,當同節點上的Pod-A發包給Pod-B時,包傳送路線如下:
不同的Node之間,Node的IP相當於外網IP,可以直接訪問,而Node內的docker0和Pod的IP則是內網IP,無法直接跨Node訪問。因此,不同Node上的Pod間需要通信,需要滿足以下兩個條件:
因此,為了實現以上兩個需求,K8s提供了CNI(Container Network Interface)供第三方實現從而進行網路管理。由此出現了一系列開源渣陵的Kubernetes中的網路插件與方案,包括:flannel,calico,cilium等等。這些網路插件集中解決了以下需求:
CNI的介紹請參考這篇文章: https://jimmysong.io/kubernetes-handbook/concepts/cni.html
在默認的Docker配置中,每個節點上的Docker服務會分別負責所在節點容器的IP分配。這樣導致的一個問題是,不同節點上容器可能獲得相同的內外沖梁高IP地址。
Flannel的設計目的就是為集群中的所有節點重新規劃IP地址的使用規則,從而使散尺得不同節點上的容器能夠獲得「同屬一個內網」且」不重復的」IP地址,並讓屬於不同節點上的容器能夠直接通過內網IP通信。
Flannel實質上是一種「覆蓋網路(overlay network)」,也就是將TCP數據包裝在另一種網路包裡面進行路由轉發和通信,目前已經支持UDP、VxLAN、AWS VPC和GCE路由等數據轉發方式,默認的節點間數據通信方式是UDP轉發。下圖展示了數據包在flannel中的流轉:
Flannel是一種典型的Overlay網路,它將已有的物理網路(Underlay網路)作為基礎,在其上建立疊加的邏輯網路,實現網路資源的虛擬化。Overlay網路有一定額外的封包和解包等網路開銷,對網路通信的性能有一定損耗。
Calico是純三層的SDN 實現,它基於BPG 協議和Linux自身的路由轉發機制,不依賴特殊硬體,容器通信也不依賴iptables NAT或Tunnel 等技術。能夠方便的部署在物理伺服器、虛擬機(如 OpenStack)或者容器環境下。同時calico自帶的基於iptables的ACL管理組件非常靈活,能夠滿足比較復雜的安全隔離需求。
Calico 還基於 iptables 還提供了豐富而靈活的網路 policy, 保證通過各個節點上的 ACLs 來提供 workload 的多租戶隔離、安全組以及其他可達性限制等功能。
核心問題是,nodeA怎樣得知下一跳的地址?答案是node之間通過BGP協議交換路由信息。
每個node上運行一個軟路由軟體bird,並且被設置成BGP Speaker,與其它node通過BGP協議交換路由信息。
可以簡單理解為,每一個node都會向其它node通知這樣的信息:
我是X.X.X.X,某個IP或者網段在我這里,它們的下一跳地址是我。
通過這種方式每個node知曉了每個workload-endpoint的下一跳地址。
K8s NetworkPoclicy 用於實現Pod間的網路隔離。在使用Network Policy前,必須先安裝支持K8s NetworkPoclicy的網路插件,包括:Calico,Romana,Weave Net,Trireme,OpenContrail等。
在未使用NetworkPolicy前,K8s中所有的Pod並不存在網路隔離,他們能夠接收任何網路流量。一旦使用NetworkPolicy選中某個namespace下的某些Pod,那麼這些Pod只能接收特定來源的流量(由Ingress屬性定義),並且只能向特定出口發送網路請求(由Egress屬性定義)。其他未被這個NetworkPolicy選中的Pod,依然不具備網路隔離。
下面是一個NetworkPolicy的例子:
下面的例子表示默認禁止所有Pod間的Ingress流量:
默認拒絕所有 Pod 之間 Egress 通信的策略為:
而默認允許所有 Pod 之間 Ingress 通信的策略為:
默認允許所有 Pod 之間 Egress 通信的策略為:
以 calico 為例看一下 Network Policy 的具體用法。首先配置 kubelet 使用 CNI 網路插件:
安裝 calio 網路插件:
首先部署一個 nginx 服務,此時,通過其他 Pod 是可以訪問 nginx 服務的:
開啟 default namespace 的 DefaultDeny Network Policy 後,其他 Pod(包括 namespace 外部)不能訪問 nginx 了:
最後再創建一個運行帶有 access=true label的 Pod 訪問的網路策略:
F. 如何指定pod的運行節點
一般情況下kubernets可以通過kube-scheler默認的調度策略合理的將pod分配到可用的節點上, 但是隨著pod數量的增加以及不同pod對資源的使用情況不同我們需要更加合理的分配集群中的資源, 所以對一些pod運行節點的控制是由必要的。
源於硬體和軟體層多樣性,我們需要將某個 pod 調度到某些特定的節點上,例如指定機房,存儲類型,網路類型等等:
有兩種方法 nodeSelector 以及 affinity 可以實現對應的需求
K8S中pod的調度都是通過節點label實現的 , 所以對於除必要的節點label外對於其它用途也要做一些規劃
設置參數
為 node 設置 taint 與 label:
or
刪除taint:
刪除扒燃node的label
查看 node上的 taint:
查看node1的label
kubernetes中是通過label-selector機制進行節點選擇,由scheler調度策略 MatchNodeSelector 進行label匹配,調度pod到目標節點,該匹配規則是強制約束。
類型包括:
限制方式:
匹配邏輯label
如果nodeAffinity中nodeSelector有多個選項,節點滿足任何一個條件即可;如果matchExpressions有多個選項,則節點必須同時滿足這些選項才能運行pod 。需要說明的是,node並沒有anti-affinity這種東西,因為NotIn和DoesNotExist能提供類似的功能。
這個 pod 同時定義了 和 兩種nodeAffinity。第一個要求 pod 運行在特定 devops 的節點上,第二個希望節點最好有對應的department:model和dedicated:app標簽, 根據權重決定了順序 NodeSelectorTerms 可以有多個,之間是或的關系,滿足任意一個既滿足, MatchExpressions 也可以有多個,他們之間是且的關系 必須都滿足
值為列表,根據權重決定順序 MatchExpressions 值為列表 關系為且,必須都滿足
PodAffinit是根據通過已運行在節點上的pod的標簽而不是node的標簽來決定被調度pod的運行節點,因為pod運行在指定的namespace所以需要自己指定運行pod的namesapce
上面這個例子中的 POD 需要調度到某個指定的主機上,至少有一個節點上運行了這樣的 POD:這個 POD 有一個app=busybox-pod的 label。podAntiAffinity則是希望最好不要調度到這樣的節點:這個節點上運行了某個 POD,而這個 POD 有app=node-affinity-pod的 label。根據前面兩個 POD 的定義,我們可以預見上面這個 POD 應該會被調度到一個busybox-pod被調度的節點上,而node-affinity-pod被調度到了該節點以外的節點
對於nodeAffinity無論是硬策略還是軟策略方式,都是調度 POD 到預期節點上,而Taints恰好與之相反,如春段虛果一個節點標記為 Taints ,除非 POD 也被標識為可以容忍污點節點,否則該 Taints 節點不會被調度pod。
比如用戶希望把 Master 節點保留給 Kubernetes 系統組件使用,或者把一組具有特殊資源預留給某些 POD,則污點就很有用了,POD 不會再被調度到 taint 標記過的節點。taint 標記節點舉例如下:
如果仍然希望某個 POD 調度到 taint 節點上,則必須在 Spec 中做出Toleration定義,才能調度到該節點,舉例如下:
effect 共有三個燃辯可選項,可按實際需求進行設置:
設置label 和 taint, edgenode為特殊屬性的節點所以需要設置taint
資源文件
運維服務部署在固定的幾台主機上, 每個主機設置 department 的label, 部署服務時指定該label
設置label
資源文件
G. 11.4 跨pod網路
現在,你知道每個pod有自己唯一的IP地址,可以通過一個扁平的、非 NAT網路和其他pod通信。Kubernetes是如何做到這一點的?簡單來說,Kubernetes不負責這塊。網路是由系統管理員或者Container Network Interface(CNI)插件建立的,而非Kubernetes本身。
Kubernetes並不會要求你使用特定的網路技術,但是授權pod(或者更准確地說,其容器)不論是否運行在同一個工作節點上,可以互相通信。pod用於通信的網路必須是:pod自己認為的IP地址一定和所有其他節談握點認為該pod擁有的IP地址一致。
查看圖 11.14。當pod A連接(發送網路包)到pod B時,pod B獲取到的源IP地址必須和pod A自己認為的IP地址一致。其間應該沒有網路地址轉換(NAT)操作——pod A發送到pod B的包必須保持源和目的地址不變。
這很重要,保證運行在pod內部的應用網路的簡猜清潔性,就像運行在同一個網關機上一樣。pod沒有NAT使得運行在其中的應用可以自己注冊在其他pod中。
image
圖11.14 Kubernetes規定pod必須通過非NAT網路進行連接
例如,有客戶端pod X和pod Y,為所有通過它們注冊的pod提供通知服務。pod X連接到pod Y並且告訴pod Y,「你好,我是pod X,IP地址為1.2.3.4,請把更新發送到這個IP地址」。提供服務的pod可以通過收到的IP地址連接第一個pod。
pod到節點及節點到pod通信也應用了無NAT通信。但是當pod和internet上的服務通信時,pod發送包的源IP不需要改變,因為pod的IP是私有的。向外發送包的源IP地址會被改成主機工作節點的IP地址。
構建一個像樣的Kubernetes集群包含按照這些要求建立網路。有不同的方法和技術來建立,在給定場景中它們都有其優點和缺點。因此,我們不會深入探究特定的技術,會闡述跨pod網路通用的工作原理。
在 11.3 節,我們看到創建了pod的IP地址以及網路命名空間,由基礎設施容器(暫停容器)來保存這些信息,然後pod容器就可以使用網路命名空間了。pod網路介面就是生成在基礎設施容器的一些東西。讓我們看一下介面是如何被創建的,以及如何連接到其他pod的介面,如圖 11.15 所示。
[圖片上傳失敗...(image-d2fcf3-1627950575030)]
圖11.15 同一節點上pod通過虛擬Ethernet介面對連接到同一個橋接
同節點pod通信
基礎設施容器啟動之前,會為容器創建一個虛擬Ethernet介面對(一個veth pair),其中一個對的介面保留在主機的命名空間中(在節點上運行ifconfig命令時可以看到vethXXX的條目),而其他的對被移入容器網路命名空間,並重命名為eth0。兩個虛擬介面就像管道的兩端(或者說像Ethernet電纜連接的兩個網路設備)——從一端進入,另一端出來,等等。
主機網路命名空間的介面會綁定到容器運行時配置使用的網路橋接上。從網橋的地址段中取IP地址賦值給容器內的eth0 介面。應用的任何運行在容器內部的程序都會發送數據到eth0 網路介面(在容器命名空間中的那一個),數據從主機命名穗侍前空間的另一個veth介面出來,然後發送給網橋。這意味著任何連接到網橋的網路介面都可以接收該數據。
如果pod A發送網路包到pod B,報文首先會經過pod A的veth對到網橋然後經過pod B的veth對。所有節點上的容器都會連接到同一個網橋,意味著它們都能夠互相通信。但是要讓運行在不同節點上的容器之間能夠通信,這些節點的網橋需要以某種方式連接起來。
不同節點上的pod通信
有多種連接不同節點上的網橋的方式。可以通過overlay或underlay網路,或者常規的三層路由,我們會在後面看到。
跨整個集群的pod的IP地址必須是唯一的,所以跨節點的網橋必須使用非重疊地址段,防止不同節點上的pod拿到同一個IP。如圖 11.16 所示的例子,節點A上的網橋使用 10.1.1.0/24 IP段,節點B上的網橋使用 10.1.2.0/24 IP段,確保沒有IP地址沖突的可能性。
圖 11.16 顯示了通過三層網路支持跨兩個節點pod通信,節點的物理網路介面也需要連接到網橋。節點 A的路由表需要被配置成圖中所示,這樣所有目的地為 10.1.2.0/24 的報文會被路由到節點B,同時節點B的路由表需要被配置成圖中所示,這樣發送到 10.1.1.0/24 的包會被發送到節點A。
圖11.16 為了讓不同節點上的pod能夠通信,網橋需要以某種方式連接
按照該配置,當報文從一個節點上容器發送到其他節點上的容器,報文先通過veth pair,通過網橋到節點物理適配器,然後通過網線傳到其他節點的物理適配器,再通過其他節點的網橋,最終經過veth pair到達目標容器。
僅當節點連接到相同網關、之間沒有任何路由時上述方案有效。否則,路由器會扔包因為它們所涉及的pod IP是私有的。當然,也可以配置路由使其在節點間能夠路由報文,但是隨著節點數量增加,配置會變得更困難,也更容易出錯。因此,使用SDN(軟體定義網路)技術可以簡化問題,SDN可以讓節點忽略底層網路拓撲,無論多復雜,結果就像連接到同一個網關上。從pod發出的報文會被封裝,通過網路發送給運行其他pod的網路,然後被解封裝、以原始格式傳遞給pod。
為了讓連接容器到網路更加方便,啟動一個項目容器網路介面(CNI)。CNI允許Kubernetes可配置使用任何CNI插件。這些插件包含
我們不會去深入探究這些插件的細節,如果想要了解更多,可以參考 https://kubernetes.io/docs/concepts/cluster-administration/addons/ 。
安裝一個網路插件並不難,只需要部署一個包含DaemonSet以及其他支持資源的YAML。每個插件項目首頁都會提供這樣一個YAML文件。如你所想,DaemonSet用於往所有集群節點部署一個網路代理,然後會綁定CNI介面到節點。但是,注意Kubetlet需要用 --network-plugin=cni 命令啟動才能使用CNI。
H. k8s網路配置DNS
Kubernetes支持Pod維度DNS策略設置,通過pod規約中dnsPolicy欄位設置,最終配置落到 /etc/resolv.conf 文件中,也就說k8s最終還是通過設置Pod容器中/etc/resolv.conf 文件來做設置解析配置,跟普通的虛擬機或者實體機是一樣,因為Pod容器本身就一個小的主機。
採用集群DNS,與配置的集群域後綴不匹配的任何 DNS 查詢 都將轉發到從節點繼承的上游名稱伺服器。簡單來說,就是使用 Kubernetes 中 kubedns 或 coredns 服務碼液進行域名解析,如果解析不成功,才會使用宿主機的 DNS 配置進行解析。
example容器/etc/resolv.conf
options ndots:5 代表當待解析域名包含.大於等於5時就會先解析域名,只有域名解析不成功時才會繼續匹配serach域或domain域
訪問test名字空間下example-svc服務,可以直接通過 example-svc 訪問,會與search域組合,最終組合成 example-svc.test.svc.cluster.local ,訪問test1名字空間下example-svc1,通過 example-svc.test1 ,會與search域組合,最終組合成 example-svc.test1.svc.cluster.local
自定義DNS策略,設置允許 Pod 忽略 Kubernetes 環境中的 DNS 設置,Pod 會使用其 dnsConfig 欄位 所提供的 DNS 設置。
用戶可以在 dnsConfig 欄位中指定以下屬性:
創建上面的 Pod 後,容器 test 會在其 /etc/resolv.conf 文件中獲取以下內容:
Pod 從運行所在的節點繼承名稱解析配置,即跟所在節點主機一致
對於以 hostNetwork 方式運行的 Pod,應顯式設置其 DNS 策略 為"ClusterFirstWithHostNet"
k8s新版本集群DNS服務是默認採用CoreDNS組件實現,1.13版本之前是採用Kube-dns
當 DNS 配置以及其它選項不合理陵慶的時候,通過向 Pod 的 /etc/hosts 文件中添加條目, 可以在 Pod 級別覆蓋對主機名的解析。你可以通過遲汪物 PodSpec 的 HostAliases 欄位來添加這些自定義條目.
pod列印/etc/hosts文件,多了以下內容
I. 簡述Kubernetes網路策略
為實現細粒度的容器間網路訪問隔離策略,Kubernetes引入Network Policy。
Network Policy的主要功能是對Pod間的網路通信進行限制和准入控制,設置允許訪問或禁止訪問的客戶端Pod列表。Network Policy定義網路策略,配合策略控制器(Policy Controller)進行策略的實現。我推薦你去看看時速雲,他們是一家全棧雲原生技術服務提供商,提供雲原生應用及數據平台產品,其中涵蓋容器雲PaaS、DevOps、微服務治理、服務網格、API網關等。大家可以去體驗一下。 如果我的回答能夠對您有幫助的話,求給大大的贊。