Kubernetes與IaaS資源融合實踐--以AWS為例

2021-02-14 Linux編程

作者丨int32bit 來源丨int32bit
1 前言

我們知道Kubernetes是強大的聲明式容器編排工具,而計算、存儲、網絡等功能均是通過以接口的形式暴露、以插件的形式實現,這種靈活開放的設計理念使Kubernetes非常容易集成外部工具擴展和強化功能。而IaaS雲平臺提供的核心功能就是計算、存儲和網絡,這意味著Kubernetes與IaaS雲平臺並不是獨立割裂的,而是天然非常適合結合在一起,功能和職責互補,二者聯合起來既能充分利用底層伺服器、網絡、存儲等基礎設施能力又能提供彈性、敏捷的基礎架構平臺。

目前Kubernetes已經能和OpenStack、AWS、Google雲等IaaS雲平臺很好的集成,比如Volume能和OpenStack的Cinder以及AWS的EBS集成,Pod網絡則能和雲平臺的VPC網絡集成,而Kubernetes Service和Ingress則分別適合與IaaS雲平臺的四層防火牆、七層防火牆集成。

本文接下來主要以Kubernetes融合AWS資源為例,主要通過實驗的方式介紹Kubernetes與IaaS資源的結合方式。

2 基礎環境配置2.1 節點配置

首先需要在AWS上創建至少3臺EC2實例,需要注意的是由於後面涉及的AWS NLB以及ALB均需要至少跨2個AZ,因此EC2實例需要至少跨2個AZ的子網。

另外Kubernetes自動創建AWS資源時需要調用AWS API,因此需要向AWS IAM進行身份和權限認證,考慮安全的問題,不建議使用AKSK的方式進行配置,而是採用Assume Role的方式使用STS進行登錄認證,需要的權限如EBS卷的創建、掛載、安全組配置、標籤設置和讀取等,實驗時為了簡化Policy策略配置,直接使用了內置的 AmazonEC2FullAccess Policy策略,創建了EC2 Role後關聯到所有的EC2實例,如圖:

實際生產時需要遵循最小權限授權原則,必須通過自定義Policy的形式僅配置Kubernetes需要的權限,Kubernetes Master節點和Node節點需要的權限不同,需要分別授權,github上有現成的Policy可以用kubernetes/cloud-provider-aws。

另外需要注意的是,Kubernetes的所有Node節點hostname必須使用EC2實例的private DNS名,原因可以參考文章:https://blog.juanwolf.fr/post/programming/why-k8s-nodes-changes-name-when-using-aws/。因此需要在所有的Node節點手動配置:

sudo hostnamectl set-hostname \

$(curl -sSL http://169.254.169.254/latest/meta-data/local-hostname)

2.2 標籤很重要

Kubernetes需要知道哪些AWS資源是屬於這個集群的,哪些是可以使用的,比如Kubernetes創建Service時為了暴露網際網路訪問需要修改EC2實例的安全組,但通常一個EC2實例可能會掛多個安全組,有些安全組是基線安全組,供一些公共服務如堡壘機使用,用戶肯定不期望Kubernetes把這些安全組規則也改了。因此Kubernetes要求用戶必須給資源打上標籤,通過標籤的方式告訴Kubernetes哪些資源可以用,其中有兩個標籤是必須的:

KubernetesCluster: 配置Kubernetes cluster名稱,對應部署Kubernetes時kubeadm的 clusterName參數,因為一個Account下可能有多個Kubernetes集群,通過這個參數用於區分是哪個集群。

kubernetes.io/cluster/int32bit-kubernetes: 填寫 shared或者 owned,即是否允許多個集群共享這些資源。

這些標籤需要打到所有要使用的資源上,包括EC2實例、安全組、子網,當然不使用的則不打,比如不需要Kubernetes託管的安全組就不要打上如上標籤。

另外Kubernetes創建負載均衡時需要知道哪些子網是私有子網(掛的是NAT Gateway路由表),哪些是公有子網(掛的是Internet Gateway路由表),因此需要給所有使用的子網打上如下標籤:

參考EKS集群VPC注意事項。

2.3 安裝與配置Kubernetes

目前安裝與配置Kubernetes主要使用kubeadm工具,不過現版本kubeadm還不支持直接傳遞 cloud provider參數,因此需要自己寫配置文件,建議先使用kubeadm生成默認的配置文件:

kubeadm config print init-defaults >kubeadm.yaml

修改 kubeadm.yaml配置文件,控制平面需要修改 controllerManager,Node平面需要修改 nodeRegistration,添加 kubeletExtraArgs的 cloud-provider參數:

nodeRegistration:

# ...

kubeletExtraArgs:

cloud-provider: aws

# ...

controllerManager:

extraArgs:

cloud-provider: aws

configure-cloud-routes: "false"

當然為了更適合國內網絡環境,建議使用阿里雲的Kubernetes容器鏡像,修改 imageRepository參數為 registry.aliyuncs.com/google_containers。

另外需要記住配置的 clusterName,默認為 kubernetes,後面打標籤和配置需要使用。

首個節點初始化:

kubeadmin init --config kubeadm.yaml

當首個節點完成init後,其他Node節點需要join,同樣需要修改 nodeRegistration參數指定cloud provider,生成默認配置文件方法如下:

kubeadm config print join-defaults >kubeadm.yaml

# 修改kubeadm.yaml的nodeRegistration以及apiServerEndpoint

kubeadm join --config kubeadm.yaml

3 通過塊存儲實現動態PVC卷

Kubernetes通過StorageClass實現動態PVC,目前支持AWS EBS、OpenStack Cinder、Ceph等驅動,利用這些雲平臺提供的底層存儲,Kubernetes可實現按需創建持久化存儲的Volume,以創建AWS EBS StorageClass為例,聲明文件如下:

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

name: standard

provisioner: kubernetes.io/aws-ebs

parameters:

type: gp2

reclaimPolicy: Retain

allowVolumeExpansion: true

volumeBindingMode: Immediate

type參數可配置選擇不同的EBS卷類型。

聲明StorageClass後就可以使用PVC了,如下申請30GB的volume。

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

name: ebs-standard-30g

spec:

accessModes:

- ReadWriteOnce

storageClassName: standard

resources:

requests:

storage: 30Gi

查看kuberctl資源:

kubectl get storageclasses -o wide

NAME PROVISIONER AGE

standard kubernetes.io/aws-ebs 17h

root@ip-192-168-193-172:~# kubectl get pvc

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE

ebs-standard-30gBound pvc-5eb81e14-1328-459d-8751-b7881c554c0a 30Gi RWO standard 17h

在AWS上可以發現對應的volume已經創建出來:

volume會自動打上Kubernetes集群以及PVC的元數據標籤。

創建一個ngix Pod使用這個PVC:

# kubectl apply -f -

kind: Pod

apiVersion: v1

metadata:

name: nginx

spec:

volumes:

- name: test-ebs-standard-30g

persistentVolumeClaim:

claimName: ebs-standard-30g

containers:

- name: nginx

image: nginx

ports:

- containerPort: 80

name: "http-server"

volumeMounts:

- mountPath: "/usr/share/nginx/html"

name: test-ebs-standard-30g

在AWS上查看volume狀態:

我們發現EBS volume自動掛載到了Pod所在Node的EC2實例上,在Node節點上可以確認:

root@ip-192-168-193-226:~# lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT

loop0 7:0088.4M1 loop /snap/core/7169

loop1 7:1018M1 loop /snap/amazon-ssm-agent/1335

loop2 7:2089.1M1 loop /snap/core/8039

loop3 7:3018M1 loop /snap/amazon-ssm-agent/1480

nvme0n1 259:00100G0 disk /var/lib/docker

nvme1n1 259:108G0 disk

└─nvme1n1p1 259:208G0 part /

nvme2n1 259:3030G0 disk /var/lib/kubelet/pods/422..be/v

\ ` c 55

ol

\umes/kubernetes.io~aws-ebs/pvc-5e..

需要注意的是,由於EBS不支持跨AZ掛載,因此當Pod漂移到其他Node時可能導致Volume無法掛載的情況,不過Scheduler會自動根據Node的AZ元數據規避這種情況。

由上面的實驗可知,Kubernetes通過聲明一個AWS StorageClass指定EBS的卷類型,然後當聲明一個PVC實例時,Kubernetes會自動根據StorageClass配置的元數據創建指定大小的Volume,當Pod聲明使用Volume時,Kubernetes會把Volume掛載到Pod所在的Node EC2實例上,如果沒有文件系統,則會自動創建文件系統,默認的文件系統類型為ext4.

4 Pod網絡與VPC集成

在之前的文章聊聊幾種主流Docker網絡的實現原理中介紹了使用Flannel的aws-vpc後端實現容器的跨主機通信,其原理是把路由配置到AWS VPC路由表中。容器使用的網絡IP位址是Flannel分配和維護的,和VPC的網絡地址完全獨立。這就會有一個問題,AWS VPC Peer為了防止IP/APR欺騙會強制檢查目標IP位址,如果不是對端VPC的IP,則會直接丟棄,因此Flannel aws-vpc雖然能實現跨子網通信,但無法實現跨VPC的通信。

amazon-vpc-cni-k8s是AWS自己維護的開源項目,實現了CNI接口與AWS VPC集成,Pod能直接分配到與Node節點相同子網的IP位址,和EC2虛擬機共享VPC資源,使得Pod和EC2實例實現網絡功能上平行,能夠充分復用VPC原有的功能,如安全組、vpc flow logs、ACL等。並且由於Pod IP就是VPC的IP,因此能夠通過VPC Peer隧道實現跨VPC通信。

4.1 amazon-vpc-cni-k8s配置

配置使用amazon-vpc-cni-k8s很簡單,需要注意的是由於使用這種方案,Node節點可能會自動添加多張網卡,因此在kubeadm配置中最好選擇主網卡的IP作為Node IP,另外podSubnet需要配置為VPC的CIDR,如下:

# cat kubeadm.yaml

nodeRegistration:

# ...

kubeletExtraArgs:

cloud-provider: aws

node-ip: 192.168.193.172# 填寫主網卡的IP位址

# ...

networking:

dnsDomain: cluster.local

podSubnet: 192.168.192.0/22# 填寫VPC的CIDR

serviceSubnet: 10.96.0.0/12

kubeadm init之後需要部署aws-node daemonset,下載yaml聲明文件如下:

curl -sSL -O https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/master/config/v1.5/aws-k8s-cni.yaml

國內網絡環境需要把image從 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni:v1.5.3改為 lmh5257/amazon-k8s-cni:v1.5.3。

使用如下命令部署:

kubectl apply -f aws-k8s-cni.yaml

部署完後由於版本的問題,所有的Node節點可能需要修改 /etc/cni/net.d/10-aws.conflist文件,添加 cniVersion版本信息並重啟kubelet服務:

# cat /etc/cni/net.d/10-aws.conflist

{

"name": "aws-cni",

"cniVersion": "0.3.1", # 需要手動添加

"plugins": [

{

"name": "aws-cni",

"type": "aws-cni",

"vethPrefix": "eni"

},

{

"type": "portmap",

"capabilities": {"portMappings": true},

"snat": true

}

]

}

使用Ubuntu AMI的EC2實例建議關閉ufw功能,新版本的Docker會把iptables的FORWARD Policy設置為DROP,參考https://github.com/moby/moby/issues/14041,這將導致Node節點無法轉發包,需要手動調整所有Node節點FORWARD policy為ACCEPT:

iptables -P FORWARD ACCEPT

AWS EKS AMI則是通過iptables restore持久化如上配置:

[Unit]

Description=Restore iptables

# iptables-restore must start after docker because docker will

# reconfigure iptables to drop forwarded packets.

After=docker.service

[Service]

Type=oneshot

ExecStart=/bin/bash -c "/sbin/iptables-restore < /etc/sysconfig/iptables"

[Install]

WantedBy=multi-user.target

4.2 amazon-vpc-cni IP位址分配原理

前面提到Pod使用的是AWS VPC的子網IP位址,而Pod是運行在EC2實例上,因此首先需要把IP分配給Pod所運行的EC2實例。如何實現?答案是給EC2實例的eni配置多IP,eni(Elastic Network Interfaces )即虛擬網絡,類似OpenStack Neutron的port,而AWS所謂的給eni分配多個IP位址,其實就是類型於OpenStack Neutron port的allowed address pairs功能。

但是AWS的eni能夠支持分配的IP個數是有限的,如何解決這個問題呢?答案是給EC2實例再綁定一個eni網卡。這樣假設一個EC2實例最多能綁定N個eni網卡,每個eni能給分配M個IP,則一個EC2實例可以最多擁有 N*M個IP,除去EC2實例host的每個eni需要佔用一個IP,則一個EC2實例可以分配的POD IP位址數量為 N*M-N,換句話說,一個Node節點至多同時運行 N*M-N個Pod。不同的EC2實例類型N和M的值都不一樣,參考AvailableIpPerENI。以 c5.large為例,能夠支持綁定的eni數量為3,每個eni最多關聯10個IP位址,則該節點最多能夠運行27個Pod。而 c5.xlarge能夠支持最大的eni數量為4,每個eni最多可關聯15個IP,則該節點最多可以運行56個Pod。生產部署時應充分考慮這種限制,選擇適合的EC2實例類型。

aws-node agent會維護每個節點的IP位址池,當Node的可用Pod IP小於某個閾值時會自動調用AWS API創建一個新的eni分配IP位址並關聯到該EC2實例,而當Node的可用Pod IP大於某個閾值時則會自動釋放eni以及IP位址。

我們可以查看Node節點的EC2實例的信息如下:

可見aws-node已經預先添加了一個eni接口並分配了20個IP。

4.3 amazon-vpc-cni網絡通信原理東西流量

為了研究網絡原理,我們exec進入容器查看網絡信息:

# kubectl exec -t -i kubernetes-bootcamp-v1-c5ccf9784-6sgkm -- bash

# ip a

3: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default

link/ether 7a:45:bf:f9:f5:44 brd ff:ff:ff:ff:ff:ff

inet 192.168.193.214/32 brd 192.168.193.214 scope global eth0

valid_lft forever preferred_lft forever

# ip r

default via 169.254.1.1 dev eth0

169.254.1.1 dev eth0 scope link

# ip neigh

192.168.193.194 dev eth0 lladdr fe:2c:94:74:24:25 STALE

169.254.1.1 dev eth0 lladdr fe:2c:94:74:24:25 PERMANENT

看過我之前的文章聊聊幾種主流Docker網絡的實現原理可以發現,和Calico非常類似,容器的IP是32位的,網關為一個假IP 169.254.1.1(IP其實是什麼無所謂),而這個假IP的MAC地址為veth對端eni eni7efd263cb92的MAC地址。換句話說,從容器出去的包會把MAC地址修改為veth pair對端eni eni7efd263cb92的MAC地址。

我們查看Pod所在的Node節點主機網絡信息如下:

# ip a | grep -A 10 -B 1 fe:2c:94:74:24:25

10: eni7efd263cb92@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP groupdefault

link/ether fe:2c:94:74:24:25 brd ff:ff:ff:ff:ff:ff link-netnsid 2

inet6 fe80::fc2c:94ff:fe74:2425/64 scope link

valid_lft forever preferred_lft forever

# ip r

default via 192.168.193.129 dev ens5 proto dhcp src 192.168.193.194 metric 100

192.168.193.128/25 dev ens5 proto kernel scope link src 192.168.193.194

192.168.193.129 dev ens5 proto dhcp scope link src 192.168.193.194 metric 100

192.168.193.214 dev eni7efd263cb92 scope link

目標為192.168.193.214,轉發給 eni7efd263cb92,即容器veth,從而實現了容器入訪的流量分發,入訪沒有問題。

問題是每個Node都有可能有多個相同子網的eni,那容器訪問其他Pod容器的出訪該如何走呢?我們可能會想到的辦法是開啟Linux內核rp_filter功能,但更優雅的做法是配置路由策略,根據分配的IP屬於哪個eni決定從哪個eni轉發出去:

# ip rule | grep 192.168.193.214

512: from all to 192.168.193.214 lookup main

1536: from192.168.193.214 to 192.168.192.0/22 lookup 2

# ip route list table 2

default via 192.168.193.129 dev ens6

192.168.193.129 dev ens6 scope link

可見凡是Pod 192.168.193.214訪問其他容器的出訪都使用table 2,而table 2的默認出口為ens6,這個正是aws-node分配的eni。

南北流量

我們查看iptables規則如下:

-A POSTROUTING -m comment --comment "AWS SNAT CHAN"-j AWS-SNAT-CHAIN-0

-A POSTROUTING -m comment --comment "AWS SNAT CHAN"-j AWS-SNAT-CHAIN-0

-A AWS-SNAT-CHAIN-0! -d 192.168.192.0/22-m comment --comment "AWS SNAT CHAN"-j AWS-SNAT-CHAIN-1

-A AWS-SNAT-CHAIN-1-m comment --comment "AWS, SNAT"-m addrtype ! --dst-type LOCAL -j SNAT --to-source 192.168.193.194--random

可見POSTROUTING會跳到AWS-SNAT-CHAIN-0子鏈,這個子鏈會判斷如果不是VPC網段的(即不是Pod網段),則跳到AWS-SNAT-CHAIN-1子鏈處理,而這個AWS-SNAT-CHAIN-1子鏈的任務其實就是把源IP改為主網卡的IP。

更多關於aws-vpc-cni的設計文檔可參考Proposal: CNI plugin for Kubernetes networking over AWS VPC。

5 Service與四層負載均衡集成

Kubernetes通過Service暴露服務,這個和傳統數據中心暴露服務的方式其實是一樣的,這個Service就是類似四層負載均衡的功能,因此可以認為Service就是一個虛擬四層防火牆,理解這個很重要。因為我們經常誤以為Service是和Pod綁定的,實際上Service與Pod是完全鬆耦合的,Service後端其實綁定的是邏輯的Endpoint,這個Endpoint就是類似於負載均衡的Member。

Endpoint除了可以關聯Pod,甚至支持關聯集群外部已有的服務,Kubernetes做的只是根據Selector自動把Pod IP添加到Endpoint中。prometheus的外部exporter,Kubernetes就顯然無法自動發現,必須通過手動修改Endpoint列表實現。

目前Kubernetes Service除了主要支持的類型為ClusterIP、NodePort以及LoadBalancer,但其實後兩者都是在ClusterIP實現之上的功能疊加。

ClusterIP默認會分配一個虛擬IP,通過IPtables轉發到Pod IP中。NodePort則相當於在ClusterIP的基礎之上做了個Node節點的IP NAT映射,使得外部能夠通過Node的IP與Service通信,與Docker的 -p參數的實現原理類似。

為了研究LoadBalancer實現原理,首先以AWS為例創建一個LoadBalancer Service:

# kubectl apply -f -

apiVersion: v1

kind: Service

metadata:

labels:

app: kubernetes-bootcamp-v1

name: kubernetes-bootcamp-v1

annotations:

service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

spec:

ports:

- port: 8080

protocol: TCP

targetPort: 8080

selector:

app: kubernetes-bootcamp-v1

type: LoadBalancer

以上需要注意的是要添加 service.beta.kubernetes.io/aws-load-balancer-type註解,否則創建的是AWS Classic Load Balancer,這種負載均衡AWS已經廢棄不推薦使用了,因此需要指定創建的Load Balancer類型為 nlb(Network Load Balancer)。

查看創建的Service:

# kubectl get service kubernetes-bootcamp-v1

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

kubernetes-bootcamp-v1 LoadBalancer10.105.6.241 a...3.elb.cn-northwest-1.amazonaws.com.cn 8080:31905/TCP 13h

可見Service的 External-IP為 nlb的域名,在 Port(s)中我們發現該Service分配了一個NodePort。

我們在AWS上查看該負載均衡如下:

Listener為Service指定的Port值。我們查看其target group如下:

可見Targets為Node節點的IP以及Node Port埠。

由此可見LoadBalancer是在NodePort的基礎之上,再通過IaaS雲平臺創建一個四層負載均衡,並把Node以及Node埠添加到後端Member列表中。

6 Ingress與七層負載均衡集成

Service是基於TCP協議的4層埠分發服務,而Ingress則使用了七層的應用層協議進行服務分發,這裡的應用指基於HTTP或者HTTPS協議的Web服務。Ingress可以認為是對Service的再次包裝轉發,支持基於主機名和URL路徑匹配的規則轉發。

目前支持基於Haproxy、Ngnix、Kong等實現Ingress,但使用比較多的Ingress是基於Nginx的反向代理實現的NginxController。而本文將使用AWS ALB Ingress Controller,從名字上可看出是基於AWS的ALB(Application Load Balancer)實現的。

Kubernetes的所有Ingress都需要手動配置安裝,AWS ALB Ingress Controller也不例外。

首先配置RBAC角色:

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.3/docs/examples/rbac-role.yaml

下載ALB ingress controller聲明文件:

wget https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.3/docs/examples/alb-ingress-controller.yaml

之所以不是直接 kubectl apply而是先要下載下來,是因為我們需要稍微修改下配置。

首先需要去掉 --cluster-name=注釋,並填寫cluster name。其次如果Node節點EC2實例沒有配置IAM Role,則需要在聲明文件中配置AKSK,我們已經關聯了IAM Role,因此會自動從metadata中獲取STS,不需要配置AKSK。

完成配置之後apply創建controller:

kubectl apply -f alb-ingress-controller.yaml

此時會在kube-system namespace下創建一個 alb-ingress-controller Deployment,Deployment會通過ReplicaSet基於 amazon/aws-alb-ingress-controller鏡像啟動Pod。

接下來我們創建一個Ingress實例如下:

# kubectl apply -f -

apiVersion: extensions/v1beta1

kind: Ingress

metadata:

name: "int32bit-aws-alb-ingress"

annotations:

kubernetes.io/ingress.class: alb

alb.ingress.kubernetes.io/scheme: internet-facing

alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 18080}]'

labels:

app: int32bit-aws-alb-ingress

spec:

rules:

- host: test.int32bit.me

http:

paths:

- path: /v1

backend:

serviceName: "kubernetes-bootcamp-v1"

servicePort: 8080

- path: /v2

backend:

serviceName: "kubernetes-bootcamp-v2"

servicePort: 8080

如上定義的訪問規則為:

if host == "test.int32bit.me":

if path == "/v1":

redirect to service kubernetes-bootcamp-v1

elif path == "/v2":

redirect to service kubernetes-bootcamp-v2

else:

404

else:

404

另外需要注意的是Kubernetes默認創建的是 internalalb,只能VPC內部訪問,如果需要暴露給網際網路,需要通過 alb.ingress.kubernetes.io/scheme註解配置alb類型為 internet-facing。ALB默認監聽的HTTP埠為80,HTTPS埠為443,由於這些埠均需要ICP備案,因此這次測試通過 alb.ingress.kubernetes.io/listen-ports配置監聽器的HTTP埠為 18080。

我們查看創建的Ingress實例:

# kubectl describe ingresses.

Name: int32bit-aws-alb-ingress

Namespace: default

Address: 82e05f75-default-int32bita-5196-1991518503.cn-northwest-1.elb.amazonaws.com.cn

Default backend: default-http-backend:80(<none>)

Rules:

HostPathBackends

---- ---- ---

test.int32bit.me

/v1 kubernetes-bootcamp-v1:8080(10.244.3.241:8080,10.244.4.6:8080,10.244.5.2:8080)

/v2 kubernetes-bootcamp-v2:8080(10.244.3.242:8080,10.244.4.7:8080,10.244.5.3:8080)

Annotations:

alb.ingress.kubernetes.io/scheme: internet-facing

kubernetes.io/ingress.class: alb

alb.ingress.kubernetes.io/listen-ports: [{"HTTP": 18080}]

其中Address為ALB的DNS域名。為了訪問我們的Ingress服務,通常還需要到域名伺服器上添加一個CNAME記錄test.int32bit.me到這個域名,這裡為了測試直接修改了本地/etc/hosts文件:

# dig +short 82e05f75-default-int32bita-5196-1991518503.cn-northwest-1.elb.amazonaws.com.cn

52.83.70.253

161.189.46.130

# echo "161.189.46.130 test.int32bit.me" >>/etc/hosts

此時我們訪問Ingress服務如下:

# curl test.int32bit.me:18080/v1

HelloKubernetes bootcamp! | Running on: kubernetes-bootcamp-v1-c5ccf9784-cmw2t | v=1

# curl test.int32bit.me:18080/v2

HelloKubernetes bootcamp! | Running on: kubernetes-bootcamp-v2-569df8ddd5-hthwp | v=2

# curl -I test.int32bit.me:18080/v3

HTTP/1.1404NotFound

Server: awselb/2.0

Date: Sat, 23Nov201910:13:54 GMT

Content-Type: text/plain; charset=utf-8

Content-Length: 0

Connection: keep-alive

可見Ingress針對我們的訪問路徑進行了Service正確轉發。

我們查看AWS ALB:

其中Listener監聽的埠為我們Ingress配置的HTTP埠。

我們查看規則如下:

和我們Ingress配置的規則一一對應,根據不同的虛擬主機名和路徑轉發到不同的target group。

我們發現target group和前面的LoadBalance配置完全一樣,都是把Node的IP以及NodePort加到targets中。這也意味著添加到Ingress的Service必須是NodePort類型,當然LoadBalancer也是通過NodePort實現,因此也是沒有問題的。

7 Cluster Autoscaler與AUTO SCALING

Kubernetes的Cluster Autoscaler功能能夠實現Node節點的彈性伸縮,當Node節點資源不足時能夠自動創建新的Node節點來運行Pod,並且自動遷移Pod到新的Node上。

這個功能依賴於雲平臺的AUTO SCALING功能,目前很多雲平臺都支持這個功能,比如OpenStack就支持通過Heat或者Senlin實現自動伸縮,社區已經實現運行在OpenStack Magnum之上的Kubernetes平臺彈性伸縮,參考Cluster Autoscaler for OpenStack Magnum。

AWS的AutoScaling與CloudWatch、LoadBalancer組合可以實現無狀態服務的自動伸縮,目前也已經實現利用AWS的AutoScaling實現Kubernetes的Node節點彈性伸縮,參考Cluster Autoscaler on AWS。

7.1 AWS AutoScaling配置

首先在AWS上創建一個Launch Configuration以及Auto Scaling Group(ASG)

Launch Configuration選擇已經安裝好Docker和kubelet、kubeadm、kubectl的AMI快照,並傳遞如下userdata數據:

#!/bin/bash

MY_HOSTNAME=$(curl -sSL http://169.254.169.254/latest/meta-data/local-hostname)

MY_IP=$(curl -sSL http://169.254.169.254/latest/meta-data/local-ipv4)

hostnamectl set-hostname "${MY_HOSTNAME}"

cat >~/kubeadm.yaml <<EOF

apiVersion: kubeadm.k8s.io/v1beta2

caCertPath: /etc/kubernetes/pki/ca.crt

discovery:

bootstrapToken:

apiServerEndpoint: 192.168.193.172:6443

token: abcdef.0123456789abcdef

unsafeSkipCAVerification: true

timeout: 5m0s

tlsBootstrapToken: abcdef.0123456789abcdef

kind: JoinConfiguration

nodeRegistration:

criSocket: /var/run/dockershim.sock

name: ${MY_HOSTNAME}

taints: null

kubeletExtraArgs:

cloud-provider: aws

node-ip: ${MY_IP}

EOF

kubeadm join --config ~/kubeadm.yaml

ASG打上如下兩個標籤:

7.2 安裝Cluster Autoscaler

下載Cluster Autoscaler,修改 --node-group-auto-discovery參數使結果與ASG打的標籤保持一致。

Ubuntu需要修改volume的ssl-certs host-path為/etc/ssl/certs/ca-certificates.crt,另外image建議修改阿里雲源: registry.cn-hangzhou.aliyuncs.com/google_containers/cluster-autoscaler:v1.12.3。

修改完畢後apply。

7.3 驗證AutoScaling功能

我們創建如下nginx Deployment:

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx

spec:

selector:

matchLabels:

app: nginx

replicas: 10

revisionHistoryLimit: 2

template:

metadata:

labels:

app: nginx

spec:

containers:

- image: nginx:latest

name: nginx

ports:

- containerPort: 80

resources:

requests:

memory: 2G

此時由於資源不足,Pod狀態為pending:

# kubectl get pod

NAME READY STATUS RESTARTS AGE

nginx-7cfc94d94-4k4750/1Pending010m

nginx-7cfc94d94-5pgbw1/1Running011m

nginx-7cfc94d94-5rtvj0/1Pending010m

nginx-7cfc94d94-66c450/1Pending010m

nginx-7cfc94d94-fxdh9 0/1Pending010m

nginx-7cfc94d94-g2hsj 0/1Pending010m

nginx-7cfc94d94-hkqrn 0/1Pending010m

nginx-7cfc94d94-j9zz2 1/1Running010m

nginx-7cfc94d94-mzjx4 1/1Running011m

nginx-7cfc94d94-xsp4s 0/1Pending010m

查看cluster-autoscaler日誌如下:

可見日誌報node資源不足,增加節點個數為4。

查看AWS ASG發現新啟動了4個EC2實例:

等了大概5分鐘後發現新的node添加進來了:

# kubectl get node

NAME STATUS ROLES AGE VERSION

ip-192-168-193-172.cn-northwest-1.compute.internalReady master 20h v1.16.3

ip-192-168-193-194.cn-northwest-1.compute.internalReady<none>2m54s v1.16.3

ip-192-168-193-226.cn-northwest-1.compute.internalReady<none>109s v1.16.3

ip-192-168-193-77.cn-northwest-1.compute.internalReady<none>76s v1.16.3

並且nginx Pod也正在創建:

8 總結

本文以AWS為例,介紹了Kubernetes的Pod網絡、PVC、Service、Ingress、Cluster Autoscaler與IaaS資源的融合,我們發現Kubernetes和底層IaaS資源不是完全割裂的,而是可以相互配合。

相關焦點

  • Kubernetes 擴展神器 Argo 實踐
    以一個實際場景為例,我們需要實現iPaaS中間件在公有雲上自動部署,大致為兩個過程,首先通過Terraform創建虛擬機,然後通過Ansible實現中間件的自動化部署和配置。這裡以Job為例,配置Analysis模板為例: apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate metadata: name: analysis-demo spec: metrics: - name: analysis-demo
  • Kubernetes擴展神器Argo實踐
    以一個實際場景為例,我們需要實現iPaaS中間件在公有雲上自動部署,大致為兩個過程,首先通過Terraform創建虛擬機,然後通過Ansible實現中間件的自動化部署和配置。如果使用Kubernetes Job,需要解決兩個問題:Terraform創建虛擬機完成後如何通知Ansible?
  • Azure對AWS性價比對比
    新的管理磁碟也出來了,可以簡化客戶的存儲管理方式,化繁為簡,返璞歸真,以disk as service的體驗,解決很多客戶頭疼的存儲管理。結合vm sclae set,可以實現iaas+Paas的體驗,完全定製化scale out體驗:
  • 50 個 GitHub 上 你值得收藏的 Kubernetes DevOps 工具
    資源:https://old-docs.jujucharms.com/2.5/en/getting-startedKubeadm為用戶提供了使用單個命令創建Kubernetes集群的最佳實踐「快速路徑」。使用這個工具,你可以在現有的基礎設施上引導集群。Kubeadm符合Kubernetes的認證指南。
  • AWS執行長Andy Jassy介紹AWS容器生態系統
    DynamoDB 更新:AWS的旗艦NoSQL資料庫服務DynamoDB[4]已更新為完整的多區域全局資料庫,無論用戶在哪一個可用區使用該服務,它都可以提供相同的一致穩定的服務。通過提供全局的資料庫表功能 - 它能夠在兩個或者以上的AWS可用區中自動完成表的複製(通過多實例寫的方式完成) 。這可以消除維護複製過程中所有數據管理的繁瑣工作。
  • JupyterHub on Kubernetes: 如何打造 Tubi 數據科學平臺
    大多數用戶傾向於用 CPU Pod,不經意間這其中有些 Pod 會被分配到 GPU 節點上,而此時新啟一個 GPU Pod 可能會遇到資源不足的錯誤,因為 GPU 節點的資源已經被其他 CPU Pod 佔用了。
  • 50+ 頂級開源 Kubernetes 工具列表
    連結: https://github.com/kubernetes/minikube成本: 免費9、KubeadmKubeadm 是自 1.4 版以來的 Kubernetes 分發工具,該工具有助於在現有基礎架構上引導最佳 Kubernetes 集群實踐,但 Kubeadm 無法配置基礎架構,其主要優點是能夠在任何地方發布最小的可行 Kubernetes
  • 如何在本地運行Kubernetes?
    kubernetes-aws.io中列舉了那些可以讓你輕鬆在AWS上創建Kubernetes集群的各種方法。而在開發階段,你可能也需要在本地運行Kubernetes集群,以便你可以在本地啟動和調試你的應用程式。一旦在本地中應用可以滿足要求,那就可以在AWS的集群中部署相同的應用。
  • IaaS、PaaS、SaaS、FaaS等概念的理解
    IT行業經常喜歡造名詞,在雲計算的浪潮下,湧現了一些我們經常看到的概念,如Iaas、paas、saas、faas、baas,這些代表什麼意思?
  • Kubernetes vs OpenStack
    所以說從目前來看,大家對kubernetes的偏愛會更多一些,特別是受開發者的熱愛。給筆者的感覺是Openstack是一個相對完善的雲作業系統,很多公司可以很方便利用他進行自己公司的資源管理,資源分配,是一個已經長大30歲成熟的老大哥,而kubernetes是一個初出茅廬富有活力的新青年,特別是大家的熱愛與追捧,看起來有點像Python和Golang的關係。
  • 【世達百科】在 AWS 中國區安裝配置 OpenShift
    部署架構說明在AWS上部署OpenShift 3,可以通過CloudFormation模版快速創建OpenShift所需要的基礎設施,並符合AWS的最佳實踐,然後通過Ansible部署OpenShift環境。
  • OpenStack 與 Kubernetes 技術融合之路
    OpenStack 與 Kubernetes 一直以來都被討論是否為取代,戰爭,還是融合的兩個大型專案?做為第一個同時整合 Kubernetes 與 OpenStack 的高峰會,現在現場可以觀察到有大量 session 都是討論 OpenStack 與 Kubernetes 確實的整合與開發。
  • Kubernetes 必備工具:2021
    K9s 會持續觀察 Kubernetes 的變化,並提供後續命令來與你觀察到的資源進行交互。HelmHelm[15] 不需要介紹,它是 Kubernetes 最著名的包管理器。你應該在 K8s 中使用包管理器,就像在程式語言中使用它一樣。Helm 允許你將應用程式打包到 Charts[16] 中,將複雜的應用程式抽象為易於定義、安裝和更新的可重用簡單組件。
  • 2020年,值得收藏的50多種Kubernetes工具
    地址:https://github.com/kubernetes/minikube價格:免費自 1.4 版本以來,Kubeadm 成為 Kubernetes 的發行工具。該工具是在已有基礎架構上搭建 Kubernetes 集群的最佳實踐。但是,Kubeadm 無法為您提供基礎架構。
  • aws生產實踐-1:規劃生產級網絡
    比如有10臺機器,您可以把他們的IP位址都寫在前綴列表裡,為其他服務設定安全組的時候,可以指定這一個前綴列表,就會為這10臺機器都開放訪問。安全組:有一個默認的安全組,實際上這個在VPC建立之前就存在,因為默認安全組是不可刪除的。DNS域列表:顯然得有這玩意。(4).創建並規划子網aws一般VPC是/16,子網可以/20。
  • 基於AWS Greengrass的機器學習模型邊緣部署實踐
    安裝用戶程序所依賴的程序包安裝到工作目錄;pip install module-name -t /path/to/my_project-dir以本實驗所需安裝模塊為例,需執行以下命令,將外部依賴庫安裝到當前目錄:pip install pandas -t .pip install numpy -t .
  • Kubernetes CRD開發實踐
    我們日常使用Kubernetes做編排工作的時候,經常會接觸Deployment、Service、Pod等資源對象,我們可以很靈活地創建其定義配置,然後執行kubectl apply命令,Kubernetes總能為我們創建相關資源對象並完成資源的註冊,進而執行資源所負責的功能。
  • Kubernetes 彈性伸縮全場景解析(三) - HPA 實踐手冊
    本文我們將會為大家講解如何使用 HPA 以及一些需要注意的細節。targetCPUUtilizationPercentage 表示:當整體的資源利用率超過 50% 的時候,會進行擴容。接下來我們做一個簡單的 Demo 來實踐下:1.
  • vivo AI計算平臺 Kubernetes集群Ingress網關實踐
    kubernetes 將業務運行環境的容器組抽象為 Pod 資源對象,並提供各種各樣的 workload(deployment、statefulset、daemonset 等)來部署 Pod,同時也提供多種資源對象來解決 Pod 之間網絡通信問題,其中,Service 解決 kubernetes 集群內部網絡通信問題 (東西向流量),Ingress 則通過集群網關形式解決 kubernetes