阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

基于 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:00582

评论

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

它们不一样!透析【观察者模式】和【发布订阅模式】

掘金安东尼

前端 设计模式 8月月更

如何用精益敏捷组合管理,提升研发效能?|ONES 研发管理大师课

万事ONES

10分钟快速入门RDS【华为云至简致远】

科技云未来

RDS

APICloud AVM 封装日期和时间选择组件

YonBuilder低代码开发平台

安卓 低代码开发 多端开发

mysql进阶(二十九)常用函数汇总

No Silver Bullet

MySQL mysql常用函数 8月月更

永续合约交易所系统开发逻辑详情

开发微hkkf5566

以数治企,韧性成长,2022 年中国 CIO 数字峰会成功举行

金蝶云·苍穹

基于华为云ModelArts的水表读数识别开发实践【华为云至简致远】

科技云未来

水表读数识别项目

华为云分布式缓存服务Redis开通及使用规划教程【华为云至简致远】

科技云未来

redis'

Taro小程序跨端开发入门实战

京东科技开发者

小程序 taro 开发 移动端

华为云服务之弹性云服务器ECS的深度使用和云端实践【华为云至简致远】

科技云未来

弹性云服务器ECS

Qt下异步使用C++调用Python文件

Geek_163f36

QCon 回顾 | Data Fabric:逻辑统一、物理分散

网易数帆

大数据 数据湖 降本增效 Data Fabric

Java泛型的继承场景

Geek_163f36

写给 Java 程序员的前端 Promise 教程

CRMEB

基于ECS实现一分钟自动化部署【华为云至简致远】

科技云未来

自动化部署

调研阶段复盘

Geek_XOXO

复盘

急了,Mysql索引中最不容易记的三个知识点通透了

知识浅谈

8月月更

开源一夏 | RuntimeException 子类

六月的雨在InfoQ

开源 8月月更

一起来学华为云数据库,RDS实践【华为云至简致远】

科技云未来

云数据库

EMQ畅谈IoT数据基础软件开源版图,引领本土开源走向全球

EMQ映云科技

开源 物联网 IoT emq 8月月更

Grid 布局介绍

CRMEB

用完华为云会议解决方案,我直接卸载了之前的会议软件【华为云至简致远】

科技云未来

云会议产品

基于华为云弹性云服务器ECS(搭载openEuler的鲲鹏通用计算增强型)完成鲲鹏代码迁移工具实践【华为云至简致远】

科技云未来

鲲鹏服务器 弹性云服务器ESC

科创人·优锘科技COO孙岗:错误问题找不到正确答案,求索万物可视的大美未来

科创人

国内部分手机游戏开始显示用户IP属地

郑州埃文科技

游戏 手游 IP归属地

2022纯手工打造1700道Java高级工程师面试宝典(含面试题解析)

Java工程师

Java 面试 八股文

企业“数字化转型”成功的2个必备条件!

优秀

数字化转型

运筹帷幄决胜千里,Python3.10原生协程asyncio工业级真实协程异步消费任务调度实践

刘悦的技术博客

Python 协程 Async Python3 协程原理

*打卡—>什么是云数据库&&沙箱实验如何用python和数据库来爬虫?【华为云至简致远】

科技云未来

云数据库

2022年中国全民健身发展白皮书

易观分析

行业分析 健身

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