1. 程式人生 > 程式設計 >Step by Step!Kubernetes持續部署指南

Step by Step!Kubernetes持續部署指南

在很久很久以前的一份工作中,我的任務是將老式的LAMP堆疊切換到Kubernetes上。那會兒我的老闆總是追逐新技術,認為只需要幾天時間就能夠完成新舊技術的迭代——鑑於那時我們甚至對容器的工作機制一無所知,所以不得不說老闆的想法真的很大膽。

在閱讀了官方檔案並且搜尋了很多資訊之後,我們開始感到不知所措——有許多新的概念需要學習:pod、容器以及replica等。對我而言,Kubernetes似乎只是為一群聰明的開發者而設計的。

然後我做了我在這一狀況下常做的事:通過實踐來學習。通過一個簡單的例子可以很好地理解錯綜複雜的問題,所以我自己一步一步完成了整個部署過程。

最後,我們做到了,雖然遠未達到規定的一週時間——我們花了將近一個月的時間來建立三個叢集,包括它們的開發、測試和生產。

本文我將詳細介紹如何將應用程式部署到Kubernetes。閱讀完本文之後,你將擁有一個高效的Kubernetes部署和持續交付工作流程。

持續整合與交付

持續整合是在每次應用程式更新時構建和測試的實踐。通過以少量的工作,更早地檢測到錯誤並立即解決。

整合完成並且所有測試都通過之後,我們就能夠新增持續交付到自動化釋出和部署的流程中。使用CI/CD的專案可以更頻繁、更可靠地釋出。

我們將使用Semaphore,這是一個快速、強大且易用地持續整合和交付(CI/CD)平臺,它能夠自動執行所有流程:

1、 安裝專案依賴項

2、 執行單元測試

3、 構建一個Docker映象

4、 Push映象到Docker Hub

5、 一鍵Kubernetes部署

對於應用程式,我們有一個Ruby Sinatra微服務,它暴露一些HTTP端點。該專案已包含部署所需的所有內容,但仍需要一些元件。


準備工作


在開始操作之前,你需要登入Github和Semaphore賬號。此外,為後續方便拉取或push Docker映象,你需要登入Docker Hub。

接下來,你需要在計算機上安裝一些工具:

  • Git:處理程式碼
  • curl:網路的“瑞士軍刀”
  • kubectl:遠端控制你的叢集

當然,千萬不要忘了Kubernetes。大部分的雲供應商都以各種形式提供此服務,選擇適合你的需求的即可。最低端的機器配置和叢集大小足以執行我們示例的app。我喜歡從3個節點的叢集開始,但你可以只用1個節點的叢集。

叢集準備好之後,從你的供應商中下載kubeconfig檔案。有些允許你直接從其web控制檯下載,有些則需要幫助程式。我們需要此檔案才能連線到叢集。

有了這個,我們已經可以開始了。首先要做的是fork儲存庫。


Fork儲存庫


在這篇文章中fork我們將使用的演示應用程式。

  1. 訪問semaphore-demo-ruby-kubernetes儲存庫,並且點選右上方的Fork按鈕
  2. 點選Clone or download按鈕並且複製地址
  3. 複製儲存庫:$ git clone https://github.com/your_repository_path…

使用Semaphore連線新的儲存庫

1、 登入到你的Semaphore

2、 點選側邊欄的連結,建立一個新專案

3、 點選你的儲存庫旁【Add Repository】按鈕

使用Semaphore測試


持續整合讓測試變得有趣並且高效。一個完善的CI 流水線能夠建立一個快速反饋迴路以在造成任何損失之前發現錯誤。我們的專案附帶一些現成的測試。

開啟位於.semaphore/semaphore.yml的初始流水線檔案,並快速檢視。這個流水線描述了Semaphore構建和測試應用程式所應遵循的所有步驟。它從版本和名稱開始。

version: v1.0
name: CI
複製程式碼

接下來是agent,它是為job提供動力的虛擬機器器。我們可以從3種型別中選擇:

agent:
  machine:
    type: e1-standard-2
    os_image: ubuntu1804
複製程式碼

Block(塊)、任務以及job定義了在流水線的每個步驟中要執行的操作。在Semaphore,block按照順序執行,與此同時,在block中的job也會並行執行。流水線包含2個block,一個是用於庫安裝,一個用於執行測試。

第一個block下載並安裝了Ruby gems。

- name: Install dependencies
  task:
    jobs:
      - name: bundle install
        commands:
          - checkout
          - cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-$SEMAPHORE_GIT_BRANCH,gems-master
          - bundle install --deployment --path .bundle
          - cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) .bundle
複製程式碼

Checkout複製了Github裡的程式碼。既然每個job都在完全隔離的機器裡執行,那麼我們必須依賴快取(cache)來在job執行之間儲存和檢索檔案。

blocks:
  - name: Install dependencies
    task:
      jobs:
        - name: bundle install
          commands:
            - checkout
            - cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-master
            - bundle install --deployment --path .bundle
            - cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) .bundle
複製程式碼

第二個block進行測試。請注意我們重複使用了checkout和cache的程式碼以將初始檔案放入job中。最後一個命令用於啟動RSpec測試套件。

- name: Tests
  task:
    jobs:
      - name: rspec
        commands:
          - checkout
          - cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-master
          - bundle install --deployment --path .bundle
          - bundle exec rspec
複製程式碼

最後一個部分我們來看看Promotion。Promotion能夠在一定條件下連線流水線以建立複雜的工作流程。所有job完成之後,我們使用 auto_promote_on來啟動下一個流水線。

promotions:
  - name: Dockerize
    pipeline_file: docker-build.yml
    auto_promote_on:
      - result: passed
複製程式碼

工作流程繼續執行下一個流水線。


構建Docker映象


我們可以在Kubernetes上執行任何東西,只要它打包在Docker映象中。在這一部分,我們將學習如何構建映象。

我們的Docker映象將包含應用程式的程式碼、Ruby以及所有的庫。讓我們先來看一下Dockerfile:

FROM ruby:2.5
 
RUN apt-get update -qq && apt-get install -y build-essential
 
ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
 
ADD Gemfile* $APP_HOME/
RUN bundle install --without development test
 
ADD . $APP_HOME
 
EXPOSE 4567
 
CMD ["bundle","exec","rackup","--host","0.0.0.0","-p","4567"]
複製程式碼

Dockerfile就像一個詳細的菜譜,包含所有構建容器映象所需要的步驟和命令:

1、 從預構建的ruby映象開始

2、 使用apt-get安裝構建工具

3、 複製Gemfile,因為它具有所有的依賴項

4、 用bundle安裝它們

5、 複製app的原始碼

6、 定義監聽埠和啟動命令

我們將在Semaphore環境中bake我們的生產映象。然而,如果你想要在計算機上進行一個快速的測試,那麼請輸入:

$ docker build . -t test-image
複製程式碼

使用Docker執行和暴露內部埠4567以在本地啟動伺服器:

$ docker run -p 4567:4567 test-image
複製程式碼

你現在可以測試一個可用的HTTP端點:

$ curl -w "\n" localhost:4567
hello world :))
複製程式碼

新增Docker Hub賬戶到Semaphore


Semaphore有一個安全的機制以儲存敏感資訊,如密碼、令牌或金鑰等。為了能夠push映象到你的Docker Hub映象倉庫中,你需要使用你的使用者名稱和密碼來建立一個Secret:

  1. 開啟你的Semaphore
  2. 在左側導航欄中,點選【Secret
  3. 點選【Creat New Secret
  4. Secret的名字應該是Dockerhub,鍵入登入資訊(如下圖所示),並儲存。

構建Docker流水線


這個流水線開始構建並且push映象到Docker Hub,它僅僅有1個block和1個job:

這次,我們需要使用更好的效能,因為Docker往往更加耗費資源。我們選擇具有四個CPU,8GB RAM和35GB磁碟空間的中端機器e1-standard-4:

version: v1.0
name: Docker build
agent:
  machine:
    type: e1-standard-4
    os_image: ubuntu1804
複製程式碼

構建block通過登入到Docker Hub啟動,使用者名稱和密碼可以從我們剛建立的secret匯入。登入之後,Docker可以直接訪問映象倉庫。

下一個命令是docker pull,它試圖拉取最新映象。如果找到映象,那麼Docker可能能夠重新使用其中的一些層,以加速構建過程。如果沒有最新映象,也無需擔心,只是需要花費長一點的時間來構建。

最後,我們push新的映象。注意,這裡我們使用SEMAPHORE_WORKFLOW_ID 變數來標記映象。

blocks:
  - name: Build
    task:
      secrets:
        - name: dockerhub
      jobs:
      - name: Docker build
        commands:
          - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
          - checkout
          - docker pull "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:latest || true
          - docker build --cache-from "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:latest -t "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID .
          - docker images
          - docker push "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID
複製程式碼

當映象準備完畢,我們進入專案的交付階段。我們將用手動promotion來擴充套件我們的Semaphore 流水線。

promotions:
  - name: Deploy to Kubernetes
    pipeline_file: deploy-k8s.yml
複製程式碼

要進行第一次自動構建,請進行push:

$ touch test-build
$ git add test-build
$ git commit -m "initial run on Semaphore“
$ git push origin master
複製程式碼

映象準備完成之後,我們就可以進入部署階段。

部署到Kubernetes


自動部署是Kubernetes的強項。我們所需要做的就是告訴叢集我們最終的期望狀態,剩下的將由它來負責。

然而,在部署之前,你必須將kubeconfig檔案上傳到Semaphore。


上傳Kubeconfig到Semaphore


我們需要第二個secret:叢集的kubeconfig。這個檔案授予可以對它的管理訪問許可權。因此,我們不希望將檔案簽入儲存庫。

建立一個名為do-k8s的secret並且將kubeconfig檔案上傳到/home/semaphore/.kube/dok8s.yaml中:

部署清單

儘管Kubernetes已經是容器編排平臺,但是我們不直接管理容器。實際上,部署的最小單元是pod。一個pod就好像一群形影不離的朋友,總是一起去同一個地方。因此要保證在pod中的容器執行在同一個節點上並且有相同的IP。它們可以同步啟動和停止,並且由於它們在同一臺機器上執行,因此它們可以共享資源。

pod的問題在於它們可以隨時啟動和停止,我們沒辦法確定它們會被分配到的pod IP。要把使用者的http流量轉發,還需要提供一個公共IP和一個負載均衡器,它負責跟蹤pod和轉發客戶端的流量。

開啟位於deploymente.yml的檔案。這是一個部署我們應用程式的清單,它被3個dash分離成兩個資源。第一個,部署資源:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: semaphore-demo-ruby-kubernetes
spec:
  replicas: 1
  selector:
    matchLabels:
      app: semaphore-demo-ruby-kubernetes
  template:
    metadata:
      labels:
        app: semaphore-demo-ruby-kubernetes
    spec:
      containers:
        - name: semaphore-demo-ruby-kubernetes
          image: $DOCKER_USERNAME/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID
複製程式碼

這裡有幾個概念需要釐清:

  • 資源都有一個名稱和幾個標籤,以便組織
  • Spec定義了最終期望的狀態,template是用於建立Pod的模型。
  • Replica設定要建立的pod的副本數。我們經常將其設定為叢集中的節點數。既然我們使用了3個節點,我將這一命令列更改為replicas:3


第二個資源是服務。它繫結到埠80並且將HTTP流量轉發到部署中的pod:

---
 
apiVersion: v1
kind: Service
metadata:
  name: semaphore-demo-ruby-kubernetes-lb
spec:
  selector:
    app: semaphore-demo-ruby-kubernetes
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 4567
複製程式碼

Kubernetes將selector與標籤相匹配以便將服務與pod連線起來。因此,我們在同一個叢集中有許多服務和部署並且根據需要連線他們。

部署流水線


我們現在進入CI/CD配置的最後一個階段。這時,我們有一個定義在semaphore.yml的CI流水線,以及定義在docker-build.yml的Docker流水線。在這一步中,我們將部署到Kubernetes。

開啟位於.semaphore/deploy-k8s.yml的部署流水線:

version: v1.0
name: Deploy to Kubernetes
agent:
  machine:
    type: e1-standard-2
    os_image: ubuntu1804
複製程式碼

兩個job組成最後的流水線:

Job 1開始部署。匯入kubeconfig檔案之後,envsubst將deployment.yaml中的佔位符變數替換為其實際值。然後,kubectl apply將清單傳送到叢集。

blocks:
  - name: Deploy to Kubernetes
    task:
      secrets:
        - name: do-k8s
        - name: dockerhub
 
      env_vars:
        - name: KUBECONFIG
          value: /home/semaphore/.kube/dok8s.yaml
 
      jobs:
      - name: Deploy
        commands:
          - checkout
          - kubectl get nodes
          - kubectl get pods
          - envsubst < deployment.yml | tee deployment.yml
          - kubectl apply -f deployment.yml
複製程式碼

Job 2將映象標記為最新,以讓我們能夠在下一次執行中將其作為快取使用。

- name: Tag latest release
  task:
    secrets:
      - name: dockerhub
    jobs:
    - name: docker tag latest
      commands:
        - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
        - docker pull "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID
        - docker tag "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:latest
        - docker push "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:latest
複製程式碼

這是工作流程的最後一步了。


部署應用程式

讓我們教我們的Sinatra應用程式唱歌。在app.rb中的App類中新增以下程式碼:

get "/sing" do
  "And now,the end is near
   And so I face the final curtain..."
end
複製程式碼

推送修改的檔案到Github:

$ git add .semaphore/*
$ git add deployment.yml
$ git add app.rb
$ git commit -m "test deployment”
$ git push origin master
複製程式碼

等到docker構建流水線完成,你可以檢視Semaphore的進度:

是時候進行部署了,點選Promote按鈕,看它是否工作:

我們已經有了一個好的開始,現在就看Kubernetes的了。我們可以使用kubectl檢查部署狀態,初始狀態是三個所需的pod並且零可用:

$ kubectl get deployments
NAME                             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
semaphore-demo-ruby-kubernetes   3         0         0            0           15m
複製程式碼

幾秒之後,pod已經啟動,reconciliation已經完成:

$ kubectl get deployments
NAME                             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
semaphore-demo-ruby-kubernetes   3         3         3            3           15m
複製程式碼

使用get all獲得叢集的通用狀態,它顯示了pod、服務、部署以及replica:

$ kubectl get all
NAME                                                  READY   STATUS    RESTARTS   AGE
pod/semaphore-demo-ruby-kubernetes-7d985f8b7c-454dh   1/1     Running   0          2m
pod/semaphore-demo-ruby-kubernetes-7d985f8b7c-4pdqp   1/1     Running   0          119s
pod/semaphore-demo-ruby-kubernetes-7d985f8b7c-9wsgk   1/1     Running   0          2m34s
 
 
NAME                                        TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)        AGE
service/kubernetes                          ClusterIP      10.12.0.1              443/TCP        24m
service/semaphore-demo-ruby-kubernetes-lb   LoadBalancer   10.12.15.50   35.232.70.45   80:31354/TCP   17m
 
 
NAME                                             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/semaphore-demo-ruby-kubernetes   3         3         3            3           17m
 
NAME                                                        DESIRED   CURRENT   READY   AGE
replicaset.apps/semaphore-demo-ruby-kubernetes-7d985f8b7c   3         3         3       2m3
複製程式碼

Service IP在pod之後展示。對於我來說,負載均衡器被分配到外部IP 35.232.70.45。需要將其更改為你的提供商分配給你的那個,然後我們來試試新的伺服器。

$ curl -w "\n" http://YOUR_EXTERNAL_IP/sing
複製程式碼

現在,離結束已經不遠了。

勝利近在咫尺


當你使用了正確的CI/CD解決方案之後,部署到Kubernetes並不是那麼困難。你現在擁有一個Kubernetes的完全自動的持續交付流水線啦。

這裡有幾個建議可以讓你在Kubernetes上隨意fork並玩轉semaphore-demo-ruby-kubernetes:

  • 建立一個staging叢集
  • 構建一個部署容器並且在裡面執行測試
  • 使用更多微服務擴充套件專案