持续集成持续部署持续交付

tech2022-09-06  110

持续集成持续部署持续交付

A lot has been written about the benefits of achieving true Continuous Integration (CI) into production systems. This tutorial will demonstrate a simple workflow that achieves CI. We’ll be using Feature Flags and Remote Config to avoid the need for feature branches in Git, as well as any sort of test or staging environments. The two main tools we’ll use to demonstrate this approach are Netlify and Bullet Train.

有关将真正的持续集成(CI)集成到生产系统中的好处的文章很多。 本教程将演示实现CI的简单工作流程。 我们将使用功能标志和远程配置,以避免在Git中以及其他任何测试或登台环境中都需要功能分支。 我们将用来演示这种方法的两个主要工具是Netlify和Bullet Train 。

CI和功能标志的含义 (What We Mean by CI and Feature Flags)

Continuous Integration is a method of development in which developers are able to constantly release their code. Developers push their new features as they finish development, at which point they are automatically tested and released immediately into a live environment.

持续集成是一种开发方法,开发人员可以在其中不断发布其代码。 开发人员在完成开发时会推送其新功能,这时他们将被自动测试并立即发布到实际环境中。

Feature flags provide a way of gating these new features from a remote control panel allowing you to turn them off and on at will without making any changes to your code. This allows you to develop a feature, and release it into production without actually changing anything from a user’s point of view.

功能标志提供了一种从远程控制面板中对这些新功能进行门控的方法,使您可以随意打开和关闭它们,而无需对代码进行任何更改。 这使您可以开发功能并将其发布到生产环境中,而无需从用户的角度实际更改任何内容。

为什么CI如此强大 (Why CI Is So Powerful)

Integrating code in smaller chunks and in a more frequent manner allows development teams to pick up issues a lot quicker, as well as getting new features to users as quickly as possible. CI also removes the need for any large releases where a team of engineers need to stay awake until the wee hours of the night to minimise disruption to their users.

以较小的块和更频繁的方式集成代码使开发团队可以更快地发现问题,并尽快为用户提供新功能。 CI还消除了任何大型发行版的需要,在大型发行版中,工程师团队需要保持清醒状态,直到晚上凌晨,以最大程度地减少对用户的干扰。

功能标记如何帮助持续集成 (How Feature Flags Aid Continuous Integration)

Feature flags provide an additional layer of confidence when releasing new features. By wrapping new features in a feature flag, the developers and product team are able to quickly enable or disable their features as required. Let’s say we introduce a new feature into production but we can immediately see that there’s a bug in the new code, because of something specific in the production environment that wasn’t evident in testing (let’s face it, it’s happened to everyone… more than once).

发布新功能时,功能标志可提供额外的信心。 通过将新功能包装在功能标志中,开发人员和产品团队可以根据需要快速启用或禁用其功能。 假设我们在生产中引入了一个新功能,但是我们可以立即看到新代码中存在一个错误,因为生产环境中的某些特定特性在测试中并未体现出来(让我们面对现实,这件事发生在每个人身上……一旦)。

Previously, this would have meant a (probably) lengthy and (definitely) painful rollback procedure and a rescheduling of the release for another ungodly hour on another day when the bug has been fixed. Instead, with a toggle of a switch the feature can be turned off for all or a subset of users and the pain is gone. Once the bug is identified and fixed, a patch release can be deployed, and the feature can be re-enabled.

以前,这将意味着(可能)冗长和(肯定)痛苦的回滚过程,并在修复该错误的另一天将发布重新计划为另一个不合时宜的小时。 取而代之的是,通过开关的切换,可以为所有或部分用户关闭该功能,而痛苦也就消失了。 一旦发现并修复了错误,便可以部署补丁程序发行版,并可以重新启用该功能。

我们的样本项目概述 (Outline of Our Sample Project)

To demonstrate integrating feature flags and remote config, we’re going to base our initial codebase on the de facto React JS tutorial of a tic-tac-toe game. This is a great tutorial for learning the basics of React, so be sure to check it out if you haven’t already.

为了演示集成功能标记和远程配置,我们将基于井字游戏的实际React JS教程建立初始代码库。 这是一个学习React基础知识的很棒的教程,因此如果您还没有学习过的话,请务必检查一下。

Don’t worry if you don’t know React or Javascript well. The concepts we’ll be going over are all about the process and tooling, and not about the code.

如果您不太了解React或Javascript,请不要担心。 我们将讨论的概念全都与流程和工具有关,而与代码无关。

Rather than repeat the tutorial from the start, we’re going to start at a point where we’ve got a basic tic-tac-toe game up and running.

而不是从头开始重复本教程,我们将从一个基本的井字游戏开始并运行的位置开始。

At this point we’ll use feature flags and remote configuration to continuously configure, push and deploy new features. To take this concept to an extreme we’ll continuously be pushing to master; no other branches will be used. We’ll introduce a bug (intentionally of course!) and push the fix to master, to demonstrate how dealing with this scenario doesn’t require a full rollback or additional branching.

至此,我们将使用功能标记和远程配置来连续配置,推送和部署新功能。 为了将这个概念发挥到极致,我们将继续努力掌握。 不会使用其他分支。 我们将引入一个错误(当然是有意为之!)并将修补程序推送到主版本,以演示如何处理这种情况不需要完全回滚或附加分支。

If you wish to follow along writing code during this tutorial, you can fork the repository here.

如果您希望在本教程中继续编写代码,可以在此处派生存储库 。

实现CI (Achieving CI)

The most common method of automating continuous integration is to have the build process trigger when you push changes to your git repository. Different build tools achieve this in different ways.

自动持续集成的最常见方法是在将更改推送到git存储库时触发构建过程。 不同的构建工具以不同的方式实现这一目标。

We’re going to use Netlify for our project. Netlify lets you connect a Git repository and automatically deploy builds every time you push to a branch.

我们将在项目中使用Netlify 。 Netlify使您可以连接Git存储库,并在每次推送到分支机构时自动部署构建。

To use Netlify, simply sign up for a free account and select the ‘New site from Git’ option in the top right of the dashboard. Once you’ve connected your GitHub account (or otherwise, if you want to use Bitbucket or GitLab) you should then be presented with the options shown below.

要使用Netlify,只需注册一个免费帐户,然后选择仪表板右上方的“来自Git的新站点”选项。 一旦您连接了GitHub帐户(或者,如果您想使用Bitbucket或GitLab),则应该显示以下选项。

In the next step, configure Netlify to run the application as follows.

在下一步中,配置Netlify以运行该应用程序,如下所示。

Netlify will now go ahead and build your application for you. It will take a few minutes, but once it’s done you should see something like the following:

Netlify现在将继续为您构建应用程序。 这将需要几分钟,但是一旦完成,您应该会看到类似以下的内容:

Browsing to that URL should show your Tic Tac Toe game in all its glory.

浏览到该URL应该显示您的Tic Tac Toe游戏的所有荣耀。

为我们的项目设置功能标志 (Setting Up Feature Flags for Our Project)

We’re going to use feature flags to control the declaration of a winner in the tic-tac-toe game. To create and manage our feature flags, we’re going to use Bullet Train as it’s currently free, but there are many other feature flag products. We’ll let you pick the one you feel is right.

我们将使用功能标记来控制井字游戏中获胜者的声明。 要创建和管理功能标志,我们将使用Bullet Train,因为它目前是免费的,但还有许多其他功能标志产品。 我们会让您选择自己认为正确的一种。

To continue with us, go ahead and create a free account on Bullet Train. Click on the ‘Create Project’ button and create your project. We named ours FF Tutorial.

要继续我们,请继续在Bullet Train上创建一个免费帐户。 单击“创建项目”按钮并创建您的项目。 我们将其命名为FF教程。

Next, we need to create a new feature for declaring a winner. Click on the ‘Create your first Feature’ button at the bottom of the next screen to bring up the following form and add in the details.

接下来,我们需要创建一个新功能来宣布获胜者。 单击下一个屏幕底部的“创建您的第一个功能”按钮,以显示以下表格并添加详细信息。

Note that we’ve kept the feature disabled to begin with.

请注意,我们从一开始就禁用该功能。

Take note of the two code snippets available beneath the feature, which will help us in the next step.

请注意该功能下方提供的两个代码段,这些代码段将在下一步提供帮助。

实施功能标志 (Implement the Feature Flag)

Firstly, let’s get this project running in our development environment. From the command line, navigate to the project folder and run the command npm i to install the necessary dependencies.

首先,让我们在开发环境中运行该项目。 在命令行中,导航到项目文件夹,然后运行命令npm i以安装必要的依赖项。

Next run npm run dev and head to http://localhost:8080 in your browser. You should see the same tic-tac-toe game that you saw on the Netlify URL.

接下来运行npm run dev并在浏览器中转到http://localhost:8080 。 您应该看到与在Netlify URL上看到的井字游戏。

We are now going to implement our new feature flag into the existing code. Let’s start by installing the Bullet Train client for JavaScript, by opening up another command line and running the following in the project directory:

现在,我们将在现有代码中实现我们的新功能标记。 首先,安装用于JavaScript的Bullet Train客户端,打开另一个命令行,然后在项目目录中运行以下命令:

npm i bullet-train-client --save

npm i bullet-train-client --save

Open up the project in your preferred editor and edit ./web/App.js.

在首选编辑器中打开项目,然后编辑./web/App.js 。

Find the calculateWinner(squares) function. This function determines whether there has been a winner or not, based on whether it can find a line of the same shape or not. We are going to have this function return null based on the value of our feature flag so that we can test filling up the board without it declaring a winner.

找到calculateWinner(squares)函数。 此功能根据是否可以找到相同形状的线来确定是否有赢家。 我们将使该函数根据功能标志的值返回null ,以便我们可以在不声明获胜者的情况下测试是否填满了面板。

At the very top of App.js, add the following line:

在App.js的最顶部,添加以下行:

var declareWinner = true;

Now, let’s initialise our Bullet Train client that we installed earlier. Copy all of code example 2 from the Features page in the Bullet Train interface and paste it just beneath the line you just added.

现在,让我们初始化我们之前安装的Bullet Train客户端。 从Bullet Train界面的“功能”页面复制所有代码示例2,然后将其粘贴到刚添加的行下方。

The environment ID in your code snippet will be the correct environment ID associated with the Development environment in your Bullet Train project. You can check this by browsing to the ‘Environment Settings’ page if you want.

您的代码段中的环境ID将是与Bullet Train项目中的开发环境相关联的正确环境ID。 您可以根据需要通过浏览到“环境设置”页面进行检查。

We now need to edit the onChange() function in the bulletTrain.init() function in the code we just pasted in to suit our needs. Replace all of the code in there with one line:

现在,我们需要根据刚刚粘贴的代码在bulletTrain.init()函数中编辑onChange()函数。 用一行替换那里的所有代码:

declareWinner = bulletTrain.hasFeature("declare-winner");

You should now have this at the top of your App.js

现在,您应该在App.js的顶部

var declareWinner = true; import bulletTrain from "bullet-train-client"; //Add this line if you're using bulletTrain via npm bulletTrain.init({ environmentID:"<your-environment-id>", onChange: (oldFlags,params)=>{ //Occurs whenever flags are changed declareWinner = bulletTrain.hasFeature("declare-winner"); } });

Scroll down to the calculateWinner(squares) function and at the top, just above the declaration of the lines constant, let’s add the line:

向下滚动到calculateWinner(squares)函数,然后在顶部的lines常量声明上方,添加以下行:

if (!declareWinner) return null;

And that’s it! Our feature flag will now determine whether or not the winner is calculated or not on every render of the game. Refresh your browser and play the game. You can no longer win and instead the entire board can now be filled with Xs and Os.

就是这样! 现在,我们的功能标志将确定在游戏的每次渲染中是否计算赢家。 刷新浏览器并玩游戏。 您不能再获胜,而是现在可以用X和O填满整个董事会。

Go back to the Bullet Train admin and toggle the feature using the switch on the right.

返回Bullet Train管理员并使用右侧的开关切换功能。

Refresh your browser and the game becomes winnable again. Check out the code for the end of this part here.

刷新您的浏览器,游戏再次变得可玩。 在此处查看本部分末尾的代码。

Commit and push your code (yes, all on master) and Netlify will automatically deploy your code. Browse to your assigned Netlify URL again and toggle the feature flag to see it working in a production environment. Sweet!

提交并推送您的代码(是的,全部在master上),Netlify将自动部署您的代码。 再次浏览到您分配的Netlify URL,并切换功能标志以查看其在生产环境中的运行。 甜!

处理错误 (Work on a Bug)

We are now going to purposefully introduce a bug into the tic-tac-toe game and show how feature flags can be used to drop a feature that is causing issues.

现在,我们将有目的地在井字游戏中引入一个错误,并说明如何使用功能标志删除引起问题的功能。

The feature we’re going to add is selection of who goes first at the start of the game. For that we will add a couple of buttons that only appear at the beginning of the game and prevent clicks on the board from adding a shape.

我们要添加的功能是在游戏开始时选择谁先走。 为此,我们将添加几个仅在游戏开始时出现的按钮,以防止在板上单击来添加形状。

Firstly, let’s set up our feature flag to wrap the new feature. In your Bullet Train project, create a new feature called select-who-goes-first as follows. Let’s leave it disabled to begin with.

首先,让我们设置功能标志以包装新功能。 在Bullet Train项目中,创建一个名为select-who-goes-first的新功能,如下所示。 让我们先禁用它。

Now, let’s add our new feature. In the render() function we’re going to render the buttons, instead of the status, if the game hasn’t started yet. At the top of the return of the render() function, replace the line:

现在,让我们添加新功能。 在render()函数中,如果游戏尚未开始,我们将渲染按钮而不是状态。 在render()函数返回的顶部,替换以下行:

<div className="status">{status}</div>

… with the following code:

…具有以下代码:

{!this.state.selected ? ( <div> Who goes first? <button onClick={() => this.setState({selected: true})}>X</button> <button onClick={() => this.setState({selected: true, xIsNext: false})}>O</button> </div> ) : ( <div className="status">{status}</div> )}

Now we want to write the code to control our new feature with the feature flag we created. As before, this needs to go in the bulletTrain.init({...}) function.

现在,我们要编写代码以使用我们创建的功能标志来控制我们的新功能。 和以前一样,这需要进入bulletTrain.init({...})函数。

First, let’s add the lifecycle function componentDidMount() to the Board component and then move the entire bulletTrain.init({...}) function inside of it, so that we can update the state of the component after the flag is retrieved:

首先,让我们将生命周期函数componentDidMount()添加到Board组件,然后将整个bulletTrain.init({...})函数bulletTrain.init({...})其中,以便在检索到标志后可以更新组件的状态:

class Board extends React.Component { componentDidMount() { bulletTrain.init({ environmentID:"<your-environment-id>", onChange: (oldFlags,params)=>{ //Occurs whenever flags are changed declareWinner = bulletTrain.hasFeature("declare-winner"); } }); } // [rest of class] }

If we left bulletTrain.init({...}) outside of the component, we would not be able to call this.setState() and have the component re-render itself from changes to our flags.

如果我们将bulletTrain.init({...})留在组件之外,我们将无法调用this.setState()并使组件通过更改标志来重新呈现自身。

Now let’s add the code to control our new feature. We want the application to behave as it did before we added this feature if the flag is disabled. To do that, let’s set the state value for selected to true if the flag is disabled. Add the following line in the bulletTrain.init({...}) method right beneath the declareWinner line.

现在,让我们添加代码来控制我们的新功能。 如果禁用了该标志,我们希望应用程序像添加此功能之前一样。 为此,如果禁用了该标志,我们将selected的状态值设置为true 。 bulletTrain.init({...})添加到bulletTrain.init({...})方法中的declareWinner行的正下方。

this.setState({selected: !bulletTrain.hasFeature("select-who-goes-first")});

Let’s go ahead and push that (again, straight into master). Once it’s built, head to your Netlify URL. You should see that nothing has changed — this is because the feature is still disabled in our Bullet Train project.

让我们继续并推动它(再次,直接成为主人)。 构建完成后,转到您的Netlify URL。 您应该看到什么都没有改变-这是因为在Bullet Train项目中该功能仍处于禁用状态。

Head over to Bullet Train and enable the feature.

前往Bullet Train并启用该功能。

Brilliant! Now we can see it working, but oh, there’s a bug! It’s possible to start the game without selecting who goes first. If you play the game like this, you can see that the status is never set to show the winner. That’s not right!

辉煌! 现在我们可以看到它正在工作,但是,哦,有个错误! 可以在不选择谁先开始的情况下开始游戏。 如果您像这样玩游戏,您会发现状态从未设置为显示获胜者。 那是不对的!

Head back over to Bullet Train and disable the feature until we can work out what’s wrong. This is where Bullet Train’s additional features, such as environments and users, come in handy. We won’t go into either of these in this tutorial, but check out the docs for more on using multiple environments or controlling features on a per-user basis.

回到Bullet Train并禁用该功能,直到我们找出问题所在为止。 这是Bullet Train的其他功能(例如环境和用户)派上用场的地方。 在本教程中,我们将不介绍这两种方法,但请查看文档以获取更多有关使用多个环境或基于每个用户控制功能的信息。

Back to our bug: there is one more line that we needed to add to the very top of handleClick() to prevent players from starting until the first player has been selected. Add the following at the top of the handleClick() function.

回到我们的错误:我们需要在handleClick()的最上方添加一行,以防止玩家在选择第一个玩家之前开始游戏。 将以下内容添加到handleClick()函数的顶部。

if (!this.state.selected) return;

Turn the feature back on again in Bullet Train and test that in your local environment and you should see that we now prevent the game starting without selecting who goes first. Excellent! Commit that into master again and push it straight to deploy.

在Bullet Train中再次打开该功能并在您的本地环境中进行测试,您应该看到我们现在阻止了游戏的开始,而无需选择先谁。 优秀的! 再次将其提交给主服务器,然后直接将其部署。

Head on over to your Netlify URL and you should see the new feature fixed and working properly.

转到您的Netlify URL,您应该看到新功能已修复并且可以正常使用。

You can see the final code at the end of this section here.

您可以在本节结束时看到最后的代码在这里 。

远程配置 (Remote Config)

We are now going to take a look at a slight variation on feature flags, called remote config. Remote config allows you to control more than just whether a feature is on or off — it allows you to change a given value remotely without changing your code. This comes in handy in a lot of different places, for example, if you want to be able to configure some aspect of the styling.

现在,我们来看看功能标记的细微变化,称为远程配置。 远程配置使您不仅可以控制某个功能是打开还是关闭,还可以控制更多-它允许您在不更改代码的情况下远程更改给定值。 例如,如果您希望能够配置样式的某些方面,这将在许多不同的地方派上用场。

In our tutorial, we’ll be using this to change the shapes used by each player in our game. Let’s create two remote config values for what shapes to use on the board. In your Bullet Train project, click on the ‘Create Feature’ button but this time, select the ‘Remote Config’ tab instead of ‘Feature Flag’. Fill in the data as per the below.

在我们的教程中,我们将使用它来更改游戏中每个玩家使用的形状。 让我们为要在板上使用的形状创建两个远程配置值。 在Bullet Train项目中,单击“创建功能”按钮,但是这次选择“远程配置”选项卡,而不是“功能标记”。 根据以下内容填写数据。

All done! Now back to the code. In our Bullet Train client’s onChange() function we need to retrieve these values and set them to the component’s state. Let’s change our this.setState() call to:

全做完了! 现在回到代码。 在Bullet Train客户的onChange()函数中,我们需要检索这些值并将它们设置为组件的状态。 让我们将this.setState()调用更改为:

this.setState({ selected: !bulletTrain.hasFeature("select-who-goes-first"), shape1: bulletTrain.getValue("shape-1") || 'X', shape2: bulletTrain.getValue("shape-2") || 'O' });

We now have the two shapes and can replace all static uses throughout App.js of ‘X’ and ‘O’ with the state values instead. There should be 3 instances of each: 1 per shape in handleClick() and 2 per shape in render() (one is in the call to return). Here’s the updated code for the reference in handleClick():

现在,我们有了这两种形状,可以用状态值代替整个App.js和O的App.js中的所有静态使用。 每个实例应有3个实例: handleClick()每个形状1个实例和render()每个形状2个实例(其中一个在return调用中)。 这是handleClick()参考的更新代码:

handleClick(i) { // ... squares[i] = this.state.xIsNext ? this.state.shape1 : this.state.shape2; // ... }

Note that for the instances in the return call in render() you’ll need to wrap the JavaScript in curly braces like this:

请注意,对于render() return调用中的实例,您需要将JavaScript用大括号括起来,如下所示:

<button onClick={() => this.setState({selected: true})}>{this.state.shape1}</button>

Commit this into master and push to your Git repository to see the changes on your Netlify URL. If you’ve done it correctly, the game should play as before with ‘X’ and ‘O’ shapes. Go ahead and change the shapes in the admin to different letters and refresh the page. If all is well you will now be playing with different shapes.

将此提交到master并推送到您的Git存储库以查看Netlify URL上的更改。 如果操作正确,则游戏应像以前一样使用“ X”和“ O”形状进行游戏。 继续,将管理员中的形状更改为不同的字母并刷新页面。 如果一切顺利,您现在将使用不同的形状。

There is a lot more you can achieve with remote config, such as controlling the styling of the entire game or say the dimensions of the board. We added even more remote config values including shape color and square size. You can see the finished code for the game here.

使用远程配置可以实现更多功能,例如控制整个游戏的样式或说板子的尺寸。 我们添加了更多的远程配置值,包括形状颜色和正方形大小。 您可以在此处查看游戏的完成代码。

其他需要考虑的事情 (Other Things to Think About)

Feature flags shouldn’t be considered to be a golden bullet, however, and they do come with certain caveats.

但是,功能标志不应被视为金弹,并且确实带有一些警告。

For example, if you’re using a framework that handles the database schema for you, such as Django or Rails, you’ll need to be careful when implementing feature flags in your models. Using feature flags in your models could result in DB schema mismatches, which may bring parts of your application down.

例如,如果您使用的是为您处理数据库架构的框架,例如Django或Rails,则在模型中实现功能标记时需要格外小心。 在模型中使用功能标志可能会导致数据库架构不匹配,从而使应用程序的某些部分崩溃。

Similarly, if you’re interacting with a third party API that has certain requirements for its consumers, using feature flags to control aspects of this could cause unexpected errors in your application. Also, vice versa, if your application provides an API for others to consume, using feature flags to control the data models of the API is not advised as it may cause problems down the line for those consumers.

同样,如果您正在与对其使用者有某些要求的第三方API进行交互,则使用功能标志控制此方面可能会在应用程序中导致意外错误。 同样,反之亦然,如果您的应用程序提供了供他人使用的API,则不建议使用功能标记来控制API的数据模型,因为这可能会给这些消费者带来问题。

Finally, we used Bullet Train in the tutorial above as it’s free to use at the moment and also open source. There are a number of other products out there doing the same thing or with slightly different variants — you should check all of these out to determine what’s best for you. For example, Launch Darkly and Airship HQ.

最后,我们在上面的教程中使用了Bullet Train ,因为它目前是免费的,并且是开源的。 还有许多其他产品在做相同的事情或具有稍微不同的变体-您应该检查所有这些以确定最适合您的产品。 例如, “黑暗发射”和“ 飞艇总部” 。

翻译自: https://www.sitepoint.com/how-to-use-feature-flags-in-continuous-integration/

持续集成持续部署持续交付

最新回复(0)