GitLab CI/CD 自動化構建與發佈實踐

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"流程介紹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CI/CD 是一種通過在應用開發階段引入自動化來頻繁向客戶交付應用的方法。CI/CD 的核心概念是持續集成、持續交付和持續部署。這篇文章中,我將會介紹基於 GitLab CI/CD 的自動化構建與發佈實踐。如下圖所示,整個流程將分爲幾個部分:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.首先開發人員在本地完成項目的開發之後,將代碼推送到 Gitlab 倉庫中。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.當代碼提交到 Gitlab 倉庫時,會觸發 Pipeline,Gitlab Runner 會根據 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":".gitlab-ci.yml","attrs":{}}],"attrs":{}},{"type":"text","text":" 配置文件運行 Pipeline 中各階段的任務。我總共定義了 3 個階段:compile,build,deploy。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.在 compile 階段,Gitlab Runner 將項目編譯成 jar 包,使用 MinIO 作爲緩存,首次編譯項目時會從 Maven 官網拉取依賴,之後會將依賴壓縮後上傳至 MinIo,在下一次編譯時就可以直接從 MinIO 下載依賴文件。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.在 build 階段,Gitlab Runner 使用在 compile 階段編譯生成的 jar 包構建 Docker 鏡像,並將鏡像推送至鏡像倉庫。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5.在 deploy 階段,Gitlab Runner 使用構建好 Docker 鏡像在 Kubernetes 集羣中部署應用。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/82/82390d482b4c2d7f18ec11812574aa16.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Gitlab CI/CD","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"GitLab 介紹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"GitLab","attrs":{}}],"attrs":{}},{"type":"text","text":" 是一個利用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Ruby on Rails","attrs":{}}],"attrs":{}},{"type":"text","text":" 開發的開源應用程序,實現一個自託管的 Git 項目倉庫,可通過 Web 界面進行訪問公開的或者私有的項目。它擁有與 GitHub 類似的功能,能夠瀏覽源代碼,管理缺陷和註釋。可以管理團隊對倉庫的訪問,它非常易於瀏覽提交過的版本並提供一個文件歷史庫。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"GitLab CI/CD 介紹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Gitlab CI/CD","attrs":{}}],"attrs":{}},{"type":"text","text":" 是一個內置在 GitLab 中的工具,用於通過持續方法進行軟件開發。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"持續集成(Continuous Integration)","attrs":{}},{"type":"text","text":":頻繁地(一天多次)將代碼集成到主幹。讓產品可以快速迭代,同時還能保持高質量。它的核心措施是,代碼集成到主幹之前,必須通過自動化測試。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"持續交付(Continuous Delivery)","attrs":{}},{"type":"text","text":":頻繁地將軟件的新版本,交付給質量團隊或者用戶,以供評審。如果評審通過,代碼就進入生產階段。持續交付可以看作持續集成的下一步。它強調的是,不管怎麼更新,軟件是隨時隨地可以交付的。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"持續部署(continuous Deployment)","attrs":{}},{"type":"text","text":":代碼通過評審以後,自動部署到生產環境。是持續部署是持續交付的下一步,持續部署的目標是,代碼在任何時刻都是可部署的,可以進入生產階段。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"GitLab Runner 介紹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"GitLab Runner","attrs":{}}],"attrs":{}},{"type":"text","text":" 用於執行 Gitlab CI/CD 觸發的一系列作業,並將結果發送回 Gitlab。GitLab Runner 可以在 Docker 容器內運行或部署到 Kubernetes 集羣中。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Pipeline","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Pipeline","attrs":{}}],"attrs":{}},{"type":"text","text":" 中文稱爲流水線,是分階段執行的構建任務。如:安裝依賴、運行測試、打包、部署開發服務器、部署生產服務器等流程,合起來稱爲 Pipeline。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/dd/dd34ec1d423aad36611e05655194d69d.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Stage","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Stage","attrs":{}}],"attrs":{}},{"type":"text","text":" 表示構建階段,可以理解爲上面所說安裝依賴、運行測試等環節的流程。我們可以在一次 Pipeline 中定義多個 Stage。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Job","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Job","attrs":{}}],"attrs":{}},{"type":"text","text":" 表示構建的作業(或稱之爲任務),表示某個 Stage 裏面執行的具體任務。我們可以在 Stages 裏面定義多個 Jobs。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Pipeline,Stage 和 Job 的關係可以用下圖表示。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8f/8f816a27c28c041b509b82db2f9cde04.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以 Gitlab 中的某個實際的 Pipeline 爲例解釋 Pipeline,Stage,Job 的含義,具體請看下圖。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c3/c339ee41a3b9209f14cb75d43c5bc70c.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"MinIO 介紹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MinIO 是一款分佈式,高性能的對象存儲服務,專爲大型私有云環境而設計。MinIO 兼容 Amazon S3 對象存儲接口,非常適合存儲大容量的非結構化數據,例如圖片、視頻、日誌文件、鏡像等等。本文將使用 MinIO 作爲編譯 Springboot 項目時使用的緩存,首次編譯項目時會從 Maven 官網拉取依賴,之後會將依賴壓縮後上傳至 MinIo,在下一次編譯時就可以直接從 MinIO 下載依賴文件。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"環境搭建","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"前提條件","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"部署好一套 Kubernetes 集羣。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安裝好 Helm 工具,關於 Helm 安裝可以參考 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"安裝 Helm","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"MinIO 部署","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Helm 是 Kubernetes 生態系統中的一個軟件包管理工具,方便我們快速部署應用。這裏選擇使用 Helm 在 Kubernetes 集羣中部署 MinIO。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加 Helm 倉庫。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"sh"},"content":[{"type":"text","text":"helm repo add minio https://helm.min.io/\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用以下命令安裝 Helm。設置用戶名爲 admin,密碼爲 admin123456,在 minio 命名空間中部署。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"sh"},"content":[{"type":"text","text":"helm install minio \\\n--set accessKey=admin,secretKey=admin123456 \\\n--namespace minio --create-namespace \\\nminio/minio\n\n#返回結果\nminio/minio\nNAME: minio\nLAST DEPLOYED: Wed Aug 18 13:23:45 2021\nNAMESPACE: minio\nSTATUS: deployed\nREVISION: 1\nTEST SUITE: None\nNOTES:\nMinio can be accessed via port 9000 on the following DNS name from within your cluster:\nminio.minio.svc.cluster.local\n\nTo access Minio from localhost, run the below commands:\n\n 1. export POD_NAME=$(kubectl get pods --namespace minio -l \"release=minio\" -o jsonpath=\"{.items[0].metadata.name}\")\n\n 2. kubectl port-forward $POD_NAME 9000 --namespace minio\n\nRead more about port forwarding here: http://kubernetes.io/docs/user-guide/kubectl/kubectl_port-forward/\n\nYou can now access Minio server on http://localhost:9000. Follow the below steps to connect to Minio server with mc client:\n\n 1. Download the Minio mc client - https://docs.minio.io/docs/minio-client-quickstart-guide\n\n 2. Get the ACCESS_KEY=$(kubectl get secret minio -o jsonpath=\"{.data.accesskey}\" | base64 --decode) and the SECRET_KEY=$(kubectl get secret minio -o jsonpath=\"{.data.secretkey}\" | base64 --decode)\n\n 3. mc alias set minio-local http://localhost:9000 \"$ACCESS_KEY\" \"$SECRET_KEY\" --api s3v4\n\n 4. mc ls minio-local\n\nAlternately, you can use your browser or the Minio SDK to access the server - https://docs.minio.io/categories/17\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了在本地電腦可以訪問到 MinIO 的 Web 界面,使用以下命令開啓端口轉發。你也可以選擇通過 NodePort 或者其他方式將 MinIO 服務暴露到集羣外部。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"sh"},"content":[{"type":"text","text":"export POD_NAME=$(kubectl get pods --namespace minio -l \"release=minio\" -o jsonpath=\"{.items[0].metadata.name}\")\nkubectl port-forward $POD_NAME 9000 --namespace minio\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瀏覽器輸入 http://localhost:9000 訪問 MinIO 界面。用戶名:admin,密碼 admin123456,是我們前面用 helm install 安裝 minio 時設置的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2f/2fbf61055b21366e93cca8dfb483dee0.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f1/f18d35b82b6f94a853ae64dbd738b234.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建一個 Bucket,命名爲 gitlab-runner-cache-maven 用於存放編譯項目的依賴文件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/16/16936f71cb704ba084f302951a6e7ad0.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d9/d97156f479c96b35ad0101c1d329ca09.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Gitlab Runner 部署","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建 Secret 保存 MinIO 的用戶名和密碼,之後 Gitlab Runner 容器會使用這個這個用戶名和密碼來登錄 MinIO。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"kubectl create secret -n acp generic s3access \\\n--from-literal=accesskey=admin \\\n--from-literal=secretkey=admin123456\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加 Gilab Helm 倉庫,並下載 Gitlab Runner Helm 資源文件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"helm repo add gitlab https://charts.gitlab.io\nhelm pull gitlab/gitlab-runner --untar\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"編輯 gitlab-runner 目錄下的 values.yaml 文件,有以下幾個配置需要修改。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"gitlabUrl","attrs":{}},{"type":"text","text":":設置 Gitlab 的 IP 地址。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"runnerRegistrationToken","attrs":{}},{"type":"text","text":":設置 Gitlab Runner 註冊的 token。進入項目 -> Settings -> CI/CD -> Runners settings 查看註冊 Gitlab Runner 所需的 registration token。","attrs":{}}]}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8e/8e6e1a360893f672922e60f89971a227.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"cache","attrs":{}},{"type":"text","text":": 設置緩存。","attrs":{}}]}]}],"attrs":{}},{"type":"codeblock","attrs":{"lang":"yaml"},"content":[{"type":"text","text":"# gitlab IP 地址\ngitlabUrl: http://gitlab ip地址/\n\n# 註冊 gitlab runner 的 token\nrunnerRegistrationToken: \"o_4r2uvptQYmmr79e2uF\"\n\nrunners:\n# 設置緩存\n cache:\n ## General settings\n cacheType: s3\n cachePath: \"gitlab-runner-elasticsearch-api\" # 緩存路徑,gitlab runner 會自動在 bucket 下創建該目錄\n cacheShared: true\n\n ## S3 settings\n s3ServerAddress: minio.minio.svc.cluster.local:9000 # kubernetes 集羣 clusterip service 訪問的地址\n s3BucketName: gitlab-runner-cache-maven # bucket 名字\n s3BucketLocation:\n s3CacheInsecure: true # http 登錄\n secretName: s3access # 使用 Minio 用戶名密碼創建的 secert\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置完成後,使用以下命令安裝 Gitlab Runner。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"sh"},"content":[{"type":"text","text":"helm install -n acp gitlab-runner-elasticsearch-api gitlab-runner\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一切順利的話,可以在 Gitlab 上看到 Gitlab Runner 成功註冊上來。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/22/22e44b9c4c00662ee460b8749e864cb4.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查看在 Kubernetes 集羣中創建的 Gitlab Runner 的 Pod","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"sh"},"content":[{"type":"text","text":"❯ kubectl get pod -n acp | grep runner\ngitlab-runner-elasticsearch-api-gitlab-runner-88f7b64fc-rdfch 1/1 Running 0 113s\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"配置 .gitlab-ci.yml 文件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Gitlab CI/CD 通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":".gitlab-ci.yml","attrs":{}}],"attrs":{}},{"type":"text","text":" 配置文件中定義流水線(Pipeline)的各個階段(Stage),以及各個階段中的若干作業(Job)。例如以下配置文件,我們定義了 3 個 Stage:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.compile:使用 openjdk 鏡像編譯項目。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.build:使用 compile 階段編譯好的 jar 包構建 Docker 鏡像,並推送至鏡像倉庫。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.deploy:在 Kubernetes 集羣中使用構建好的 Docker 鏡像部署應用。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"yaml"},"content":[{"type":"text","text":"stages:\n - compile\n - build\n - deploy\n\nvariables:\n KUBECONFIG: /tmp/config\n CI_REGISTRY: 你的鏡像倉庫 IP\n CI_REGISTRY_IMAGE: 你的鏡像倉庫項目路徑\n\n# maven 依賴緩存\ncache:\n paths:\n - cache \n\n# 編譯項目\ncompile:\n stage: compile\n image: openjdk:8-jdk-alpine\n # 只有打 tag 時纔會觸發任務\n only:\n - tags\n # 編譯項目,跳過單元測試,指定本地依賴目錄\n script:\n - ./mvnw package -Dmaven.test.skip=true -Dmaven.repo.local=cache\n # 將編譯好的 jar 包傳遞給下一個階段,用於 kaniko 構建 docker 鏡像\n artifacts:\n paths:\n - target\n\n# 構建鏡像\nbuild:\n image: gcr.io/kaniko-project/executor:debug # 可能需要手動提前下載\n stage: build\n only:\n - tags\n script:\n # 使用 kaniko 構建 docker 鏡像\n - mkdir -p /kaniko/.docker\n - echo \"{\\\"auths\\\":{\\\"$CI_REGISTRY\\\":{\\\"username\\\":\\\"$REGISTRY_USER\\\",\\\"password\\\":\\\"$REGISTRY_PASS\\\"}}}\" > /kaniko/.docker/config.json\n - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG\n\n#部署到 kubernetes 環境中\ndeploy:\n image: bitnami/kubectl:1.19\n stage: deploy\n only:\n - tags\n script:\n # 設置 kubectl 容器的 kubeconfig 文件,使 kubectl 容器有在 Kubernetes 中部署應用的權限\n - echo $kube_config | base64 -id > $KUBECONFIG\n - sed -i \"s/CI_COMMIT_TAG/$CI_COMMIT_TAG/g\" manifests/deployment.yaml\n - cat manifests/*.yaml\n - kubectl apply -f manifests/\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意事項:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了安全性,我們將鏡像倉庫的用戶名、密碼以及 Kubernetes 集羣的 kubeconfig 文件設置在 Gitlab 的 Secret variables 中。進入項目 -> Settings -> CI/CD -> Secret variables。注意 kubeconfig 文件的值是 base64 加密後的,鏡像倉庫的用戶名和密碼正常設置即可。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c2/c26510b69c1da74b7b64a2dbb9a9ca68.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要在項目根路徑下創建一個 cache 目錄,用於臨時存放從 MinIo 下載的依賴,這個目錄名可以自定義,要和 .gitlab-ci.yml 文件中設置的 cache path 一致。在 compile 階段指定 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-Dmaven.repo.local=cache","attrs":{}}],"attrs":{}},{"type":"text","text":" 參數使用 cache 作爲依賴倉庫,這樣就可以用上從 MinIO 中提取的依賴了。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 build 階段使用 compile 階段編譯好的 jar 包構建 Docker 鏡像,Dockerfile 內容如下。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"yaml"},"content":[{"type":"text","text":"FROM openjdk:8-jdk-alpine \n# 設置工作目錄 \nWORKDIR /app \n \n# 語言,時區設置 \nENV TZ=Asia/Shanghai \nENV LANG=en_US.UTF-8 \nENV LANGUAGE=en_US:en \nENV iC_ALL=en_US.UTF-8 \n \nEXPOSE 8080 \n# 拷貝 compile 階段編譯好的 jar 包 \nCOPY target/*.jar elasticsearch-api.jar \nCMD [\"java\",\"-jar\",\"elasticsearch-api.jar\"]\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"deploy 階段用於在 Kubernetes 環境中部署的資源文件在 manifests 目錄中,根據你自身項目的情況來編寫。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8a/8a7055f395384d319db568fa7dfe7cb8.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"流程驗證","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"完成項目的開發之後,將代碼推送到 Gitlab 倉庫中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"git add .\ngit commit -m \"首次觸發任務\"\ngit push\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此時並不會觸發 Pipline,因爲我們在 .gitlab-ci.yml 配置文件中設置了只有打了 tag 纔會觸發 Pipline。推送 tag 觸發 Pipeline。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"git tag 3.0.4\ngit push origin 3.0.4\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"等待 Pipeline 執行完成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/04/0408057ef38148ae16d20c7e1f69ff72.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"點擊 Pipeline 編號查看詳情,可以看到裏面有 3 個 Stage。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1a/1a7300e147fd0c34ea0d82c84f898b69.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一次 Pipeline 在 compile 階段會比較慢,因爲編譯項目時需要從公網下載依賴,在編譯完成以後會將 cache 目錄中的依賴壓縮打包後上傳到 Minio,等到下一次編譯就可以直接拿來使用,避免重複去公網下載,速度會加快不少。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/86/86799f31d39235b89aa2a7c505972478.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 MinIO 上可以看到 Gitlab Runner 上傳上來的依賴的壓縮文件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/83/83dab88a78193c1dcbb2f83de49cda11.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"build 階段會從 compile 階段中 artifact 設置的 target 目錄中獲取編譯好了 jar 包構建 Docker 鏡像。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/41/414f61b4d046a2147f2e1888443cdf45.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"deploy 階段將鏡像部署到 Kubernetes 環境中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4f/4fdd11077e613dbb4a3c756133af834e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此就完成了這一整套 Gitlab CI/CD 流程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在鏡像倉庫中可以看到構建好的鏡像。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c4/c4d2b9f20fb45e3d08f2a664c68ea06d.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Kubernetes 中可以看到部署的應用。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"❯ kubectl get pod -n acp | grep elasticsearch\nelasticsearch-api-7d9656cf5d-5sngv 2/2 Running 0 30s\nelasticsearch-api-7d9656cf5d-9zgzt 2/2 Running 0 30s\nelasticsearch-api-7d9656cf5d-tthx5 2/2 Running 0 30s\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在我們對代碼做了修改,現在再次推送新的 tag 觸發 Pipeline。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"git add .\ngit commit -m \"第二次觸發任務\"\ngit push\n\ngit tag 3.0.5\ngit push origin 3.0.5\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5b/5b24a23dc9b25758fa96e001b807346c.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到這次在 compile 階段並沒有去公網下載依賴,而是直接使用從 Minio 下載了依賴。整個 Pipeline 流程也從 5 分鐘縮短至 1 分鐘。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b1/b10ff6dbc04c2af711e080d73bb84656.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"參考資料","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[1]","attrs":{}},{"type":"link","attrs":{"href":"https://about.gitlab.com/","title":"","type":null},"content":[{"type":"text","text":"Gitlab","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[2]","attrs":{}},{"type":"link","attrs":{"href":"https://docs.gitlab.com/ee/ci/index.html","title":"","type":null},"content":[{"type":"text","text":"Gitlab CI/CD","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[3]","attrs":{}},{"type":"link","attrs":{"href":"https://docs.gitlab.com/runner/","title":"","type":null},"content":[{"type":"text","text":"GitLab Runner","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[4]","attrs":{}},{"type":"link","attrs":{"href":"https://docs.gitlab.com/ce/ci/pipelines.html","title":"","type":null},"content":[{"type":"text","text":"Pipelines","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[5]","attrs":{}},{"type":"link","attrs":{"href":"https://docs.gitlab.com/ce/ci/yaml/README.html#stages","title":"","type":null},"content":[{"type":"text","text":"Stages","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[6]","attrs":{}},{"type":"link","attrs":{"href":"https://docs.gitlab.com/ce/ci/pipelines.html#jobs","title":"","type":null},"content":[{"type":"text","text":"Jobs","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[7]","attrs":{}},{"type":"link","attrs":{"href":"https://helm.sh/zh/docs/intro/install/","title":"","type":null},"content":[{"type":"text","text":"安裝 Helm","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://docs.gitlab.com/ee/ci/docker/using_kaniko.html","title":"","type":null},"content":[{"type":"text","text":"Use kaniko to build Docker images","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://cloud.51cto.com/art/202108/679757.htm","title":"","type":null},"content":[{"type":"text","text":"常用容器鏡像構建工具和方案介紹","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/vARJJeNLBKgxn4T7oqC41Q","title":"","type":null},"content":[{"type":"text","text":"如何在 Docker 中使用 Docker","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://zhuanlan.zhihu.com/p/102171749","title":"","type":null},"content":[{"type":"text","text":"深入理解container--容器運行時","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://segmentfault.com/a/1190000039904303","title":"","type":null},"content":[{"type":"text","text":"Container Runtime (一) :介紹","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://segmentfault.com/a/1190000039918599","title":"","type":null},"content":[{"type":"text","text":"Container Runtime (二) :低級容器運行時","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://segmentfault.com/a/1190000039957112","title":"","type":null},"content":[{"type":"text","text":"Container Runtimes (三):高級容器運行時","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://segmentfault.com/a/1190000039957984","title":"","type":null},"content":[{"type":"text","text":"Container Runtimes(四): Kubernetes Container Runtimes & CRI","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://docs.idevops.site/gitlabci/chapter05/01/05-%E9%85%8D%E7%BD%AE%E4%BD%BF%E7%94%A8minio%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8/","title":"","type":null},"content":[{"type":"text","text":"配置使用分佈式緩存","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://cloud.tencent.com/developer/article/1684099","title":"","type":null},"content":[{"type":"text","text":"用 GitLab 做 CI/CD 是什麼感覺,太強了!!","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://blinkfox.github.io/2018/11/22/ruan-jian-gong-ju/devops/gitlab-ci-jie-shao-he-shi-yong/","title":"","type":null},"content":[{"type":"text","text":"GitLab CI/CD 介紹和使用","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://docs.gitlab.com/ee/ci/","title":"","type":null},"content":[{"type":"text","text":"GitLab CI/CD 官網","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://zhuanlan.zhihu.com/p/105157319","title":"","type":null},"content":[{"type":"text","text":"DevOps之Gitlab-CICD實踐篇","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xiangflight.github.io/gitlab-cd-practice/","title":"","type":null},"content":[{"type":"text","text":"GitLab CI/CD 實踐","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.redhat.com/zh/topics/devops/what-is-ci-cd","title":"","type":null},"content":[{"type":"text","text":"CI/CD是什麼?如何理解持續集成、持續交付和持續部署","attrs":{}}]}]}]}],"attrs":{}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章