版權聲明:本文為博主原創文章,歡迎轉載,轉載請注明作者、原文超鏈接 ,博主地址:http://www.cnblogs.com/SuperXJ/
上一章描述了基於spring cloud的微服務實例(實現微服務的自動發現、自動注冊、負載均衡等),同時描述了基於jenkins從代碼提交后自動化構建並自動化部署微服務容器的例子,但是從上一章不難看出微服務都是直接部署在docker上,實際生產環境微服務數量會非常多,直接用docker部署會非常繁瑣不好管理,且容器本身並沒有高可用、服務發現、負載均衡和監控機制,雖然微服務本身具備一定的高可用、負載均衡和服務發現機制,但是仍然無法完全避免由於基礎環境故障和版本升級等情況導致微服務可用性降低甚至中斷,而kubernetes可以解決這些問題,同時還有更多美妙的功能,比如水平擴展、數據持久化,自動發布回滾、秘鑰配置管理、批處理執行,結合jenkins極大的加速生產的構建和部署速度,可以說k8s是專為devops而生,本章就開始k8s之旅,先介紹幾個kubernetes核心概念:
Label:標簽,其實就是給資源分組,然后可以執行一定的組策略,例如我們可以給node打標簽,可以讓Pod運行在指定標簽的node上,也可以給pod打標簽。
Pod:kubernetes最小調度單位,一般來說一個POD只運行一個容器,也可以一個Pod里運行一組容器,k8s中我們不直接操作容器,而是操作Pod。
replication controller:副本控制器,保證pod個數始終與設定值一致,如果遇到pod故障,節點離線等,控制器會刪除這些狀態異常的pod,重新調度生成新的pod。通過label匹配pod,在彈性伸縮、滾動升級中發揮重要作用,本實驗使用新版上功能更強大的Deployment代替replication controller。
service:服務,是一個虛擬概念,邏輯上代理后端pod。眾所周知,pod生命周期短,狀態不穩定,pod異常后新生成的pod ip會發生變化,之前pod的訪問方式均不可達。通過service對pod做代理,service有固定的ip和port,ip:port組合自動關聯后端pod,即使pod發生改變,kubernetes內部更新這組關聯關系,使得service能夠匹配到新的pod。這樣,通過service提供的固定ip,用戶再也不用關心需要訪問哪個pod,以及pod會否發生改變,大大提高了服務質量。如果pod使用rc創建了多個副本,那么service就能代理多個相同的pod,通過kube-proxy,實現負載均衡。
Volumes:K8s數據持久化解決方案,pod可以像掛載文件系統一樣掛載volumes,volumes支持本地存儲、ceph、gluster等等。
Namespace:讓應用有自己的隱私,每個租戶有獨享的service\pod\volume\Deployment空間。
flanneld:跨主機的POD無法直接通信,需要使用第三方解決方案,我這里用的是flanneld,當然也可以用其他的,由於容器無法跨node通信,所以引入flanneld,一個overlay技術,讓容器可以跨node通信,原理是讓集群中的不同節點主機創建的Docker容器都具有全集群唯一的虛擬IP地址 ,Flannel實質上是一種"覆蓋網絡(overlay network)",也就是將TCP數據包裝在另一種網絡包里面進行路由轉發和通信,目前已經支持UDP、VxLAN、AWS VPC和GCE路由等數據轉發方式
Skydns:為了能夠通過服務的名字在集群內部進行服務的相互訪問,需要創建一個虛擬的DNS服務來完成服務名到ClusterIP的解析,Skydns通過下面組成1)etcd:NDS存儲2)kube2sky:將kubernetes Master中的Service(服務)注冊到etcd 3)skyDNS:提供NDS域名解析服務 4)healthz:提供對skydns服務的健康檢查功能
Ingress:7層路由,簡單說就是通過識別URL將不同URL轉到不同service,例如,http://xiongjian/a 轉到server_1,http://xiongjian/b 轉到server_2,其實和上一章說的spring cloud微服務的功能重復了。建議使用微服務的就行了。
本章是基於上一節的環境,確認機器已經完成docker部署、關閉防火牆和selinux、使用上一節的微服務應用eurekaserver,服務端口8761。同樣本篇會在每個環節對新概念再做一點簡單介紹,關於詳細情況了解有需要的話,可以網上搜索一下相關資料。
本次實驗環境為兩台虛機 IP分別為192.168.226.131和130,其中131為k8s master, 130為k8s nodes(本該至少啟兩個node的,無奈電腦配置實在太低),開始准備工作
配置本地鏡像倉庫(不用鏡像庫,后面的yaml文件會找不到本地鏡像)
Master節點上運行:
Docker pull registry
Docker run -d -p 5000:5000 registry
Docker tag eureka centos-master:5000/eureka
Docker push centos-master:5000/eureka
檢測是否成功: curl -XGET http://centos-master:5000/v2/_catalog
顯示{"repositories": ["eureka"]}
配置訪問模式(不配置后面會報錯,運維K8s創建pod默認使用的是https,這里改成http,同時指向centos-master主機的本地鏡像倉庫)
Master節點上 /etc/sysconfig/docker 文件里加上
OPTIONS='--insecure-registry centos-master:5000'
ADD_REGISTRY='--add-registry centos-master:5000'
Service docker restart
Node節點上/etc/docker/daemon.json
{"insecure-registries": ["centos-master:5000"]}
Service docker restart
1、安裝k8s
master上部署etcd(一個分布式強一致性的key/value存儲,可以理解為一個存儲k8s信息的數據庫)、kube-apiserver(管理k8s集群的統一入口、提供restful API支持,校驗和配置Pod、Service和Replication Controller),kube-scheduler(負責資源調度、負責Pods在各個節點上的分配 ),kube-controller-manager(負責容錯、擴容縮容、定期關聯replicationController和pod,保證定義的復制數量與實際運行pod的數量總是一致的,實際上我使用Deployment,它主要職責同樣是為了保證pod的數量和健康,90%的功能與Replication Controller完全一樣,可以看做新一代的Replication Controller。但是,它又具備了Replication Controller之外的很多新特性 )。
Nodes上部署Kube-proxy(服務發現 、定期從etcd獲取所有的service根據service信息創建代理、客戶pod訪問其他pod都經過proxy轉發 )、Kubelet(管理容器的守護進程、管理Docker主機來啟動容器的管理程序、定期從etcd獲取分配到本機的pod信息,啟動或停止容器、接收apiserver的HTTP請求,匯報pod的運行狀態)和 flanneld
1.1 兩台虛機上 /etc/hosts 增加
centos-master = 192.168.226.131
centos-minion-1 = 192.168.226.130
1.2 兩台虛機上配置yum源並安裝
配置yum:
[virt7-docker-common-release]
name=virt7-docker-common-release
baseurl=http://cbs.centos.org/repos/virt7-docker-common-release/x86_64/os/gpgcheck=0
安裝:yum -y install kubernetes etcd flannel
1.3 在兩台虛機上配置 /etc/kubernetes/config
# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true" # journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0" # Should this cluster be allowed to run privileged docker containers
KUBE_ALLOW_PRIV="--allow-privileged=false" # How the replication controller and scheduler find the kube-apiserver 主節點的地址,主要為replication controller和scheduler可以順利找到apiserver
KUBE_MASTER="--master=http://centos-master:8080"
1.4 在兩台虛機上配置/etc/sysconfig/flanneld
# etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="http://centos-master:2379" # etcd config key. This is the configuration key that flannel queries# For address range assignment
FLANNEL_ETCD_PREFIX="/kube-centos/network"
1.5 在master主機上配置
1.5.1 /etc/etcd/etcd.conf
# [member]
ETCD_NAME=default
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379" #[cluster]
ETCD_ADVERTISE_CLIENT_URLS="http://0.0.0.0:2379"
1.5.2 /etc/kubernetes/apiserver
# The address on the local server to listen to. master監聽所有IP
KUBE_API_ADDRESS="--address=0.0.0.0" # The port on the local server to listen on. 監聽端口
KUBE_API_PORT="--port=8080" # Port kubelets listen on從節點上kubelet進程監聽的端口號
KUBELET_PORT="--kubelet-port=10250" # Comma separated list of nodes in the etcd cluster 配置ETCD服務地址
KUBE_ETCD_SERVERS="--etcd-servers=http://centos-master:2379" # Address range to use for services service可以分配的IP地址范圍
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.254.0.0/16" # Add your own!
KUBE_API_ARGS=""
1.5.3 配置etcd, etcd里保存了Flannel分配給每個node docker0的IP地址范圍,可通過查看ifconfig docker0 發現每個機器都是 172.30.*。* ,但是不會沖突。
systemctl start etcd etcdctl mkdir /kube-centos/network etcdctl mk /kube-centos/network/config "{ \"Network\": \"172.30.0.0/16\", \"SubnetLen\": 24, \"Backend\": { \"Type\": \"vxlan\" } }"
1.6 nodes上配置
1.6.1 /etc/kubernetes/kubelet
# The address for the info server to serve on
KUBELET_ADDRESS="--address=0.0.0.0" # The port for the info server to serve on
KUBELET_PORT="--port=10250" # You may leave this blank to use the actual hostname# Check the node number!
KUBELET_HOSTNAME="--hostname-override=centos-minion-1" # Location of the api-server
KUBELET_API_SERVER="--api-servers=http://centos-master:8080" # Add your own!
KUBELET_ARGS=""
1.6.2 /etc/sysconfig/flanneld
# etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="http://centos-master:2379" # etcd config key. This is the configuration key that flannel queries# For address range assignment
FLANNEL_ETCD_PREFIX="/kube-centos/network"
1.7 啟動服務
Master上
systemctl restart kube-apiserver
systemctl restart kube-controller-manager
systemctl restart kube-scheduler
systemctl restart flanneld
systemctl restart docker
kubectl config set-cluster default-cluster --server=http://centos-master:8080 kubectl config set-context default-context --cluster=default-cluster --user=default-admin kubectl config use-context default-context
Node上
systemctl restart kube-proxy
systemctl restart kubelet
systemctl restart flanneld
systemctl restart docker
1.8 測試一下
[root@centos-master k8s]# kubectl get nodes
NAME STATUS AGE
centos-minion-1 Ready 5m
[root@centos-master k8s]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
euraka 0.1 22432d0e5013 20 days ago 206.4 MB
可以看到剛剛啟動的node節點centos-minion-1,另外在回顧一下我們之前創建的euraka的鏡像,這就是上一節的eurekaserver容器鏡像,用於微服務注冊的,這個鏡像會自動啟動一個eurekaserver服務,端口是8761映射到主機的8761。
2、運行一個Pod
2.1創建eureka.yaml文件,用於創建Pod(方便長期維護與跟蹤,建議使用yaml配置文件方式來運行pods )
apiVersion: v1
kind: Pod
metadata:
labels:
name: eurekaserver
spec:
containers:
- name: eurekaserver
image: centos-mater:5000/eureka
創建一個pod,這個pod和容器的名字是eurekaserver,使用的image是我們私有倉庫的 centos-mater/eureka
2.2 創建Pod,Kubectl create -f eureka.yaml
然后kubectl get pod查看eurekaserver一直處於ContainerCreating 狀態,kubectl describe pod eurekaserver 顯示如下報錯,查看一下,,原來依賴於googlepause,不翻牆連不上,大坑。。
"StartContainer" for "POD" with ErrImagePull: "image pull failed for gcr.io/google_containers/pause-amd64:3.0, this may be because there are no credentials on this request. details: (Get https://gcr.io/v1/_ping: dial tcp 74.125.23.82:443: getsockopt: connection refused)"
自己下載pause,然后打tag,也就是本地建一個image偽裝成google的pause image,這里感謝阿里雲。
[root@centos-master k8s]#docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0
[root@centos-master k8s]# docker tag registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 gcr.io/google_containers/pause-amd64:3.0
好了刪除pod之后再次運行
Kubectl delete -f eureka.yaml
Kubectl create -f eureka.yaml
[root@centos-master k8s]# kubectl get pods
NAME READY STATUS RESTARTS AGE
eurekaserver 1/1 Running 0 26m
[root@centos-master k8s]# kubectl describe pod eurekaserver
Name: eurekaserver
Namespace: default
Node: centos-minion-1/192.168.226.130
Labels: <none>
Status: Running
IP: 172.30.81.2
Controllers: <none>
Containers:
eurekaserver:
Container ID: docker://dad09de860ee5756c57a1454c65854278b3d7c90cb2bed0d43f7734673eb1424
Image: centos-master:5000/eureka
Image ID: docker-pullable://centos-master:5000/eureka@sha256:0afc1a6d7e24f594a36087bf83adcecc34ed6ac5a59f11b75d6557c30e777a98
Port: 8761/TCP
State: Running
Ready: True
Restart Count: 0
Volume Mounts: <none>
Environment Variables: <none>
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
No volumes.
QoS Class: BestEffort
Tolerations: <none>
Events:
可以看到pod在節點上centos-minion-1運行成功,IP為172.30.81.2就是上面flanneld分配的,對外服務端口為8761,使用的是我們私有鏡像庫的eureka
驗證一下,訪問http://192.168.226.130:8761:eureka
3、Deployment
運行一個Deployment,如果在yaml中指定了標簽,就可以讓pod運行到指定的node上或者一組指定的node中。(Deployment 的作用是管理一組pod副本,可以在Deployment對象中描述所期望的運行狀態,Deployment控制器為您將現在的實際狀態轉換成您期望的狀態 ,Deployment是一個非常強大的工具,集成了上線部署、滾動升級、創建副本、暫停上線任務,恢復上線任務,回滾到以前某一版本(成功/穩定)的Deployment等功能,在某種程度上,Deployment可以幫我們實現無人值守的上線,大大降低我們的上線過程的復雜溝通、操作風險。)
3.1 編寫deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: eurekaserver-deploy
spec:
replicas: 2
template:
metadata:
labels:
app: eurekaserver
spec:
containers:
- name: eureka
image: centos-master:5000/eureka
ports:
- containerPort: 8761
- hostPort:8761
容器的8761端口映射到node的8761端口。
3.2 運行查看結果
[root@centos-master k8s]# kubectl create -f deployment.yaml
[root@centos-master k8s]# kubectl get pods
NAME READY STATUS RESTARTS AGE
eurekaserver-deploy-252831566-cb1gn 1/1 Running 0 2m
eurekaserver-deploy-252831566-kbdf7 1/1 Running 0 2m
[root@centos-master k8s]# kubectl describe deployment eurekaserver-deploy
Name: eurekaserver-deploy
Namespace: default
Labels: app=eurekaserver
Selector: app=eurekaserver
Replicas: 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: eurekaserver-deploy-252831566 (2/2 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
3m 3m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set eurekaserver-deploy-252831566 to 2
[root@centos-master k8s]#kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE rss-site 2 2 2 2 1m
Replica Set(簡稱RS)是k8s新一代的Pod controller。與RC相比僅有selector存在差異,RS支持了set-based selector(可以使用in、notin、key存在、key不存在四種方式來選擇滿足條件的label集合)。Deployment是基於RS實現的,我們可以使用kubectl get rs命令來查看Deployment創建的RS:
[root@centos-master k8s]# kubectl get rs
NAME DESIRED CURRENT READY AGE
eurekaserver-deploy-252831566 2 2 2 34m
兩個提供同樣服務的eurekaserver pod都啟動了。
3.3 微服務在線滾動升級
當集群中的某個服務需要升級時,我們需要停止目前與該服務相關的所有Pod,然后重新拉取鏡像並啟動。如果集群規模比較大,則這個工作就變成了一個挑戰,而且先全部停止然后逐步升級的方式會導致較長時間的服務不可用。老版本的Kubernetes提供了rolling-update(滾動升級)功能來解決上述問題。滾動升級通過執行kubectl rolling-update命令一鍵完成,該命令創建了一個新的RC,然后自動控制舊的RC中的Pod副本數量逐漸減少到0,同時新的RC中的Pod副本數量從0逐步增加到目標值,最終實現了Pod的升級。需要注意的是,系統要求新的RC需要與舊的RC在相同的命名空間(Namespace)內,即不能把別人的資產偷偷轉移到自家名下,現在使用deployment,只需要用如下辦法即可實現滾動升級:
假如后續我們eurekaserver 微服務有更新了,我們可以更新deployment.yaml 中 image: centos-master:5000/eureka 例如修改為2.0新版本image: centos-master:5000/eureka:v2.0 (前提創建了這個新版鏡像)[root@centos-master k8s]#kubectl apply -f deployment.yaml deployment "eurekaserver-deploy" configured
這樣就完成了滾動升級,同時deployment可以支持升級暫停等。
3.4 微服務在線擴容
將pods拉伸至5,只需修改配置文件中的replicas: 5 就會從2個POD擴容到5個
[root@centos-master k8s]# kubectl replace -f deployment.yaml
deployment "deployment.yaml " replaced
3.5 在線維護(我的測試環境只有一台node,這個無法測試 :( )
將要維護的Minion節點標識為SchedulingDisabled kubectl cordon centos-minion-1
遷移要維護Minion節點上的容器 kubectl drain centos-minion-1
維護完成后,撤銷SchedulingDisabled 標識 kubectl uncordon centos-minion-1
3.6 計算節點宕機處理
無需任何操作,deployment有類似於vmware HA 的機制。
4、k8s service
前面描述了k8s pod、deployment、RS,結合jenkins可以實現容器自動部署、升級、回滾、擴容、和保持生產環境一直運行指定數量的pod等,接下來實現服務的發布和負載均衡,可以使用這一節的k8s service,我理解也可以使用上一章微服務架構中的zuul和feign。實際生產還是如果采用微服務架構,還是推薦使用feign,這樣可以更好的和微服務其他服務單元配合。
在實際生產環境中,對Service的訪問可能會有兩種來源:Kubernetes集群內部的程序(Pod)和Kubernetes集群外部,為了滿足上述的場景,Kubernetes service有以下三種類型:
ClusterIP:提供一個集群內部的虛擬IP以供Pod訪問。
NodePort:在每個Node上打開一個端口以供外部訪問。
LoadBalancer:通過外部的負載均衡器來訪問。
以LoadBalancer service為例
創建文件eurekaserver-service.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 80
targetPort: 8761
selector:
app: eurekaserver
創建service
[root@centos-master k8s]# kubectl create -f eurekaserver-service.yaml
這個service代理了所有具有app: eurekaserver標簽的pod(這個標簽需要在創建Pod或者deployment yaml文件中指定label: - app:eurekaserver 屬性),服務對外端口是80,服務ip在創建service的時候生成,也可以指定,這組ip:port在service的生命周期里保持固定。訪問192.168.226.130:80的流量會被重定向到后端pod的8761端口上,就是targetPort指定的。這樣只要service沒有被銷毀,就可以通過訪問http://192.168.226.130:80:eureka 進入eureka微服務服務發現主頁了,無論后端pod或deployment 如何變化,這個主頁都不變。
5、namespace、密鑰管理等
生產環境作用很大,結合密鑰等做多租戶。
6、問題和展望
寫到這里,有沒有發現什么小問題,本章開始建的私有倉庫沒有做數據持久化,所有創建的容器也沒有做數據持久化,導致每次只要服務重啟就倉庫的鏡像就沒有了需要重新創建,我總結了一下,實現容器數據持久化主要有如下一些方法:
1、容器通過訪問當前宿主機上的指定目錄實現同主機之間共享目錄,將容器運行的數據存放在宿主機上實現容器數據持久化。
2、通過共享卷方式為容器提供持久化存儲,共享卷分為塊存儲(如ceph、EMC VPLEX等)、文件存儲(如NFS、HDFS、NETAPP等)、對象存儲(OpenStack swift, ceph rgw等),共享型的卷存儲能夠很方便地支持容器在集群各節點之間的遷移,另外由於對象存儲是通過URL訪問的,是一種無狀態的架構,符合雲原生應用架構,應該是最符合容器持久化存儲需求的,當然從性能上看肯定還是塊存儲最好,所以要根據容器應用的特點選擇合適的共享存儲。
3、將容器和持久化存儲綁定在一起,實現同步運行和遷移。如ClusterHQ的Flocker,flocker將docker容器和數據卷目錄整合在一起,當你用flocker管理你的有狀態服務時,數據卷目錄將會跟隨你的容器在不同的主機上運行。
最簡單的可以在創建pod時將容器的文件映射到node主機上實現,但是如果發生pod跨node遷移數據就沒有了,這里我推薦使用Kubernetes的的 Persistent Volumes(就是開篇概念里面講的那個Volumes),它可以實現以上集中方式的數據持久化,微服務容器數據持久化還是非常重要的,K8S看來一篇也寫不完。實驗環境機器的配置越來越捉襟見肘了,不知道再往后面能不能跑的動,准備自購服務器了。。。。。。