Step by Step!Kubernetes持續部署指南

本文是做者經過親身實踐,從零基礎開始,一步一步總結出來的Kubernetes持續部署工做流程。文章從前期的工具準備開始,到復刻存儲庫、測試、構建鏡像、構建流水線最後進行部署,全部的工做流程都一一展示在文章中,對於想要擁有全自動持續交付流水線的用戶將有很大的借鑑意義。node


在好久好久之前的一份工做中,個人任務是將老式的LAMP堆棧切換到Kubernetes上。那會兒個人老闆老是追逐新技術,認爲只須要幾天時間就可以完成新舊技術的迭代——鑑於那時咱們甚至對容器的工做機制一無所知,因此不得不說老闆的想法真的很大膽。git

在閱讀了官方文檔而且搜索了不少信息以後,咱們開始感到不知所措——有許多新的概念須要學習:pod、容器以及replica等。對我而言,Kubernetes彷佛只是爲一羣聰明的開發者而設計的。github

而後我作了我在這一情況下常作的事:經過實踐來學習。經過一個簡單的例子能夠很好地理解錯綜複雜的問題,因此我本身一步一步完成了整個部署過程。web

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

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

持續集成與交付

持續集成是在每次應用程序更新時構建和測試的實踐。經過以少許的工做,更早地檢測到錯誤並當即解決。api

集成完成而且全部測試都經過以後,咱們就可以添加持續交付到自動化發佈和部署的流程中。使用CI/CD的項目能夠更頻繁、更可靠地發佈。緩存

咱們將使用Semaphore,這是一個快速、強大且易用地持續集成和交付(CI/CD)平臺,它可以自動執行全部流程:安全

一、 安裝項目依賴項ruby

二、 運行單元測試

三、 構建一個Docker鏡像

四、 Push鏡像到Docker Hub

五、 一鍵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_repos...

使用Semaphore鏈接新的存儲庫

一、 登陸到你的Semaphore

二、 點擊側邊欄的連接,建立一個新項目

三、 點擊你的存儲庫旁【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-$SEMAPHORE_GIT_BRANCH,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-$SEMAPHORE_GIT_BRANCH,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就像一個詳細的菜譜,包含全部構建容器鏡像所須要的步驟和命令:

一、 從預構建的ruby鏡像開始

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

三、 複製Gemfile,由於它具備全部的依賴項

四、 用bundle安裝它們

五、 複製app的源代碼

六、 定義監聽端口和啓動命令

咱們將在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集羣
  • 構建一個部署容器而且在裏面運行測試
  • 使用更多微服務擴展項目
相關文章
相關標籤/搜索