可落地微服务on k8s的持续集成/部署方案
我們隔一流的軟件生產工藝還有多遠?在距離15000公里外,Amazon一年可以進行5000萬次部署,在這一邊某電商平臺的研發部門里,讓他們引以為傲的是他們正在進行“敏捷”開發模式,并對外號稱他們是以每周為迭代來進行升級。時間是定在周四(因為這樣如果出現問題,周五還可以修復,不用周六加班),但是周四的晚上,開發/測試/還有產品的負責人是妥妥的要留下來了奮斗到天明了,如果運氣好的話還可以在 12點之前回家。運氣這種事情,總是不太好說。
這是我們曾經經歷的痛, 由于缺少自動化測試以及完整的上線發布流程,每次一上線,總能折騰個4到5小時。所以后來在我們自己實現這套新零售的saas系統的時候,從一開始我就在思考如何避免這個問題。我們采用了微服務架構,由30多個微服務組成, 部署在K8S上, 測試環境與生產環境都是借助于gitlab ci來完成的,并且同時可以支持騰訊和阿里云的k8s容器服務。這個花了3天調研和實施出來的持續集成方案在時間收益上已經給我們帶來了超過 20倍的回報 。
微服務
微服務存在著多個服務獨立部署的情況,在沒有k8s之前需要自己實現一套完整的持續部署工具,這個復雜度及成本可以說80%的公司都承受不起。但是微服務部署在 k8s上之后一切就變的簡單的多了。
單個服務在k8s中的部署可以用 kubectl set image的語句在ci job中實現
kubectl --record deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1如果是30多個甚至更多的服務,我們把所有的資源定義文件存放在一個單獨的代碼倉庫中進行維護和版本管理會更合適一些。在沒有helm之前,我們可能通過 kubectl apply -f ./ 的方式基于整個文件夾來創建和更新 k8s資源。即使這樣,我們在不同測試環境與生產環境由于不同的配置需要有兩個文件夾來保存資源定義文件。
Helm
helm可以將多個k8s的資源定義文件打包在一起進行整體的部署、更新,就像一個應用程序一樣。
這種場景就特別適合微服務,我們可以將所有的服務以及中間件、數據庫、證書等資源定義在一個helm package下來進行部署和更新。
一個helm package的構成主要由模板和參數來構成,模板就是 k8s的資源定義文件,在模板中可以引用外部的參數,我們一組微服務的應用可以用同一個包只需要替換不同的參數即可。
我們以一個簡單的單體應用來舉例,它包括一個api, 一個mysql數據庫,以及一個ingress配置將api就暴露在k8s集群之外。在本地安裝好helm client之后可以使用helm create [name]來創建一個package。
helm create Helm在templates中新建api-deploy.yaml, ingress.yaml, mysql-svc.yaml,這些文件的內容和我們平臺創建k8s資源的定義文件是一樣的,只不過我們在這里可以使用helm提供的參數。
比如命名空間我們就可以這樣來使用
kind: Service apiVersion: v1 metadata: name: {{ .Values.image.api.name }} namespace: {{ .Values.namespace }} spec: {{ if .Values.image.api.nodePort }}type: NodePort{{ end }} ports: - port: 80 targetPort: 80 {{ if .Values.image.api.nodePort }}nodePort: {{ .Values.image.api.nodePort }}{{ end }} selector: name: {{ .Values.image.api.name }}在模板中還支持條件判斷,比如在測試環境我們可以配置nodePort來暴露服務,而生產環境中則不支持。
在values.yaml中我們定義以下內容供測試環境使用,在生產中可以將namespace改成生產環境對應的命名空間名以及移除nodePort節點即可。
namespace: hunterpro image: api: name: hunterpro-api version: 1.0.0.2991 nodePort: 31013之后我們可以使用helm install -n [name] ./helm 來將這個helm package部署到k8s集群中 。 ./helm為包定義目錄。helm 所連接的k8s集群為kubectl的配置。
Helm package Chartmuseum
一個helm的包存放在一個文件夾內,同時我們還可以使用 helm pakcage將它打包成一個文件來方便與其它人共享。同時也可以像上傳docker我鏡像一樣上傳helm package,并且同樣可以在倉庫上保存不同的版本。Chartmuseum就是一個開源的 helm package倉庫服務。
我們可以使用docker將它快速安裝
docker run --rm -it \ -p 8080:8080 \ -v $(pwd)/charts:/charts \ -e DEBUG=true \ -e STORAGE=local \ -e STORAGE_LOCAL_ROOTDIR=/charts \ chartmuseum/chartmuseum:v0.8.1以上我們就可以拿到一個遠程 helm package repository的地址,只需要將它加入本地的repo list中即可
helm repo add [name] [url]官方還提供了helm push插件,讓我們可以輕松地將本地的helm package推送到遠程的倉庫
$ helm push mychart/ [repo name] Pushing mychart-0.3.2.tgz to [repo name]... Done.完整實踐
有了對 helm以及helm package repository的初步了解,我們就可以進入到我們整個ci方案了。
1. 開發提交代碼到dev分支
2. 觸發項目dev構建流水線 ?gitlab ci開始進行代碼的構建,使用項目內dockerfile來構建 docker鏡像
3. 鏡像構建成功之后推送到鏡像倉庫 (我們根據不同的分支會把鏡像分別推到阿里或者鏡像云)
4. 觸發buildscript dev構建流水線 (傳送參數 :當前版本以及當前更新服務名稱)
5. build script 下載最新代碼并將helm package的參數內的對應服務的鏡像版本更新至當前版本
6. 提交更改之后的代碼
7. ?helm package 并且helm push將最新的包推送到遠程鏡像
8. 用最新的 helm package來更新開發的集群
我們使用gitlab ci來做持續集成,如果不了解gitlab ci可以查看這篇之前的文章。
GitLab CI 自動部署netcore web api 到Docker
.Net & Docker(二)5分鐘快速用Docker部署你自己的GitLab
?當我們的gitlab runner配置好之后,由于涉及到k8s我們還需要做以下事情。
????- 安裝kubectl 并配置連接到集群
????- 安裝helm client (helm client會直接使用kubectl 的連接操作對應集群)
????- 安裝 helm push 插件
????- 由于我們在流水線執行過程中更新代碼并推送用到了python腳本,所以我們需要安裝python3.6
安裝kubectl ?
與k8s相關的學習最大的問題是它有比較多新的概念,剛開始學習會比較難。而第二大問題就是那堵墻,在很多情況下會讓我們直接想放棄。在安裝kubectl 的時候,如果是 centos我們可以使用阿里的鏡像:
cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF然后再安裝kubectl
yum install -y kubectl安裝helm
centos 可以使用snap來安裝并添加阿里的穩定倉庫源來進初始化,默認使用谷哥的穩定源會失敗 。
sudo snap install helm --classic helm init --upgrade -i registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.14.1 --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts https://yq.aliyun.com/articles/159601安裝python和 PyYAML庫
我們上面提到我們要通過 buildscript的構建流水線去更新helm pakcage內某個服務的鏡像版本
image: auctionapi: name: auction-api nodePort: 31100 version: 1.0.2532 deliveryapi: name: delivery-api nodePort: 31049 version: 1.0.2542 fundapi: name: fund-api nodePort: 31034 version: 1.0.2538 gatewaymp: name: gateway-mp-api nodePort: 31039 version: 1.0.2544比如我們的 values.yaml 參數配置是這樣的。當我們提交gateway-mp-api 之后,當前的版本號會升級主為 1.0.2545 。gateway-mp-api的構建流水線會將版本號為 1.0.2545的鏡像推到鏡像倉庫,接下來我們要做的就是通過 helm upgrade 來更新我們在k8s中的服務。
gitlab ci中可以通過 web hook 的方式觸發另一個倉庫的ci,所以我們在這里每一個 api推送完鏡像之后都會觸發 buildscript的更新,并傳入參數:當前服務名稱(對應values.yaml中的 服務key) 和最新版本號。
然后借助一段python腳本來更新values.yaml, 下面的代碼用到了python的 yaml庫,可以比較方便的操作yaml格式的文件。
import?sys import yaml with open("values.yaml") as f: value = yaml.load(f) value['image'][sys.argv[1]]['version'] = sys.argv[2] with open("values.yaml","w") as f: yaml.dump(value, f)buildscript 的.gitlab-ci.yaml文件 script
script: - helm init --client-only --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts - helm repo add $helmRepoName $helmRepoUrl - cd BuildScript/dev-wotui/wotui-dev - python3.6 update.py $image $version - git add . - git commit -m "upgrade to $version" || true - git push origin wotui/develop - cd ../ - helm push ./wotui-dev $helmRepoName -v $version - helm repo update - helm upgrade $helmReleaseName $helmPackageName所完成的步驟包括:
????- 初始化helm 僅客戶端
????- 添加遠程 helm 倉庫
????- 更改 helm chart values.yaml 對應的服務鏡像版本
????- 提交buildscript 代碼
????- 推送 helm package
????- 更新微服務
服務項目的構建流水線?
每一個服務自己的gitlab-ci文件中只需要完成構建自己的鏡像并推送到鏡像倉庫,之后再用curl發起buildscript 項目的構建并傳入對應參數即可。?
stages: - image - deploy variables: versionNo: $MAIN_VERSION.$SECONDARY_VERSION.$CI_PIPELINE_ID registry: registry-vpc.cn-shanghai.aliyuncs.com/ registryUser: ******** registryPwd: ******** repository: namespace/delivery-api docker image: stage: image tags: - wotui only: - wotui/develop script: - docker build -t "$registry$repository:$versionNo" ./Collectin.Delivery.API - docker login -u $registryUser -p $registryPwd $registry - docker push "$registry$repository:$versionNo" deploy: stage: deploy variables: GIT_STRATEGY: none tags: - wotui only: - wotui/develop script: - curl -X POST -F token=${PUBLISH_TRIGGER_TOKEN} -F ref=wotui/develop -F "variables[version]=$versionNo" -F "variables[image]=deliveryapi" http://code.collectin.cn/api/v4/projects/50/trigger/pipelin以上,當你提交代碼到服務的分支,接下來就是視頻中全自動的編譯,打包,部署流程。
關于微服務與K8S
只要找到了合適的方法,微服務就沒有那么復雜 。即使不是粒度非常細的微服務,哪怕是粗粒度的服務,同樣可以借用于K8S來進行運維管理。?在現在它可以節省大量的運維操作時間,在未來它也可以很好地適應業務快速擴展。?隨著企業對于開發人員的要求不斷增高,如果你希望在未來的兩到三年晉升成為架構師,那么微服務架構和K8S是你不得不掌握的必要技能。
下面是一套根據我們近兩年微服務項目實戰開發以及k8s運維經驗中提取出來的課程,8月重啟錄制,開放購買。?點擊左下角閱讀原文了解更多信息。
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結
以上是生活随笔為你收集整理的可落地微服务on k8s的持续集成/部署方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 跨语言调用Hangfire定时作业服务
- 下一篇: 历久弥新 - 微软万亿市值背后的文化支撑