我想写这本指南有一段时间,因为DevOps是其中一项尚未被广泛讨论的事物,但是您可以将一些简单的操作集成到您的工作流程中,这将使您的开发人员的生活变得更加轻松。
我绝不是DevOps专家,我只是在分享实验的最后一年中遇到的问题。
您应该尝试以下几个原因:
- 您可以大大提高生产率。
- 设置完所有内容后,就不需要太多注意了。
- 每次您推送代码时,感觉都很棒。
在本系列中:我将讨论连续交付(CD),报告,错误日志记录和Github操作。 好消息是,您可以自己选择所需的内容,而忽略其余部分,因为各个部分大部分都是独立的。
我将使用Javascript编写项目示例以及我们将要提供的所有服务。 哦,我们还需要VPS来部署我们的服务器。 您可以使用自己的计算机。
我们将首先制作一个简单的CD服务器,该服务器每次提交母带时都会部署您的代码。 有两个部分:
- 设置CD服务器
- 配置推送Webhooks
设置CD服务器
注意 :此代码基于Nikita Kolmogorov的node-cd 。
我不会介绍设置服务器的基础知识。 如果对此有疑问,可以参考由您自己写的本指南 。
我们的目标是创建一个简单的服务器,该服务器在每次收到外部消息时运行Shell脚本。 该脚本将从Github下载最新的代码库,安装新的依赖项(如果有),然后通过pm2
重新启动应用程序。
首先,我们需要确保webhook请求是真实的。 事实是,知道我们希望接收Webhook的URL,任何人都可以发送请求,从而能够随意重启我们的应用程序。 我们只想将这种能力赋予GitHub。
解决该问题的一种方法是检查发件人的IP并将其与GitHub地址的已知列表进行匹配。 那应该可行,但这不是防弹解决方案,因为IP可能会随着时间而改变。
我们将使用一个更强大的解决方案:加密,特别是HMAC。 HMAC或基于哈希的消息身份验证代码是一种使用密钥对消息进行签名的方法。 从本质上讲,它将消息和秘密连接起来以哈希结果。 由于输入的微小变化将彻底改变哈希,因此产生“正确”哈希的唯一方法是知道密钥。
对于HMAC,我们需要生成一个密钥,该密钥将提供给GitHub。 GitHub将使用该密钥签署所有webhook请求。 在我们的服务器代码中,一旦收到请求,我们就自己计算哈希并将其与得到的哈希值进行比较。 如果两个哈希相同,则意味着发送方知道密钥,因此确实是GitHub发送了请求。
但是,HMAC不会加密消息。 因此,如果某人能够从GitHub截获该消息,那么他将能够看到您将提交推送到了存储库。 对于我们来说,这不是什么大问题,但是如果您打算将HMAC用于机密内容,则应格外小心。
好了,足够多的讨论,让我们编写一些代码。 我们将从处理HMAC的两个辅助函数开始。
const secret = process.env.GITHUB_SECRET;
function createComparisonSignature ( body ) {
const hmac = crypto.createHmac( 'sha1' , secret);
const bodyString = JSON .stringify(body);
const bodySignature = hmac.update(bodyString).digest( 'hex' );
return `sha1= ${bodySignature} ` ;
}
function compareSignatures ( signature, comparisonSignature ) {
const source = Buffer.from(signature);
const comparison = Buffer.from(comparisonSignature);
return crypto.timingSafeEqual(source, comparison);
}
函数createComparisonSignature
计算哈希值, compareSignatures
比较我们的哈希值和从请求中得到的值。 我们将需要导入crypto
,这是一个内置的Node.js模块,您可以猜到它处理密码。
另外,请注意const secret
部分。 您将需要创建一个.env
文件,并将GitHub密钥放在此处。
const crypto = require ( 'crypto' );
在我们的路由器代码中,我们将获取密钥,使用上述功能对其进行检查,然后基于该检查进行操作。
const signature = req.header( 'X-Hub-Signature' );
const comparisonSignature = createComparisonSignature(req.body);
if (!compareSignatures(signature, comparisonSignature)) {
console .log( 'Bad signature' );
res.status( 403 ).end();
return ;
}
如您所见,如果我们收到无效的密钥,我们只需发送403并丢弃请求。 如果哈希正确,我们继续…
现在,下一步是可选的,但它确实很简单,可能会使内容更具可读性。 我们要做的是用“内部”项目名称映射存储库名称。 最好在代码中看到它:
const projects = {
'project-abc-server' : 'abc' ,
'project-xyz-backend' : 'xyz' ,
};
const repository = req.body.repository.name;
const project = projects[repository];
现在,我们可以在代码中将我们的项目称为abc
和xyz
,稍后将方便使用。 此外,我们可以保留“已批准”项目的列表,并在我们不期望的情况下抛出400
状态代码:
if (!project) {
console .log( 'Project not found' );
res.status( 400 ).end();
return ;
}
最后,魔术部分:我们根据更新的项目执行Shell脚本。 我们将从一个可以运行任何脚本的辅助函数开始:
function execScript ( project, filePath ) {
if (!fs.existsSync(filePath)) {
return ;
}
const execCallback = ( error, stdout, stderr ) => {
if (error) {
console .log( `Failed to deploy ${project} ` );
return ;
}
if (stderr.length > 0 ) {
console .log( `Failed to deploy ${project} ` );
return ;
}
console .log( `Deployed ${project} ` );
}
childProcess.execFile(filePath, execCallback);
}
在这里,我们再次利用Node.js API(即fs
和child_process
分别检查文件是否存在并执行二进制文件。 我们将执行结果记录到控制台。
注意 :npm警告被视为错误并被写入stderr。 这意味着,如果您的项目缺少描述或存储库URL,即使您的脚本在技术上应有的执行,您仍将收到“无法部署”错误。
这是我们使用execScript
函数的方式:
const scriptPath = `./projects/ ${project} .sh` ;
console .log( `Executing task at: ${scriptPath} ` );
execScript(project, scriptPath);
res.status( 200 ).end();
至于脚本本身,通常可以归结为:
cd ~/app/directory/
git pull -q
npm install
pm2 restart app
就是这样! 将其包装到express.js样板中,您将获得最简单的CD服务器!
配置推送Webhooks
剩下要做的就是告诉GitHub我们创建的所有功能。
在项目的Settings -> Webhooks
,转到Settings -> Webhooks
,然后单击“ Add webhook
。 在这里,您将需要粘贴上一步中创建的服务器的URL以及密钥。 我还将Content-Type
设置为application/json
,但这取决于您。
点击Add Webhook
,GitHub将向您的服务器发送测试请求,因此您应该在应用程序的日志中看到它。 此外,GitHub还会向您显示CD服务器的响应状态代码,因此,如果您获得200,则表示一切正常。
包起来
在这里,我们首先设置了一个简单而功能强大的服务器来进行连续部署。 它对于简单的工作流程( npm install && pm2 restart app
)非常有效,但由于Shell脚本可以执行任意逻辑,因此它也可能包含复杂的流程。
然后,我们使用GitHub webhooks触发服务器上的部署,因此每次推送都会更新我们的应用程序。
From: https://hackernoon.com/devops-shouldnt-be-hard-cd-server-yy152kkm