Amazon ECR 提供开箱即用的镜像存储服务,在云原生应用程序构建交付中可以使用极狐 GitLab CI/CD 将构建的镜像推送到 Amazon ECR。本文将掩饰如何使用极狐 GitLab CI/CD 来完成著名的 [bookinfo](https://github.com/istio/istio/tree/master/samples/bookinfo?trk=cndc-detail) 中所有镜像的构建并推送到 Amazon ECR。
### **Amazon ECR**
Amazon ECR(Elastic Container Registry)是亚马逊云科技托管容器映像注册表服务,可以将构建的镜像推送到 ECR 进行托管。
ECR 的创建非常方便,在 Services 中查找到 ECR,然后选择 **Create repoistory**,之后输入 repository name 创建即可:
![image.png](https://dev-media.amazoncloud.cn/8d2af0d6b78e4a818ebc96d93f0ef295_image.png "image.png")
接着使用 `docker` 命令就可以进行镜像的构建、推送了。ECR 的身份验证需要用到 amazon CLI 的 `get-login-password` 命令:
```
\$ aws ecr get-login-password --region cn-north-1 | docker login --username AWS --password-stdin your-account-id.dkr.ecr.cn-north-1.amazonaws.com.cn
```
正确使用 amazon CLI 之前需要进行配置,会用到 `Amazon Access Key ID` 和 `Amazon Secret Access Key` 两个变量。这两个值可以在 My Security Credentials --> Amazon IAM Credentials --> Create Access key 来创建:
![image.png](https://dev-media.amazoncloud.cn/1d887b6ba08d454aa7261fd9d3017a83_image.png "image.png")
使用创建的两个 Key 进行 amazon configure:
```
\$ aws configure
AWS Access Key ID [****************6AQJ]: YOUR-AWS-Access-Key-ID
AWS Secret Access Key [****************IU0C]: YOUR-AWS-Secret-Access-Key
Default region name [cn-north-1]: cn-north-1
Default output format [json]: json
```
接着执行:
```
\$ aws ecr get-login-password --region cn-north-1 | docker login --username AWS --password-stdin your-account-id.dkr.ecr.cn-north-1.amazonaws.com.cn
```
即可完成 ECR 的身份验证,随后就可以将镜像通过 push 命令上传到 ECR 并进行托管。
### **极狐 GitLab CI/CD 构建 bookinfo 镜像**
使用极狐 GitLab CI/CD 进行容器镜像构建可以使用 docker build 的方式:
```
build:
image: docker:latest
stage: build
services:
- docker:20.10.7-dind
script:
- docker login -u "\$CI_REGISTRY_USER" -p "\$CI_REGISTRY_PASSWORD" \$CI_REGISTRY
- docker build -t \$CI_REGISTRY_IMAGE:1.0.0 .
- docker push \$CI_REGISTRY_IMAGE:1.0.0
```
也可以使用更加安全的 kaniko 方式:
```
build:
stage: build
tags:
- k3s
image:
name: registry.jihulab.com/jh-xiaomage-devops/go-demo/kaniko:debug
entrypoint: [""]
script:
- mkdir -p /kaniko/.docker
- echo "{\\"auths\\":{\\"\${CI_REGISTRY}\\":{\\"auth\\":\\"\$(printf "%s:%s" "\${CI_REGISTRY_USER}" "\${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\\n')\\"}}}" > /kaniko/.docker/config.json
- >-
/kaniko/executor
--context "\${CI_PROJECT_DIR}"
--dockerfile "\${CI_PROJECT_DIR}/Dockerfile"
--destination "\${CI_REGISTRY_IMAGE}:1.0.0"
```
关于这两种方式的优劣以及使用详情可以查看文章[云原生时代,保证容器镜像安全分几步](https://mp.weixin.qq.com/s/zLuDjvB_Eo86ChQFNK2Oqg?trk=cndc-detail)?
如果将 bookinfo Demo 的镜像全部构建出来并推送到 ECR,会遇到两个问题:
- 变量(敏感信息)在极狐 GitLab CI/CD Pipeline 中的传递。
将 Amazon ECR 的身份认证和极狐 GitLab CI/CD Pipeline 相结合的过程,需要分两步走: amazon ecr get-login-password 和 docker login/build/push。因此第一个 job 需要先获取 ECR 认证 token,然后将 token 传递给第二个 job 当作 docker login 所需的 password
- 同样的构建过程/命令需要执行多次
bookinfo 有 6 个镜像,如何用更加精简的 Pipeline 代码来完成工作,尽量减少重复的劳动(copy & paste)
针对第一个问题,可以使用 `artifacts` 来完成环境变量在极狐 GitLab CI/CD Job 之间的传递;
针对第二个问题,可以将重复执行的代码抽象成 `template`,然后用 `include` 引入即可。下面分别讲述用法。
#### **artifacts 关键字完成环境变量在 Job 间的传递**
极狐 GitLab CI/CD Job 能够输出一些文件或者目录,而这些文件或者目录就被称之为 Job artifacts。可以将上一个 Job 的内容输出到 artifacts,然后第二个 Job 可以直接读取 artifacts 里面的内容,可以用这个思路完成 Amazon ECR 的鉴权验证并完成镜像的构建与推送。极狐 GitLab CI/CD `.gitlab-ci.yml` 文件内容如下:
```
variables:
DOCKER_REGISTRY: 719293495888.dkr.ecr.cn-north-1.amazonaws.com.cn
BASIC_PATH: .
IMAGE_TAG: 1.0.0
IMAGE_NAME: bookinfo
stages:
- pre-work
- build
get_credentials:
stage: pre-work
tags:
- aws
image:
name: amazon/aws-cli
entrypoint: [""]
script:
- aws ecr get-login-password --region cn-north-1 > ./credentials.json
artifacts:
paths:
- credentials.json
expire_in: 1 hour
image_build:
stage: build
image: .docker:20.10.7-dind
dependencies:
- get_credentials
script:
- cat credentials.json | docker login --username AWS --password-stdin \$ECR_TOKEN \$DOCKER_REGISTRY
- cd \$BASIC_PATH
- docker build -t \$DOCKER_REGISTRY/\$IMAGE_NAME:\$IMAGE_TAG .
- docker push \$DOCKER_REGISTRY/\$IMAGE_NAME:\$IMAGE_TAG
```
第一个 Job `get_credentials` 获取到 Amazon ECR 的 Token,将 Token 存储到 `actifacts`,然后第二个 Job 可以读取到 Token 并使用 `docker login & docker build & docker push` 完成镜像的构建并推送到 Amazon ECR。
> Token 属于敏感信息,使用 artifacts 的方式不安全,不建议使用这种方式进行敏感信息传递。可以先手动获取 Amazon ECR Token,然后添加到极狐 GitLab CI/CD variables 中,然后在 CI/CD 中直接引用。本文这么做,仅仅是掩饰 artifacts 的用法之一。
![image.png](https://dev-media.amazoncloud.cn/6564bb0bf65448d6a87256716891a99c_image.png "image.png")
#### **include 插入 template,减少代码冗余**
由于 istio 的 bookinfo sample 中有 6 个镜像需要构建,details、mongodb、mysql、productpage、ratings 以及 reviews:
```
.
├── build-services.sh
├── details
├── mongodb
├── mysql
├── productpage
├── ratings
└── reviews
6 directories, 1 file
```
也即意味着需要每次都执行下面构建镜像的代码:
```
image_build:
stage: build
image: .docker:20.10.7-dind
dependencies:
- get_credentials
script:
- docker login --username AWS --password-stdin \$ECR_TOKEN \$DOCKER_REGISTRY
- docker build -t \$DOCKER_REGISTRY/\$IMAGE_NAME:\$IMAGE_TAG .
- docker push \$DOCKER_REGISTRY/\$IMAGE_NAME:\$IMAGE_TAG
```
这种方式不仅会导致 `.gitlab-ci.yml` 文件的冗长,而且重复度很高。在这种情况下,可以使用极狐 GitLab template 语法,将重复执行的代码抽象成一个 template,然后存储起来,在任何极狐 GitLab CI/CD 中可以直接使用 `include` 语法引入使用,大大减少了代码冗余,而且方便在团队或者组织内容推广。
可以将第一步中的代码写入一个 yml 文件,然后存储起来。在使用的时候使用 `include` 语法引入即可。使用 `include` 之后的代码如下:
```
variables:
CS_ANALYZER_IMAGE: registry.gitlab.cn/security-products/container-scanning/trivy:4
SECURE_ANALYZERS_PREFIX: "registry.gitlab.cn/security-products"
IMAGE_TAG: "1.0.0"
stages:
- pre-work
- build
- test
include:
- remote: 'https://jihulab.com/xiaomage/teamplates/raw/main/docker-image-build-tamplates.yml'
.image_build:
image: docker:latest
stage: build
ratings_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/ratings"
IMAGE_NAME: "ratings"
productpage_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/productpage"
IMAGE_NAME: "productpage"
details_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/details"
IMAGE_NAME: "details"
reviews_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/reviews/reviews-wlpcfg"
IMAGE_NAME: "reviews"
mongodb_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/mongodb"
IMAGE_NAME: "mongodb"
mysql_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/mysql"
IMAGE_NAME: "mysql"
```
开头 `include` 的 `template` 就是将与 Amazon ECR 鉴权验证 & 镜像构建的过程抽象出来的内容,在第一部分中已经有展示。触发 Pipeline 之后的构建结果如下:
![image.png](https://dev-media.amazoncloud.cn/e7a509cf96a34e3f9c89c5df6f7a7751_image.png "image.png")
这样就可以在 Amazon ECR 中看到推送的镜像:
![image.png](https://dev-media.amazoncloud.cn/133b2e511cd1429a9d5b80dafa8b42dc_image.png "image.png")