写点什么

基于 Amazon EC2 Container Service 构建安全高可用的 Docker 私有库

  • 2019-11-13
  • 本文字数:6191 字

    阅读完需:约 20 分钟

基于Amazon EC2 Container Service构建安全高可用的Docker私有库

1. 背景

Docker hub 作为 docker 官方镜像仓库,提供了大量 Docker 镜像的托管服务。但使用它来托管企业私有 Docker 镜像存在一些问题,比如:


(1)Docker hub 托管私有镜像的服务目前只面对收费账户;


(2)使用 Docker hub 托管私有镜像并不适合一些特殊场景,比如工作环境网络是内网,不允许访问外网,那就更不可能到 Docker hub 去下载镜像。


在这种情况下,如果能构建一个安全可靠的 Docker 私有库,将会是一个更好的选择。本文将介绍在 Amazon EC2 Container Service 基础上结合 AWS Elastic LoadBalancer、AWS Autoscaling Group、AWS S3 及 Docker 官方提供的 registry 镜像构建安全、高可用的 Docker 私有库的方案,帮助您轻构实现这一需求。

2. 方案详解

我们会使用 AWS CloudFormation 服务,使用自定义的模板脚本声明所需的资源,实现自动化构建。接下来结合我们的模板脚本对本方案进行详细介绍。


注意:以下内容与代码相关部分只贴出主要代码,部分代码用…表示省略;红字部分请替换成您自己账号相关的信息。


完整模板代码地址:https://s3-us-west-2.amazonaws.com/blog.leonli.org/registry.yml



或者点击按钮直接在控制台中运行:

2.1 架构图


根据以上架构图,基本数据传输过程为:


(1)Docker 客户端向镜像仓库发送的 pull/push 等命令事实上都是通过 docker daemon 转换成 restful 请求的形式再发送给镜像仓库的。在本架构中,我们利用 AWS Elastic LoadBalancer(简称 ELB)接收客户端发来的请求,作为整个架构的接入层。由于我们要求数据是通过 TLS 加密传输的,所以我们需要使用 AWS IAM 中的 server certificate(由 AWS IAM 账户上传的 TLS 证书)与 ELB 关联,实现对客户端发来的请求进行加密。


(2)ELB 会将请求反向代理给后端分布在不同可用区的两台 Container Instance(安装了 Docker 运行环境的 EC2 实例),Container Instance 中运行了 Docker registry 服务。当请求到达 registry 时,我们需要首先使用内置在 registry 中的用户认证文件(比如本架构中使用 apache htpasswd 创建的基本用户名密码保护文件),进行用户认证,认证不通过,则驳回请求,认证通过,才可以读写数据。


(3)我们将数据统一存储在一个只供创建者使用的 S3 Bucket 中。

2.2 基于 AWS ECS 运行 Docker Registry 服务

Amazon EC2 Container Service (ECS) 是一项高度可扩展的高性能容器管理服务,它让您能够在托管的 Amazon EC2 实例群集上轻松运行 Docker 应用程序。 Amazon ECS 主要有以下几个组件:ECS Cluster、 Container Instance、Task , ECS Service。这里我们基于 ECS 运行了 Docker registry 服务,架构如下:



(1)首先我们在模板中定义了一个 ECS Cluster,用来管理相关的 Container Instance。ECS 提供了 ECS-Optimize AMI 来创建 EC2 实例作为 Container Instance,ECS-Optimize AMI 已经内置 Docker 运行环境和 Container Agent 代理,可以为我们节省安装这些环境所需的时间。Container Instance 在启动时可以由 Container Agent 根据配置文件/etc/ecs/ecs.config 中的 ClusterName 属性的值知道需要将实例注册到哪个 ECS Cluster 上。


因为我们要使用 Auto Scaling 服务实现对 EC2 实例的伸缩控制。所以我们使用 Auto Scaling 的 Launch Config 组件声明我们的 Container Instance。并通过 UserData 传入 Shell 脚本,此脚本主要完成以下三件事:


– 调用 echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config ,将 Container Instance 注册到我们的 ECS Cluster 中。


– 创建/docker-registry/certs 目录和/docker-registry/auth 目录,并调用 aws s3 copy 命令从指定的 S3 Bucket 中复制 TLS 证书文件和 htpasswd 用户认证文件。这些文件将在运行 Docker Registry 时使用到。


– 调用 cfn-signal 命令,通知 AutoScaling Group 资源 EC2 实例已经启动完毕。


Container Instance 相关的主要模板代码如下:


(2)然后我们定义了一个 TaskDefinition 来声明我们要运行的容器及其相关配置。这里我们运行的是 Docker registry 镜像的容器,它是官方提供的用于构建镜像仓库服务的镜像文件。

col 1

ContainerInstances:


Type: AWS::AutoScaling::LaunchConfiguration


Properties:



UserData:


Fn::Base64: !Sub |


#!/bin/bash -xe


echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config


yum install -y aws-cfn-bootstrap


yum install -y aws-cli


sudo rm -rf /docker-registry


sudo mkdir -p /docker-registry/certs


sudo chmod 777 /docker-registry/certs


sudo mkdir -p /docker-registry/auth


sudo chmod 777 /docker-registry/auth


aws s3 cp s3://{SSLCertificateFileName} /docker-registry/certs/domain.crt


aws s3 cp s3://{SSLKeyFileName} /docker-registry/certs/domain.key


aws s3 cp s3://{HtpasswdFileName} /docker-registry/auth/htpasswd


/opt/aws/bin/cfn-signal -e {AWS::StackId} –resource ECSAutoScalingGroup –region ${AWS::Region}


– 配置环境变量 REGISTRY_AUTH、REGISTRY_AUTH_HTPASSWD_REALM、REGISTRY_AUTH_HTPASSWD_PATH 指定宿主机目录/auth/htpasswd 文件作为 Basic Authentication 基础用户认证文件,从而实现用户授权认证。


– 配置环境变量 REGISTRY_HTTP_TLS_CERTIFICATE、REGISTRY_HTTP_TLS_KEY 指定宿主机目录/certs/中存放的 domain.crt 作为 TLS 证书文件,domain.key 作为 TLS 秘钥,从而实现 TLS 传输加密。


– 通过配置环境变量 REGISTRY_STORAGE 指定 registry 的存储驱动为 AWS S3,配置 REGISTRY_STORAGE_S3_REGION 为存储的 S3 所在 Region,配置 REGISTRY_STORAGE_S3_BUCKET 为存储的 Bucket。从而实现将镜像文件存储到 AWS S3 指定的 Bucket 中。


关于构建私有库的更多细节和更多配置可以通过 Docker 官方文档进行了解:Deploy a registry server


TaskDefinition 相关的主要模板代码如下:

col 1

RegistryTaskDefinition:


Type: AWS::ECS::TaskDefinition


Properties:


NetworkMode: bridge


ContainerDefinitions:


– Name: RegistryContainer


Image: registry:2



PortMappings:


– ContainerPort: 443


HostPort: 443



Environment:


– Name: REGISTRY_AUTH


Value: htpasswd


– Name: REGISTRY_AUTH_HTPASSWD_REALM


Value: “Registry Realm”


– Name: REGISTRY_AUTH_HTPASSWD_PATH


Value: /auth/htpasswd


– Name: REGISTRY_HTTP_TLS_CERTIFICATE


Value: /certs/domain.crt


– Name: REGISTRY_HTTP_TLS_KEY


Value: /certs/domain.key


– Name: REGISTRY_HTTP_ADDR


Value: 0.0.0.0:443


– Name: REGISTRY_STORAGE


Value: s3


– Name: REGISTRY_STORAGE_S3_REGION


Value: !Ref AWS::Region


– Name: REGISTRY_STORAGE_S3_BUCKET


Value: !GetAtt StorageBucket.DomainName



(3)最后我们定义了一个 ECS Service,指定 TaskDefinition、Cluster 和所需运行的任务个数 DesiredCount。这样,我们就构建了一个运行着 docker registry 镜像的 ECS 服务了。

2.2 如何实现高可用性

(1) 跨可用区部署服务


– 首先,我们在模板中声明了一个 VPC 和两个私有子网 PrivateSubnet1 和 PrivateSubnet2,这两个子网是分别部署在不同可用区的。


– 其次,我们的 ECS 服务是通过 Elastic Load Balancing(ELB)来平衡多个容器的负载,ELB 是高可用且自动伸缩的。我们在模板中定义了一个命名为 RegistryELB 的 ELB 组件,指定它是 internet-facing 模式可供外网访问、并且是跨可用区的。ELB 接收外网的请求,并且将请求代理给 Container Instance 中的容器。


– 最后,我们在模板中声明了一个 Auto Scaling Group,指定 VPCZoneIdentifier 为跨可用区的两个子网 PublicSubnet1 和 PublicSubnet2,由 RegistryELB 代理请求,从而实现跨可用区部署服务。


(2) 利用 Auto Scaling 保障可用实例数量


当服务遇到一些突发或者预期的高流量时,或者您的服务出现某些异常时,可以利用 Auto Scaling 服务保障可用实例数量。比如某台 Container Instance 宕机了,那么可以利用 Auto Scaling 自动启动相同配置的另一台 Container Instance 代理宕机的实例继续提供服务。


大部分情况下,企业 Docker 私有库承受的流量负载不会太大,所以本方案不介绍 Auto Scaling 的扩展策略,当然您也可以根据自己的业务需要修改模板代码,实现此功能。本方案使用 Auto Scaling 主要是为了保障可用实例数量一直维持在 DesiredCapacity,这个参数是我们通过模板的参数传入的。


Auto Scaling Group 相关的主要模板代码如下:

col 1

ECSAutoScalingGroup:


Type: AWS::AutoScaling::AutoScalingGroup



Properties:



LaunchConfigurationName: !Ref ‘ContainerInstances’


MinSize: !Ref MinSize


MaxSize: !Ref MaxSize


DesiredCapacity: !Ref DesiredCapacity



(3) 利用 s3 确保数据完整性


Amazon Simple Storage Service (Amazon S3) 是 AWS 提供的对象存储服务,它可用于在 Web 上的任何位置存储和检索任意数量的数据,能够提供 99.999999999% 的持久性,使用 S3 来存储 Docker 私有库的镜像文件,可以确保数据完整。Docker registry 镜像支持使用 S3 作为存储驱动,只需传入存储所在 Bucket 和所在 Region 即可。我们在模板中声明了一个 Bucket 用来存储镜像数据。并且这个 Bucket 只能由创建者进行控制。

2.3 如何实现安全性

(1) 利用 TLS 加密传输


Docker 官方建议与私有库通信的所有数据传输皆使用 TLS 加密,我们通过上传 IAM server certificate 证书并将其与 ELB 关联,ELB 的 Listener 使用 SSL 安全监听 443 端口,并将请求代理给实例的 443 端口,实例上也需要安装 TLS 证书。从而实现全链路的安全传输。


(2)利用 apache htpasswd 实现基本用户认证


如果我们创建的私有库没有用户认证机制,那么无论是谁只要知道私有库链接,就可以肆无忌惮地访问和操作我们托管在私有库的文件,这显然不是我们想要看到的。所以需要实现用户认证,只有通过认证的用户,才有权进行相关操作。


这里,我们使用 apache htpasswd 实现基本用户认证。这也是 Docker registry 镜像默认支持的认证方式。

3. 构建过程

3.1 准备工作

(1) 域名


您必须拥有一个用来指向 registry 的域名,这样您才可以通过域名访问您的私有库。


(2)TLS 证书

col 1

yourcertificate.crt intermediate-certificates.pem > yourcertificate.crt


Docker 官方建议私有库数据传输基于 TLS,这样您还需要为您的域名申请 CA 认证,得到 TLS 证书和秘钥文件。有以下两种方式:


向 CA 供应商购买。


利用 Let’s Encrypt 生成免费的证书,具体操作参考letsencrypt官网。


另外,如果您的证书供应商还提供给你 intermedia 证书,那么您需要将它与你的证书进行合并,运行如下命令可以进行合并

col 1

aws iam upload-server-certificate —server-certificate-name YourCertName —certificate-body file:///path/to/certificate.pem —certificate-chain file:///path/to/chained.pem —private-key file:///path/to/private_key.pem


(3)上传 IAM Server Certificate

col 1

{


“ServerCertificateMetadata”: {


“Path”: “/”,


“ServerCertificateName”: “MyRegistryCert”,


“ServerCertificateId”: “ASCAJHAWE3QRHEHH3L6KM”,


“Arn”: “arn:aws:iam::xxxxxxxxx:server-certificate/YourCertName”,


“UploadDate”: “2017-07-01T16:16:45.125Z”,


“Expiration”: “2017-09-26T12:15:00Z”


}


}

col 1

openssl x509 -inform DER -in Certificate.der -outform PEM -out Certificate.pem


当 Docker 客户端向我们的 ELB 发送请求时,出于安全考虑,需要对传输加密,这时可以利用 IAM Server Certificate,将我们的 TLS 证书、秘钥以及证书链文件上传,IAM 会帮我们自动生成一个 Server Certificate,再将它与 ELB 绑定即可。


可以使用 AWS CLI 按照以下命令上传:

col 1

openssl rsa -in PrivateKey.pem -out PrivateKey.pem


上传完成后,您会得到类似以下响应信息,将红字部分 ARN 记录下来,后面构建时需要它作为参数传入模板中。


注意:


– TLS 证书、秘钥及证书链必须都是 PEM 格式,如果不是,可以使用以下命令进行转换:


– AWS IAM 要求证书秘钥是不加密的,所以当您的秘钥是加密时,需要使用以下命令转换


– 如果您在上传 Server Certificate 过程中遇到问题,可参考 AWS 官网指南:


Working with Server Certificate


(4)htpasswd 用户认证文件


使用 apache htpasswd 来做基本认证,首先需要安装 httpd,安装完后运行以下命令可以生成一个包含 一条用户名和密码记录的 htpasswd 文件

col 1

htpasswd -c username password > htpasswdfile


后面如果需要加入更多条记录,只需将 -c 参数去除,运行上面相同的命令即可。


(5) 将证书、秘钥和认证文件上传到 S3


我们需要将前面几步生成好的 TLS 证书、秘钥、htpasswd 文件上传到 S3 中,Container Instance 启动后会从 S3 中将这些文件拷贝下来,通过映射到容器中供 registry 容器使用。


创建一个 S3 Bucket, 将这几个相关文件上传。

3.2 自动化构建

接下来,我们在 AWS Management Console 中使用 CloudFormation 指定我们的模板文件创建一个 Stack。如何使用 CloudFormation 创建 Stack,请参考 AWS CloudFormation 用户指南:CloudFormation入门


Stack 构建成功后,在输出栏会有输出值 RegistryELBDns,它是我们创建的 ELB 的 DNS 域名。我们需要将我们之前 TLS 证书签发的域名 DNS 解析(CName 解析)转发到这个 ELB 的 DNS 域名。这样就可以使用我们自己的域名访问我们的私有库了。

3.3 开始使用

构建完 Docker 私有库后,现在可以开始使用了。


首先,在 Docker 客户端,我们可以从 Docker Hub 上拉取一个 ubuntu 镜像。

col 1

docker pull ubuntu:16.04


然后 tag 这个镜像到我们自己的域名下

col 1

docker tag ubuntu:16.04 myregistrydomain.com/my-ubuntu


登录我们的私有库

col 1

docker login myregistrydomain.com


输入用户名,密码后,调用 push 命令将镜像上传

col 1

docker push myregistrydomain.com/my-ubuntu


上传完后,可以调用 pull 命令拉取镜像

col 1

docker pull myregistrydomain.com/my-ubuntu

4. 总结

本文介绍了如何利用 AWS ECS 及其他 AWS 服务构建一个高可用、安全可靠的 docker 私有库,通过本方案的详细介绍和构建实践,相信您对于 docker registry 以及 AWS Elastic LoadBalancer、AWS Autoscaling Group、AWS S3 及 AWS CloudFormation 有了更进一步的认识。接下来,您可以利用 AWS ECS 容器管理服务及 Docker 容器技术,更加轻松地构建和管理您的应用程序,发挥更大的效益。


作者介绍



李磊,AWS 解决方案架构师,负责基于 AWS 的云计算方案的架构设计,同时致力于 AWS 云服务在国内和全球的应用和推广。在大规模并发后台架构,电商系统,社交网络平台、互联网领域应用,DevOps 以及 Serverless 无服务器架构等领域有着广泛的设计与实践经验。在加入 AWS 之前超过十年的开发和架构设计经验, 带领团队攻克各种技术挑战,总是希望站在技术的最前沿。


本文转载自 AWS 技术博客。


原文链接:


https://amazonaws-china.com/cn/blogs/china/using-amazon-ecs-to-build-private-docker-hub/


2019-11-13 08:00697

评论

发布
暂无评论
发现更多内容

分布式柔性事务之最大努力通知事务详解

古月木易

分布式事务

Kafka 消息丢失与消费精确一次性

奈学教育

kafka

2020年7月国产数据库排行:华为、腾讯发新品,中兴、阿里结硕果

墨天轮

数据库 阿里 排行榜

四面阿里巴巴回来分享面经总结,定级P7架构师

小吴选手

架构 技术 面试 Spring Boot 阿里

架构师训练营 - 第五课作业 -20200708- 一致性HASH

👑👑merlan

极客大学架构师训练营 一致性哈希

蟒周刊/427:机器狗已在公开发售,支持用 Python 对其编程...

ZoomQuiet大妈

Python 大妈 蟒营® 蟒周刊 101camp

高效程序员的七个好习惯——你有吗?

小谈

程序员 面试 JVM springboot SpringCloud

系统架构师week04 Homework - 互联网架构技术手段和方案

尔东雨田

极客大学架构师训练营

自动特征工程在推荐系统中的研究

天枢数智运营

人工智能 推荐系统

信创舆情一线--印度封禁59款中国App

统小信uos

App 舆情 印度

五分钟让你搞懂Nginx负载均衡原理及四种负载均衡算法

架构大数据双料架构师

阿里大型企业级开发必用微服务:深入浅出SpringBoot2.x

小闫

spring jdk 面试 后端 springboot

原创 | TDD工具集:JUnit、AssertJ和Mockito (二十五)运行测试-在IDE中运行测试

编程道与术

Java intellij-idea 编程 TDD 单元测试

2.3万个MongoDB数据库遭黑客比特币勒索,你中招了吗?中招怎么办?

墨天轮

比特币 数据库 oracle mongodb 黑客

业务学习-美团闪购

第519区

猿灯塔:最详细Dubbo相关面试题

猿灯塔

数据产品经理的具象化

松子(李博源)

大数据 产品经理 数据产品

nightingale安装详解

曾祥斌

AndroidStudio真机调试 - Waiting for Debugger

麦洛

Android Studio 真机调试

分布式柔性事务之最大努力通知事务详解

奈学教育

分布式事务

选择排序

wjchenge

忘掉 Snowflake,感受一下性能高出 587 倍的全局唯一 ID 生成算法

穿甲兵

redis 架构 分布式 CAP Go 语言

Kafka 消息丢失与消费精确一次性

古月木易

kafka

区块链正处于手脚并用攀爬的“攻坚时刻”

CECBC

数据上链 市场选择

第五周作业

武鹏

太阳马戏团在疫情下的组合式创新

石云升

商业模式 组合式创新 思想实验

java基础思维导图,让java不再难懂 (建议收藏))

码哥小胖

面试 Spring Boot Java 分布式

一致性hash

彭阿三

一致性hash

听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译

程序员DMZ

spring Spring源码编译

最详细的Java/后端学习路线

犬来八荒

ThreadPoolExecutor 线程池使用

郭儿的跋涉

线程 多线程 线程池

基于Amazon EC2 Container Service构建安全高可用的Docker私有库_语言 & 开发_亚马逊云科技 (Amazon Web Services)_InfoQ精选文章