问题描述
想要自动化生成和部署一个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。
执行计划
- 确定最新的Linux AMI(Amazon Machine Image)ID
- 创建密钥对和实例
- 连接到实例
- 安装和配置Docker守护程序
- 运行容器
确定最新的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月云顾问的简短论述)。
请注意,这是一个解决方案指南,具体操作可能因环境和需求而异。请根据实际情况进行调整和测试。
参考资料
- AWS EC2 User Guide – Finding an AMI
- Boto3 EC2 Example – Creating an EC2 Instance with Python
- Amazon ECS Developer Guide – Docker Basics
- Amazon ECS Developer Guide – Private Auth for Container Instances
- GitHub – Python ECS Example
- GitHub – Python Docker AWS Example Project
- StackOverflow – Change AWS ECS Service’s Security Groups
请注意,这是一个解决方案指南,具体操作可能因环境和需求而异。请根据实际情况进行调整和测试。