HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

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

评论

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

竟然有人想看我的「日记」,满足一下大家

非著名程序员

学习 程序人生 提升认知

回“疫”录(22):我以为结束了,其实才开始

小天同学

疫情 回忆录 现实纪录 纪实

nginx 概念及上手

HelloZyjS

Android | Tangram动态页面之路(五)Tangram原理

哈利迪

android

程序员需要了解的硬核知识大全

苹果看辽宁体育

Java c 计算机基础

我的编程之路-4(进阶)

顿晓

进阶 看书 编程之路

敏捷为什么会失败之「PA-SA-WAKA-DA」理论

易成研发中心

Scrum 敏捷开发 Agile

2020年全球经济萎缩,火花国际PLUS逆袭而来闪耀数字经济

极客编

企业数字化转型:用 SpreadJS 打造互通互链的电力系统物联网

葡萄城技术团队

数字化转型 SpreadJS 电力

ARTS_20200520

凌轩

Java ARTS 打卡计划

Elastic Stack 系列专辑

Yezhiwei

elasticsearch Logstash Kibana ELK Elastic Stack

JVM源码分析之Java对象头实现

猿灯塔

深入剖析ThreadLocal原理

JFound

Java

Redis 命令执行过程(下)

程序员历小冰

redis 源码分析

万字长文带你看懂Mybatis缓存机制

程序员小岑

Java 源码 技术 mybatis

如何做好 To B 的 SAAS 服务

路边水果摊

SASS 企业 服务

为提升网点业务员效率,我们做的事情。

黄大路

商业

JavaScript 基础拾遗(一)

hq

Java 学习 文章收集

天天都是520

Neco.W

爱情 表白日

关于架构的几件小事:System context

北风

系统架构 系统性思考 架构师 系统上下文 极客大学架构师训练营

SpringBoot瘦身

JFound

Spring Boot sprnig

职场“潜”规则

L3C老司机

个人成长 职场 新人 人才培养 能力模型

识别代码中的坏味道(四)

Page

敏捷开发 面向对象 重构 CleanCode 代码坏味道

kotlin 200行代码开发一个简化版Guice

陈吉米

Java kotlin guice ioc mynlp

SQLite是什么

山楂大卷

sqlite 数据库 RDBMS 存储

JVM源码分析之synchronized实现

猿灯塔

推动敏捷,就是推动软件业变革

盛安德软件

敏捷 推动软件业变革

Redis6.0 多线程源码分析

代码诗人

redis 源码 技术 线程模型

Django的ListView超详细用法(含分页paginate功能)

BigYoung

Python django ListView 分页

Enhanced Github:一个 GitHub 专用的好插件

非著名程序员

GitHub 程序员 效率工具

当我们持续感觉很糟糕要怎么办

董一凡

写作 生活质量 情感

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