我是Docker的新手,我之前的经验是将Java Web应用程序(在Tomcat容器中运行)部署到Elastic Beanstalk。我以前的管道是这样的:将提交检入git,触发Jenkins作业,构建应用程序JAR(或WAR)文件,将其发布到Artifactory,然后将相同的JAR部署到使用eb deploy
在Elastic Beanstalk中的应用程序。 (道歉,如果"管道"是保留条款;我在概念上使用它。)
顺便说一句,我也将使用Gitlab进行CI / CD而不是Jenkins(由于组织原因而无法控制),但从Jenkins到Gitlab的跳跃对我来说似乎很直接 - 当然而不是直接部署WAR到部署Dockerized容器的跳跃。
转移到Docker世界,我想管道会是这样的:将提交检入git,触发Gitlab CI,然后构建JAR或WAR文件,将其发布到Artifactory,然后使用构建Docker镜像的Dockerfile
,将Docker镜像发布到Amazon ECR(可能?)......然后我老实说不确定Elastic Beanstalk集成将如何从那里开始。我知道它与Dockerrun.aws.json
文件有关,可能需要调用AWS CLI。
我刚刚观看了亚马逊的一个名为Running Microservices and Docker on AWS Elastic Beanstalk的网络研讨会,该网络研讨会声称在我的回购根目录中应该有一个Dockerrun.aws.json
文件,它基本上定义了与EB的集成。但是,似乎JSON文件包含指向ECR中各个Docker镜像的链接,这会让我失望。每次构建新图像时,该链接是否会发生变化?我想象CI需要动态更新repo中的JSON文件......这对我来说几乎感觉像是反模式。
在我上面链接的网络研讨会中,主持人创建了他的Docker镜像,并使用CLI手动推送ECR。然后他手动将Dockerrun.aws.json
文件上传到EB。然而,他并不需要上传应用程序,因为它已经包含在Docker镜像中。这一切对我来说都很奇怪,我怀疑我是否正确理解事物。 Dockerrun.aws.json
文件是否需要在每次构建时更改?或者我是否以错误的方式思考这个问题?
答案 0 :(得分:2)
自从我发布此问题以来的8个月里,我学到了很多东西,并且我们已经开始采用其他更好的技术。但是我将发布我对最初问题的回答所学到的知识。
Dockerrun.aws.json 文件与ECS任务定义几乎完全相同。即使您仅部署单个容器,使用Beanstalk的Multi-Docker容器部署版本(而不是单个容器)也很重要。 IMO,他们应该摆脱Beanstalk的单一容器平台,因为它毫无用处。但是,假设您已将Beanstalk设置为Multi-Container Docker平台,则Dockerrun.aws.json文件看起来像这样:
{
"AWSEBDockerrunVersion": 2,
"containerDefinitions": [
{
"name": "my-container-name-this-can-be-whatever-you-want",
"image": "my.artifactory.com/docker/my-image:latest",
"environment": [],
"essential": true,
"cpu": 10,
"memory": 2048,
"mountPoints": [],
"volumesFrom": [],
"portMappings": [
{
"hostPort": 80,
"containerPort": 80
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/aws/elasticbeanstalk/my-image/var/log/stdouterr.log",
"awslogs-region": "us-east-1",
"awslogs-datetime-format": "%Y-%m-%d %H:%M:%S.%L"
}
}
}
]
}
如果您最终决定将整个过程转换为ECS服务而不是使用Beanstalk,这将变得非常容易,因为上面的示例JSON通过提取“ containerDefinitions”部分直接转换为ECS任务定义。 。因此,等效的ECS任务定义可能看起来像这样:
[
{
"name": "my-container-name-this-can-be-whatever-you-want",
"image": "my.artifactory.com/docker/my-image:latest",
"environment": [
{
"name": "VARIABLE1",
"value": "value1"
}
],
"essential": true,
"cpu": 10,
"memory": 2048,
"mountPoints": [],
"volumesFrom": [],
"portMappings": [
{
"hostPort": 0,
"containerPort": 80
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/aws/ecs/my-image/var/log/stdouterr.log",
"awslogs-region": "us-east-1",
"awslogs-datetime-format": "%Y-%m-%d %H:%M:%S.%L"
}
}
}
]
此处的主要区别在于,使用Beanstalk版本时,您需要将端口80映射到端口80,因为在Beanstalk上运行Docker的局限性在于您不能在同一实例上复制容器,而在ECS中可以。这意味着在ECS中,您可以将容器端口映射到主机端口“零”,这实际上只是告诉ECS在临时范围内选择一个随机端口,该端口允许您在单个实例上堆叠多个容器副本。其次,如果要传递环境变量,则使用ECS,需要将它们直接注入到Task Definition JSON中。在Beanstalk的世界中,您不需要将环境变量放入Dockerrun.aws.json文件中,因为Beanstalk在控制台中具有用于管理环境变量的单独工具。
事实上,Dockerrun.aws.json文件实际上应该被认为是模板。由于Beanstalk上的Docker在后台使用ECS,因此只需将Dockerrun.aws.json作为模板,并使用它生成自己的Task Definition JSON,即可将托管环境变量注入到最终的“ environment”属性中。 JSON。
当我第一次问这个问题时,我遇到的一个大问题是,每次部署时是否必须更新此Dockerrun.aws.json文件。我发现,这取决于您要如何部署事物。您可以,但不必。如果您编写Dockerrun.aws.json文件,使“ image”属性引用:latest
Docker映像,则无需更新该文件。您需要做的就是反弹Beanstalk实例(即重新启动环境),它将提取Artifactory(或ECR或您发布图像的任何其他位置)上可用的:latest
Docker图像。因此,所有构建管道所需要做的就是将:latest
Docker映像发布到您的Docker存储库,然后使用awscli使用如下命令触发Beanstalk环境的重启:
$ aws elasticbeanstalk restart-app-server --region=us-east-1 --environment-name=myapp
但是,这种方法有很多缺点。如果您有一个将:latest
映像发布到同一存储库的dev / unstable分支,则当环境碰巧自行重启时,您就有部署该不稳定分支的风险。因此,我建议对Docker标签进行版本控制,并仅部署版本标签。因此,您无需指向my-image:latest
,而是指向类似my-image:1.2.3
的东西。这确实意味着您的构建过程将必须在每个构建上更新Dockerrun.aws.json文件。然后,您还需要做的不仅仅是简单的重新启动应用服务器。
在这种情况下,我编写了一些bash脚本,这些脚本使用jq utility来以编程方式更新JSON中的“ image”属性,将字符串“ latest”替换为当前的构建版本。然后,我必须调用awsebcli工具(请注意,这是与普通awscli工具不同的软件包)来更新环境,如下所示:
$ eb deploy myapp --label 1.2.3 --timeout 1 || true
在这里,我正在做一些骇人听闻的事情:eb deploy
命令不幸地成为永远。 (这是我们切换到纯ECS的另一个原因;令人难以置信的是Beanstalk。)该命令在整个部署时间内都挂起,在我们的情况下可能要花费30分钟或更长时间。这对于构建过程来说是完全不合理的,因此我强制该过程在1分钟后超时(它实际上继续进行部署;它只是断开了我的CLI客户端的连接并向我返回失败代码,即使随后可能成功)。 || true
是一种可有效告诉Gitlab忽略失败退出代码并假装成功的黑客。这显然是有问题的,因为无法确定Elastic Beanstalk部署是否确实失败了。我们以为它永远不会。
使用eb deploy
的另一件事:默认情况下,此工具将自动尝试将构建目录中的所有内容压缩成ZIP,然后将整个ZIP上载到Beanstalk。你不需要那个您所需要做的就是更新Dockerrun.aws.json。为了做到这一点,我的构建步骤是这样的:
jq
使用最新版本标签更新Dockerrun.aws.json
文件zip
创建一个名为deploy.zip
的新ZIP文件并将Dockerrun.aws.json
放入其中.elasticbeanstalk/config.yml
的文件(如下所述)eb deploy ...
命令然后,您需要在.elasticbeanstalk/config.yml
的构建目录中找到一个文件,如下所示:
deploy:
artifact: deploy.zip
global:
application_name: myapp
default_region: us-east-1
workspace_type: Application
当您调用eb deploy
时,awsebcli知道会自动查找此文件。这个特定文件说的是查找名为deploy.zip的文件,而不是尝试将整个目录本身压缩为ZIP。
因此,:latest
部署方法存在问题,因为您可能会部署不稳定的内容;版本化的部署方法存在问题,因为部署脚本更加复杂,并且因为除非您希望构建管道花费30分钟以上的时间,否则部署可能不会成功,并且实际上没有办法(自己监视每个部署。)
无论如何,设置起来还需要做很多工作,但是我建议您尽可能迁移到ECS。 (尽管还有很多工作要做,但还是最好迁移到EKS。)Beanstalk有很多问题。