距离上一篇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))"