您好,登錄后才能下訂單哦!
本文是作者通過親身實踐,從零基礎開始,一步一步總結出來的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:網絡的“Swiss Knife”
當然,千萬不要忘了Kubernetes。大部分的云供應商都以各種形式提供此服務,選擇適合你的需求的即可。最低端的機器配置和集群大小足以運行我們示例的app。我喜歡從3個節點的集群開始,但你可以只用1個節點的集群。
集群準備好之后,從你的供應商中下載kubeconfig文件。有些允許你直接從其web控制臺下載,有些則需要幫助程序。我們需要此文件才能連接到集群。
有了這個,我們已經可以開始了。首先要做的是fork存儲庫。
在這篇文章中fork我們將使用的演示應用程序。
訪問semaphore-demo-ruby-kubernetes存儲庫,并且點擊右上方的Fork按鈕
點擊Clone or download按鈕并且復制地址
使用Semaphore連接新的存儲庫
1、 登錄到你的Semaphore
2、 點擊側邊欄的鏈接,創建一個新項目
3、 點擊你的存儲庫旁【Add Repository】按鈕
持續集成讓測試變得有趣并且高效。一個完善的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
工作流程繼續執行下一個流水線。
我們可以在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 :))
Semaphore有一個安全的機制以存儲敏感信息,如密碼、令牌或密鑰等。為了能夠push鏡像到你的Docker Hub鏡像倉庫中,你需要使用你的用戶名和密碼來創建一個Secret:
打開你的Semaphore
在左側導航欄中,點擊【Secret】
點擊【Creat New Secret】
這個流水線開始構建并且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的強項。我們所需要做的就是告訴集群我們最終的期望狀態,剩下的將由它來負責。
然而,在部署之前,你必須將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的模型。
第二個資源是服務。它綁定到端口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集群
構建一個部署容器并且在里面運行測試
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。