使用Traefik和Let’s Encrypt在Docker Swarm集群上部署NodeJS后端

在这篇简短的文章中,我想介绍如何在Docker Swarm集群上轻松部署NodeJS应用程序。 简而言之,我将设置由AWS EC2实例组成的Swarm集群(1),然后将NodeJS应用程序运行到容器中(2),在公共docker注册表上发布新创建的应用程序的映像(3),然后进行部署服务并启动NodeJS后端,(4)另外,作为反向代理,我们将使用由Let’s Encrypt生成的SSL证书部署Traefik

什么是Docker Swarm?

Docker Swarm是与Docker Engine集成的集群流程。 它包含许多出色的功能,例如服务发现,扩展应用程序,负载平衡,服务网格和滚动升级。 所有这些功能在Docker网站上都有很好的文档说明,因此,我建议您阅读文档并详细了解这些功能。

设置EC2实例

在AWS上配置Docker的最简单方法是使用docker-machine轻松设置虚拟机。 我们需要有至少3个节点组成的Swarm集群,这意味着给定命令必须执行3次。 请注意,每次执行该命令(给定命令中的最后一个参数)时,都必须更改主机名。 我假设您已经创建了对AWS的编程访问权限,并且aws_access_key_id和aws_secret_access_key存储在此文件中cat ~/.aws/credentials

  docker-machine create --driver amazonec2 --amazonec2-region eu-central-1 --amazonec2-instance-type t2.nano --amazonec2-security-group swarm-sg --amazonec2-open-port 9200 / tcp- amazonec2-开放端口9300 / tcp --amazonec2-开放端口2377 / tcp --amazonec2-开放端口7946 / tcp --amazonec2-开放端口4789 / udp --amazonec2-开放端口7946 / udp swarm- 1个 

该命令包含创建Swarm集群所需的端口。 请注意,这只是一个示例,在生产环境中, 您不应将这些端口公开给Internet。 在生产环境中,群集的互通必须由包含Docker API访问权限的私有安全网络进行管理。

创建虚拟机后,您可以轻松地连接到每个虚拟机并初始化集群。

创建Docker Swarm集群

可以不需要通过SSH直接连接到新创建的VM来管理Docker,虽然可以,但是我们将通过Docker API连接来管理Docker并初始化集群。

通过执行以下命令连接到实例之一。

 评估$(docker-machine env swarm-1) 

并启动Swarm集群

  docker swarm初始化 

请注意,Swarm中的节点具有两个角色: managerworker 。 借助RAFT,管理器用于维护集群,调度服务并保持集群状态。 为了设置生产集群,您需要创建适当数量的管理器,以在任何节点丢失时提供高可用性和冗余。 同样,请参考Docker文档。 工人用于运行容器。

初始化集群后,复制以下命令返回的命令

docker swarm init

然后添加剩余的虚拟机以解决3个节点群集。 同样,无需通过SSH连接到其他节点,只需使用

eval $(docker-machine env swarm-2)

并将该节点加入现有集群。 对swarm-3.也重复该步骤swarm-3.

您应该具有以下类似于此输出的集群:

 码头节点ls 
ID HOSTNAME状态可用性管理器状态引擎版本
89sc0gu7qcjs5qmu2ze51f55m * swarm-1 Ready Active Leader 18.09.0
rw0snnoawg4elt8imc436bhuw swarm-2 Ready Active 18.09.0
8ixuiribve7rh90li9zsyya7j swarm-3准备就绪18.09.0

我们有一个由3个节点组成的集群,只有一个节点具有管理者角色,其余两个节点是工作者。

创建运行在容器中的NodeJS应用

这是我的队友Łukasz编码的一个非常简单的NodeJS后端,它公开了两个端点,其中一个端点返回从容器本身获取的环境变量,另一个是运行状况检查。

  const os = require('os'); 
const express = require('express');
const morgan = require('morgan');
const helmet = require('helmet');
const compression = require('compression');
  const PORT = process.env.PORT ||  3000; 
const app = express();
  app.use(morgan(app.get('env')==='production'?'combined':'dev'))); 
app.use(helmet());
app.use(compression());
  app.get('/',(req,res)=> { 
res.json({
...要求标头,
主机名:os.hostname(),
日期:new Date()。toISOString()
});
});
  app.get('/ healthcheck',(req,res)=> { 
res.json({status:'UP'});
});
  app.use((_ req,res,_next)=> { 
res.sendStatus(404);
});
  app.use((err,_req,res,_next)=> { 
console.error(err.stack);
res.sendStatus(500);
});
  app.listen(PORT,()=> { 
console.log('服务器正在监听端口',PORT);
});
  process.on('uncaughtException',err => { 
console.error(err.stack);
process.exit(1);
});

源代码发布在我的Github帐户上。

应用程序运行状况检查是应用程序生命周期的关键部分,尤其是对于生产环境。 一般规则是从非常基本的运行状况检查开始,然后在您熟悉编码的后端后添加更多增强功能。 实际上,这就是DEV OPS所针对的,因为Swarm / SysOps需要与您的开发团队合作,解释为什么运行状况检查很重要以及应如何在生产环境中部署它们。 这是一种工作文化,而不仅仅是一种角色,是一种态度和您积极主动的态度。

开发Dockerfile并在公共Docker注册表上发布

准备好代码后,我们需要准备Dockerfile并构建映像并将其发布在任何Docker Registry上。 您可以使用Docker Hub,AWS ECR或任何私有存储库。

这是构建NodeJS应用程序的基本Dockerfile。

 从节点:lts-alpine 
运行apk add --update --no-cache \
卷曲
ARG NODE_ENV =生产
ENV NODE_ENV $ NODE_ENV
  WORKDIR / usr / src / app 
COPY包* .json ./
运行npm安装
复制。 。
  HEALTHCHECK --interval = 1m --timeout = 3s --start-period = 15s \ 
CMD curl -fs http:// localhost:3000 / healthcheck || 1号出口
 博览会3000 
CMD [“ npm”,“开始”]

为了构建应用程序,必须执行以下命令

  docker build -t jakubhajek / nodejs-backend。 

请注意,您必须用您的Docker Hub帐户替换我的名字,然后通过执行docker login.命令docker login.

创建堆栈文件

定义两个服务时,我创建了一个堆栈文件。 第一个是后端,这是我们的NodeJS后端,第二个是我们应用程序的Traefik反向代理。 Traefik将自动发现启动了多少个后端副本,并将自动将传入请求路由到适当的后端。 这是一个了不起的功能,在我们分发体系结构时非常有用。

Traefik提供的全部魔力是通过添加labels并基于该反向代理路由请求正确实现的。

请参阅NodeJS后端的定义:

 后端: 
图片:jakubhajek / nodejs-backend
网络:
-traefik-net
部署:
模式:复制
复制品:3
标签:
-traefik.port = 3000
-traefik.docker.network =代理
-traefik.frontend.rule = Host:node-app.cometari.com
资源:
限制:
记忆体:128M

请注意标签部分,并且不需要暴露后端端口,Traefik内部通信也可以提高安全性。

请参阅Traefik服务的定义:

  traefik: 
图片:traefik
端口:
-“ 80:80”
-“ 443:443”
-“ 8080:8080”
网络:
-traefik-net
命令:
---api
--码头工人
---docker.swarmmode
---docker.watch
---entrypoints =名称:http地址:: 80重定向。入口点:https
---entrypoints =名称:https地址:: 443 TLS
---defaultentrypoints = http,https
---acme
---acme.email=jakub.hajek@cometari.com
---acme.storage = / certificates / acme.json
---acme.entryPoint = https
---acme.httpChallenge.entryPoint = http
---acme.onhostrule = true
---acme.acmelogging = true
---logLevel = INFO
---accessLog
部署:
放置:
约束:[node.role ==经理]
模式:复制
复制品:1
数量:
-“ /var/run/docker.sock:/var/run/docker.sock”
-“ traefik-certificates:/ certificates”

堆栈文件的整个定义在我的Github存储库中存储的docker-compose.yml文件中。

我还添加了“让我们加密”配置,因为您大部分时间都可以看到它非常简单且安全。 启动堆栈后几秒钟将自动生成证书。

运行堆栈

一旦堆栈文件准备就绪,就可以启动它了,因此必须执行以下命令:

  docker stack deploy -c docker-compose.yml app_1 

然后您可以执行命令:

 码头工人堆栈ls 
 码头工人服务ls 

第一个命令返回在堆栈文件中定义的服务列表,第二个命令返回每个定义的服务的副本数。

Traefik仪表板

在端口8080上,启动了一个不错的UI,该UI表示已连接的前端和正在运行的后端。 可视化应该可以帮助您了解请求如何在整个堆栈中传输。

测试应用程序和整个堆栈

创建任何域,例如node-app。 cometari.com (这是我出于测试目的而创建的,但您可以使用任何域),将属于已创建的EC2实例的三个IP地址(docker-machine ls —将列出IP地址)指向为A记录。 因此,默认情况下,您将获得DNS轮询。

如果您没有域,您仍然可以通过执行以下命令来测试堆栈:

  curl -H“主机:node-app.cometari.com”  

Traefik将接受请求,并基于HTTP HOST标头将请求路由到后端实例,这是一个非常酷的功能。

分析后端生成的输出,尤其要看一下每次执行请求时主机名应该不同的主机名。 这是通过DNS循环实现的,但在内部也要感谢Docker Swarm,每个请求都将到达相应的后端。

您可以尝试缩小正在运行的后端副本的数量并重复测试,也可以通过添加副本来扩大规模(请参阅: docker service scale)

完成任何更新后,导航回到仪表板以查看堆栈的行为。

摘要

我们在这里取得的成就:

  • 创建了Docker Swarm集群,
  • 开发了NodeJS后端,
  • 开发了具有基本运行状况检查的Dockerfile,
  • 构建图像并将其发布到存储库中,
  • 创建基本图像并通过标签和环境变量修改图像,
  • 开发的堆栈文件,包括两个服务:后端(NodeJS)和反向代理(Traefik),
  • 配置的SSL证书具有自动续订(Traefik),
  • 配置自动发现新的后端,

通过配置一些强大的工具,可以实现许多非常受欢迎的功能。 整个配置是明确的,并存储在存储库中,因此您可以轻松跟踪更改。 你也是

待办事项清单

  • 将带有证书的持久卷迁移到密钥值数据库(例如领事)
  • 创建Jenkins管道以轻松部署新版本的配置或发布我们后端的最新版本
  • 监视堆栈的每个组件(Prometheus)

如果对本文或与DevOps有关的任何其他技术方面有任何评论和问题,请与我联系。