如何構建有效的初始部署管道

已發表: 2022-03-11

我喜歡構建東西——開發者不喜歡什麼? 我喜歡為有趣的問題思考解決方案、編寫實現和創建漂亮的代碼。 但是,我不喜歡的是操作。 運營是構建優秀軟件涉及的一切——從設置服務器到將代碼交付到生產環境的一切。

這很有趣,因為作為一名自由 Ruby on Rails 開發人員,我經常需要創建新的 Web 應用程序並重複找出 DevOps 方面的過程。 幸運的是,在創建了幾十個應用程序之後,我終於確定了一個完美的初始部署管道。 不幸的是,並不是每個人都像我一樣弄清楚了——最終,這些知識使我冒險嘗試並記錄了我的過程。

在本文中,我將向您介紹我在項目開始時使用的完美管道。 使用我的管道,每次推送都經過測試,主分支被部署到暫存,並使用來自生產的新數據庫轉儲,並且版本化標籤被部署到生產,備份和遷移自動發生。

請注意,因為它是的管道,所以它也是固執己見且適合我的需求; 但是,您可以隨意更換您不喜歡的任何東西,並用您喜歡的任何東西替換它。 對於我的管道,我們將使用:

  • GitLab託管代碼。
    • 原因:我的客戶希望他們的代碼保密,GitLab 的免費套餐非常棒。 此外,集成的免費 CI 也很棒。 感謝 GitLab!
    • 替代方案:GitHub、BitBucket、AWS CodeCommit 等等。
  • GitLab CI用於構建、測試和部署我們的代碼。
    • 為什麼:它與 GitLab 集成並且是免費的!
    • 替代方案:TravisCI、Codeship、CircleCI、使用 Fabric8 DIY 等等。
  • Heroku來託管我們的應用程序。
    • 原因:它開箱即用,是開始的完美平台。 您將來可以更改此設置,但並非每個新應用程序都需要在專門構建的 Kubernetes 集群上運行。 甚至 Coinbase 也是從 Heroku 開始的。
    • 替代方案:AWS、DigitalOcean、Vultr、使用 Kubernetes 進行 DIY 等等。

老派:創建一個基本應用程序並將其部署到 Heroku

首先,讓我們為不使用任何花哨的 CI/CD 管道而只想部署他們的應用程序的人重新創建一個典型的應用程序。

傳統代碼託管和部署操作示意圖

您要創建什麼樣的應用程序並不重要,但您需要 Yarn 或 npm。 對於我的示例,我正在創建一個 Ruby on Rails 應用程序,因為它帶有遷移和 CLI,並且我已經為它編寫了配置。 歡迎您使用您喜歡的任何框架或語言,但您需要 Yarn 來執行我稍後執行的版本控制。 我正在創建一個簡單的 CRUD 應用程序,只使用幾個命令並且沒有身份驗證。

讓我們測試一下我們的應用程序是否按預期運行。 我繼續創建了一些帖子,以確保。

在開發中運行的應用程序

讓我們通過推送我們的代碼並運行遷移將其部署到 Heroku

 $ heroku create toptal-pipeline Creating ⬢ toptal-pipeline... done https://toptal-pipeline.herokuapp.com/ | https://git.heroku.com/toptal-pipeline.git $ git push heroku master Counting objects: 132, done. ... To https://git.heroku.com/toptal-pipeline.git * [new branch] master -> master $ heroku run rails db:migrate Running rails db:migrate on ⬢ toptal-pipeline... up, run.9653 (Free) ...

最後讓我們在生產環境中測試一下

在生產中運行的應用程序

就是這樣! 通常,這是大多數開發人員離開他們的業務的地方。 將來,如果您進行更改,則必須重複上述部署和遷移步驟。 如果您沒有遲到晚餐,您甚至可以運行測試。 這是一個很好的起點,但讓我們更多地考慮一下這種方法。

優點

  • 快速設置。
  • 部署很容易。

缺點

  • Not DRY:需要在每次更改時重複相同的步驟。
  • 未版本化:“我將昨天的部署回滾到上週的”在三週後並不是很具體。
  • 不錯的代碼證明:你知道你應該運行測試,但沒有人在看,所以儘管偶爾會出現錯誤的測試,但你可能會推動它。
  • 不錯的演員證明:如果心懷不滿的開發人員決定通過推送代碼來破壞您的應用程序,並顯示您沒有為您的團隊訂購足夠的比薩餅的消息怎麼辦?
  • 無法擴展:允許每個開發人員進行部署將使他們能夠在生產級別訪問應用程序,這違反了最小權限原則。
  • 沒有暫存環境:特定於生產環境的錯誤在生產之前不會出現。

完美的初始部署管道

我今天要嘗試一些不同的事情:讓我們進行假設性對話。 我要給“你”一個聲音,我們將討論如何改進當前的流動。 來吧,說點什麼。

說什麼? 等等——我會說話嗎?

是的,這就是我要給你發言權的意思。 你好嗎?

我很好。 這感覺很奇怪

我明白,但只是順其自然。 現在,讓我們談談我們的管道。 運行部署最煩人的部分是什麼?

哦,這很容易。 我浪費的時間。 你有沒有試過推送到 Heroku?

是的,看著你的依賴項下載和應用程序作為git push的一部分被構建是可怕的!

我知道,對吧? 這太瘋狂了。 我希望我不必那樣做。 還有一個事實是我必須在部署後*運行遷移,所以我必須觀看節目並檢查以確保我的部署通過

好的,您實際上可以通過使用&&鏈接兩個命令來解決後一個問題,例如git push heroku master && heroku run rails db:migrate ,或者只是創建一個 bash 腳本並將其放入您的代碼中,但仍然是一個很好的答案,時間和重複是一種真正的痛苦。

是的,真的很爛

如果我告訴你你可以用 CI/CD 管道立即修復這個問題怎麼辦?

現在怎麼辦? 那是什麼?

CI/CD 代表持續集成 (CI) 和持續交付/部署 (CD)。 當我剛開始時,我很難準確理解它是什麼,因為每個人都使用了諸如“開發和運營的融合”之類的模糊術語,但簡單地說:

  • 持續集成:確保所有代碼都合併到一個地方。 讓您的團隊使用 Git,您將使用 CI。
  • 持續交付:確保您的代碼持續準備好交付。 這意味著快速生成產品的讀取分發版本。
  • 持續部署:從持續交付中無縫獲取產品並將其部署到您的服務器。

哦,我現在明白了。 這是關於讓我的應用程序神奇地部署到世界!

我最喜歡的解釋 CI/CD 的文章是 Atlassian 的。 這應該可以解決您的任何問題。 無論如何,回到問題。

是的,回到那個。 如何避免手動部署?

設置 CI/CD 管道以在推送到master服務器時進行部署

如果我告訴你你可以用 CI/CD 立即修復這個問題怎麼辦? 您可以推送到您的 GitLab 遠程 ( origin ),然後將生成一台計算機以直接將您的代碼推送到 Heroku。

沒門!

對啊! 讓我們再次跳回代碼。

一個簡單的部署 CI/CD 管道示意圖

創建一個包含以下內容的.gitlab-ci.yml ,將toptal-pipeline為您的 Heroku 應用程序的名稱:

 image: ruby:2.4 before_script: - > : "${HEROKU_EMAIL:?Please set HEROKU_EMAIL in your CI/CD config vars}" - > : "${HEROKU_AUTH_TOKEN:?Please set HEROKU_AUTH_TOKEN in your CI/CD config vars}" - curl https://cli-assets.heroku.com/install-standalone.sh | sh - | cat >~/.netrc <<EOF machine api.heroku.com login $HEROKU_EMAIL password $HEROKU_AUTH_TOKEN machine git.heroku.com login $HEROKU_EMAIL password $HEROKU_AUTH_TOKEN EOF - chmod 600 ~/.netrc - git config --global user.email "[email protected]" - git config --global user.name "CI/CD" variables: APPNAME_PRODUCTION: toptal-pipeline deploy_to_production: stage: deploy environment: name: production url: https://$APPNAME_PRODUCTION.herokuapp.com/ script: - git remote add heroku https://git.heroku.com/$APPNAME_PRODUCTION.git - git push heroku master - heroku pg:backups:capture --app $APPNAME_PRODUCTION - heroku run rails db:migrate --app $APPNAME_PRODUCTION only: - master

將其向上推,並在項目的 Pipelines 頁面中觀察它是否失敗。 那是因為它缺少您 Heroku 帳戶的身份驗證密鑰。 不過,解決這個問題相當簡單。 首先,您需要您的 Heroku API 密鑰。 從“管理帳戶”頁面獲取它,然後在 GitLab 存儲庫的 CI/CD 設置中添加以下秘密變量

  • HEROKU_EMAIL :您用於登錄 Heroku 的電子郵件地址
  • HEROKU_AUTH_KEY :您從 Heroku 獲得的密鑰

GitLab CI/CD 設置頁面中的秘密變量的圖像

這應該會導致在每次推送時部署一個工作的 GitLab 到 Heroku。 至於發生了什麼:

  • 在推動掌握
    • Heroku CLI 在容器中安裝和驗證。
    • 你的代碼被推送到 Heroku。
    • 在 Heroku 中捕獲數據庫的備份。
    • 正在運行遷移。

您已經可以看到,您不僅可以通過將所有內容自動化到git push來節省時間,而且還可以在每次部署時創建數據庫備份! 如果出現任何問題,您將擁有數據庫的副本以恢復。

創建暫存環境

但是等等,快速提問,您的生產特定問題會發生什麼? 如果您因為開發環境與生產環境差異太大而遇到奇怪的錯誤怎麼辦? 我曾經在運行遷移時遇到過一些奇怪的 SQLite 3 和 PostgreSQL 問題。 具體細節我不知道,但這是很有可能的。

我在開發中嚴格使用 PostgreSQL,我從不與這樣的數據庫引擎不匹配,並且我會努力監控我的堆棧是否存在潛在的不兼容性。

嗯,這是乏味的工作,我為你的紀律鼓掌。 就個人而言,我太懶惰了。 但是,您能否保證所有潛在的未來開發人員、合作者或貢獻者都盡職盡責?

Errrr——是的,不。 你讓我到了那裡。 其他人會搞砸的。 不過,你的意思是什麼?

我的觀點是,你需要一個登台環境。 這就像生產,但不是。 暫存環境是您排練部署到生產並及早發現所有錯誤的地方。 我的登台環境通常鏡像生產,並且我在登台部署時轉儲生產數據庫的副本,以確保沒有討厭的極端情況擾亂我的遷移。 使用暫存環境,您可以停止像豚鼠一樣對待您的用戶。

這是有道理的! 那麼我該怎麼做呢?

這就是有趣的地方。 我喜歡將master直接部署到staging。

等等,這不是我們現在部署生產的地方嗎?

是的,但現在我們將部署到登台。

但是如果master部署到staging,我們如何部署到生產中呢?

通過使用幾年前你應該做的事情:版本化我們的代碼並推送 Git 標籤。

Git標籤? 誰使用 Git 標籤?! 這聽起來像是很多工作。

確實是這樣,但幸運的是,我已經完成了所有這些工作,您只需轉儲我的代碼,它就會工作。

暫存和生產部署如何工作的概述

首先,在.gitlab-ci.yml文件中添加一個關於 staging deploy 的塊,我創建了一個名為toptal-pipeline-staging的新 Heroku 應用程序:

 … variables: APPNAME_PRODUCTION: toptal-pipeline APPNAME_STAGING: toptal-pipeline-staging deploy_to_staging: stage: deploy environment: name: staging url: https://$APPNAME_STAGING.herokuapp.com/ script: - git remote add heroku https://git.heroku.com/$APPNAME_STAGING.git - git push heroku master - heroku pg:backups:capture --app $APPNAME_PRODUCTION - heroku pg:backups:restore `heroku pg:backups:url --app $APPNAME_PRODUCTION` --app $APPNAME_STAGING --confirm $APPNAME_STAGING - heroku run rails db:migrate --app $APPNAME_STAGING only: - master - tags ...

然後將生產塊的最後一行更改為在語義版本化的 Git 標記上運行,而不是在 master 分支上運行:

 deploy_to_production: ... only: - /^v(?'MAJOR'(?:0|(?:[1-9]\d*)))\.(?'MINOR'(?:0|(?:[1-9]\d*)))\.(?'PATCH'(?:0|(?:[1-9]\d*)))(?:-(?'prerelease'[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?(?:\+(?'build'[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$/ # semver pattern above is adapted from https://github.com/semver/semver.org/issues/59#issuecomment-57884619

現在運行它會失敗,因為 GitLab 足夠聰明,只允許“受保護”分支訪問我們的秘密變量。 要添加版本標籤,請轉到 GitLab 項目的存儲庫設置頁面並將v*添加到受保護的標籤。

在存儲庫設置頁面中添加到受保護標籤的版本標籤的圖像

讓我們回顧一下現在發生的事情:

  • 在推送到 master 或推送標記的提交時
    • Heroku CLI 在容器中安裝和驗證。
    • 你的代碼被推送到 Heroku。
    • 在 Heroku 中捕獲數據庫生產的備份。
    • 備份將轉儲到您的登台環境中。
    • 遷移在暫存數據庫上運行。
  • 在推送帶有語義版本標籤的提交時
    • Heroku CLI 在容器中安裝和驗證。
    • 你的代碼被推送到 Heroku。
    • 在 Heroku 中捕獲數據庫生產的備份。
    • 遷移在生產數據庫上運行。

你現在覺得強大嗎? 我覺得很強大。 我記得,我第一次來這麼遠的時候,我打電話給我的妻子,詳細解釋了整個管道。 而且她連技術都沒有。 我對自己印象非常深刻,你也應該如此! 幹得好!

測試每一次推送

但還有更多,因為計算機無論如何都會為你做事,它也可以運行你懶得做的所有事情:測試、linting 錯誤,幾乎任何你想做的事情,如果其中任何一個失敗,他們就贏了不要繼續部署。

我喜歡在我的管道中使用它,它使我的代碼審查變得有趣。 如果一個合併請求通過了我所有的代碼檢查,它就值得被審查。

每次推送測試的圖片

添加test塊:

 test: stage: test variables: POSTGRES_USER: test POSTGRES_PASSSWORD: test-password POSTGRES_DB: test DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSSWORD}@postgres/${POSTGRES_DB} RAILS_ENV: test services: - postgres:alpine before_script: - curl -sL https://deb.nodesource.com/setup_8.x | bash - apt-get update -qq && apt-get install -yqq nodejs libpq-dev - curl -o- -L https://yarnpkg.com/install.sh | bash - source ~/.bashrc - yarn - gem install bundler --no-ri --no-rdoc - bundle install -j $(nproc) --path vendor - bundle exec rake db:setup RAILS_ENV=test script: - bundle exec rake spec - bundle exec rubocop

讓我們回顧一下現在發生的事情:

  • 在每次推送或合併請求時
    • Ruby 和 Node 設置在一個容器中。
    • 已安裝依賴項。
    • 該應用程序經過測試。
  • 在推送到 master 或推送標記的提交時,並且只有在所有測試都通過時
    • Heroku CLI 在容器中安裝和驗證。
    • 你的代碼被推送到 Heroku。
    • 在 Heroku 中捕獲數據庫生產的備份。
    • 備份將轉儲到您的登台環境中。
    • 遷移在暫存數據庫上運行。
  • 在推送帶有語義版本標記的提交時,並且僅當所有測試都通過時
    • Heroku CLI 在容器中安裝和驗證。
    • 你的代碼被推送到 Heroku。
    • 在 Heroku 中捕獲數據庫生產的備份。
    • 遷移在生產數據庫上運行。

退後一步,驚嘆於您已經完成的自動化水平。 從現在開始,您所要做的就是編寫代碼並推送。 如果您願意,可以在 staging 中手動測試您的應用程序,當您有足夠的信心將其推向世界時,請使用語義版本控制對其進行標記!

自動語義版本控制

是的,它很完美,但是缺少一些東西。 我不喜歡查找應用程序的最新版本並明確標記它。 這需要多個命令並分散我幾秒鐘的注意力。

好吧,伙計,停下! 夠了。 你現在只是過度設計它。 它有效,它很棒,不要因為太過分而毀了一件好事。

好吧,我有充分的理由去做我將要做的事情。

求教,賜教。

我曾經和你一樣。 我對這個設置很滿意,但後來我搞砸了。 git tag按字母順序列出標籤, v0.0.11高於v0.0.2 。 我曾經不小心標記了一個版本,並繼續這樣做了大約六個版本,直到我看到我的錯誤。 那時我決定也將其自動化。

我們重新來過吧

好的,所以,幸運的是,我們擁有 npm 的強大功能,所以我找到了一個合適的包:運行yarn add --dev standard-version並將以下內容添加到您的package.json文件中:

 "scripts": { "release": "standard-version", "major": "yarn release --release-as major", "minor": "yarn release --release-as minor", "patch": "yarn release --release-as patch" },

現在您需要做最後一件事,將 Git 配置為默認推送標籤。 目前,您需要運行git push --tags來上推標籤,但在常規git push上自動執行此操作就像運行git config --global push.followTags true一樣簡單。

要使用新管道,只要您想創建發布運行:

  • 補丁發布的yarn patch
  • 次要版本的yarn minor
  • 主要版本的yarn major

如果您不確定“主要”、“次要”和“補丁”這三個詞的含義,請在語義版本控製網站上閱讀更多相關信息。

現在您終於完成了管道,讓我們回顧一下如何使用它!

  • 寫代碼。
  • 提交並推送它以測試並將其部署到登台。
  • 使用yarn patch來標記補丁版本。
  • git push將其推出生產。

總結和進一步的步驟

我只是觸及了 CI/CD 管道可能實現的表面。 這是一個相當簡單的例子。 通過將 Heroku 替換為 Kubernetes,您可以做更多事情。 如果您決定使用 GitLab CI,請閱讀 yaml 文檔,因為您可以通過在部署之間緩存文件或保存工件來做更多事情!

您可以對此管道進行的另一個巨大更改是引入外部觸發器來運行語義版本控制和發布。 目前,ChatOps 是他們付費計劃的一部分,我希望他們將其發佈為免費計劃。 但想像一下能夠通過單個 Slack 命令觸發下一張圖像!

CI/CD 部署管道圖,其中生產部署在外部觸發,可能通過聊天或 webhook

最終,隨著您的應用程序開始變得複雜並需要係統級依賴項,您可能需要使用容器。 發生這種情況時,請查看我們的指南:Docker 入門:簡化 Devops。

這個示例應用程序確實是實時的,您可以在此處找到它的源代碼。