凡泰極客導讀:
眾所周知,Kubernetes 是一款由 Google 發起的開源系統,目的在於提高集群環境下管理容器化應用的效率。有些人將其稱為容器編排平臺,而 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我們將使用的演示應用程式。
訪問semaphore-demo-ruby-kubernetes存儲庫,並且點擊右上方的Fork按鈕
點擊Clone or download按鈕並且複製地址
複製存儲庫:
$ git clone https://github.com/your_repository_path…
使用Semaphore連接新的存儲庫
1、 登錄到你的Semaphore
2、 點擊側邊欄的連結,創建一個新項目
3、 點擊你的存儲庫旁【Add Repository】按鈕
持續集成讓測試變得有趣並且高效。一個完善的CI 流水線能夠創建一個快速反饋迴路以在造成任何損失之前發現錯誤。我們的項目附帶一些現成的測試。
打開位於.semaphore/semaphore.yml的初始流水線文件,並快速查看。這個流水線描述了Semaphore構建和測試應用程式所應遵循的所有步驟。它從版本和名稱開始。
接下來是agent,它是為job提供動力的虛擬機。我們可以從3種類型中選擇:
agent: machine: type: e1-standard-2 os_image: ubuntu1804Block(塊)、任務以及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 - cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) .bundleCheckout複製了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 - 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 - bundle exec rspec最後一個部分我們來看看Promotion。Promotion能夠在一定條件下連接流水線以創建複雜的工作流程。所有job完成之後,我們使用 auto_promote_on來啟動下一個流水線。
promotions: - name: Dockerize pipeline_file: docker-build.yml auto_promote_on: - result: passed工作流程繼續執行下一個流水線。
我們可以在Kubernetes上運行任何東西,只要它打包在Docker鏡像中。在這一部分,我們將學習如何構建鏡像。
我們的Docker鏡像將包含應用程式的代碼、Ruby以及所有的庫。讓我們先來看一下Dockerfile:
FROM ruby:2.5 RUN apt-get update -qq && apt-get install -y build-essential ENV APP_HOME /appRUN mkdir $APP_HOMEWORKDIR $APP_HOME ADD Gemfile* $APP_HOME/RUN bundle install 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:4567hello world :))Semaphore有一個安全的機制以存儲敏感信息,如密碼、令牌或密鑰等。為了能夠push鏡像到你的Docker Hub鏡像倉庫中,你需要使用你的用戶名和密碼來創建一個Secret:
打開你的Semaphore
在左側導航欄中,點擊【Secret】
點擊【Creat New Secret】
Secret的名字應該是Dockerhub,鍵入登錄信息(如下圖所示),並保存。
這個流水線開始構建並且push鏡像到Docker Hub,它僅僅有1個block和1個job:
這次,我們需要使用更好的性能,因為Docker往往更加耗費資源。我們選擇具有四個CPU,8GB RAM和35GB磁碟空間的中端機器e1-standard-4:
version: v1.0name: Docker buildagent: 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的強項。我們所需要做的就是告訴集群我們最終的期望狀態,剩下的將由它來負責。
然而,在部署之前,你必須將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/v1kind: Deploymentmetadata: name: semaphore-demo-ruby-kubernetesspec: 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這裡有幾個概念需要釐清:
第二個資源是服務。它綁定到埠80並且將HTTP流量轉發到部署中的pod:
--- apiVersion: v1kind: Servicemetadata: name: semaphore-demo-ruby-kubernetes-lbspec: selector: app: semaphore-demo-ruby-kubernetes type: LoadBalancer ports: - port: 80 targetPort: 4567Kubernetes將selector與標籤相匹配以便將服務與pod連接起來。因此,我們在同一個集群中有許多服務和部署並且根據需要連接他們。
我們現在進入CI/CD配置的最後一個階段。這時,我們有一個定義在semaphore.yml的CI流水線,以及定義在docker-build.yml的Docker流水線。在這一步中,我們將部署到Kubernetes。
打開位於.semaphore/deploy-k8s.yml的部署流水線:
version: v1.0name: Deploy to Kubernetesagent: 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.ymlJob 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 deploymentsNAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGEsemaphore-demo-ruby-kubernetes 3 0 0 0 15m幾秒之後,pod已經啟動,reconciliation已經完成:
$ kubectl get deploymentsNAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGEsemaphore-demo-ruby-kubernetes 3 3 3 3 15m使用get all獲得集群的通用狀態,它顯示了pod、服務、部署以及replica:
$ kubectl get allNAME READY STATUS RESTARTS AGEpod/semaphore-demo-ruby-kubernetes-7d985f8b7c-454dh 1/1 Running 0 2mpod/semaphore-demo-ruby-kubernetes-7d985f8b7c-4pdqp 1/1 Running 0 119spod/semaphore-demo-ruby-kubernetes-7d985f8b7c-9wsgk 1/1 Running 0 2m34s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/kubernetes ClusterIP 10.12.0.1 443/TCP 24mservice/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 AGEdeployment.apps/semaphore-demo-ruby-kubernetes 3 3 3 3 17m NAME DESIRED CURRENT READY AGEreplicaset.apps/semaphore-demo-ruby-kubernetes-7d985f8b7c 3 3 3 2m3Service 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集群
構建一個部署容器並且在裡面運行測試
使用更多微服務擴展項目