今天這篇文章裏咱們來說一下Kubernetes
裏的Service
對象。其實前面的文章《Kubernetes初體驗--部署運行Go項目》裏咱們已經與Service
有過一次短暫接觸了,在那篇文章裏我說用Deployment
對象部署完應用後還須要向外界暴露入口才能經過HTTP訪問到Kubernetes
集羣裏的應用Pod
,當時使用的是這樣一條命令,其實就是建立的Service
對象:node
kubectl expose deployment my-go-app --type=NodePort ...
複製代碼
那麼在這篇文章裏咱們就來聊一下:程序員
Service
對象,在Kubernetes
裏它是幹什麼用的;Kubernetes
裏怎麼發現Service
;Service
;文章前面半部分理論知識多一點,稍顯枯燥,後半部分會用一個實踐練習給以前用Deployment
部署好的應用Pod
們加上Service
,讓外部請求能訪問到Kubernetes
集羣裏的應用,併爲Pod
提供負載均衡。web
和以前文章裏介紹的Pod,ReplicaSet,Deployment同樣,Service也是Kubernetes裏的一個API對象,而 Kubernetes 之因此須要 Service,一方面是由於Pod
的 IP 不是固定的,另外一方面則是由於一組Pod
實例須要Service
提供複雜均衡功能。因此Service
是在邏輯抽象層上定義了一組Pod
,爲他們提供一個統一的固定IP和訪問這組Pod
的負載均衡策略。shell
下面是Service
對象的經常使用屬性設置:後端
Pod
;kube-proxy
使用;下面是一個典型的Service
定義:api
apiVersion: v1
kind: Service
metadata:
name: hostnames
spec:
selector:
app: hostnames
ports:
- name: default
protocol: TCP
port: 80
targetPort: 9376
複製代碼
Kubernetes
中有四種Service類型:微信
Kubernetes
服務,使用公有云服務的CloudProvider
建立LoadBalancer類型的Service,同時會自動建立NodePort和ClusterIP類型的Service,LoadBalancer會把請求路由到NodePort和ClusterIP類型的Service上。以上四種類型除了ExternalName
,Kubernetes
的kube-proxy
組件都會爲Service
提供VIP(虛擬IP),kube-proxy
支持兩種模式:iptables和ipvs。涉及到很多知識,感興趣的能夠去極客時間上看這篇文章:Service, DNS與服務發現markdown
上面的第三和第四種類型的Service
在本地試驗不了,因此後面的例子咱們主要經過NodePort
類型的Service
學習它的基本用法。網絡
在Kubernetes
裏的內部組件kube-dns
會監控Kubernetes API,當有新的Service
對象被建立出來後,kube-dns
會爲Service
對象添加DNS A記錄(從域名解析 IP 的記錄)app
對於 ClusterIP
模式的 Service
來講,它的 A 記錄的格式是:
..svc.cluster.local,當你訪問這條 A 記錄的時候,它解析到的就是該 Service 的 VIP 地址。
對於指定了 clusterIP=None 的 Headless Service來講,它的A記錄的格式跟上面同樣,可是訪問記錄後返回的是Pod的IP地址集合。Pod 也會被分配對應的 DNS A 記錄,格式爲: ...svc.cluster.local
咱們會在後面的實踐練習裏經過nslookup
印證DNS記錄是否符合這裏說的格式
跟其餘Kubernetes
裏的API對象,Service
也是經過YAML
文件定義而後提交給Kubernetes
後由ApiManager
建立完成。一個典型的NodePort
類型的Service
的定義以下所示:
apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
type: NodePort
selector:
app: go-app
ports:
- name: http
protocol: TCP
nodePort: 30080
port: 80
targetPort: 3000
複製代碼
這裏定義的Service對象會去管控咱們在以前的文章《K8s上的Go服務怎麼擴容、發版更新、回滾、平滑重啓?教你用Deployment全搞定!》裏用Deployment
建立的Go
應用的三個Pod
副本。
➜ kubectl get pods -l app=go-app
NAME READY STATUS RESTARTS AGE
my-go-app-864496b67b-6hm7r 1/1 Running 1 16d
my-go-app-864496b67b-d87kl 1/1 Running 1 16d
my-go-app-864496b67b-qxrsr 1/1 Running 1 16d
➜
複製代碼
咱們用**kubectl apply -f service.yaml **命令把定義好的Service
提交給Kubernetes
:
➜ kubectl apply -f service.yaml
service/app-service created
複製代碼
被Service
的selector
選中的Pod
,就稱爲Service
的 Endpoints
,可使用 kubectl get ep 命令看到它們,以下所示:
➜ kubectl get ep app-service
NAME ENDPOINTS AGE
app-service 172.17.0.6:3000,172.17.0.7:3000,172.17.0.8:3000 8m38s
複製代碼
須要注意的是,只有處於Running
狀態,且 readinessProbe 檢查經過的Pod
,纔會出如今Service
的 Endpoints
列表裏。當某一個Pod
出現問題時,Kubernetes
會自動把它從 Service
裏摘除掉。
使用 kubectl get svc能夠查看到剛纔看到的Service
的信息和狀態。
➜ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
app-service NodePort 10.108.26.155 <none> 80:30080/TCP 116m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 89d
複製代碼
上面咱們建立了一個NodePort
類型的Service
,在下面的端口映射spec.ports配置裏,每一個端口映射裏出現了三種port:nodePort、port、targetPort。那這三種port
都表明的什麼意思呢?
Service
所使用的端口,集羣內部使用**:**訪問Service
的EndPoints
(Service
選中的Pod)。Service
所使用的端口,從集羣外部使用**:**訪問Service
的EndPoints
。若是不指nodePort,會默認使用port字段指定的端口。若是你不顯式地聲明 nodePort
字段,會隨機分配可用端口來設置代理。這個端口的範圍默認是 30000-32767。targetPort
是後面的Pod
監聽的端口,容器裏的應用也應該監聽這個端口,Service
會把請求發送到這個端口。因此結合剛纔咱們建立的app-service這個Service
的信息,在集羣內部使用10.108.26.155:80 訪問Pod
裏的應用。由於咱們試驗使用的minikube
是個單節點的集羣,NodeIP
能夠經過 minikube ip命令得到。
➜ minikube ip
192.168.64.4
複製代碼
因此從集羣外部,經過192.168.64.4:30080訪問Pod
裏的應用。
➜ curl 192.168.64.4:30080
Hello World
Hostname: my-go-app-75d6d768ff-mlqnh% ➜ curl 192.168.64.4:30080
Hello World
Hostname: my-go-app-75d6d768ff-4x8p8% ➜ curl 192.168.64.4:30080
Hello World
Hostname: my-go-app-75d6d768ff-vt7dx%
複製代碼
經過屢次訪問,咱們能夠看到請求會經過Service
發給不一樣的應用Pod
。Pod
裏的應用就是在原來的文章裏一直使用的例子的基礎上加了一行獲取系統Hostname
的代碼:
...
func index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World")
hostname, _ := os.Hostname()
fmt.Fprintf(w, "Hostname: %s", hostname)
}
...
複製代碼
最後咱們進到Pod
裏看一下Service
建立後kube-dns
組件在集羣裏爲app-service
這個Service
對象建立的DNS A記錄,由於Service
定義裏指定的名字是app-service
,命名空間的話由於沒有指定就是默認的default
命名空間,因此咱們使用nslookup app-service.default.svc.cluster.local 查看一下這條DNS
記錄,進入到其中一個Pod
裏,執行上述查詢的結果以下:
/app # nslookup app-service.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: app-service.default.svc.cluster.local
Address: 10.108.26.155
複製代碼
對於Service
的EndPoints
也是有DNS
記錄的,由於不是Headless Service
,因此須要用**nslookup .app-service.default.svc.cluster.local* 查詢DNS
記錄。
/app # nslookup *.app-service.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: *.app-service.default.svc.cluster.local
Address: 172.17.0.8
Name: *.app-service.default.svc.cluster.local
Address: 172.17.0.6
Name: *.app-service.default.svc.cluster.local
Address: 172.17.0.7
複製代碼
上面查詢出來三條DNS
記錄,正好跟Service
管控的Pod
數量可以對上。
今天的文章裏我結合實例講述了Kubernetes
裏Service
對象的基本使用方法和對象自己的一些原理,其實須要計算機網絡知識掌握的好才能從更深層次瞭解各類模式的Service
的實現原理,這方面的內容推薦極客時間裏的專欄文章深刻剖析Kubernetes Service。
到這裏若是你認真看了我寫的關於Kubernetes的這幾篇文章,再回看我以前的文章Kubernetes入門實踐--部署運行Go項目,就會以爲文章裏的例子很好理解了。Kubernetes
的確是學習曲線比較陡峭,我也是在邊學邊練。但願個人這些入門文章能幫助到想學Kubernetes
的後端程序員們,你們一塊兒進步一塊兒漲薪。
看到這裏了,若是喜歡個人文章能夠幫我點個贊,我會每週經過技術文章分享個人所學所見,感謝你的支持。微信搜索關注公衆號「網管叨bi叨」第一時間獲取個人文章推送。回覆「k8s」獲取文章裏所用實踐練習的源碼和yaml文件。