在AWS上自动化部署自定义Docker镜像

129次阅读
没有评论

问题描述

想要自动化生成和部署一个Docker容器,并且希望在AWS上启动该容器。用户已经使用Packer创建了Docker镜像,并使用Packer的后处理模块将其上传到AWS ECR。现在用户想要在AWS上启动该容器,但不想使用Web界面,而是希望通过命令行来实现自动化。

用户有一些关于如何在AWS上运行Docker容器的问题,特别是关于选择基础实例的方式。用户还想了解如何正确设置环境并启动镜像。

解决方案

请注意以下操作注意版本差异及修改前做好备份。

EC2部署

本解决方案的目的是演示用户可以在AWS EC2实例上部署容器,而无需使用Web界面或人工交互。

范围和限制

在这个用例中,我们只想手动启动一个容器,用于演示或研究目的,因此我们明确决定不进行编排。

对于复杂且可能更加现实的部署场景,这个示例可能不适用。为了简单起见,我们将使用EC2服务并在其中创建一个虚拟机。

根据短暂的文档研究,似乎没有预先安装Docker守护程序的官方AMI。因此,我们需要部署一些默认的EC2实例,并在其中安装Docker守护程序,然后启动守护程序。

如果使用私有注册表,需要登录到注册表。为了简单起见,我还提供了一个从公共Docker Hub注册表运行容器的示例。

我不会提供一个可以复制粘贴的自动化脚本,而是提供一系列可以根据个人需求轻松调整的步骤。无论如何,中间结果都需要解析和传递操作,这是一个纯编程活动的关注点。

可以使用bash和awscli编写一个模板化的自动化脚本,也可以使用Python和AWS的boto3 SDK。

考虑到Python可以更好地处理从AWS获取的JSON响应,可以执行SSH登录和远程主机上的bash命令,因此建议考虑使用Python。

执行计划

  1. 确定最新的Linux AMI(Amazon Machine Image)ID
  2. 创建密钥对和实例
  3. 连接到实例
  4. 安装和配置Docker守护程序
  5. 运行容器

确定最新的Linux AMI ID

根据官方文档搜索最新的Amazon Linux镜像:

$ aws ec2 describe-images --owners amazon --filters 'Name=name,Values=amzn2-ami-hvm-2.0.????????.?-x86_64-gp2' 'Name=state,Values=available' --query 'reverse(sort_by(Images, &CreationDate))[:1].ImageId' --output text

创建密钥对和实例

使用awscli创建密钥对:

$ aws ec2 create-key-pair --key-name ec2-docker-test

然而,API会将您的公钥包装在JSON数据消息中。因此,使用Python处理此数据消息并将其写入.pem文件更容易:

import boto3

ec2 = boto3.resource('ec2')

# 创建一个文件来存储密钥
outfile = open('ec2-keypair.pem', 'w')

# 调用boto ec2函数创建密钥对
key_pair = ec2.create_key_pair(KeyName='ec2-docker-test')

# 捕获密钥并将其存储在文件中
KeyPairOut = str(key_pair.key_material)
print(KeyPairOut)
outfile.write(KeyPairOut)

使用之前获取的AMI ID,我们现在可以创建虚拟机:

instances = ec2.create_instances(
    ImageId=AMI_ID,
    MinCount=1,
    MaxCount=1,
    InstanceType='t2.micro',
    KeyName='ec2-docker-test'
)

AWS API还允许查找实例状态和IP。使用更多的Python/boto3代码,您可以获取此数据以了解何时可以继续。

测试SSH连接

一旦实例启动并运行,您可以通过远程命令执行来执行任何您想要的操作。

在Bash脚本中,您只需使用ssh或scp上传本地bash脚本并在远程主机上运行它:

ssh -t <REMOTE_IP> -i <KEY_FILE> <COMMAND>

使用Python,您可以使用paramiko等其他模块建立SSH连接并在远程主机上运行命令:

import paramiko

client = paramiko.SSHClient()
client.connect(ip, username=username, key_filename=key_filename, port=port, timeout=timeout, auth_timeout=timeout)
stdin, stdout, stderr = ssh.exec_command("whoami")

为了简化起见,我列出了进一步的命令。在您的脚本中,您可能希望使用基本的辅助子例程来进行调用,例如ssh_call(command)

安装和配置Docker守护程序

如上所述,从这里开始的任何其他操作都是在远程主机上运行的命令。

也就是说,最后的步骤是根据官方文档中的示例进行配置。进行配置的方式与在本地执行相同,但您需要将其包装在脚本中并根据需要传递中间结果。

以下是一些示例命令:

sudo yum update -y
sudo amazon-linux-extras install docker
sudo service docker start
sudo usermod -a -G docker ec2-user
aws ecr get-login --no-include-email --region region

运行容器

从私有注册表中拉取自定义镜像并在其中运行容器:

docker pull aws_account_id.REGISTRY.ecr.REGION.amazonaws.com/REPOSITORY/IMAGE:TAG
docker run --name MYSERVICENAME -d -p PORT_HOST:PORT_CONTAINER IMAGE:TAG

从公共Docker Hub注册表中拉取官方公共镜像并在其中运行另一个监听端口8001的容器:

docker run --name nginx -d -p 8001:80 nginx:latest

使用ECS的替代方案

根据Python/boto3示例,可以使用AWS ECS服务定义机器集群并在其中运行容器。

请注意,ECS集群可以使用AWS EC2或AWS Fargate提供实际的虚拟机。

如果我们选择EC2,我们需要选择一个针对ECS进行优化的最新机器映像,但是我们需要为机器分配一个ECS优化的角色。简而言之,这些机器将具有ECS代理;通过其配置,我们将机器分配给目标集群。

一个小小的奖励是,ECS机器已经预先安装了Docker守护程序。

因此,我们创建一个ECS集群并向其中添加一个虚拟机。

ecs_client.create_cluster(clusterName=cluster_name)
ec2_client.run_instances(
    ImageId=AMI_ID,
    MinCount=1,
    MaxCount=1,
    InstanceType="t2.micro",
    IamInstanceProfile={
        "Name": "ecsInstanceRole"
    },
    UserData="#!/bin/bash \n echo ECS_CLUSTER=" + cluster_name + " >> /etc/ecs/ecs.config"
)

在集群中,我们可以创建一个任务定义和服务,它们都是容器概念的抽象。

ecs_client.register_task_definition(
    containerDefinitions=[
        {
            "name": "<MY_SERVICE>",
            "image": "<MY_IMAGE>",
            "portMappings": [
                {
                    "containerPort": 80,
                    "hostPort": 80
                }
            ]
        }
    ],
    family="hello_world"
)

现在,您可能会问自己,实例上的Docker守护程序如何知道您的私有注册表以拉取指定的镜像?为此,机器的ECS代理需要进行额外的配置。

现在,您可以启动服务,该服务将使用先前定义的任务告诉先前授权的Docker守护程序从ECR拉取先前构建和发布的镜像,并在您已包含在ECS集群中的机器上运行容器。

ecs_client.create_service(
    cluster=cluster_name,
    serviceName=service_name,
    taskDefinition=task_name,
    desiredCount=1,
    clientToken='request_identifier_string',
    deploymentConfiguration={
        'maximumPercent': 200,
        'minimumHealthyPercent': 50
    }
)

在ECS上配置安全组已经在StackOverflow上进行了强调。简而言之,这些是与安全组管理相关的API部分的进一步调用。

结论

Bash和Python实现选项都有其优点和缺点。

从长远来看,Python是一种通用的编程语言,拥有庞大的社区,可以处理来自云API或系统进程的复杂字符串操作和抽象。

在不了解部署目标的情况下,上述步骤的端到端可能不值得努力,因为对于部署一组相关容器的情况(这是更典型的场景),其他方法可能更加实用。

在进入技术实现之前,最好做出明确的决策和选择,以解决业务目标和预算问题:

  • 从长远来看,自动化需要运行多少次以及在哪个时间段内,与从头开始实现自动化的时间相比如何?
  • 评估您是否只需要EC2,还是ECS与EC2一起使用Fargate(用于无状态任务),或者您可能需要Kubernetes?

进一步阅读:《托管Kubernetes与Amazon ECS的好处》(2019年8月云顾问的简短论述)。

请注意,这是一个解决方案指南,具体操作可能因环境和需求而异。请根据实际情况进行调整和测试。

参考资料

请注意,这是一个解决方案指南,具体操作可能因环境和需求而异。请根据实际情况进行调整和测试。

正文完