无法从私有子网上 EC2 实例上的容器访问 AWS CloudWatch
Unable to access AWS CloudWatch from containers on EC2 instance on private subnet
我有一个包含三个 EC2 实例的 AWS VPC 设置,其中一个可以访问 public 互联网,而另外两个则不能。应用程序服务(在 node.js 中开发)在所有三个实例中都是 docker 容器化和 运行。我正在将他们的日志从文件移动到 AWS CloudWatch。该应用程序使用 Winston 记录器,我添加了 winston-cloudwatch 来为 CloudWatch 提供传输。
export const logger = new winston.createLogger({
transports: [],
handleExceptions: false,
exitOnError: false
});
if (process.env.NODE_ENV === 'production') {
const cloudWatchConfig = {
logGroupName: process.env.CLOUDWATCH_GROUP_NAME,
logStreamName: process.env.SERVICE,
awsRegion: process.env.CLOUDWATCH_REGION,
messageFormatter: ({ level, message }) => `[${process.env.SERVICE}:${level}] : ${message}`
}
logger.add(new WinstonCloudWatch(cloudWatchConfig))
}
具有 public 互联网访问权限的 EC2 实例上的容器 运行ning 正在将日志发送到 CloudWatch;那里没有问题。但是,私有子网上实例上的那些 运行ning 不能。第一个问题是从该 EC2 实例访问 CloudWatch 服务器,为此我在私有子网上为服务 com.amazonaws.ca-central-1.logs 设置了一个 VPC 接口端点。之后我可以从实例访问 CloudWatch,即我可以使用命令行指令访问 CloudWatch,例如:
$ aws logs describe-log-groups --log-group-name my-app-logs
等我还可以使用“put-log-events”子命令创建日志。
但是,在此实例的容器中,我仍然无法连接到 CloudWatch。这是我得到的错误:
UnknownEndpoint: Inaccessible host: logs.ca-central-1.amazonaws.com'. This service may not be available in the
ca-central-1' region.
at Request.ENOTFOUND_ERROR (/users-svc/node_modules/aws-sdk/lib/event_listeners.js:530:46)
at Request.callListeners (/users-svc/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/users-svc/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/users-svc/node_modules/aws-sdk/lib/request.js:688:14)
at error (/users-svc/node_modules/aws-sdk/lib/event_listeners.js:362:22)
at ClientRequest. (/users-svc/node_modules/aws-sdk/lib/http/node.js:99:9)
at ClientRequest.emit (events.js:400:28)
at ClientRequest.emit (domain.js:470:12)
at TLSSocket.socketErrorListener (_http_client.js:475:9)
at TLSSocket.emit (events.js:400:28) { code: 'UnknownEndpoint', region: 'ca-central-1', hostname: 'logs.ca-central-1.amazonaws.com',
retryable: true, originalError: Error: getaddrinfo EAI_AGAIN
logs.ca-central-1.amazonaws.com
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:69:26) {
errno: -3001,
code: 'NetworkingError',
syscall: 'getaddrinfo',
hostname: 'logs.ca-central-1.amazonaws.com',
region: 'ca-central-1',
retryable: true,
time: 2021-08-17T16:30:35.796Z }, time: 2021-08-17T16:30:35.796Z }
因此,容器中的应用程序服务无法到达 CloudWatch 主机。
我是 AWS 的新手,所以这可能是由于一个微不足道的遗漏造成的。但是,我怀疑问题出在容器无法在私有子网上找到此 CloudWatch 接口端点,这可能是因为它无法将主机名解析为 IP 地址。当容器通过覆盖网络相互连接时,它们应该如何连接到这个端点? Interface EndPoint 获得在私有子网上分配的 IP 地址,这允许实例通过 AWS 的 PrivateLink 连接到 AWS 服务(在本例中为 CloudWatch)。由于我可以从私有子网上的这个实例连接到 CloudWatch,该实例可以将主机名解析为 IP 地址。我如何让这个实例上的容器做同样的事情?实例上可以访问 public 互联网的容器正在使用 Google 的 DNS 服务器 8.8.8.8 和 9.9.9.9,因此它们能够解析 CloudWatch 主机名的 public IP 地址。私有子网上实例上的容器必须需要某种方式来解析 CloudWatch 主机名在私有子网上的 IP 地址。
顺便说一下,我已经看到在启用容器日志记录方面对 awslogs 驱动程序和 CloudWatch 代理的引用,但我认为它们提供了将日志从容器获取到 CloudWatch 的替代方法;没有他们我应该也能做到。
事实证明,这个问题是由于在我的 VPC 中分配给私有子网的子网地址 space 与 docker swarm 创建其子网 space 的方式发生冲突造成的默认。这是回答我的询问的 Whosebug 来源,特别是用户 cueedee 发布的第一个答案,而不是他的第二个。我首先尝试了第二种方法,即在 docker-compose-yml 中添加以下内容:
networks:
default:
ipam:
config:
- subnet: '192.168.0.0/24'
driver: 'default'
这不起作用:docker stack deploy 没有更新该指令所表达的地址 space。在此之后,我尝试像这样强制更新:
$ docker swarm init --default-addr-pool 192.168.0.0/16
--force-new-cluster
仍然,docker swarm 没有更新其子网地址 space。最后,我按照 cueedee 在他的第一个答案中的建议做了,即重新创建群:
$ docker swarm init --default-addr-pool 192.168.0.0/16
这给了我想要的结果,我可以通过以下方式验证:
$docker 信息
Swarm: active
NodeID:
Is Manager: true
ClusterID:
Managers: 3
Nodes: 3
Default Address Pool: 192.168.0.0/16
SubnetSize: 24
使用原始 docker-compose.yml 重新部署应用程序消除了我面临的问题:私有子网上的容器现在能够将日志发送到 CloudWatch。
我有一个包含三个 EC2 实例的 AWS VPC 设置,其中一个可以访问 public 互联网,而另外两个则不能。应用程序服务(在 node.js 中开发)在所有三个实例中都是 docker 容器化和 运行。我正在将他们的日志从文件移动到 AWS CloudWatch。该应用程序使用 Winston 记录器,我添加了 winston-cloudwatch 来为 CloudWatch 提供传输。
export const logger = new winston.createLogger({
transports: [],
handleExceptions: false,
exitOnError: false
});
if (process.env.NODE_ENV === 'production') {
const cloudWatchConfig = {
logGroupName: process.env.CLOUDWATCH_GROUP_NAME,
logStreamName: process.env.SERVICE,
awsRegion: process.env.CLOUDWATCH_REGION,
messageFormatter: ({ level, message }) => `[${process.env.SERVICE}:${level}] : ${message}`
}
logger.add(new WinstonCloudWatch(cloudWatchConfig))
}
具有 public 互联网访问权限的 EC2 实例上的容器 运行ning 正在将日志发送到 CloudWatch;那里没有问题。但是,私有子网上实例上的那些 运行ning 不能。第一个问题是从该 EC2 实例访问 CloudWatch 服务器,为此我在私有子网上为服务 com.amazonaws.ca-central-1.logs 设置了一个 VPC 接口端点。之后我可以从实例访问 CloudWatch,即我可以使用命令行指令访问 CloudWatch,例如:
$ aws logs describe-log-groups --log-group-name my-app-logs
等我还可以使用“put-log-events”子命令创建日志。
但是,在此实例的容器中,我仍然无法连接到 CloudWatch。这是我得到的错误:
UnknownEndpoint: Inaccessible host:
logs.ca-central-1.amazonaws.com'. This service may not be available in the
ca-central-1' region. at Request.ENOTFOUND_ERROR (/users-svc/node_modules/aws-sdk/lib/event_listeners.js:530:46) at Request.callListeners (/users-svc/node_modules/aws-sdk/lib/sequential_executor.js:106:20) at Request.emit (/users-svc/node_modules/aws-sdk/lib/sequential_executor.js:78:10) at Request.emit (/users-svc/node_modules/aws-sdk/lib/request.js:688:14) at error (/users-svc/node_modules/aws-sdk/lib/event_listeners.js:362:22) at ClientRequest. (/users-svc/node_modules/aws-sdk/lib/http/node.js:99:9) at ClientRequest.emit (events.js:400:28) at ClientRequest.emit (domain.js:470:12) at TLSSocket.socketErrorListener (_http_client.js:475:9) at TLSSocket.emit (events.js:400:28) { code: 'UnknownEndpoint', region: 'ca-central-1', hostname: 'logs.ca-central-1.amazonaws.com', retryable: true, originalError: Error: getaddrinfo EAI_AGAIN logs.ca-central-1.amazonaws.com at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:69:26) { errno: -3001, code: 'NetworkingError', syscall: 'getaddrinfo', hostname: 'logs.ca-central-1.amazonaws.com', region: 'ca-central-1', retryable: true, time: 2021-08-17T16:30:35.796Z }, time: 2021-08-17T16:30:35.796Z }
因此,容器中的应用程序服务无法到达 CloudWatch 主机。
我是 AWS 的新手,所以这可能是由于一个微不足道的遗漏造成的。但是,我怀疑问题出在容器无法在私有子网上找到此 CloudWatch 接口端点,这可能是因为它无法将主机名解析为 IP 地址。当容器通过覆盖网络相互连接时,它们应该如何连接到这个端点? Interface EndPoint 获得在私有子网上分配的 IP 地址,这允许实例通过 AWS 的 PrivateLink 连接到 AWS 服务(在本例中为 CloudWatch)。由于我可以从私有子网上的这个实例连接到 CloudWatch,该实例可以将主机名解析为 IP 地址。我如何让这个实例上的容器做同样的事情?实例上可以访问 public 互联网的容器正在使用 Google 的 DNS 服务器 8.8.8.8 和 9.9.9.9,因此它们能够解析 CloudWatch 主机名的 public IP 地址。私有子网上实例上的容器必须需要某种方式来解析 CloudWatch 主机名在私有子网上的 IP 地址。
顺便说一下,我已经看到在启用容器日志记录方面对 awslogs 驱动程序和 CloudWatch 代理的引用,但我认为它们提供了将日志从容器获取到 CloudWatch 的替代方法;没有他们我应该也能做到。
事实证明,这个问题是由于在我的 VPC 中分配给私有子网的子网地址 space 与 docker swarm 创建其子网 space 的方式发生冲突造成的默认。这是回答我的询问的 Whosebug 来源
networks:
default:
ipam:
config:
- subnet: '192.168.0.0/24'
driver: 'default'
这不起作用:docker stack deploy 没有更新该指令所表达的地址 space。在此之后,我尝试像这样强制更新:
$ docker swarm init --default-addr-pool 192.168.0.0/16 --force-new-cluster
仍然,docker swarm 没有更新其子网地址 space。最后,我按照 cueedee 在他的第一个答案中的建议做了,即重新创建群:
$ docker swarm init --default-addr-pool 192.168.0.0/16
这给了我想要的结果,我可以通过以下方式验证:
$docker 信息
Swarm: active
NodeID:
Is Manager: true
ClusterID:
Managers: 3
Nodes: 3
Default Address Pool: 192.168.0.0/16
SubnetSize: 24
使用原始 docker-compose.yml 重新部署应用程序消除了我面临的问题:私有子网上的容器现在能够将日志发送到 CloudWatch。