在 AWS 中运行容器及应用监控实践
1. 创建 CI/CD 管道
首先,我们要使用一个脚本,其链接为:http://bit.ly/2w3oVHw 。接下来开始创建管道,步骤如下:
1.
启动并配置 CloudFormation 堆栈
- 执行以下命令:
$ git add helloworld-codepipeline-cf-template.py
$ git commit -m "Adding Pipeline to deploy our helloworld application using ECS"
$ git push
$ python helloworld-codepipeline-cf-template.py > helloworld-codepipeline-cf.template
$ aws cloudformation create-stack \
--stack-name helloworld-codepipeline \
--capabilities CAPABILITY_NAMED_IAM \
--template-body file://helloworld-codepipeline-cf.template
这里使用
CAPABILITY_NAMED_IAM
功能,是因为我们在 IAM 级别定义了自定义名称。
-
配置 GitHub 凭证
由于我们不想在 GitHub 中以明文形式存储 GitHub 凭证,所以首次需要手动编辑管道:- 在浏览器中打开 https://console.aws.amazon.com/codepipeline 。
- 选择新创建的管道。
- 点击顶部的“Edit”。
- 点击 GitHub 操作上的笔图标。
- 在右侧菜单中点击“Connect to GitHub”,并按照步骤授权 AWS CodePipeline。
-
在仓库步骤中选择
helloworld
项目和master
分支。 - 点击“Update”,保存管道更改,最后点击“Save and Continue”。
完成上述操作后,几秒钟内管道将触发,首次部署将开始进行,至此 CI/CD 管道创建完成。
2. 整体资源创建回顾
在之前的操作中,我们运用 DevOps 最佳实践,使用 Docker 和 ECS 容器技术。在了解 Docker 基本工作原理后,为应用创建了容器,本地运行后,在 AWS 上创建了新资源来运行 Docker 容器。具体资源包括:
- 一个 ECR 仓库,用于管理容器的不同版本。
- 两个具有自动扩展功能的 ECS 集群,分别用于暂存和生产环境。
- 两个 ALB,用于将流量代理到容器。
- 一组任务和一个 ECS 服务,用于配置和部署应用。
- 最后,使用 CodeBuild、CodePipeline 及其与 CloudFormation 的集成重新实现了 CI/CD 管道。
3. 监控与告警的重要性
在之前构建了先进的基础设施并遵循 DevOps 原则实施了许多工程最佳实践,但还未涉及“衡量一切”这一关键原则。“衡量一切”的核心概念是收集可操作的反馈,创建反馈循环以评估变更的影响。这一理念并非 DevOps 独有,大多数知名公司都会依赖类似系统来动态引导团队朝着正确方向发展,因为在做决策和保持竞争力时,直觉和感觉已不够用。
将这一概念应用于基础设施和服务,可实现监控和告警解决方案,这对于任何生产环境都是必不可少的。接下来,我们将对应用和基础设施进行更改,以更好地监控系统,并在一些关键指标上实现告警功能,提高应用的可用性。
4. AWS CloudWatch 简介
CloudWatch 集中了监控解决方案的大部分基本功能,之前创建自动扩展组时曾使用过其部分功能,实际上它的功能远不止于此。在基础设施领域,数据主要有两种类型:指标(Metrics)和日志(Logs),此外还有第三种类型的数据——事件(Events)。可以通过 Web 控制台、命令行界面、API 和各种 SDK 访问 CloudWatch。
4.1 指标(Metrics)
指标常用于监控可量化的事物,如系统指标(CPU 利用率、空闲内存、网络消耗)、页面视图或 HTTP 状态(应用中的当前错误率)。在 CloudWatch 中,指标被定义为元组,包含以下内容:
| 元素 | 说明 |
| ---- | ---- |
| 资源 ID | 标识具体的资源 |
| 服务名称 | 所属的 AWS 服务 |
| 指标名称 | 具体的指标标识 |
| 指标值 | 指标的具体数值 |
| 时间戳 | 记录指标的时间 |
例如,指标
i-e434d47f | EC2 | CPUUtilization | 13.4 | 2017-08-14T12:00:00.000Z
表示在 2017 年 8 月 14 日 12:00:00.000Z 时,EC2 实例 ID 为
i-e434d47f
的 CPU 利用率为 13.4%。大多数 AWS 服务会与 CloudWatch 原生集成,可通过访问 https://console.aws.amazon.com/cloudwatch ,使用左侧的指标菜单或“Metrics Summary”页面上的“Browse Metrics”按钮浏览不同服务生成的指标。
以下是查看 S3 存储桶数据量指标的步骤:
1. 从 CloudWatch 仪表板点击“Browse Metrics”。
2. 从“Namespaces”部分选择 S3 服务。
3. 选择“Storage Metrics”。
4. 找到用于存储工件的存储桶,并选择“BucketSizeBytes”指标。
4.2 日志(Logs)
日志文件是监控系统最常用的方式之一,它是指标的很好补充,提供了更多灵活性。因为不像指标那样局限于键值对系统,日志文件可以提供应用中事件的详细信息。例如,通过指标系统发现应用错误率增加,但要了解具体情况,需要查看应用日志,看是否有异常、堆栈跟踪或错误消息来帮助排查问题。不过,日志文件比指标大得多,存储成本更高,索引、搜索和聚合也更困难。
CloudWatch 日志围绕以下几个关键概念组织:
- 每个日志称为一个日志事件,包含原始消息和时间戳。
- 由唯一源产生的日志事件被分组到一个日志流中。
- 日志流将日志事件发送到日志组,每个日志组有自己的数据保留策略(如保留日志事件的天数、谁可以访问这些日志等)。
以下是检索 CodeBuild 执行日志事件的步骤:
1. 在浏览器中打开 https://console.aws.amazon.com/cloudwatch 。
2. 点击左侧菜单中的“Logs”。
3. 从显示的不同日志组中,选择一个
/aws/codebuild/
组以访问日志流。
4. 打开一个日志流以访问 CodeBuild 产生的日志。
4.3 事件(Events)
CloudWatch 事件是 AWS 特有的概念,可以看作是日志和指标的混合体。事件和指标一样有标识符和上下文,同时还可以携带自定义信息的有效负载。AWS 在其基础设施和服务中广泛使用事件,每当环境中的资源发生变化时,AWS 会创建一个事件并发送到 CloudWatch 事件服务可订阅的流中。可以创建规则来匹配感兴趣的事件,并将信息发送到 SQS 或 SNS 等服务,或使用预编程功能或 Lambda 直接执行代码。
5. 使用 CloudWatch 监控“HelloWorld”应用
了解了 CloudWatch 的不同监控功能后,我们将对“HelloWorld”应用进行更改,以充分利用 CloudWatch 的优势。首先从生成更好的日志开始,然后添加指标和事件,最后对基础设施及其权限进行更改以开始收集这些数据。
5.1 添加日志到应用
最初创建应用时,最后一行添加了
console.log('Server running')
来表明应用正在运行,但这显然不够。为了改进,我们将创建一个新的日志记录器。
创建自定义日志记录器
为了使日志更有用,需要将其置于上下文中。在快速变化的环境中,希望在消息中提供额外信息,包括日志类型(信息、警告、严重等)、应用版本以及错误产生的准确时间戳。如果所有日志都汇总在一处,还可以包括产生日志的服务和服务器名称。
我们将使用
winston
库来改进日志记录,它是 JavaScript 中最常用的日志记录库之一。以下是具体步骤:
1. 进入
helloworld
应用的根目录:
$ cd helloworld
-
安装
winston
库:
$ npm install winston --save
--save
选项会使
npm
将包定义包含在
package.json
文件中。
-
打开
helloworld.js
文件,在http
变量初始化后,初始化winston
库:
var http = require("http")
var winston = require("winston")
- 创建一个新变量来指定代码版本,假设该信息稍后通过环境变量提供,目前简单导入值:
var version = process.env.HELLOWORLD_VERSION
- 创建自定义日志记录器,指定需要时间戳并定义时间戳的代码:
var logger = new winston.Logger({
transports: [new winston.transports.Console({
timestamp: function() {
var d = new Date()
return d.toISOString()
},
})]
})
-
使用
rewriter
功能为所有日志添加剩余上下文:
logger.rewriters.push(function(level, msg, meta) {
meta.version = version
return meta
})
-
将原来的
console.log("Server running")
替换为logger.info("Server running")
。
现在可以本地运行应用进行测试:
$ node helloworld.js
应用启动后,产生的日志如下:
2017-09-01T01:59:06.095Z - info: Server running version=undefined
helloworld.js
文件应与 http://bit.ly/2uHZ0p3 中的示例一致。
5.2 处理 EC2 上的日志收集
在 EC2 上,目前控制台日志未被捕获,需要对
upstart
脚本进行更改以保存控制台日志并设置
HELLOWORLD_VERSION
环境变量。
修改
upstart
脚本
打开
scripts/helloworld.conf
文件,编辑脚本部分如下:
script
set -a
. /usr/local/helloworld/version
mkdir -p /var/log/helloworld/
chown ec2-user:ec2-user /var/log/helloworld/
exec su --session-command="/usr/bin/node /usr/local/helloworld/helloworld.js >> /var/log/helloworld/helloworld.log 2>&1" ec2-user
end script
第一组更改允许定义
HELLOWORLD_VERSION
环境变量,通过添加
set -a
强制导出变量并引入
/usr/local/helloworld/version
文件(后续创建)。第二组更改将控制台输出记录到文件系统,创建
/var/log
目录并修改启动应用的命令以将标准输出和错误输出保存到新目录。
日志轮转配置
添加新日志时,需要考虑日志轮转,我们使用
logrotate
来实现。在
helloworld
目录的根目录下创建新文件夹
conf
和新文件
logrotate
:
$ mkdir conf
$ touch conf/logrotate
编辑
logrotate
文件,添加以下配置:
/var/log/helloworld/*.log {
rotate 3
size=100M
copytruncate
nocompress
}
可以根据需要调整配置。
生成版本文件
目标是每次使用 CodeDeploy 发布新代码时生成新的版本文件。利用 CodeDeploy 运行时设置的环境变量来生成版本。在
script
目录下创建新脚本
setversion.sh
并设置可执行权限:
$ touch scripts/setversion.sh
$ chmod +x scripts/setversion.sh
打开文件并添加以下内容:
#!/bin/sh
echo "HELLOWORLD_VERSION=${APPLICATION_NAME}-${DEPLOYMENT_GROUP_NAME}-${DEPLOYMENT_GROUP_ID}-${DEPLOYMENT_ID}" > /usr/local/helloworld/version
修改 CodeDeploy 配置
打开
appspec.yml
文件,在
ValidateService
钩子之后添加新钩子以触发
setversion.sh
脚本:
AfterInstall:
- location: scripts/setversion.sh
timeout: 180
在
files
部分添加以下内容以处理
logrotate
配置文件:
- source: conf/logrotate
destination: /etc/logrotate.d/helloworld
同时,为处理额外的依赖项,在部署 logrotate 配置后添加以下内容:
- source: node_modules
destination: /usr/local/helloworld/node_modules
新的
appspec.yml
文件应与 http://bit.ly/2uI4BvE 中的示例一致。
最后,提交所有更改:
$ git add helloworld.js node_modules package.json conf scripts/setversion.sh appspec.yml scripts/helloworld.conf
$ git commit -m "Adding logging to helloworld"
$ git push
通过管道,这些更改将部署到 EC2 实例,很快就能在主机上看到日志填充。接下来,我们将为应用添加指标和事件。
在 AWS 中运行容器及应用监控实践
5.3 添加指标和事件到应用
5.3.1 添加指标
在应用中添加指标可以帮助我们更好地了解应用的运行状态。我们可以使用 AWS SDK for JavaScript 来向 CloudWatch 发送自定义指标。
首先,确保已经安装了 AWS SDK for JavaScript:
$ npm install aws-sdk --save
然后,在
helloworld.js
文件中添加以下代码来发送自定义指标:
var AWS = require('aws-sdk');
var cloudwatch = new AWS.CloudWatch();
// 定义一个函数来发送指标
function sendMetric() {
var params = {
MetricData: [
{
MetricName: 'HelloWorldRequests',
Dimensions: [
{
Name: 'Service',
Value: 'HelloWorldService'
}
],
Unit: 'Count',
Value: 1
}
],
Namespace: 'HelloWorldNamespace'
};
cloudwatch.putMetricData(params, function (err, data) {
if (err) {
console.log('Error sending metric:', err);
} else {
console.log('Metric sent successfully:', data);
}
});
}
// 在应用启动时发送一个指标
sendMetric();
上述代码定义了一个名为
HelloWorldRequests
的指标,用于记录应用的请求次数。每次应用启动时,会向 CloudWatch 发送一个值为 1 的指标。
5.3.2 添加事件
添加事件可以帮助我们捕获应用中的特定行为或状态变化。我们可以使用 AWS SDK for JavaScript 来向 CloudWatch Events 发送自定义事件。
在
helloworld.js
文件中添加以下代码来发送自定义事件:
var AWS = require('aws-sdk');
var cloudwatchevents = new AWS.CloudWatchEvents();
// 定义一个函数来发送事件
function sendEvent() {
var params = {
Entries: [
{
Detail: JSON.stringify({
message: 'HelloWorld application started'
}),
DetailType: 'HelloWorldEvent',
Resources: [],
Source: 'HelloWorldSource'
}
]
};
cloudwatchevents.putEvents(params, function (err, data) {
if (err) {
console.log('Error sending event:', err);
} else {
console.log('Event sent successfully:', data);
}
});
}
// 在应用启动时发送一个事件
sendEvent();
上述代码定义了一个名为
HelloWorldEvent
的事件,用于记录应用的启动行为。每次应用启动时,会向 CloudWatch Events 发送一个包含特定消息的事件。
6. 创建告警
在监控系统中,告警是非常重要的一部分。通过设置告警,我们可以在关键指标超过阈值时及时收到通知,从而采取相应的措施。
6.1 使用 CloudWatch 创建告警
我们可以使用 CloudWatch 为关键指标创建告警。以下是创建一个针对 CPU 利用率的告警的步骤:
- 打开 CloudWatch 控制台:https://console.aws.amazon.com/cloudwatch 。
- 在左侧导航栏中,点击“Alarms”。
- 点击“Create alarm”。
-
在“Specify metric and conditions”部分:
- 选择要监控的指标,例如 EC2 实例的 CPU 利用率。
- 设置告警的阈值,例如当 CPU 利用率超过 80% 时触发告警。
- 选择评估周期和数据点数,例如每 5 分钟评估一次,连续 2 个数据点超过阈值触发告警。
-
在“Actions”部分:
- 选择当告警状态发生变化时要执行的操作,例如发送通知到 SNS 主题。
-
在“Name and description”部分:
- 为告警命名并添加描述。
- 点击“Create alarm”完成告警创建。
6.2 使用 SNS 发送通知
SNS(Simple Notification Service)是 AWS 提供的一种消息通知服务。我们可以将 CloudWatch 告警与 SNS 集成,当告警触发时,通过 SNS 发送通知。
以下是创建 SNS 主题并订阅通知的步骤:
- 打开 SNS 控制台:https://console.aws.amazon.com/sns 。
- 在左侧导航栏中,点击“Topics”。
- 点击“Create topic”。
- 选择主题类型(例如标准主题),输入主题名称并点击“Create topic”。
- 在主题页面,点击“Create subscription”。
- 选择协议(例如电子邮件、短信等),输入订阅者的信息(例如电子邮件地址)并点击“Create subscription”。
- 回到 CloudWatch 告警设置页面,在“Actions”部分选择刚刚创建的 SNS 主题。
这样,当 CloudWatch 告警触发时,SNS 会向订阅者发送通知。
7. 总结
通过以上步骤,我们完成了在 AWS 中运行容器、创建 CI/CD 管道、监控应用和设置告警的整个流程。具体操作如下:
1.
CI/CD 管道创建
:使用 CloudFormation 模板创建管道,并配置 GitHub 凭证,实现应用的自动化部署。
2.
资源创建
:运用 DevOps 最佳实践,在 AWS 上创建了一系列资源,包括 ECR 仓库、ECS 集群、ALB 等,用于运行和管理 Docker 容器。
3.
应用监控
:使用 CloudWatch 对应用进行监控,包括添加日志、指标和事件,通过配置
winston
库、AWS SDK 等工具,实现对应用运行状态的全面了解。
4.
告警设置
:使用 CloudWatch 创建告警,并与 SNS 集成,当关键指标超过阈值时及时通知相关人员。
整个流程的流程图如下:
graph LR
A[创建 CI/CD 管道] --> B[创建资源]
B --> C[应用监控]
C --> D[添加日志]
C --> E[添加指标]
C --> F[添加事件]
C --> G[处理日志收集]
C --> H[日志轮转配置]
C --> I[生成版本文件]
C --> J[修改 CodeDeploy 配置]
C --> K[提交更改]
D --> L[创建自定义日志记录器]
E --> M[安装 AWS SDK]
E --> N[发送自定义指标]
F --> O[安装 AWS SDK]
F --> P[发送自定义事件]
G --> Q[修改 upstart 脚本]
H --> R[创建 logrotate 文件]
I --> S[创建 setversion.sh 脚本]
J --> T[修改 appspec.yml 文件]
K --> U[部署到 EC2 实例]
C --> V[设置告警]
V --> W[创建 CloudWatch 告警]
V --> X[创建 SNS 主题]
V --> Y[订阅 SNS 通知]
W --> Z[配置告警操作]
Z --> AA[选择 SNS 主题]
通过实施这些步骤,我们可以确保应用在 AWS 上的稳定运行,并及时发现和处理潜在的问题,提高应用的可用性和可靠性。