1. 程式人生 > 實用技巧 >k8s記錄-常識

k8s記錄-常識

k8s~術語解釋

簡介

Kubernetes是一個開源的,用於管理雲平臺中多個主機上的容器化的應用,Kubernetes的目標是讓部署容器化的應用簡單並且高效(powerful),Kubernetes提供了應用部署,規劃,更新,維護的一種機制。

Kubernetes一個核心的特點就是能夠自主的管理容器來保證雲平臺中的容器按照使用者的期望狀態執行著(比如使用者想讓apache一直執行,使用者不需要關心怎麼去做,Kubernetes會自動去監控,然後去重啟,新建,總之,讓apache一直提供服務),管理員可以載入一個微型服務,讓規劃器來找到合適的位置,同時,Kubernetes也系統提升工具以及人性化方面,讓使用者能夠方便的部署自己的應用(就像canary deployments)。

節點node

  • master > 負責排程
  • worker > 負責專案執行

Kubernetes節點有執行應用容器必備的服務,而這些都是受Master的控制。
每次個節點上當然都要執行Docker。Docker來負責所有具體的映像下載和容器執行。

Kubernetes主要由以下幾個核心元件組成:

  1. etcd儲存了整個叢集的狀態;
  2. apiserver提供了資源操作的唯一入口,並提供認證、授權、訪問控制、API註冊和發現等機制;
  3. controller manager負責維護叢集的狀態,比如故障檢測、自動擴充套件、滾動更新等;
  4. scheduler負責資源的排程,按照預定的排程策略將Pod排程到相應的機器上;
  5. kubelet負責維護容器的生命週期,同時也負責Volume(CVI)和網路(CNI)的管理;
  6. Container runtime負責映象管理以及Pod和容器的真正執行(CRI);
  7. kube-proxy負責為Service提供cluster內部的服務發現和負載均衡;

除了核心元件,還有一些推薦的Add-ons:

  1. kube-dns負責為整個叢集提供DNS服務
  2. Ingress Controller為服務提供外網入口
  3. Heapster提供資源監控
  4. Dashboard提供GUI
  5. Federation提供跨可用區的叢集
  6. 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都包括以下狀態資訊

  1. 地址:包括hostname、外網IP和內網IP
  2. 條件(Condition):包括OutOfDisk、Ready、Me* moryPressure和DiskPressure
  3. 容量(Capacity):Node上的可用資源,包括CPU、記憶體和Pod總數
  4. 基本資訊(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來方便的管理應用。典型的應用場景包括:

  1. 定義Deployment來建立Pod和ReplicaSet
    2.滾動升級和回滾應用
  2. 擴容和縮容
  3. 暫停和繼續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有三種類型:

  1. Service Account:用來訪問Kubernetes API,由Kubernetes自動建立,並且會自動掛載到Pod的/run/secrets/kubernetes.io/serviceaccount目錄中;
  2. Opaque:base64編碼格式的Secret,用來儲存密碼、金鑰等;
  3. kubernetes.io/dockerconfigjson:用來儲存私有docker registry的認證資訊。
  • Ingress

在本篇文章中你將會看到一些在其他地方被交叉使用的術語,為了防止產生歧義,我們首先來澄清下。

  1. 節點:Kubernetes叢集中的伺服器;
  2. 叢集:Kubernetes管理的一組伺服器集合;
  3. 邊界路由器:為區域網和Internet路由資料包的路由器,執行防火牆保護區域網絡;
  4. 叢集網路:遵循Kubernetes網路模型實現群集內的通訊的具體實現,比如flannel 和 OVS。
  5. 服務:使用標籤選擇器標識一組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,以下是它的步驟

  1. kubectl建立deployment
  2. deployment建立Replcasset
  3. 根據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使用。
  • 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.localsvc.cluster.localcluster.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的實現

1 當我們為指定的專案新增ingress支援之後,它會在“負載均衡”標籤頁出現,並顯示出你的域名解析到的服務。

2 我們的ingress是支援https的,所以需要為你的域名配置對應的證書,我們在配置檔案裡新增

3 自動為ingress-controller裡的配置檔案新增nginx配置項,然後自動reload它,讓它生效

當有新的ingress服務註冊之後,配置檔案會發生變化

4 你的服務對應的nginx是在自己服務的ymal裡進行配置的,一般來說,微服務的閘道器層都應該建立 一個ingress-nginx來對外提供服務!

下面說一下yaml的nginx部分的功能:
  1. 註明ingress的名稱
  2. 填寫之前建立的密文名稱(ingress https 證書)
  3. 填寫你的服務所在的namespace,不寫預設為default
  4. 填寫你要轉發的服務的域名
  5. 填寫你服務的名稱和pod的埠
ymal程式碼如下
# 構建反射代理
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
它在ingress-nginx裡生成的完整配置如下
 ## 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 是什麼

  1. Helm 是 Deis 開發的一個用於 Kubernetes 應用的包管理工具,主要用來管理 Charts。有點類似於 Ubuntu 中的 APT 或 CentOS 中的 YUM。
  2. Helm Chart 是用來封裝 Kubernetes 原生應用程式的一系列 YAML 檔案。可以在你部署應用的時候自定義應用程式的一些 Metadata,以便於應用程式的分發。
    對於應用釋出者而言,可以通過 Helm 打包應用、管理應用依賴關係、管理應用版本併發布應用到軟體倉庫。
  3. 對於使用者而言,使用 Helm 後不用需要編寫複雜的應用部署檔案,可以以簡單的方式在 Kubernetes 上查詢、安裝、升級、回滾、解除安裝應用程式。

做為 Kubernetes 的一個包管理工具,Helm具有如下功能:

  1. 建立新的 chart
  2. chart 打包成 tgz 格式
  3. 上傳 chart 到 chart 倉庫或從倉庫中下載 chart
  4. 在Kubernetes叢集中安裝或解除安裝 chart
  5. 管理用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構建一個應用

三個概念

  1. chart:包含了建立Kubernetes的一個應用例項的必要資訊
  2. config:包含了應用釋出配置資訊
  3. 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 檔案作用

  1. name: [必須] Chart的名稱
  2. version: [必須] Chart的版本號,版本號必須符合 SemVer 2:http://semver.org/
  3. description: [可選] Chart的簡要描述
  4. keywords: - [可選] 關鍵字列表,便於檢索
  5. home: [可選] 專案地址
  6. 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檔案的模板。