亚马逊AWS官方博客

使用反向代理在混合云场景下进行Amazon ECR 镜像推拉

Amazon Elastic Container Registry(ECR)

Amazon Elastic Container Registry (Amazon ECR) 是一种 AWS 托管容器映像注册表服务,安全、可扩展且可靠。Amazon ECR 使用 AWS 支持具有基于资源的权限的私有容器映像存储库。IAM这样,指定用户或 Amazon EC2 实例就可以访问您的容器存储库和映像。您可以使用首选 CLI 来推送、拉取和管理 Docker 映像、开放容器计划 (OCI) 映像和兼容 OCI 的构件。

 

混合云(Hybrid Cloud)

随着各种行业把越来越多的应用从本地数据中心(私有云)搬迁到云上,形成了公有云和私有云融合的场景 – 混合云。出于安全考虑,企业会将数据存放在私有云中,但是同时又希望可以获得公有云的计算资源,在这种情况下混合云被越来越多的采用,它将公有云和私有云进行混合和匹配,以获得最佳的效果,这种个性化的解决方案,达到了既省钱又安全的目的。

 

方案描述

在混合云的场景下,基于以下原因:

  1. 不希望改变原有的持续集成的流程和工具,比如Jenkins持续集成流水线(CI);
  2. 基于客户本身的安全原则,代码和脚本只能保留在本地,只允许最终的镜像上传到云端

我们希望在本地数据中心进行docker镜像(images)的构建,然后通过VPN或是MPLS专线,而不是通过因特网,把构建好的镜像推送到云端的私有镜像仓库,如Amazon Elastic Container Registry(ECR)。

Amazon Elastic Container Registry(ECR)属于AWS的公共服务(public services)

  1. 在AWS VPC内部可以使用VPC endpoints的解决方案使VPC内的应用可以私密安全地访问ECR,
  2. 在已经搭建好MPLS专线连通本地数据中心和AWS云的客户,可以使用AWS Direct Connect的Public Virtual Interface来访问ECR
  3. 通过VPN,或者在测试阶段不希望占用MPLS带宽的场景下,可以通过在AWS云端设置反向代理,本地数据中心通过反向代理对ECR进行安全私密地访问

本方案描述如何在AWS云端构建反向代理(Nginx)来对ECR进行安全私密访问。

总体的架构设计如下:

本方案包括三个部分:

  • VPC Private Link resources的创建
    1. AWS PrivateLink endpoints for ECR – 允许反向代理服务器通过Privatelink访问ECR, ECR需要创建两个interface endpoints (本方案以新加坡区域为示例):
      • amazonaws.region.ecr.api
      • amazonaws.region.ecr.dkr
    1. Gateway VPC endpoint for S3 – AWS ECR的镜像实际上由背后的S3存储桶来存储镜像layers,S3 VPC endpoint允许反向代理服务器访问镜像S3存储桶:
      • amazonaws.region.s3(本方案以新加坡区域为示例)
  • 反向代理服务器,包括以下配置
    • 代理ECR service endpoint(本方案以新加坡为例):

api.ecr.<region>.amazonaws.com

    • 代理ECR仓库endpoint

<account>.dkr.ecr.<region>.amazonaws.com

    • 代理底层S3存储桶

prod-<region>-starport-layer-bucket.s3.<region>.amazonaws.com

  • 本地数据中心服务器,如jenkins服务器,本方案在VPC内部创建EC2,并配置所在子网为纯私有子网(无到igw和nat的路由记录)来模拟本地数据中心服务器

 

创建ECR PrivateLink interface endpoints和S3 gateway endpoint

在即将创建代理服务器所在的VPC,为以下两个服务创建interface endpoints:

    • amazonaws.region.ecr.api
    • amazonaws.region.ecr.dkr

为终端节点启用私有DNS名称:
为S3创建网关interface endpoint:

配置代理服务器EC2

本方案使用一台c5.large的AWS EC2(Amazon Linux2)作为反向代理服务器,并为该服务器分配2个私有辅助ip。该服务器的三个私有ip分别作为模拟代理S3,ECR API和ECR 仓库终端节点的三个服务器(S3,ECR API和ECR仓库终端节点可以分别使用单独的服务器)。

创建测试ECR仓库

于控制台创建测试ECR仓库

获取仓库的推送地址,地址的格式为 <account>.dkr.ecr.<region>.amazonaws.com

在代理服务器EC2上安装配置nginx

http server配置

三个监听于80端口的server分别对应如下proxy_pass

  • https://460811381319.dkr.ecr.ap-southeast-1.amazonaws.com
  • https://api.ecr.ap-southeast-1.amazonaws.com
  • https://prod-ap-southeast-1-starport-layer-bucket.s3.ap-southeast-1.amazonaws.com

完整配置参考如下:

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;
    server_names_hash_bucket_size 128;
    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80;
        #server_name  460811381319.dkr.ecr.ap-southeast-1.amazonaws.com;
        server_name localhost;
        root         /usr/share/nginx/html;

        location / {
            root   html;
            index  index.html index.htm;
            proxy_pass https://460811381319.dkr.ecr.ap-southeast-1.amazonaws.com;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }


    server {
        listen       80;
        listen       [::]:80;
        server_name  api.ecr.ap-southeast-1.amazonaws.com;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
  location / {
            root   html;
            index  index.html index.htm;
            proxy_pass https://api.ecr.ap-southeast-1.amazonaws.com;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header X-Forwarded-Proto $scheme;
    }
}
    server {
        listen       80;
        server_name  prod-ap-southeast-1-starport-layer-bucket.s3.ap-southeast-1.amazonaws.com;
        root         /usr/share/nginx/html;

        location / {
            root   html;
            index  index.html index.htm;
            proxy_pass https://prod-ap-southeast-1-starport-layer-bucket.s3.ap-southeast-1.amazonaws.com;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

nginx stream配置

配置三个upstream:

  • server 460811381319.dkr.ecr.ap-southeast-1.amazonaws.com:443
  • server api.ecr.ap-southeast-1.amazonaws.com:443
  • server prod-ap-southeast-1-starport-layer-bucket.s3.ap-southeast-1.amazonaws.com:443

三个server分别对应三个辅助ip,完整配置参考如下:

stream {
    upstream aws-sg-ecr {
        hash $remote_addr consistent;
        server 460811381319.dkr.ecr.ap-southeast-1.amazonaws.com:443 weight=5;
    }

   upstream aws-sg-ecr-api {
        hash $remote_addr consistent;
        server api.ecr.ap-southeast-1.amazonaws.com:443 weight=5;
    }

   upstream aws-sg-s3 {
        hash $remote_addr consistent;
        server prod-ap-southeast-1-starport-layer-bucket.s3.ap-southeast-1.amazonaws.com:443 weight=5;
    }

    server {
        listen 172.31.1.93:443;
        proxy_connect_timeout 300s;
        proxy_timeout 300s;
        proxy_pass aws-sg-ecr;
    }
   server {
        listen 172.31.1.167:443;
        proxy_connect_timeout 300s;
        proxy_timeout 300s;
        proxy_pass aws-sg-ecr-api;
    }
   server {
        listen 172.31.1.98:443;
        proxy_connect_timeout 300s;
        proxy_timeout 300s;
        proxy_pass aws-sg-s3;
    }

重启nginx并确认nginx服务正常运行,三个辅助ip的443端口均在监听:

配置模拟客户端

为实现模拟在本地数据中心对云上ECR镜像的推拉,我们在同一VPC创建一个新子网(如果在不同VPC内,需做VPC对等连接),并启动一台新的EC2机器于该子网,确保在以下步骤完成之前,该EC2能被ssh连接并可以安装必须软件包(这里我们把该子网的路由表配置为公有子网对应路由表):

    1. 赋予EC2具有ECR读写权限的role(如无该role请参考ECR security章节创建)
    2. ssh进入EC2机器
    3. 安装并启动docker(yum install -y docker)
    4. 使用docker pull 拉取mysql镜像 (docker pull mysql)

确认代理服务器三个辅助ip可以通过端口443被连接:

配置/etc/hosts如下:


该配置目的是使得在ecr的操作中能把对应的api解析到对应的代理服务器的http proxy pass和upstream server。

更改模拟服务器子网路由表,令该EC2只有本地路由记录,参考如下:

从同一VPC(或已做对等连接的VPC)的其他EC2,ssh进入该模拟服务器(注意安全组设置需对这些EC2开放22端口)。

此时在模拟服务器执行yum update应超时。

查看此前已经pull的mysql镜像:

将该镜像docker tag成符合ECR命名规则的镜像:

docker tag 0627ec6901db 460811381319.dkr.ecr.ap-southeast-1.amazonaws.com/helloworld:latest

登录ECR

aws ecr get-login-password –region ap-southeast-1 | docker login –username AWS –password-stdin 460811381319.dkr.ecr.ap-southeast-1.amazonaws.com

推送镜像到ECR

docker push 460811381319.dkr.ecr.ap-southeast-1.amazonaws.com/helloworld:v0.1

确认镜像已经被推送到了ECR仓库:

删除模拟服务器helloworld镜像,并从ECR拉取:

docker pull 460811381319.dkr.ecr.ap-southeast-1.amazonaws.com/helloworld:v0.1

 

总结

在混合云的场景下,在私有云/本地数据中心对Amazon ECR不通过internet进行操作,除了可以使用Direct Connect + Public VIF的形式来实现之外,还可以在测试环境中利用搭建一个反向代理服务器,来代理ECR和S3的TCP和HTTP请求,让测试和生产分离,不影响生产的Direct Connect连接;在没有搭建MPLS专线的场景下,也可以使用VPN + 反向代理的模式,来达到在私有云进行镜像构建,通过代理将镜像安全地推送到公有云的效果。

 

参考:

  • 如何通过DX public vif连接到private network

https://aws.amazon.com/premiumsupport/knowledge-center/connect-private-network-dx-vif/

  • 创建public vif

https://docs.aws.amazon.com/directconnect/latest/UserGuide/create-vif.html#create-public-vif

  • ECR service endpoint

https://docs.aws.amazon.com/general/latest/gr/ecr.html

  • 使用ECR VPC PrivateLink

https://docs.aws.amazon.com/AmazonECR/latest/userguide/vpc-endpoints.html

 

本篇作者

李俊杰

AWS解决方案架构师,负责基于AWS的云计算方案的咨询与架构设计,同时致力于容器方面研究和推广。在加入AWS之前曾在金融行业IT部门负责传统金融系统的现代化改造,对传统应用的改造,容器化具有丰富经验。