距离上一篇 Concourse 相关的文章发布,已过去两年有余,期间因为没什么使用场景,不知道该怎么继续写下去,于是就断了。这次,我终于有机会将 Concourse 用到我自己的 home lab,并成功完成了一条 pipeline。
背景及需求 偶然在网上看到了一个可以多端直播推流的工具,叫 Ant Media Server ,但是它的安装程序并不支持我正在用的 Ubuntu 22.04 LTS
,同时它也没有提供制作好的 Docker 镜像,只能自己手动构建。可手动构建也太不优雅,根本不能忍,所以萌生了一个需求:监控 Ant Media Server
的 GitHub releases,如果有新的版本发布,那么就自动构建新的 Docker 镜像,并推送到我的 Docker Hub 中。
开始动手 首先,我要实现在 Concourse 里面监控 GitHub release。github-release 这个 resource type 就是干这件事的,所以我们可以在 pipeline 中定义这样一个 resource:
1 2 3 4 5 6 7 8 9 10 resources: - name: ant-media-server type: github-release source: owner: ant-media repository: Ant-Media-Server tag_filter: "ams-v?([^v].*)"
资源光在 resources
里面定义好还不够,我们需要在 pipeline 里面用 get
这个 task 来让 Concourse 做出从这个资源获取数据的操作。所以,开始写 pipeline 咯。
1 2 3 4 5 6 jobs: - name: build-image public: true plan: - get: ant-media-server trigger: true
这样就实现了让 Concourse 监控这个 GitHub release,并在发布新 release 的时候触发 pipeline 运行。而这个 task 在运行的时候,会将 release 中的 artifact 下载到 ant-media-server
这个目录中,所以我们也不用担心下载文件的问题。同时它还会把 release 的版本号写在 version
这个文件中,后面我们可以利用这个文件来生成 Docker 镜像的 tag。
有了 Ant Media Server 的成品文件,按照官方文档 的说法,接下来只要做两件事:下载 Dockerfile,执行 docker build
命令就行。但是放在 pipeline 里面,就没这么简单了。
先做第一件事,下载 Dockerfile。感谢 jgriff/http-resource 这个仓库,它可以实现在 Concourse 里面通过 HTTP 下载一个文件。那么接下来 pipeline 里面可以这么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 resource_types: - name: http-resource type: docker-image source: repository: jgriff/http-resource resources: - name: ant-media-server-dockerfile type: http-resource source: url: https://raw.githubusercontent.com/ant-media/Scripts/master/docker/Dockerfile_Process jobs: - name: build-image public: true plan: - in_parallel: - get: ant-media-server trigger: true - get: ant-media-server-dockerfile
现在 Dockerfile 可以下载到了,但是它是被保存在 ant-media-server-dockerfile/body
这个文件里面的,我们需要把它移动到 ant-media-server
这个目录里,才能保证后面成功运行 docker build
。所以接下来要用 mv
命令把文件移过去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 jobs: - name: build-image public: true plan: - task: move-dockerfile config: platform: linux image_resource: type: docker-image source: repository: ubuntu inputs: - name: ant-media-server - name: ant-media-server-dockerfile outputs: - name: ant-media-server run: path: mv args: ["ant-media-server-dockerfile/body" , "ant-media-server/Dockerfile" ]
有了 Dockerfile,接下来就可以开始着手构建了。不用想,对于构建 Docker 镜像这样常见的 task,Concourse 预先制作好了 concourse/oci-build-task
这个镜像来给我们用。
但是首先我们需要创建一个包含着 build args 的文件,因为文档的 docker build
命令中提到了 --build-arg AntMediaServer=<Replace_With_Ant_Media_Server_Zip_File>
这个参数,而 Ant Media Server 的 zip 文件名又会随着 release 而变化,同时 oci-build-task
的参数 BUILD_ARGS_*
并不支持 shell 命令,也就是说我不能通过 BUILD_ARGS_AntMediaServer=ant-media-server-community-$(cat version).zip
这样的方法来生成,那么只能用 oci-build-task
的 BUILD_ARGS_FILE
参数,传进去一个生成好的 build args file。
所以我们需要在 pipeline 中增加这两步来完成镜像的构建操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 jobs: - name: build-image public: true plan: - task: generate-build-args config: platform: linux image_resource: type: docker-image source: repository: ubuntu inputs: - name: ant-media-server outputs: - name: ant-media-server run: path: sh args: - -exc - 'echo "AntMediaServer=ant-media-server-community-$(cat ant-media-server/version).zip" > ant-media-server/build_args.txt' - task: build-image privileged: true config: platform: linux image_resource: type: registry-image source: repository: concourse/oci-build-task inputs: - name: ant-media-server outputs: - name: image params: CONTEXT: ant-media-server BUILD_ARGS_FILE: ant-media-server/build_args.txt caches: - path: cache run: path: build
oci-build-task
成功后,会把镜像保存到 image/image.tar
文件中。要将它上传到 Docker Hub,我们还需要定义一个 registry-image
类型的 resource,来指定要将镜像上传到哪里。
因为上传 Docker Hub 需要登陆,而把 token 写在 pipeline 里面是非常蠢的行为,所以我把登陆信息放到了 Vault 中。向 Vault 放登陆信息很简单,在 /concourse
这个 path 中新建两个 secret 就可以了:
/shared/dockerhub_username
,key 是 value
,value 填写 Docker Hub 的用户名
/shared/dockerhub_token
,key 是 value
,value 填写 Docker Hub 的 access token
之所以我把登陆信息放到 /shared
这个 path 下,是因为我在 Vault 中配置了这个 path 作为一个公共的 path,在构建的时候要根据实际情况来修改,比如改成 team 的名字,或者放在 /{team}/{pipeline}/
下面。具体请参考 Concourse 与 Vault 集成相关的文档,这里不再赘述。
放好登陆信息后,就可以添加这样一个 resource:
1 2 3 4 5 6 7 8 9 resources: - name: ant-media-server-docker type: registry-image icon: docker source: repository: "((dockerhub_username))/ant-media-server" username: "((dockerhub_username))" password: "((dockerhub_token))"
然后在 pipeline 最后增加两个 task,一个是读取 ant-media-server/version
的值,将其写在名为 tag
的变量中,后面我们会用这个变量来指定镜像的 tag;另一个就是对 ant-media-server-docker
这个资源执行 put
的操作,将 image/image.tar
这个镜像上传到 Docker Hub。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 jobs: - name: build-image public: true plan: - load_var: tag file: ant-media-server/version format: trim - put: ant-media-server-docker params: image: image/image.tar bump_aliases: true version: "((.:tag))"
完整的 pipeline 至此这个 pipeline 就完成了,下面我附上已经部署过的版本,供参考。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 --- resource_types: - name: http-resource type: docker-image source: repository: jgriff/http-resource resources: - name: ant-media-server type: github-release source: owner: ant-media repository: Ant-Media-Server tag_filter: "ams-v?([^v].*)" - name: ant-media-server-dockerfile type: http-resource source: url: https://raw.githubusercontent.com/ant-media/Scripts/master/docker/Dockerfile_Process - name: ant-media-server-docker type: registry-image icon: docker source: repository: "((dockerhub_username))/ant-media-server" username: "((dockerhub_username))" password: "((dockerhub_token))" jobs: - name: build-image public: true build_log_retention: builds: 5 plan: - in_parallel: - get: ant-media-server trigger: true - get: ant-media-server-dockerfile - load_var: tag file: ant-media-server/version format: trim reveal: true - task: move-dockerfile config: platform: linux image_resource: type: docker-image source: repository: ubuntu inputs: - name: ant-media-server - name: ant-media-server-dockerfile outputs: - name: ant-media-server run: path: mv args: ["ant-media-server-dockerfile/body" , "ant-media-server/Dockerfile" ] - task: generate-build-args config: platform: linux image_resource: type: docker-image source: repository: ubuntu inputs: - name: ant-media-server outputs: - name: ant-media-server run: path: sh args: - -exc - 'echo "AntMediaServer=ant-media-server-community-$(cat ant-media-server/version).zip" > ant-media-server/build_args.txt' - task: build-image privileged: true config: platform: linux image_resource: type: registry-image source: repository: concourse/oci-build-task inputs: - name: ant-media-server outputs: - name: image params: CONTEXT: ant-media-server BUILD_ARGS_FILE: ant-media-server/build_args.txt caches: - path: cache run: path: build - put: ant-media-server-docker params: image: image/image.tar bump_aliases: true version: "((.:tag))"