作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
阿明·沙·吉拉尼的头像

Amin Shah Gilani

Amin是一名开发者和企业家,他喜欢简洁的文字, 为CI/CD编写的测试驱动Ruby和ES6代码.

Expertise

工作经验

8

Share

我喜欢做东西——没有哪个开发人员不喜欢? 我喜欢想出解决有趣问题的办法, 编写实现, 编写漂亮的代码. 然而,我不喜欢的是 operations. 运营就是一切 not 参与构建出色的软件——从设置服务器到将代码发布到生产环境.

这很有趣, 因为作为一个自由的Ruby on Rails开发人员, 我经常不得不创建新的web应用程序,并重复找出问题所在的过程 DevOps side of things. 幸运的是,在创建了几十个应用程序之后,我终于选定了一个完美的 initial 部署管道. Unfortunately, 不是每个人都像我一样最终都明白了, 这些知识促使我大胆尝试并记录我的过程.

在本文中,我将向您介绍在项目开始时使用的完美管道. With my pipeline, 每一次推送都经过测试, 将主分支部署到登台,并从生产中转储一个新的数据库, 版本化的标签被部署到生产环境,备份和迁移会自动进行.

Note, since it’s my pipeline, it’s also opinionated and suited to my needs; however, 你可以随意换掉任何你不喜欢的东西,换上任何你喜欢的东西. 对于我的管道,我们将使用:

  • GitLab to host code.
    • 为什么:我的客户更喜欢他们的代码保密,而GitLab的免费层很棒. 此外,集成的免费CI非常棒. Thanks GitLab!
    • 替代方案:GitHub、BitBucket、AWS CodeCommit等等.
  • GitLab CI 构建、测试和部署我们的代码.
    • 为什么:它与GitLab集成并且是免费的!
    • 替代方案:TravisCI, Codeship, CircleCI, DIY与Fabric8,以及更多.
  • Heroku to host our app.
    • 为什么:它可以开箱即用,是一个完美的开始平台. 你可以在将来改变它, 但并不是每个新应用都需要在专门构建的Kubernetes集群上运行. 就连Coinbase也从Heroku起步.
    • 替代方案:AWS、DigitalOcean、Vultr、使用Kubernetes DIY等等.

老派:创建一个基本的应用程序并将其部署到Heroku

First, 让我们为那些不使用任何花哨的CI/CD管道而只想部署他们的应用程序的人重新创建一个典型的应用程序.

传统代码托管和部署操作的关系图

不管你创建的是哪种类型的应用,你都需要使用Yarn或npm. For my example, 我正在创建一个Ruby on Rails应用程序,因为它附带了迁移和CLI, 我已经写出了它的构型. 欢迎您使用您喜欢的任何框架或语言, 但是您需要Yarn来完成我稍后要做的版本控制. 我正在创建一个简单的CRUD应用程序,只使用几个命令,没有身份验证.

让我们测试应用是否按预期运行. 我继续创建了一些帖子,只是为了确保.

正在开发的应用程序

And let’s 把它部署到赫罗库 通过推送代码和运行迁移

创建total -pipeline
创建如可答:toptal-pipeline... done
http://toptal-pipeline.herokuapp.com/ | http://git.heroku.com/toptal-pipeline.git
$ Git push heroku master
计数对象:132,完成.
...
To http://git.heroku.com/toptal-pipeline.git
 * [new branch]      master -> master
$ Heroku运行rails db:migrate
运行rails db:migrate可答top -pipeline... up, run.9653 (Free)
...

最后,让我们在生产环境中进行测试

在生产环境中运行的应用程序

And that’s it! 通常,这是大多数开发人员离开他们的操作的地方. 将来,如果进行更改,则必须重复上述部署和迁移步骤. 你甚至可以运行测试,如果你没有迟到的晚餐. 这是一个很好的起点,但让我们再多思考一下这个方法.

Pros

  • Quick to set up.
  • 部署很容易.

Cons

  • 非DRY:每次更改都需要重复相同的步骤.
  • “我将把昨天的部署回滚到上周的部署”这句话从现在起的三周后并不是很具体.
  • 不是防坏代码:您知道您应该运行测试, 但没人注意, 所以你可能会推它,尽管偶尔会有错误的测试.
  • 并非防坏行为者:如果一个心怀不满的开发者决定破坏你的应用程序,并向你推送一个信息,说你没有为你的团队订购足够的披萨?
  • 不能扩展:允许每个开发人员都有部署的能力会让他们在生产级别访问应用程序, violating the 最小特权原则.
  • 没有登台环境:特定于生产环境的错误直到生产环境才会出现.

完美的初始部署管道

今天我要尝试一些不同的东西:让我们进行一个假设的对话. 我将给“你”一个声音,我们将讨论如何改善这种电流. 说吧,说点什么.

Say what? Wait—I can talk?

是的,这就是我说给你发言权的意思. How are you?

I’m good. This feels weird

我明白,但随它去吧. 现在,我们来谈谈管道. 运行部署最烦人的部分是什么?

Oh, that’s easy. 我浪费的时间. 你试过推到Heroku吗?

是的,看着你的依赖下载和应用程序被构建作为 git push is horrible!

I know, right? It’s insane. 我真希望我不用那么做. 还有一个事实是,我必须在部署之后运行迁移,所以我必须观看演示并检查以确保我的部署运行顺利

好的,你实际上可以解决后一个问题,通过链接这两个命令 &&, like Git push heroku master && Heroku运行rails db:migrate, 或者只是创建一个bash脚本并将其放入代码中, but still, great answer, 时间和重复是真正的痛苦.

是啊,真的很糟糕

如果我告诉你,你可以用CI/CD管道立即修复这个位?

A what now? What is that?

CI/CD代表持续集成(CI)和持续交付/部署(CD)。. 当我刚开始工作时,我很难确切地理解它是什么,因为每个人都使用模糊的术语,比如“开发和运营的合并”,” but put simply:

  • 持续集成: 确保所有代码都合并到一个地方. 让您的团队使用Git,您将使用CI.
  • 持续交付: 确保您的代码随时可以发布. 这意味着你可以快速生产出可阅读的产品版本.
  • 持续部署: 无缝地将产品从持续交付中取出,并将其部署到您的服务器上.

Oh, I get it now. 它是关于让我的应用神奇地部署到世界上!

我最喜欢的一篇解释CI/CD的文章是Atlassian写的 here. 这应该能澄清你的任何疑问. 不管怎样,回到问题上来.

Yeah, back to that. 如何避免手工部署?

设置推送到时部署的CI/CD管道 master

如果我告诉你,你可以用一个CI/CD立即修复这个位? 你可以推送到你的GitLab远程(origin),然后生成一台计算机,直接将你的代码推送给Heroku.

No way!

Yeah way! 让我们再次回到代码中.

一个简单的部署CI/CD管道图

Create a .gitlab-ci.yml 用下面的内容,换出 toptal-pipeline 为您的Heroku应用程序的名称:

image: ruby:2.4

before_script:
  - >
   : "${HEROKU_EMAIL:?请在您的CI/CD配置变量中设置HEROKU_EMAIL}”
  - >
   :“$ {HEROKU_AUTH_TOKEN:?请在您的CI/CD配置变量中设置HEROKU_AUTH_TOKEN}”
  -卷曲http://cli-assets.heroku.com/install-standalone.sh | sh
  - |
    cat >~/.netrc <

将其推上,并在项目的pipeline页面中观察它的失败. 这是因为它丢失了您的Heroku帐户的认证密钥. 不过,修复这个问题相当简单. 首先你需要你的Heroku API key. Get it from the 管理帐户页面,然后添加以下内容 secret variables 在你的GitLab仓库的CI/CD设置:

  • HEROKU_EMAIL:您用于登录Heroku的电子邮件地址
  • HEROKU_AUTH_KEY你从Heroku那里得到的钥匙

GitLab CI/CD设置页面中秘密变量的图片

这将导致每次推送时都有一个工作的GitLab到Heroku部署. 至于发生了什么:

  • 在推到主人时
    • Heroku CLI在容器中安装和验证.
    • 你的代码被推给了Heroku.
    • 在Heroku中捕获数据库的备份.
    • Migrations are run.

您已经看到,通过将所有事情自动化,您不仅节省了时间 git push,您还会在每次部署时创建数据库备份! 如果出现任何问题,您将拥有数据库的副本以进行恢复.

创建暂存环境

但是,等等,快速的问题,您的特定于生产的问题会发生什么? 如果由于开发环境与生产环境差异太大而遇到奇怪的bug怎么办? 当我运行迁移时,我曾经遇到过一些奇怪的SQLite 3和PostgreSQL问题. 具体细节我不知道,但很有可能.

我严格使用PostgreSQL进行开发, 我从来没有像这样错配数据库引擎, 并且我勤奋地监控我的堆栈是否存在潜在的不兼容性.

那是件乏味的工作,我赞赏你的自律. 就我个人而言,我太懒了. However, 你能保证所有潜在的未来开发者都能做到这一点吗, collaborators, or contributors?

Errrr— Yeah, no. You got me there. 其他人会把它搞砸. 你的观点是什么?

我的意思是,你需要一个登台环境. 这就像制作,但又不是. 登台环境是您演练部署到生产环境并尽早发现所有错误的地方. 我的登台环境通常反映生产环境, 并且我将生产数据库的副本转储到暂存部署上,以确保不会出现令人讨厌的极端情况扰乱我的迁移. 在登台环境中,您可以不再像对待小白鼠那样对待用户.

This makes sense! 我该怎么做呢?

这就是有趣的地方. I like to deploy master 直接到分级.

等等,那不是我们现在部署生产的地方吗?

是的,但现在我们要部署到暂存.

But if master 部署到登台,我们如何部署到生产?

通过使用几年前就应该做的事情:控制代码的版本并推送Git标签.

Git tags? Who uses Git tags?! 这听起来好像有很多工作要做.

It sure was, but thankfully, 我已经做了所有的工作,你可以把我的代码转储,它就能工作了.

概述阶段部署和生产部署的工作方式

首先,将关于暂存部署的块添加到 .gitlab-ci.yml 文件,我创建了一个新的Heroku应用程序,叫做 toptal-pipeline-staging:

…

variables:
  APPNAME_PRODUCTION: toptal-pipeline
  APPNAME_STAGING: toptal-pipeline-staging


deploy_to_staging:
  stage: deploy
  environment:
    name: staging
    url: http:// APPNAME_STAGING美元.herokuapp.com/
  script:
    - git remote add heroku http://git.heroku.com/ APPNAME_STAGING美元.git
    - Git push heroku master
    —heroku pg:backups:capture——app $APPNAME_PRODUCTION
    —heroku pg:backups:restore '——app $APPNAME_PRODUCTION '——app $APPNAME_STAGING——confirm $APPNAME_STAGING
    —Heroku运行rails db:migrate——app $APPNAME_STAGING
  only:
    - master
    - tags

...

然后更改生产块的最后一行,使其运行在语义上版本化的Git标签上,而不是主分支上:

deploy_to_production:
...
  only:
    - /^v(?'MAJOR'(?:0|(?:[1-9]\d*)))\.(?'MINOR'(?:0|(?:[1-9]\d*)))\.(?'PATCH'(?:0|(?:[1-9]\d*)))(?:-(?“预映”+ (\ [0-9A-Za-z -).[0-9A-Za-z-]+)*))?(?:\+(?“构建”(0-9A-Za-z -) + (\.[0-9A-Za-z-]+)*))?$/
    上面的semver模式改编自http://github.com/semver/semver.org/issues/59 # issuecomment - 57884619

现在运行这个将会失败,因为GitLab足够聪明,只允许“受保护的”分支访问我们的秘密变量. 要添加版本标签,请转到您的GitLab项目的存储库设置页面并添加 v* to protected tags.

版本标记被添加到存储库设置页面中的受保护标记的图像

让我们回顾一下现在发生的事情:

  • 在推送到主服务器或推送标记的提交时
    • Heroku CLI在容器中安装和验证.
    • 你的代码被推给了Heroku.
    • 在Heroku中捕获数据库产品的备份.
    • 备份转储在您的暂存环境中.
    • 迁移在暂存数据库上运行.
  • 在推入标记为提交的语义版本时
    • Heroku CLI在容器中安装和验证.
    • 你的代码被推给了Heroku.
    • 在Heroku中捕获数据库产品的备份.
    • 迁移在生产数据库上运行.

你现在感到强大了吗? I feel powerful. I remember, 我第一次来这么远的地方, 我打电话给我的妻子,详细地解释了整个管道. 她甚至不懂技术. 我对自己印象深刻,你也应该如此! 干得好,走了这么远!

Testing Every Push

But there’s more, 反正电脑也在帮你做事, 它还可以运行所有您懒得做的事情:测试, linting errors, 你想做什么都可以, 如果其中任何一个失败了, 他们不会继续部署.

我喜欢在我的管道中使用它,它使我的代码审查变得有趣. 如果一个合并请求通过了我所有的代码检查,它就值得被审查.

每次推送时的测试图像

Add a test block:

test:
  stage: test
  variables:
    POSTGRES_USER:测试
    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 http://deb.nodesource.com/setup_8.x | bash
    - apt-get update -qq && 安装libpq-dev
    - curl -o- l http://yarnpkg.com/install.sh | bash
    - source ~/.bashrc
    - yarn
    安装打包器——no-ri——no-rdoc
    - bundle install -j $(nproc)——path vendor
    - bundle exec rake db:setup RAILS_ENV=test . exe
  script:
    - bundle exec rake规格
    - bundle exexrubocop

让我们回顾一下现在发生的事情:

  • 在每次推送或合并请求时
    • Ruby和Node设置在一个容器中.
    • 安装依赖项.
    • The app is tested.
  • 在推送到master或推送标记的提交时, 前提是所有的测试都通过了
    • Heroku CLI在容器中安装和验证.
    • 你的代码被推给了Heroku.
    • 在Heroku中捕获数据库产品的备份.
    • 备份转储在您的暂存环境中.
    • 迁移在暂存数据库上运行.
  • 在推入标记为commit的语义版本时, 前提是所有的测试都通过了
    • Heroku CLI在容器中安装和验证.
    • 你的代码被推给了Heroku.
    • 在Heroku中捕获数据库产品的备份.
    • 迁移在生产数据库上运行.

退后一步,惊叹于你已经完成的自动化水平. 从现在开始,您所要做的就是编写代码并推送. 如果你喜欢,可以在阶段阶段手动测试你的应用, 当你有足够的信心把它推向世界, 用语义版本标记它!

自动语义版本控制

是啊,很完美,但还是少了点什么. 我不喜欢查找应用程序的最新版本并明确标记它. 这需要多个命令,让我分心几秒钟.

Okay, dude, stop! That’s enough. 你现在只是过度设计了. 它很有效,很聪明,不要因为做得过火而毁了一件好事.

好吧,我这么做是有充分理由的.

Pray, enlighten me.

我曾经和你一样. 我对这个安排很满意,但后来我搞砸了. git tag 按字母顺序列出标签, v0.0.11 is above v0.0.2. 我曾经不小心标记了一个版本,并在六个版本中继续这样做,直到我发现自己的错误. 那时我决定把这个也自动化.

Here we go again

好了,谢天谢地,我们有npm的强大功能,所以我找到了一个合适的包:Run Yarn添加——dev标准版本 并添加以下内容到您的 package.json file:

  "scripts": {
    “释放”:“标准版”,
    “major”:“纱线释放-释放-作为主要”,
    “minor”:“纱线放行——放行——作为minor”,
    "patch": "纱线释放-释放-作为补丁"
  },

现在您需要做最后一件事,将Git配置为默认推送标签. 现在,你需要逃跑 git push --tags 把标签往上推,但是会自动地定期这样做 git push 就像跑步一样简单吗 Git配置——全局推送.followTags true.

要使用你的新管道,当你想要创建一个发布运行时:

  • yarn patch for patch releases
  • yarn minor for minor releases
  • yarn major for major releases

如果你不确定“专业”这个词是什么,” “minor,” and “patch” mean, 更多信息请访问 语义版本控制站点.

现在您终于完成了您的管道,让我们回顾一下如何使用它!

  • Write code.
  • 提交并推送它进行测试,并将其部署到登台.
  • Use yarn patch 标记补丁发布.
  • git push 将其推向生产环境.

总结和进一步步骤

我只是触及了CI/CD管道的表面. 这是一个相当简单的例子. 通过将Heroku与Kubernetes交换,您可以做更多的事情. 如果您决定使用GitLab CI,请阅读 yaml docs 因为通过在部署之间缓存文件或保存工件,您可以做更多的事情!

您可以对该管道进行的另一个巨大更改是引入外部触发器来运行语义版本控制和发布. Currently, ChatOps 是他们付费计划的一部分,我希望他们把它发布到免费计划中. 但想象一下,通过一个Slack命令就能触发下一个图像!

从外部触发生产部署的CI/CD部署管道图, 可能通过聊天或网络钩子

Eventually, 当您的应用程序开始变得复杂并需要系统级依赖时, 您可能需要使用容器. 当这种情况发生时,请查看我们的指南: Docker入门:简化Devops .

这个示例应用程序确实是实时的,您可以找到它的源代码 here.

了解基本知识

  • 什么是开发管道?

    开发管道是您在开发过程中所经历的任务和流程链.

  • 什么是部署管道?

    部署管道是您在部署过程中要经历的任务和流程链.

  • CI/CD是什么意思?

    CI/CD是持续集成(将代码持续地集成到一个地方)的缩写。, 持续交付(构建可以持续交付的产品), 持续部署(将产品持续部署到生产环境中).

  • What is GitOps?

    GitOps是完全通过Git管理的DevOps.

就这一主题咨询作者或专家.
Schedule a call
阿明·沙·吉拉尼的头像
Amin Shah Gilani

Located in 拉合尔,旁遮普,巴基斯坦

Member since January 27, 2017

About the author

Amin是一名开发者和企业家,他喜欢简洁的文字, 为CI/CD编写的测试驱动Ruby和ES6代码.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Expertise

工作经验

8

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.