20

使用Travis在Docker Hub上管理开源Docker镜像

 6 years ago
source link: http://www.infoq.com/cn/articles/managing-opensource-docker-images-on-docker-hub-using-travis?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

我最近正在开展一个小项目,学习使用Docker工具链,并了解其工作原理。 我决定构建(又一个) etcd 的开源Docker镜像并将其发布在Docker Hub上。 像所有开源项目一样,构建一个有效的Docker镜像非常简单,并没有花太多时间。反而是如何镜像让更多人觉得有用会花费更多的时间。 在这个过程中,我学到了一些关于使用 DockerDocker HubTravis 的内容,但仍然有一些问题没有得到解答。 因此,我认为值得记录我的发现和未解答的问题,以获得反馈并提高我的理解。

首先阐述我的要求:

  1. 支持最新版本和一些旧版本的应用程序似乎是Docker社区中的常见做法。 支持常见发行版及其精简版本似乎也是一种常见做法。 因此,该镜像应该遵循社区的做法。
  2. Github上的README自动同步到Docker Hub上的描述是必须的。 我的假设是开发人员通常会在Docker Hub上发现Docker镜像。 所以只在Github上有一个很好的自述文件是不够的。 该文档也必须存在于Docker Hub上。 我不想在我的git仓库中编写文档,然后每次有更改时手动将其复制到Docker Hub。
  3. 镜像本身应该非常易于使用,既可以作为服务器运行etcd,也可以作为CLI客户端运行。 我不打算讨论这个,因为这与本章主题有点无关。 但是如果你感兴趣的话,请查看 README 并在Github项目中打开一个issue来反馈问题。

第一个挑战是建立一个自动化流程来构建新镜像并将其上传到Docker Hub。 有一篇 很好的博客 记录了如何使用Travis实现这一目标,以便每次推送到代码库都会构建一个新镜像像,并且当合并分支时,构建的新镜像将被推送到Docker Hub。 设置很简单。

尽管简单,但它不符合我的任何要求。 下面尝试逐一解决。

问题#1 - 自动同步README

虽然上面提及到的Travis构建过程可以构建并将镜像推送到Docker Hub,但它无法同步README。 Docker CLI也不支持更新Docker Hub上的描述。

Docker Hub有一个名为 Automated Builds 的东西,通过配置,可以达到Travis作业产生的结果。 并且,它还可以从Github仓库中提取README的内容,以便在Docker Hub上作为描述使用。 因此,我们可以使用Automated Builds来构建镜像并从Github同步README。 这解决了我们的一项要求。

 7251-1533403363964.png 

Automated Builds的工作原理是我们必须在Docker Hub上配置它以指定Docker上下文的路径(即Dockerfile的路径),从该Dockerfile构建镜像并打上自己的标记。 这意味着从仓库构建的每个镜像,必须在唯一路径上存在Dockerfile。 这很快就会成为一个问题。

问题#2 - 运行测试

虽然Docker Hub上的Automated Builds解决了README同步问题,但自动构建还存在一些其他问题。 你无法将其用作运行测试和获取PR测试状态的工具。 根本没有办法从Docker Hub的构建系统获得反馈给Github。 这对于开发流程至关重要

所以有一件事是肯定的 - 如果构建失败,我们需要回到Travis运行测试并阻止PR。

问题#3 - 为多个版本构建镜像

我不仅想构建etcd的最新版本,也包括之前的一些版本,并构建这些版本的发行版本。

支持的版本:

3.3(最新)

3.2

支持的发行版本:

Debian:stretch-slim

alpine

因此,我们必须构建总共4个镜像 - 每个发行版本对应一个版本。

首先看看多版本问题。

我开始查看其他开源Docker镜像是如何实现这一点的。 结果并不令人满意。

看了MySQL镜像的Dockerfiles。 每个版本的MySQL的docker镜像都在仓库的“版本目录”中有一个专用的Dockerfile。 他们几乎没有区别。 请参阅这两个MySQL的Dockerfiles - MySQL 5.6MySQL 5.7 的Dockerfile。 在76行代码中,它们只有一行不同,并且只是版本不同。

我在 Nginx的Dockerfiles 中观察到了一个非常相似的模式。 平均而言,每个Dockerfile中每个基本镜像的每个版本只有5-10%的行不同。

这看起来像是大量的重复代码。 我不想这样,因为每个镜像中唯一不同的是版本和包管理器(Alpine和Debian使用不同的包管理器)。

Dockerfile的ARG 可用于简化此操作。 通过将值传递给docker build命令,ARG可用于定义在构建镜像时可以设置的变量。 这非常方便。 由于每个版本的Dockerfiles唯一不同的是 URL中的版本本身 ,我可以轻松地使用它并使用相同的Dockerfile和以下命令为不同版本构建docker镜像:

docker build . -t "3.3" --build-arg version=3.3
docker build . -t "3.2" --build-arg version=3.2

在带有环境矩阵的Travis中使用它,我们可以自动为多个版本构建Docker镜像。 请参阅.travis.yml中为每个版本构建多个镜像的代码段:

language: bash
services: docker
env:
  matrix:
  - VERSION=3.3.1
  - VERSION=3.2.19
script:
  - docker build . -t "$VERSION" --build-arg version=$VERSION

这解决了多版本的多镜像问题。 但我们仍然需要解决为每个版本的每个基本镜像构建镜像的问题。

问题#4 - 为多个基本镜像进行构建

除了为多个版本构建镜像,我们还存在为多个基本镜像构建镜像的问题。 现在仅仅使用ARG是不够的。

在这种情况下,挑战变成了每个发行版可能都有自己的具体操作方式,例如包管理。 Alpine和Debian使用不同的包管理器。 因此仅使用ARG不足以处理所有更改。 还需要管理如何进行包的安装。 在这种情况下,我们需要一个安装包,但不需要运行etcd。 如果我们能够以某种方式获得没有任何额外的etcd二进制文件,我们就可以制作出完美的Docker镜像。

多阶段Docker构建和构建器模式

Docker的Multi-Stage Builds 是一种干净利落地构建Docker镜像的方式。 通常在构建过程中,我们会安装大量随机包。 成功构建应用程序后,清理可能会很麻烦。

构建Docker镜像时,镜像总是越小越好。 有些人使用bash脚本编写清理指令。 有些人将开发和生产Dockerfiles分开,开发Dockerfile会产生一个较大包体的镜像,其包含开发所需的一切,生产Dockerfile会生成一个仅具有开发Docker镜像所必需工件的docker镜像。 但这两种方法都有各自的不足。

多阶段构建简化了其中的一部分,并提供了更好地组织Dockerfiles的方法。 你可以在Dockerfiles中添加多个阶段,为每个阶段构建一个完全独立的Docker镜像,并使用其他构造可以轻松地将工件从一个阶段复制到另一个阶段。 从Docker Hub上的文档中查看此示例:

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

首先,注意这个Dockerifle有多个FROM指令。 每条指令表示不同的构建阶段,从而产生完全不同的镜像。 COPY指令采用名为--from的参数,可用于从其他构建阶段复制文件。 使用此选项来复制你想要用来构建镜像的文件,并留下不需要的所有内容。

多阶段构建与ARG结合可以解决我们的问题。 现在可以同时使用version和base_image,并使用base_image和FROM指令在构建时选择基本镜像。

FROM debian:stretch-slim
...

FROM "$base_image"
...
COPY --from=0 /path/to/etcd-binary /usr/local/bin
CMD ["etcd"]

然后运行以下命令来构建所有的镜像:

# Version 3.3 on debian:stretch-slim
docker build . -t "3.3" --build-arg version=3.3 \
    --build-arg base_image="debian:stretch-slim"
# Version 3.3 on alpine:latest
docker build . -t "3.3:alpine" --build-arg version=3.3 \
    --build-arg base_image="alpine:latest"
# Version 3.2 on debian:stretch-slim
docker build . -t "3.2" --build-arg version=3.2 \
    --build-arg base_image="debian:stretch-slim"
# Version 3.2 on alpine:latest
docker build . -t "3.2:alpine" --build-arg version=3.2 \
    --build-arg base_image="alpine:latest"

在这里查看 完整的Dockerfile

这为我们解决为多版本构建镜像和多个基本镜像代码重复的问题。

Multi-Stage Builds有一些很酷的功能。 我建议检查一下,以获得更好的Docker体验。

有了这个,可以通过 环境构建矩阵 提供的不同环境来构建在Travis上的所有镜像,在每个环境合并代码到主分支,构建镜像、运行测试、推送构建好的镜像到Docker Hub。 这是一个示例.travis.yml文件(上一个示例的扩展):

language: bash
services: docker
env:
  matrix:
  - VERSION=3.3.1 BASE_IMAGE=debian:stretch-slim
  - VERSION=3.2.19 BASE_IMAGE=debian:stretch-slim
  - VERSION=3.3.1 BASE_IMAGE=alpine
  - VERSION=3.2.19 BASE_IMAGE=alpine
script:
  - |
    docker build . -t "$VERSION" \
      --build-arg version=$VERSION \
      --build-arg base_image="$BASE_IMAGE"

但同步README的问题仍然存在,并且Travis似乎也无法做到。 下面看看如何解决。

问题#5 - 捆绑在一起

我们在这里取得了以下进展:

  1. 我们知道如何为每个版本和每个基本镜像构建Docker镜像。 可用一种可管理的方式执行此操作,而无需重复代码。 并且可以在Travis上进行构建。
  2. 可以在Travis上构建镜像并推送到Docker。 但是无法在Docker Hub上获得README。
  3. 可以使用Automated Builds在Docker Hub上构建镜像,并将README同步到Docker Hub。 但是我们无法为Github上的每个pull请求构建镜像,并且如果构建失败则阻止合并pull请求。 并且不会给Github任何反馈。

因此,虽然我们可以使用Travis构建所有镜像并为每个PR运行测试,但无法使用它来推送镜像。 而对于Docker Hub则相反。 实际上,最简单形式的Docker Hub的Automated Build系统不能用于基于ARG的设置,因为Automated Builds可以使用Docker上下文,即在构建设置中为“每个指定的路径”构建镜像,并期望这些路径中存在Dockerfile。 你无法在此类设置中传递CLI参数。 因此,我们的自定义docker构建命令不适用于Automated Builds。

自定义构建阶段钩子,用于Docker Hub上的自动构建

自定义构建阶段钩子允许在不同的构建阶段执行自定义脚本,从而对自动构建进行更高级的自定义。 例如,可以覆盖构建阶段以将额外参数传递给docker构建,或者可以覆盖推送阶段以推送到多个仓库。 当然,你还可以做很多事情。

这最终解决了我们所有的问题:

  1. 我们可以使用Travis设置为每个PR构建和运行测试,但不能将镜像推送到Docker Hub。
  2. 只能在主分支上使用自动构建和自定义阶段钩子。 这将把我们的镜像推送到Docker Hub并同步来自Github的README。

162-1533403364348.jpeg  auYnMrv.jpg!web 

构建钩子脚本是很通用的。 我参考在Travis中构建镜像的脚本来编写钩子脚本!

在此处查看整个设置:

  1. Dockerfile
  2. .travis.yml
  3. 自定义构建钩子

结论

没有重复代码,易于构建的过程,良好的开发体验和README的自动同步 - 这对我来说非常有用,我对最终的设置非常满意。

有人可能会说使用Docker Automated Build和Travis很难维护。 可以通过自定义构建钩子将PR状态推送到Github,但这不是我到目前为止所探讨的,与在Docker Hub上维护Travis和Automated Builds相比,它看起来也需要更多精力。

但有一件事不确定是使用ARG构建Docker镜像的方法和多阶段构建。 虽然它适合我,但我在这个领域的经验是有限的,我想得到反馈。 你怎么看? 还有构建和分发开源Docker镜像更好的方法吗? 请通过评论部分告诉我你的想法。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK