k8s~術語解釋
簡介
Kubernetes是一個開源的,用於管理雲平台中多個主機上的容器化的應用,Kubernetes的目標是讓部署容器化的應用簡單並且高效(powerful),Kubernetes提供了應用部署,規划,更新,維護的一種機制。
Kubernetes一個核心的特點就是能夠自主的管理容器來保證雲平台中的容器按照用戶的期望狀態運行着(比如用戶想讓apache一直運行,用戶不需要關心怎么去做,Kubernetes會自動去監控,然后去重啟,新建,總之,讓apache一直提供服務),管理員可以加載一個微型服務,讓規划器來找到合適的位置,同時,Kubernetes也系統提升工具以及人性化方面,讓用戶能夠方便的部署自己的應用(就像canary deployments)。
節點node
- master > 負責調度
- worker > 負責項目運行
Kubernetes節點有運行應用容器必備的服務,而這些都是受Master的控制。
每次個節點上當然都要運行Docker。Docker來負責所有具體的映像下載和容器運行。
Kubernetes主要由以下幾個核心組件組成:
- etcd保存了整個集群的狀態;
- apiserver提供了資源操作的唯一入口,並提供認證、授權、訪問控制、API注冊和發現等機制;
- controller manager負責維護集群的狀態,比如故障檢測、自動擴展、滾動更新等;
- scheduler負責資源的調度,按照預定的調度策略將Pod調度到相應的機器上;
- kubelet負責維護容器的生命周期,同時也負責Volume(CVI)和網絡(CNI)的管理;
- Container runtime負責鏡像管理以及Pod和容器的真正運行(CRI);
- kube-proxy負責為Service提供cluster內部的服務發現和負載均衡;
除了核心組件,還有一些推薦的Add-ons:
- kube-dns負責為整個集群提供DNS服務
- Ingress Controller為服務提供外網入口
- Heapster提供資源監控
- Dashboard提供GUI
- Federation提供跨可用區的集群
- Fluentd-elasticsearch提供集群日志采集、存儲與查詢
術語解釋
- pods
在Kubernetes中,最小的管理元素不是一個個獨立的容器,而是Pod,Pod是最小的,管理,創建,計划的最小單元.一個Pod(就像一群鯨魚,或者一個豌豆夾)相當於一個共享context的配置組,在同一個context下,應用可能還會有獨立的cgroup隔離機制,一個Pod是一個容器環境下的“邏輯主機”,它可能包含一個或者多個緊密相連的應用,這些應用可能是在同一個物理主機或虛擬機上。
- 同一個Pod中的應用可以共享磁盤
- 由於docker的架構,一個Pod是由多個相關的並且共享磁盤的容器組成
- Labels
標簽其實就一對 key/value ,被關聯到對象上,比如Pod,標簽的使用我們傾向於能夠標示對象的特殊特點,並且對用戶而言是有意義的(就是一眼就看出了這個Pod是尼瑪數據庫),但是標簽對內核系統是沒有直接意義的。標簽可以用來划分特定組的對象(比如,所有女的),標簽可以在創建一個對象的時候直接給與,也可以在后期隨時修改,每一個對象可以擁有多個標簽,但是,key值必須是唯一的
- Namespace
Namespace是對一組資源和對象的抽象集合,比如可以用來將系統內部的對象划分為不同的項目組或用戶組。常見的pods, services, replication controllers和deployments等都是屬於某一個namespace的(默認是default),而node, persistentVolumes等則不屬於任何namespace
- Replication Controller
Replication Controller 保證了在所有時間內,都有特定數量的Pod副本正在運行,如果太多了,Replication Controller就殺死幾個,如果太少了,Replication Controller會新建幾個,和直接創建的pod不同的是,Replication Controller會替換掉那些刪除的或者被終止的pod,不管刪除的原因是什么(維護阿,更新啊,Replication Controller都不關心)。基於這個理由,我們建議即使是只創建一個pod,我們也要使用Replication Controller。Replication Controller 就像一個進程管理器,監管着不同node上的多個pod,而不是單單監控一個node上的pod,Replication Controller 會委派本地容器來啟動一些節點上服務(Kubelet ,Docker)。
- Node
Node是Pod真正運行的主機,可以物理機,也可以是虛擬機。為了管理Pod,每個Node節點上至少要運行container runtime(比如docker或者rkt)、kubelet和kube-proxy服務。
每個Node都包括以下狀態信息
- 地址:包括hostname、外網IP和內網IP
- 條件(Condition):包括OutOfDisk、Ready、Me* moryPressure和DiskPressure
- 容量(Capacity):Node上的可用資源,包括CPU、內存和Pod總數
- 基本信息(Info):包括內核版本、容器引擎版本、OS類型等
- ReplicaSets
ReplicaSet是下一代復本控制器。ReplicaSet和 Replication Controller之間的唯一區別是現在的選擇器支持。Replication Controller只支持基於等式的selector(env=dev或environment!=qa),但ReplicaSet還支持新的,基於集合的selector(version in (v1.0, v2.0)或env notin (dev, qa))。在試用時官方推薦ReplicaSet。
- Services
Kubernetes Pod是平凡的,它門會被創建,也會死掉(生老病死),並且他們是不可復活的。 ReplicationControllers動態的創建和銷毀Pods(比如規模擴大或者縮小,或者執行動態更新)。每個pod都由自己的ip,這些IP也隨着時間的變化也不能持續依賴。這樣就引發了一個問題:如果一些Pods(讓我們叫它作后台,后端)提供了一些功能供其它的Pod使用(讓我們叫作前台),在kubernete集群中是如何實現讓這些前台能夠持續的追蹤到這些后台的?答案是:Service
Kubernete Service 是一個定義了一組Pod的策略的抽象,我們也有時候叫做宏觀服務。這些被服務標記的Pod都是(一般)通過label Selector決定的(下面我們會講到我們為什么需要一個沒有label selector的服務)
- Volumes
容器中的磁盤的生命周期是短暫的,這就帶來了一系列的問題,第一,當一個容器損壞之后,kubelet 會重啟這個容器,但是文件會丟失-這個容器會是一個全新的狀態,第二,當很多容器在同一Pod中運行的時候,很多時候需要數據文件的共享。Kubernete Volume解決了這個問題.
一個Kubernetes volume,擁有明確的生命周期,與所在的Pod的生命周期相同。因此,Kubernetes volume獨立與任何容器,與Pod相關,所以數據在重啟的過程中還會保留,當然,如果這個Pod被刪除了,那么這些數據也會被刪除。更重要的是,Kubernetes volume 支持多種類型,任何容器都可以使用多個Kubernetes volume。
- Deployment
Deployment為Pod和ReplicaSet提供了一個聲明式定義(declarative)方法,用來替代以前的ReplicationController來方便的管理應用。典型的應用場景包括:
- 定義Deployment來創建Pod和ReplicaSet
2.滾動升級和回滾應用 - 擴容和縮容
- 暫停和繼續Deployment
比如一個簡單的nginx應用可以定義為
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80
- Secret
Secret解決了密碼、token、密鑰等敏感數據的配置問題,而不需要把這些敏感數據暴露到鏡像或者Pod Spec中。Secret可以以Volume或者環境變量的方式使用。
Secret有三種類型:
- Service Account:用來訪問Kubernetes API,由Kubernetes自動創建,並且會自動掛載到Pod的/run/secrets/kubernetes.io/serviceaccount目錄中;
- Opaque:base64編碼格式的Secret,用來存儲密碼、密鑰等;
- kubernetes.io/dockerconfigjson:用來存儲私有docker registry的認證信息。
- Ingress
在本篇文章中你將會看到一些在其他地方被交叉使用的術語,為了防止產生歧義,我們首先來澄清下。
- 節點:Kubernetes集群中的服務器;
- 集群:Kubernetes管理的一組服務器集合;
- 邊界路由器:為局域網和Internet路由數據包的路由器,執行防火牆保護局域網絡;
- 集群網絡:遵循Kubernetes網絡模型實現群集內的通信的具體實現,比如flannel 和 OVS。
- 服務:使用標簽選擇器標識一組pod成為的Kubernetes Service。 除非另有說明,否則服務的虛擬IP僅可在集群內部訪問。
什么是Ingress?
通常情況下,service和pod的IP僅可在集群內部訪問。集群外部的請求需要通過負載均衡轉發到service在Node上暴露的NodePort上,然后再由kube-proxy將其轉發給相關的Pod。
Ingress可以給service提供集群外部訪問的URL、負載均衡、SSL終止、HTTP路由等。為了配置這些Ingress規則,集群管理員需要部署一個Ingress controller,它監聽Ingress和service的變化,並根據規則配置負載均衡並提供訪問入口。
- ConfigMap
ConfigMap用於保存配置數據的鍵值對,可以用來保存單個屬性,也可以用來保存配置文件。ConfigMap跟secret很類似,但它可以更方便地處理不包含敏感信息的字符串。
k8s的yaml說明
理解k8s里的幾個概念
Kubernetes 通過各種 Controller 來管理 Pod 的生命周期。為了滿足不同業務場景,Kubernetes 開發了 Deployment、ReplicaSet、DaemonSet、StatefuleSet、Job 等多種 Controller。最常用的 Deployment用來建立Pod,以下是它的步驟
- kubectl創建deployment
- deployment創建Replcasset
- 根據Replicaset創建pod
replicas的命名方式 :deployment名稱+隨機數
pod的命名方式:replicas名稱+隨機數
理解targetport,port,nodeport
targetPort
targetPort很好理解,targetPort是pod上的端口,從port和nodePort上到來的數據最終經過kube-proxy流入到后端pod的targetPort上進入容器。
port
這里的port表示:service暴露在cluster ip上的端口,:port 是提供給集群內部客戶訪問service的入口,而在集群內部,各個服務之間我們也可以通過服務名+Port來訪問指定的服務,k8s內部會把服務解析到對應的pod上面。
nodePort
nodePort是kubernetes提供給集群外部客戶訪問service入口的一種方式(另一種方式是LoadBalancer),所以,:nodePort 是提供給集群外部客戶訪問service的入口。
k8s yaml
# yaml格式的pod定義文件完整內容: apiVersion: v1 #必選,版本號,例如v1 kind: Pod #必選,Pod metadata: #必選,元數據 name: string #必選,Pod名稱 namespace: string #必選,Pod所屬的命名空間 labels: #自定義標簽 - name: string #自定義標簽名字 annotations: #自定義注釋列表 - name: string spec: #必選,Pod中容器的詳細定義 containers: #必選,Pod中容器列表 - name: string #必選,容器名稱 image: string #必選,容器的鏡像名稱 imagePullPolicy: [Always | Never | IfNotPresent] #獲取鏡像的策略 Alawys表示下載鏡像 IfnotPresent表示優先使用本地鏡像,否則下載鏡像,Nerver表示僅使用本地鏡像 command: [string] #容器的啟動命令列表,如不指定,使用打包時使用的啟動命令 args: [string] #容器的啟動命令參數列表 workingDir: string #容器的工作目錄 volumeMounts: #掛載到容器內部的存儲卷配置 - name: string #引用pod定義的共享存儲卷的名稱,需用volumes[]部分定義的的卷名 mountPath: string #存儲卷在容器內mount的絕對路徑,應少於512字符 readOnly: boolean #是否為只讀模式 ports: #需要暴露的端口庫號列表 - name: string #端口號名稱 containerPort: int #容器需要監聽的端口號 hostPort: int #容器所在主機需要監聽的端口號,默認與Container相同 protocol: string #端口協議,支持TCP和UDP,默認TCP env: #容器運行前需設置的環境變量列表 - name: string #環境變量名稱 value: string #環境變量的值 resources: #資源限制和請求的設置 limits: #資源限制的設置 cpu: string #Cpu的限制,單位為core數,將用於docker run --cpu-shares參數 memory: string #內存限制,單位可以為Mib/Gib,將用於docker run --memory參數 requests: #資源請求的設置 cpu: string #Cpu請求,容器啟動的初始可用數量 memory: string #內存清楚,容器啟動的初始可用數量 livenessProbe: #對Pod內個容器健康檢查的設置,當探測無響應幾次后將自動重啟該容器,檢查方法有exec、httpGet和tcpSocket,對一個容器只需設置其中一種方法即可 exec: #對Pod容器內檢查方式設置為exec方式 command: [string] #exec方式需要制定的命令或腳本 httpGet: #對Pod內個容器健康檢查方法設置為HttpGet,需要制定Path、port path: string port: number host: string scheme: string HttpHeaders: - name: string value: string tcpSocket: #對Pod內個容器健康檢查方式設置為tcpSocket方式 port: number initialDelaySeconds: 0 #容器啟動完成后首次探測的時間,單位為秒 timeoutSeconds: 0 #對容器健康檢查探測等待響應的超時時間,單位秒,默認1秒 periodSeconds: 0 #對容器監控檢查的定期探測時間設置,單位秒,默認10秒一次 successThreshold: 0 failureThreshold: 0 securityContext: privileged:false restartPolicy: [Always | Never | OnFailure]#Pod的重啟策略,Always表示一旦不管以何種方式終止運行,kubelet都將重啟,OnFailure表示只有Pod以非0退出碼退出才重啟,Nerver表示不再重啟該Pod nodeSelector: obeject #設置NodeSelector表示將該Pod調度到包含這個label的node上,以key:value的格式指定 imagePullSecrets: #Pull鏡像時使用的secret名稱,以key:secretkey格式指定 - name: string hostNetwork:false #是否使用主機網絡模式,默認為false,如果設置為true,表示使用宿主機網絡 volumes: #在該pod上定義共享存儲卷列表 - name: string #共享存儲卷名稱 (volumes類型有很多種) emptyDir: {} #類型為emtyDir的存儲卷,與Pod同生命周期的一個臨時目錄。為空值 hostPath: string #類型為hostPath的存儲卷,表示掛載Pod所在宿主機的目錄 path: string #Pod所在宿主機的目錄,將被用於同期中mount的目錄 secret: #類型為secret的存儲卷,掛載集群與定義的secre對象到容器內部 scretname: string items: - key: string path: string configMap: #類型為configMap的存儲卷,掛載預定義的configMap對象到容器內部 name: string items: - key: string path: string
這些語法適合於rancher平台,因為rancher平台的底層也是k8s為基礎的。
k8s~k8s里的服務Service
k8s用命名空間namespace
把資源進行隔離,默認情況下,相同的命名空間里的服務可以相互通訊,反之進行隔離。
服務Service
1.1 Service
Kubernetes中一個應用服務會有一個或多個實例(Pod,Pod可以通過rs進行多復本的建立),每個實例(Pod)的IP地址由網絡插件動態隨機分配(Pod重啟后IP地址會改變)。為屏蔽這些后端實例的動態變化和對多實例的負載均衡,引入了Service這個資源對象,如下所示:
apiVersion: v1 kind: Service metadata: name: nginx-svc labels: app: nginx spec: type: ClusterIP ports: - port: 80 targetPort: 80 selector: #service通過selector和pod建立關聯 app: nginx
根據創建Service的type類型不同,可分成4種模式:
ClusterIP
: 默認方式。根據是否生成ClusterIP又可分為普通Service和Headless Service兩類:- 普通Service:通過為Kubernetes的Service分配一個集群內部可訪問的
固定虛擬IP
(Cluster IP),實現集群內的訪問。為最常見的方式。 - Headless Service:該服務不會分配Cluster IP,也不通過kube-proxy做反向代理和負載均衡。而是通過DNS提供穩定的絡ID來訪問,DNS會將headless service的后端直接解析為podIP列表。主要供StatefulSet使用。
- 普通Service:通過為Kubernetes的Service分配一個集群內部可訪問的
NodePort
:除了使用Cluster IP之外,還通過將service的port映射到集群內每個節點的相同一個端口,實現通過nodeIP:nodePort從集群外訪問服
務。LoadBalancer
:和nodePort類似,不過除了使用一個Cluster IP和nodePort之外,還會向所使用的公有雲申請一個負載均衡器(負載均衡器后端映射到各節點的nodePort),實現從集群外通過LB訪問服務。ExternalName
:是 Service 的特例。此模式主要面向運行在集群外部的服務,通過它可以將外部服務映射進k8s集群,且具備k8s內服務的一些特征(如具備namespace等屬性),來為集群內部提供服務。此模式要求kube-dns的版本為1.7或以上。這種模式和前三種模式(除headless service)最大的不同是重定向依賴的是dns層次,而不是通過kube-proxy。
比如,在service定義中指定externalName的值"my.database.example.com":
此時k8s集群內的DNS服務會給集群內的服務名 ..svc.cluster.local 創建一個CNAME記錄,其值為指定的"my.database.example.com"。
當查詢k8s集群內的服務my-service.prod.svc.cluster.local時,集群的 DNS 服務將返回映射的CNAME記錄"foo.bar.example.com"。
備注:
前3種模式,定義服務的時候通過selector指定服務對應的pods,根據pods的地址創建出endpoints作為服務后端;Endpoints Controller會watch Service以及pod的變化,維護對應的Endpoint信息。kube-proxy根據Service和Endpoint來維護本地的路由規則。當Endpoint發生變化,即Service以及關聯的pod發生變化,kube-proxy都會在每個節點上更新iptables,實現一層負載均衡。
而ExternalName模式則不指定selector,相應的也就沒有port和endpoints。
ExternalName和ClusterIP中的Headles Service同屬於Headless Service的兩種情況。Headless Service主要是指不分配Service IP,且不通過kube-proxy做反向代理和負載均衡的服務。
1.2 Port
Service中主要涉及三種Port: * port
這里的port表示service暴露在clusterIP上的端口,clusterIP:Port 是提供給集群內部
訪問kubernetes服務的入口。
-
targetPort
containerPort,targetPort是pod上的端口,從port和nodePort上到來的數據最終經過kube-proxy流入到后端pod的targetPort上進入容器。 -
nodePort
nodeIP:nodePort 是提供給從集群外部訪問kubernetes服務的入口。
總的來說,port和nodePort都是service的端口,前者暴露給從集群內訪問服務,后者暴露給從集群外訪問服務。從這兩個端口到來的數據都需要經過反向代理kube-proxy流入后端具體pod的targetPort,從而進入到pod上的容器內。
1.3 IP
使用Service服務還會涉及到幾種IP:
-
ClusterIP
Pod IP 地址是實際存在於某個網卡(可以是虛擬設備)上的,但clusterIP就不一樣了,沒有網絡設備承載這個地址。它是一個虛擬地址,由kube-proxy使用iptables規則重新定向到其本地端口,再均衡到后端Pod。當kube-proxy發現一個新的service后,它會在本地節點打開一個任意端口,創建相應的iptables規則,重定向服務的clusterIP和port到這個新建的端口,開始接受到達這個服務的連接。 -
Pod IP
Pod的IP,每個Pod啟動時,會自動創建一個鏡像為gcr.io/google_containers/pause的容器,Pod內部其他容器的網絡模式使用container模式,並指定為pause容器的ID,即:network_mode: "container:pause容器ID",使得Pod內所有容器共享pause容器的網絡,與外部的通信經由此容器代理,pause容器的IP也可以稱為Pod IP。 -
節點IP
Node-IP,service對象在Cluster IP range池中分配到的IP只能在內部訪問,如果服務作為一個應用程序內部的層次,還是很合適的。如果這個service作為前端服務,准備為集群外的客戶提供業務,我們就需要給這個服務提供公共IP了。指定service的spec.type=NodePort,這個類型的service,系統會給它在集群的各個代理節點上分配一個節點級別的端口,能訪問到代理節點的客戶端都能訪問這個端口,從而訪問到服務。
k8s~跨namespace的service相互訪問
在k8s里,你可以通過服務名去訪問相同namespace里的服務,然后服務可以解析到對應的pod,從而再由pod轉到對應的容器里,我們可以認為這個過程有兩個port的概念,service port 就是服務的port,在k8s配置文件里用port
表示,還有一個是pod和容器的port,用targetPort表示,其中pod和容器的port你可以認為它是一個。
多namespace的service場景
我們通常會把mysql,redis,rabbitmq,mongodb這些公用組件放在一個namespace里,或者每個公用組件都有自己的namespace,而你的業務組件會統一放在自己的namespace里,這時就涉及到了跨namespace的數據通訊問題。
k8s的服務名DNS解析
Kubernetes 目前使用的kube-dns來實現集群內部的service dns記錄解析。默認情況下/etc/resolv.conf里,它的內容是統一的格式。
/ # more /etc/resolv.conf
nameserver 172.19.0.10 search saas.svc.cluster.local svc.cluster.local cluster.local options ndots:5
search doamin列表默認情況下,它只包含本地域名
。這可以通過在search關鍵字后面列出所需的域搜索路徑來添加。kubernetes為每個容器配置默認是${namespace}.svc.cluster.local
svc.cluster.local
cluster.local
。在一次dns域名查詢時,將會嘗試使用每個search doamin依次搜索少於ndots點(默認值為1)的解析器查詢,直到找到匹配項。對於具有多個子域的環境,建議調整選項ndots:n,以避免man-in-the-middle攻擊和root-dns-servers的不必要通信。
noots:5
這個我們可以把它理解成服務名dns解析的層次,例如{服務名}是一級,而{服務名}.{命名空間}為二層,{服務名}.{命名空間}.svc.cluster.local是第三層,上面的配置一共有5層,同時也開啟了5層,這樣做可以保證最大限度的找到你的服務,但對於解析的性能是有影響的。
請注意,如果搜索域對應的服務器不是本地的,那么這個查詢過程可能會很慢,並且會產生大量的網絡流量。如果其中一個搜索域域沒有可用的服務器,則查詢將超時。
同一集群跨namespace訪問
如果你要連接namespace是redis的,服務名是redis-master的服務,你可以這樣去配置你的連接:
spring: profiles: redis-prod redis: host: redis-master.redis port: 6379 password: 123456 database: 1
它采用了服務名+命名空間的格式,如果是相同的namespace,可以直接使用服務名來解析。
k8s~為服務添加ingress的實現
ingress產生的原因
ClusterIP的方式只能在集群內部訪問
NodePort方式的話,測試環境使用還行,當有幾十上百的服務在集群中運行時,NodePort的端口管理是災難。
LoadBalance方式受限於雲平台,且通常在雲平台部署ELB還需要額外的費用。
所幸k8s還提供了一種集群維度暴露服務的方式,也就是ingress。ingress可以簡單理解為service的service,他通過獨立的ingress對象來制定請求轉發的規則,把請求路由到一個或多個service中。這樣就把服務與請求規則解耦了,可以從業務維度統一考慮業務的暴露,而不用為每個service單獨考慮,下面是一個簡單的ingress應用圖,實現了簡單的請求轉發
ingress和ingress-controller
ingress對象:
指的是k8s中的一個api對象,一般用yaml配置。作用是定義請求如何轉發到service的規則,可以理解為配置模板。
ingress-controller:
具體實現反向代理及負載均衡的程序,對ingress定義的規則進行解析,根據配置的規則來實現請求轉發。
簡單來說,ingress-controller才是負責具體轉發的組件,通過各種方式將它暴露在集群入口,外部對集群的請求流量會先到ingress-controller,而ingress對象是用來告訴ingress-controller該如何轉發請求,比如哪些域名哪些path要轉發到哪些服務等等。
ingress-controller並不是k8s自帶的組件,實際上ingress-controller只是一個統稱,用戶可以選擇不同的ingress-controller實現,目前,由k8s維護的ingress-controller只有google雲的GCE與ingress-nginx兩個,其他還有很多第三方維護的ingress-controller,具體可以參考 官方文檔。但是不管哪一種ingress-controller,實現的機制都大同小異,只是在具體配置上有差異。一般來說,ingress-controller的形式都是一個pod,里面跑着daemon程序和反向代理程序。daemon負責不斷監控集群的變化,根據ingress對象生成配置並應用新配置到反向代理,比如nginx-ingress就是動態生成nginx配置,動態更新upstream,並在需要的時候reload程序應用新配置。為了方便,后面的例子都以k8s官方維護的nginx-ingress為例。
為服務添加ingress的實現

2 我們的ingress是支持https的,所以需要為你的域名配置對應的證書,我們在配置文件里添加

3 自動為ingress-controller里的配置文件添加nginx配置項,然后自動reload它,讓它生效

當有新的ingress服務注冊之后,配置文件會發生變化

4 你的服務對應的nginx是在自己服務的ymal里進行配置的,一般來說,微服務的網關層都應該建立 一個ingress-nginx來對外提供服務!
- 注明ingress的名稱
- 填寫之前建立的密文名稱(ingress https 證書)
- 填寫你的服務所在的namespace,不寫默認為default
- 填寫你要轉發的服務的域名
- 填寫你服務的名稱和pod的端口
# 構建反射代理 kind: Ingress apiVersion: extensions/v1beta1 metadata: name: hello-world-ingress namespace: saas annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/use-regex: "true" spec: tls: - hosts: - www.abc.com secretName: saas-tls rules: - host: www.abc.com http: paths: - backend: serviceName: hello-world servicePort: 9001
## start server www.abc.com server { server_name www.abc.com ; listen 80 ; listen [::]:80 ; listen 443 ssl http2 ; listen [::]:443 ssl http2 ; set $proxy_upstream_name "-"; # PEM sha: c24ba9e405ed77662c0fd7546a908ef45ca76066 ssl_certificate /etc/ingress-controller/ssl/default-fake-certificate.pem; ssl_certificate_key /etc/ingress-controller/ssl/default-fake-certificate.pem; ssl_certificate_by_lua_block { certificate.call() } location ~* "^/" { set $namespace "saas"; set $ingress_name "hello-world-ingress"; set $service_name "hello-world"; set $service_port "{0 9001 }"; set $location_path "/"; rewrite_by_lua_block { lua_ingress.rewrite({ force_ssl_redirect = true, use_port_in_redirects = false, }) balancer.rewrite() plugins.run() } header_filter_by_lua_block { plugins.run() } body_filter_by_lua_block { } log_by_lua_block { balancer.log() monitor.call() plugins.run() } if ($scheme = https) { more_set_headers "Strict-Transport-Security: max-age=15724800; includeSubDomains"; } port_in_redirect off; set $balancer_ewma_score -1; set $proxy_upstream_name "saas-hello-world-9001"; set $proxy_host $proxy_upstream_name; set $pass_access_scheme $scheme; set $pass_server_port $server_port; set $best_http_host $http_host; set $pass_port $pass_server_port; set $proxy_alternative_upstream_name ""; client_max_body_size 1m; proxy_set_header Host $best_http_host; # Pass the extracted client certificate to the backend # Allow websocket connections proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header X-Request-ID $req_id; proxy_set_header X-Real-IP $the_real_ip; proxy_set_header X-Forwarded-For $the_real_ip; proxy_set_header X-Forwarded-Host $best_http_host; proxy_set_header X-Forwarded-Port $pass_port; proxy_set_header X-Forwarded-Proto $pass_access_scheme; proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Scheme $pass_access_scheme; # Pass the original X-Forwarded-For proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for; # mitigate HTTPoxy Vulnerability # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/ proxy_set_header Proxy ""; # Custom headers to proxied server proxy_connect_timeout 5s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_buffering off; proxy_buffer_size 4k; proxy_buffers 4 4k; proxy_request_buffering on; proxy_http_version 1.1; proxy_cookie_domain off; proxy_cookie_path off; # In case of errors try the next upstream server before returning an error proxy_next_upstream error timeout; proxy_next_upstream_timeout 0; proxy_next_upstream_tries 3; proxy_pass http://upstream_balancer; proxy_redirect off; } } ## end server www.abc.com
k8s~kubectl常用命令
查看所有 pod 列表, -n 后跟 namespace, 查看指定的命名空間
kubectl get pod kubectl get pod -n kube kubectl get pod -o wide
查看 RC 和 service 列表, -o wide 查看詳細信息
kubectl get rc,svc kubectl get pod,svc -o wide kubectl get pod <pod-name> -o yaml
顯示 Node 的詳細信息
kubectl describe node 192.168.0.212
顯示 Pod 的詳細信息, 特別是查看 pod 無法創建的時候的日志
kubectl describe pod <pod-name> eg: kubectl describe pod redis-master-tqds9
根據 yaml 創建資源, apply 可以重復執行,create 不行
kubectl create -f pod.yaml kubectl apply -f pod.yaml
基於 pod.yaml 定義的名稱刪除 pod
kubectl delete -f pod.yaml
刪除所有包含某個 label 的pod 和 service
kubectl delete pod,svc -l name=<label-name>
刪除所有 Pod
kubectl delete pod --all
查看 endpoint 列表
kubectl get endpoints
執行 pod 的 date 命令
kubectl exec <pod-name> -- date kubectl exec <pod-name> -- bash kubectl exec <pod-name> -- ping 10.24.51.9
通過bash獲得 pod 中某個容器的TTY,相當於登錄容器
kubectl exec -it <pod-name> -c <container-name> -- bash eg: kubectl exec -it redis-master-cln81 -- bash
查看容器的日志
kubectl logs <pod-name> kubectl logs -f <pod-name> # 實時查看日志 kubectl log <pod-name> -c <container_name> # 若 pod 只有一個容器,可以不加 -c
查看注釋
kubectl explain pod kubectl explain pod.apiVersion
k8s~helm的介紹
Helm 是什么
- Helm 是 Deis 開發的一個用於 Kubernetes 應用的包管理工具,主要用來管理 Charts。有點類似於 Ubuntu 中的 APT 或 CentOS 中的 YUM。
- Helm Chart 是用來封裝 Kubernetes 原生應用程序的一系列 YAML 文件。可以在你部署應用的時候自定義應用程序的一些 Metadata,以便於應用程序的分發。
對於應用發布者而言,可以通過 Helm 打包應用、管理應用依賴關系、管理應用版本並發布應用到軟件倉庫。 - 對於使用者而言,使用 Helm 后不用需要編寫復雜的應用部署文件,可以以簡單的方式在 Kubernetes 上查找、安裝、升級、回滾、卸載應用程序。
做為 Kubernetes 的一個包管理工具,Helm具有如下功能:
- 創建新的 chart
- chart 打包成 tgz 格式
- 上傳 chart 到 chart 倉庫或從倉庫中下載 chart
- 在Kubernetes集群中安裝或卸載 chart
- 管理用Helm安裝的 chart 的發布周期
Helm 組件及相關術語
本文中講到的是helm V2最新版本,V3版本也已經發布了beta版,在 Helm 3 中,Tiller 被移除了。
-
Helm
Helm 是一個命令行下的客戶端工具。主要用於 Kubernetes 應用程序 Chart 的創建、打包、發布以及創建和管理本地和遠程的 Chart 倉庫。 -
Tiller
Tiller 是 Helm 的服務端,部署在 Kubernetes 集群中。Tiller 用於接收 Helm 的請求,並根據 Chart 生成 Kubernetes 的部署文件( Helm 稱為 Release ),然后提交給 Kubernetes 創建應用。Tiller 還提供了 Release 的升級、刪除、回滾等一系列功能。 -
Chart
包含了創建Kubernetes的一個應用實例的必要信息,Helm 的軟件包,采用 TAR 格式。類似於 APT 的 DEB 包或者 YUM 的 RPM 包,其包含了一組定義 Kubernetes 資源相關的 YAML 文件。 -
Repoistory
Helm 的軟件倉庫,Repository 本質上是一個 Web 服務器,該服務器保存了一系列的 Chart 軟件包以供用戶下載,並且提供了一個該 Repository 的 Chart 包的清單文件以供查詢。Helm 可以同時管理多個不同的 Repository。 -
Release
是一個 chart 及其配置的一個運行實例,使用 helm install 命令在 Kubernetes 集群中部署的 Chart 稱為 Release。
Helm 工作流程
-
Chart Install 過程
Helm 從指定的目錄或者 TAR 文件中解析出 Chart 結構信息。
Helm 將指定的 Chart 結構和 Values 信息通過 gRPC 傳遞給 Tiller。
Tiller 根據 Chart 和 Values 生成一個 Release。
Tiller 將 Release 發送給 Kubernetes 用於生成 Release。 -
Chart Update 過程
Helm 從指定的目錄或者 TAR 文件中解析出 Chart 結構信息。
Helm 將需要更新的 Release 的名稱、Chart 結構和 Values 信息傳遞給 Tiller。
Tiller 生成 Release 並更新指定名稱的 Release 的 History。
Tiller 將 Release 發送給 Kubernetes 用於更新 Release。 -
Chart Rollback 過程
Helm 將要回滾的 Release 的名稱傳遞給 Tiller。
Tiller 根據 Release 的名稱查找 History。
Tiller 從 History 中獲取上一個 Release。
Tiller 將上一個 Release 發送給 Kubernetes 用於替換當前 Release。 -
Chart 處理依賴說明
Tiller 在處理 Chart 時,直接將 Chart 以及其依賴的所有 Charts 合並為一個 Release,同時傳遞給 Kubernetes。因此 Tiller 並不負責管理依賴之間的啟動順序。Chart 中的應用需要能夠自行處理依賴關系。
k8s~helm的安裝過程
上一講說了一些helm的基本概念,而今天主要說一下如何把helm部署到服務器上,在helm3之前的版本里,它由客戶端helm和服務端tiller組成,而helm3.0之后它去掉了tiller,而直接與k8s通訊,可以說在部署上更簡單了,而今天我們主要還是部署2.x版本的helm.
下載安裝包
https://get.helm.sh/helm-v2.16.5-linux-amd64.tar.gz
腳本文檔rbac-config.yml
apiVersion: v1 kind: ServiceAccount metadata: name: tiller namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: tiller roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: tiller namespace: kube-system
構建rbac
kubectl create -f rbac-config.yaml
初始化tiller
helm init --service-account tiller --skip-refresh
對於 Kubernetes v1.16.0 以上的版本,有可能會碰到 Error: error installing: the server could not find the requested resource 的錯誤。這是由於 extensions/v1beta1 已經被 apps/v1 替代。相信在2.15 或者 3 版本發布之后, 應該就不會遇到這個問題了。還是生態比較慢的原因。
重新初始化tiller
helm init -i registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.14.3 --stable-repo-url http://mirror.azure.cn/kubernetes/charts/ --service-account tiller --override spec.selector.matchLabels.'name'='tiller',spec.selector.matchLabels.'app'='helm' --output yaml | sed 's@apiVersion: extensions/v1beta1@apiVersion: apps/v1@' | kubectl apply -f -
查看tiller的pod信息
kubectl get pods -n kube-system | grep tiller tiller-deploy-7c7b67c9fd-kxh6p 1/1 Running 0 4m58s
到現在為止,我們的helm就安裝功能了,之后我們裝運行helm來進行charts的安裝
k8s~helm3更方便的部署
上級講了helm2的安裝,並且在安裝過程中可能會出現問題,主要是與k8s版本沖突的問題,而最新的helm3對整個helm的架構都有了一個改進,它只有一個客戶端的helm程序,由它進行連接k8s集群,完成對charts的部署工作。
下載helm3
https://get.helm.sh/helm-v3.0.0-linux-amd64.tar.gz
查看配置信息
[root@i-pcwovafu bin]# helm env HELM_NAMESPACE="default" HELM_KUBECONTEXT="" HELM_BIN="helm" HELM_DEBUG="false" HELM_PLUGINS="/root/.local/share/helm/plugins" HELM_REGISTRY_CONFIG="/root/.config/helm/registry.json" HELM_REPOSITORY_CACHE="/root/.cache/helm/repository" HELM_REPOSITORY_CONFIG="/root/.config/helm/repositories.yaml"
添加公用的倉庫
helm repo add stable http://mirror.azure.cn/kubernetes/charts helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts helm repo update
指定對應的k8s集群
這一步非常關鍵,它是helm與k8s通訊的保證,這一步就是把k8s環境變量KUBECONFIG進行配置
export KUBECONFIG=/root/.kube/config #可以寫到/etc/profile里
查詢一個charts
[root@i-pcwovafu ~]# helm search hub nginx URL CHART VERSION APP VERSION DESCRIPTION https://hub.helm.sh/charts/choerodon/nginx-file... 0.1.0 1.13.5-alpine A Helm chart for Kubernetes https://hub.helm.sh/charts/cloudposse/nginx-ing... 0.1.8 A Helm chart for Nginx Ingress https://hub.helm.sh/charts/cloudposse/nginx-def... 0.5.0 A Helm chart for nginx-default-backend to be us... https://hub.helm.sh/charts/cloudposse/fail-whale 0.1.1 A Helm chart that provides a mainte
構建一個nginx的用例
helm create nginx helm nignx-demo ./nginx
查詢我們的nginx pod
[root@i-pcwovafu bin]# rancher kubectl get pods -n default NAME READY STATUS RESTARTS AGE web-nginx-858f7d9cc5-hlhkj 1/1 Running 0 2m14s
也可以使用helm命令來查看
[root@i-pcwovafu bin]# helm list NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION web-nginx default 1 2020-04-07 17:09:53.480335758 +0800 CST deployed nginx-0.1.0 1.16.0
這樣一個最簡單的helm應用就建立好了!
k8s~helm構建一個應用
三個概念
- chart:包含了創建Kubernetes的一個應用實例的必要信息
- config:包含了應用發布配置信息
- release:是一個chart及其配置的一個運行實例
建立一個helm charts
helm create hello-world
-
Chart.yaml 用於描述這個Chart的相關信息,包括名字、描述信息以及版本等。
僅僅是一些簡單的文本描述 -
values.yaml 用於存儲 templates 目錄中模板文件中用到變量的值。
-
NOTES.txt 用於介紹 Chart 部署后的一些信息,例如:如何使用這個 Chart、列出缺省的設置等。
-
Templates 目錄下是 YAML 文件的模板,該模板文件遵循 Go template 語法。
Templates 目錄下 YAML 文件模板的值默認都是在 values.yaml 里定義的,比如在 deployment.yaml 中定義的容器鏡像。
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
其中的 .Values.image.repository 的值就是在 values.yaml 里定義的 nginx,.Values.image.tag 的值就是 stable。
以上兩個變量值是在 create chart 的時候就自動生成的默認值,你可以根據實際情況進行修改。實際上都是靜態文本,只在是執行的時候才被解析.
構建一個helm應用
打開 Chart.yaml,可以看到內容如下,配置名稱和版本
apiVersion: v1 appVersion: "1.0" description: A Helm chart for Kubernetes name: mychart version: 0.1.0
編輯 values.yaml,它默認會在 Kubernetes 部署一個 Nginx。下面是 mychart 應用的 values.yaml 文件的內容:
$ cat mychart/values.yaml # Default values for mychart. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: repository: nginx tag: stable pullPolicy: IfNotPresent service: type: ClusterIP port: 80 ingress: enabled: false annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" path: / hosts: - chart-example.local tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. # limits: # cpu: 100m # memory: 128Mi # requests: # cpu: 100m # memory: 128Mi nodeSelector: {} tolerations: [] affinity: {}
檢查模塊配置
$ helm lint hello-world/
打包
helm package hello-world helm package mychart --debug #顯示詳細信息
啟動helm本地倉庫(helm3已被移除)
helm serve --repo-path /data/helm/repository/ --url http://172.17.0.22:8879/charts/ &
倉庫刷新和查詢應用
$ helm repo update $ helm search mychart NAME CHART VERSION APP VERSION DESCRIPTION local/hello-world 0.1.0 1.0 A Helm chart for Kubernetes
在 Kubernetes 中部署應用
Chart 被發布到倉儲后,就可以通過 helm install 命令部署該 Chart。
helm install hello local/hello-world
查看Release的狀態信息
helm status wordpress
升級charts
helm upgrade wordpress stable/wordpress
helm upgrade --install --force hello-world ./hello.tgz --namespace test # 也可以指定命名空間和它的taz包
回滾到上一個版本
helm rollback hello-world 1 # 向上歸滾一個版本
k8s~helm里的yaml的介紹
Chart.yaml 文件作用
- name: [必須] Chart的名稱
- version: [必須] Chart的版本號,版本號必須符合 SemVer 2:http://semver.org/
- description: [可選] Chart的簡要描述
- keywords: - [可選] 關鍵字列表,便於檢索
- home: [可選] 項目地址
- sources: - [可選] 當前Chart的下載地址列表
charts依賴
可以在requirements.yaml里去配置它的依賴關系, 它支持兩種方式表示依賴關系,可以使用requirements.yaml或者直接將依賴的Chart放置到charts目錄中。
dependencies: - name: mariadb version: 7.x.x repository: https://kubernetes-charts.storage.googleapis.com/ condition: mariadb.enabled tags: - wordpress-database
templates 目錄
templates目錄下的yaml文件,遵循Go template語法。使用過Hugo的靜態網站生成工具的人應該對此很熟悉。
templates目錄中存放了Kubernetes部署文件的模版,比如deployment.yaml,service.yaml等,它里面引用的變量來自values.yaml里
- 生成最終的yaml文件-文本對齊
{{ include "test" | indent 4}} - 生成最終的yaml文件-去除空行
{{- define "test" -}}模版內容{{- end -}} - 變量(默認值)的使用
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
上面的{{ .Values.image.repository }}
表示values.yaml文件里的image節點下的repository元素的內容
數據卷Volume、數據卷聲明VolumeClaim
k8s應用(包括有狀態跟無狀態應用),需要使用數據卷的話,需要在存儲卷中進行設置和聲明,下面列出持久化數據卷的聲明跟設置的模板:
數據卷設置:
apiVersion: v1 kind: PersistentVolume -這里說明是持久化數據卷 metadata: finalizers: - kubernetes.io/pv-protection labels: alicloud-pvname: {{ .Values.volumes.name }} -數據卷標簽,eg:XXX-data name: {{ .Values.volumes.name }} -數據卷名稱,eg:XXX-data spec: accessModes: - ReadWriteMany -權限 capacity: storage: {{ .Values.volumes.storage }} -容量大小,eg:10Gi flexVolume: driver: alicloud/nas -數據卷類型是nas options: path: {{ .Values.volumes.path }} -數據卷路徑,eg:/tmp server: {{ .Values.volumes.server }} -數據卷服務商,eg:xxxxx.nas.aliyuncs.com vers: '3' persistentVolumeReclaimPolicy: Retain storageClassName: nas
數據卷聲明:
apiVersion: v1 kind: PersistentVolumeClaim -持久化數據卷聲明 metadata: annotations: pv.kubernetes.io/bind-completed: 'yes' pv.kubernetes.io/bound-by-controller: 'yes' finalizers: - kubernetes.io/pvc-protection name: {{ .Values.volumes.name }} spec: accessModes: - ReadWriteMany resources: requests: storage: {{ .Values.volumes.storage }} -容量,eg:10Gi selector: matchLabels: alicloud-pvname: {{ .Values.volumes.name }} storageClassName: nas volumeName: {{ .Values.volumes.name }}
伸縮配置 HorizontalPodAutoscaler
應用彈性伸縮配置,這個可以配置最大、最小副本集跟伸縮條件的參數到values.yaml文件里面
kind: HorizontalPodAutoscaler apiVersion: autoscaling/v1 metadata: name: {{ include "admin.appname" . }}-hpa -admin.appname就是后面執行helm命令的時候倒數第二個參數,為什么前面是admin呢,admin就是你配置Chart.yaml的時候里面的name變量的值 spec: scaleTargetRef: kind: Deployment name: {{ include "admin.appname" . }} apiVersion: apps/v1beta2 minReplicas: 1 -最小副本集 maxReplicas: 10 -最大副本集 targetCPUUtilizationPercentage: 70 -伸縮條件
配置項ConfigMap
配置項設置,一般每個項目有都對應的環境參數,比如:數據庫、redis等這些賬號密碼類的參數,這些可以抽離出來當成一個配置項處理
apiVersion: v1 kind: ConfigMap metadata: name: {{ .Values.envConfigName }} -每個環境就配置一個配置項 data: {{- range $k, $v := .Values.configDatas }} -這里是循環遍歷configDatas這個變量 {{ $k | indent 2 }}.yml: >- -下面這兩行配置一個key->value的配置項(即文件名->文件內容) {{ $v | indent 4 }} {{- end -}}
鏡像密碼配置Secret
將鏡像的密碼配置到保密字典中
apiVersion: v1 kind: Secret metadata: name: image-secret -name隨意寫 data: .dockerconfigjson: {{ .Files.Get "image.pwd" | b64enc }} -內容 type: kubernetes.io/dockerconfigjson
TLs證書配置(后面配置ingress的時候要用到,不然無法用https)
apiVersion: v1 kind: Secret metadata: name: tls-secret data: tls.crt: {{ .Files.Get "XXXXX.com.pem" | b64enc }} tls.key: {{ .Files.Get "XXXXX.com.key" | b64enc }} type: Opaque
下次主要說一下幾個重要的yaml文件的模板。