集成 Concourse-CI 和 Atlassian Open DevOps
许多团队使用自己的工具来满足其需求,或者使用他们已经使用了多年的传统工具。这些工具对于团队遵循的开发流程至关重要,但却没有与 Jira 的现成集成。所幸的是,使用云开发人员文档 —《Atlassian 开发人员》中提供的 Atlassian REST API 构建自定义集成非常简单。Concourse-CI 是一款 CI/CD 产品,在撰写本文时,尚未集成到 Atlassian Marketplace 中。本文演示了如何使用 Atlassian REST API 在 Jira 和 Concourse-CI 之间构建基本的集成。
先决条件
使用必要的文档来设置 Docker、docker-compose 和 Concourse-CI。Concourse-CI 在 Docker 上运行,并提供用于简化入门流程的 docker-compose 脚本。
在此处阅读 Atlassian ImageLabeller 演示应用。本文演示了如何使用 Concourse-CI 将 ImageLabeller 的 SubmitImage 组件部署到 AWS 中。
Docker
按照相关文档设置 Docker 和 docker-compose:
Docker:https://docs.docker.com/get-docker/
docker-compose: https://docs.docker.com/compose/install/
Concourse-CI
安装 Docker 和 docker-compose 后,您可以使用提供的 docker-compose.yml 文件启动 concourse-CI。
按照 Concourse-CI 快速入门指南开始:https://concourse-ci.org/quick-start.html#docker-compose-concourse。本指南要求将证书安全地传递给 Concourse-CI。为此,本指南使用 Concourse-CI AWS Secrets Manager 集成。
Concourse-CI 与 AWS Secrets Manager 集成
以下是关于如何集成 Concourse-CI 与 AWS Secrets Manager 的文档。按照文档中的说明启用集成并开始使用。
为了使集成正常工作,需对用于启动 Concourse-CI 的 docker-compose.yml 文件稍作修改。采用由 Concourse-CI 提供的默认 docker-compose.yml 文件,并添加
CONCOURSE_AWS_SECRETSMANAGER_ACCESS_KEY、CONCOURSE_AWS_SECRETSMANAGER_SECRET_KEY 和 CONCOURSE_AWS_SECRETSMANAGER_REGION。
version: '3'
services:
concourse-db:
image: postgres
environment:
POSTGRES_DB: concourse
POSTGRES_PASSWORD: concourse_pass
POSTGRES_USER: concourse_user
PGDATA: /database
concourse:
image: concourse/concourse
command: quickstart
privileged: true
depends_on: [concourse-db]
ports: ["8080:8080"]
environment:
CONCOURSE_POSTGRES_HOST: concourse-db
CONCOURSE_POSTGRES_USER: concourse_user
CONCOURSE_POSTGRES_PASSWORD: concourse_pass
CONCOURSE_POSTGRES_DATABASE: concourse
CONCOURSE_EXTERNAL_URL: http://localhost:8080
CONCOURSE_ADD_LOCAL_USER: test:test
CONCOURSE_MAIN_TEAM_LOCAL_USER: test
# instead of relying on the default "detect"
CONCOURSE_WORKER_BAGGAGECLAIM_DRIVER: overlay
CONCOURSE_CLIENT_SECRET: Y29uY291cnNlLXdlYgo=
CONCOURSE_TSA_CLIENT_SECRET: Y29uY291cnNlLXdvcmtlcgo=
CONCOURSE_X_FRAME_OPTIONS: allow
CONCOURSE_CONTENT_SECURITY_POLICY: "*"
CONCOURSE_CLUSTER_NAME: tutorial
CONCOURSE_WORKER_CONTAINERD_DNS_SERVER: "8.8.8.8"
CONCOURSE_WORKER_RUNTIME: "containerd"
CONCOURSE_ENABLE_ACROSS_STEP: "true"
CONCOURSE_ENABLE_PIPELINE_INSTANCES: "true"
CONCOURSE_AWS_SECRETSMANAGER_ACCESS_KEY: <add access key>
CONCOURSE_AWS_SECRETSMANAGER_SECRET_KEY: <add secret key>
CONCOURSE_AWS_SECRETSMANAGER_REGION: <add a region>
设置集成后,且 Concourse-CI 在启用集成的情况下运行时,将以下密钥添加到 AWS Secrets Manager 中。
/concourse/main/bitbucket_username
/concourse/main/bitbucket_api_key
/concourse/main/bitbucket_ssh_key
/concourse/main/docker_username
/concourse/main/docker_api_key
/concourse/main/AWS_ACCESS_KEY_ID
/concourse/main/AWS_SECRET_ACCESS_KEY
/concourse/main/AWS_DEFAULT_REGION
如果读者未使用 Bitbucket 作为其代码,也未使用 JFrog 作为 Docker 存储库,则可能需要更换 Bitbucket 和 Docker 密钥。我们将此密钥调整,以适应读者的个人工具作为一项练习。
![bitbucket 和 docker 密钥](https://wac-cdn.atlassian.com/dam/jcr:6a88cc07-2be7-4e75-b541-dafadefa8a16/bitbucket-and-docker-secrets.png?cdnVersion=2569)
使用 Concourse-CI
在运行 docker-compose up -d 之前和之后运行 docker ps -a,以便检查 Concourse-CI 是否正常启动。
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker-compose up -d
Creating network "restapiproject_default" with the default driver
Creating restapiproject_concourse-db_1 ... done
Creating restapiproject_concourse_1 ... done
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bd2b5afd0ac7 concourse/concourse "dumb-init /usr/loca…" 3 seconds ago Up 2 seconds 0.0.0.0:8080->8080/tcp restapiproject_concourse_1
bd9005b45636 postgres "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 5432/tcp restapiproject_concourse-db_1
在运行 fly -t tutorial login -c http://localhost:8080 -u test -p test 之后,转至http://localhost:8080/
fly -t tutorial login -c http://localhost:8080 -u test -p test
logging in to team 'main'
target saved
目前还没有定义 pipelines。
hello world pipeline
通过关注这个 Concourse-CI 文档:https://concourse-ci.org/tutorial-hello-world.html,设置 hello world pipeline。这是引入 Fly cli 和习惯从命令行使用 Concourse-CI 的必要条件。
下一节演示如何使用 Concourse-CI 将写入 Golang 中的 AWS Lambda 部署到单个 AWS 区域,以及如何将 Jira 事务更新编写为该流程的一部分。
使用 Concourse-CI 部署 Submitimage
使用 Concourse-CI 部署 Submitimage Lambda 有三个步骤。第一步是编写一个简单的 bash 脚本,该脚本使用 Jira Cloud REST API 在 Jira 事务中写入评论。这是我们可以创建的最简单的集成。第二步是使用构建和部署 Golang AWS Lambda 所必备的工具创建 Docker 镜像。最后一步是编写两个 Concourse-CI 配置文件。第一个配置文件为 parent.yml,用于监控 SubmitImage 存储库中是否存在新分支,并创建新 pipelines,以部署来自这些分支的提交。第二个配置文件为 child.yml,用于定义部署变更所需的一组步骤。
第 1 步 — 通过 REST API 更新 Jira 事务
此更新脚本使用 Jira Cloud 平台 REST API 编写针对特定 Jira 事务的评论。每次运行该脚本时都必须设置五个必需的参数。每次运行特定管道时,jiraUsername、JiraApiToken 和工作区通常都是相同的。issueKey 将取决于正在部署的特定分支的名称。Jira 的最佳做法就是将 Jira 事务 ID 放在分支名称中,并在处理特定事务时提交消息。本文假设采用最佳实践,且分支名称相当于 Jira 事务 ID。
如何找到参数
Jira 用户名
Jira 用户名是用于登录 Jira 的电子邮件地址。
Jira API 令牌
转至 “帐户设置”
![JIRA API 令牌帐户设置](https://wac-cdn.atlassian.com/dam/jcr:8a27e9fc-bdc1-4018-a6e7-c62c20d29f61/Jira-API-Tokens-account-settings.png?cdnVersion=2569)
单击“安全”
![帐户安全](https://wac-cdn.atlassian.com/dam/jcr:3d789977-82be-4b56-b775-b5cdf087b041/Account-security.png?cdnVersion=2569)
单击“创建和管理 API 令牌”
工作区
假设有一个 Jira 实例 URL 为: https://pmmquickstartguides01.atlassian.net/jira/software/projects/IM/boards/1?selectedIssue=IM-203,工作区为 pmmquickstartguide01。
事务密钥
假设有一个 Jira 实例 URL 为: https://pmmquickstartguides01.atlassian.net/jira/software/projects/IM/boards/1?selectedIssue=IM-203,Jira 事务密钥为 IM-203。将 Jira 事务 ID 置于提交消息和分支名称中,这样集成就可以将更新写入正确的位置。
评论
评论可以是任何内容。
更新脚本
更新脚本是一个简单的 bash shell 脚本,该脚本使用 Jira 事务评论 REST API 端点。这里是 API 调用文档。可以修改该脚本,以便通过遵循所提供的进行额外 API 调用的模式来实现更深入的集成。将此脚本复制到名为 concourse-ci-integration.sh 的文件中,然后将其放入名为 updateScript 的 Bitbucket 或 GitHub 代码存储库中。
#!/usr/bin/env bash
addCommentToIssue() {
printf "addCommentToIssue\n"
local jiraUsername=$1
shift
local jiraApiToken=$1
shift
local workspace=$1
shift
local issueKey=$1
shift
local comment=$1
shift
curl -s --request POST \
--url 'https://'"${workspace}"'.atlassian.net/rest/api/3/issue/'"${issueKey}"'/comment' \
--user "${jiraUsername}"':'"${jiraApiToken}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{
"body": {
"type": "doc",
"version": 1,
"content": [{
"type": "paragraph",
"content": [{
"text": "'"${comment}"'",
"type": "text"
}]
}]
}
}' | jq
}
main() {
printf "main\n"
while getopts ":c:k:o:u:t:w:" opt; do
case $opt in
c)
local comment=$OPTARG
;;
k)
local issueKey=$OPTARG
;;
o)
local op=$OPTARG
;;
u)
local jiraUsername=$OPTARG
;;
t)
local jiraApiToken=$OPTARG
;;
w)
local workspace=$OPTARG
;;
*)
printf "invalid option: -${OPTARG}\n" >&2
exit 1
;;
esac
done
case $op in
ac)
addCommentToIssue ${jiraUsername} ${jiraApiToken} ${workspace} ${issueKey} "${comment}"
;;
*)
printf "invalid op: ${op}\n" >&2
exit 1
;;
esac
}
main "$@"
第 2 步 — 自定义 Dockerfile
使用构建和部署写入 Golang 中的 AWS Lambda 所需工具创建自定义 Dockerfile。Dockerfile 用于安装一些实用程序,然后添加 AWS SAM 和 Golang。镜像 Git 可从 Bitbucket 存储库中克隆在步骤 1 中创建的更新脚本。您需要用您所创建的任意存储库替换这个 Bitbucket 存储库,以便保存更新脚本。Docker 会构建这个 Dockerfile 并将其推送给 Docker 存储库。
Dockerfile
# syntax = docker/dockerfile:1.3
FROM ubuntu:20.04
MAINTAINER wmarusiak@atlassian.com
WORKDIR /workspace
RUN apt-get update \
&& apt-get -y upgrade \
&& apt-get -y install curl unzip tar openssh-client git jq
RUN curl https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip -L -o aws-sam-cli.zip \
&& mkdir sam-installation \
&& unzip aws-sam-cli.zip -d sam-installation \
&& ./sam-installation/install \
&& sam --version
RUN curl https://go.dev/dl/go1.18.2.linux-amd64.tar.gz -L -o go.tar.gz \
&& rm -rf /usr/local/go \
&& tar -C /usr/local -xzf go.tar.gz
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan bitbucket.org >> ~/.ssh/known_hosts
RUN --mount=type=ssh git clone git@bitbucket.org:pmmquickstartguides01/updatescript.git
ENV PATH $PATH:/usr/local/go/bin
第 3 步 — Concourse-CI 部署管道 yaml 文件
创建一个名为 concourse 的新存储库,并在其中放入两个文件。Parent.yml 会创建一个管道,用于监控存储库中是否存在与正则表达式相匹配的新分支。branch_regex 是 IM-*,可匹配所有以 IM- 开头的分支。当创建了与正则表达式 IM-* 相匹配的新分支时,parent.yml 将使用 child.yml 中的配置创建一个新管道。您应该更新这个文件,以指向您自己的 SubmiTimag 代码存储库版本。您可以在这里找到 SubmitImage 代码。将此存储库的代码复制到具有写入权限的存储库中。
parent.yml
resource_types:
- name: git-branches
type: registry-image
source:
repository: aoldershaw/git-branches-resource
resources:
- name: feature-branches
type: git-branches
source:
uri: git@bitbucket.org:pmmquickstartguides01/submitimage.git
branch_regex: IM-*
private_key: ((bitbucket_ssh_key))
- name: examples
type: git
source:
uri: git@bitbucket.org:pmmquickstartguides01/concourse.git
private_key: ((bitbucket_ssh_key))
jobs:
- name: set-feature-pipelines
plan:
- in_parallel:
- get: feature-branches
trigger: true
- get: examples
- load_var: branches
file: feature-branches/branches.json
- across:
- var: branch
values: ((.:branches))
set_pipeline: dev
file: examples/child.yml
vars: {branch: ((.:branch.name))}
Child.yml 定义了一个包含四个步骤的管道。首先,它会运行 sam validate 来验证 AWS CloudFormation template.yml 文件是否有效。然后,它会运行 sam build 将软件包 SubmiTimage 构建成可部署的工件。第三,它会运行 sam deploy 将更新后的 SubmiTimage 代码部署到 AWS 中。最后,管道会调用在步骤 1 中编写的更新脚本,以便将评论写回到相匹配的 Jira 事务中。
child.yml
resources:
- name: repo
type: git
source:
uri: git@bitbucket.org:pmmquickstartguides01/submitimage.git
branch: ((branch))
private_key: ((bitbucket_ssh_key))
jobs:
- name: deploy-submit-image
plan:
- get: repo
trigger: true
- task: run-sam-validate
config:
platform: linux
image_resource:
type: registry-image
source:
repository: docker.atl-paas.net/wmarusiak/ubuntuawssam
username: ((docker_username))
password: ((docker_api_key))
inputs: # add the get step as an input to this task
- name: repo
run:
path: sam
args: ["validate", "-t", "repo/template.yml"]
params:
AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID))
AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY))
AWS_DEFAULT_REGION: ((AWS_DEFAULT_REGION))
- task: run-sam-build
config:
platform: linux
image_resource:
type: registry-image
source:
repository: docker.atl-paas.net/wmarusiak/ubuntuawssam
username: ((docker_username))
password: ((docker_api_key))
inputs: # add the get step as an input to this task
- name: repo
run:
path: sam
args: ["build", "-t", "repo/template.yml"]
params:
AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID))
AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY))
AWS_DEFAULT_REGION: ((AWS_DEFAULT_REGION))
- task: run-sam-deploy
config:
platform: linux
image_resource:
type: registry-image
source:
repository: docker.atl-paas.net/wmarusiak/ubuntuawssam
username: ((docker_username))
password: ((docker_api_key))
inputs: # add the get step as an input to this task
- name: repo
run:
path: sam
args: ["deploy", "-t", "repo/template.yml", "--stack-name", "OpenDevOpsSubmitImage", "--s3-bucket", "open-devops-code-us-west-1-756685045356", "--capabilities", "CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
params:
AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID))
AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY))
AWS_DEFAULT_REGION: ((AWS_DEFAULT_REGION))
- task: run-update-script
config:
platform: linux
image_resource:
type: registry-image
source:
repository: docker.atl-paas.net/wmarusiak/ubuntuawssam
username: ((docker_username))
password: ((docker_api_key))
input:
-name: repo
run:
path: /workspace/updatescript/concourse-ci-integration.sh
args: ["-c", "successfully deployed submitImage via concourse-ci", "-k", "((branch))", "-o", "ac", "-u", "((bitbucket_username))", "-t", "((bitbucket_api_key))", "-w", "pmmquickstartguides01"]
如何启动父项管道
使用 parent.yml 运行目录中的以下三个命令。第一个命令会登录本地运行的 Concourse-CI 实例。第二个命令会根据 parent.yml 中的配置创建一个名为 wm 的管道。第三个命令会取消暂停 wm 管道。
fly -t tutorial login -c http://localhost:8080 -u test -p test
fly -t tutorial set-pipeline -p wm -c parent.yml
fly -t tutorial unpause-pipeline -p wm
运行这三个命令后,转到 Concourse-CI Web 客户端,查看 wm 管道是否已启动并运行。
![concourse ci Web 客户端图片](https://wac-cdn.atlassian.com/dam/jcr:b851dc8d-1d3d-4b51-84e3-f1321c922481/concourse-ci-web-client.png?cdnVersion=2569)
使用 parent.yml 运行 fly set-pipline 后,会有一个 wm 管道。该管道用于监控 SubmitImage 中是否存在任何新功能分支,并为与 parent.yml 中的正则表达式相匹配的每个功能分支创建管道。
![fly set pipeline](https://wac-cdn.atlassian.com/dam/jcr:8594096e-b478-401e-a8c8-a7b7d810aec8/fly-set-pipeline.png?cdnVersion=2569)
单击 wm 管道,查看正在运行的步骤。请注意,功能分支步骤列出了 IM-61 分支。这是目前 SubmitImage 中存在的唯一与 parent.yml 中的正则表达式相匹配的分支。点击自动启动的开发管道,以查看其运行的步骤。
![VM 管道在 fly set 管道之后](https://wac-cdn.atlassian.com/dam/jcr:e4aeaec5-3ad8-48cd-9a1f-a29168af4d93/WM-pipeline-after-fly-set-pipeline%20.png?cdnVersion=2569)
请注意,有一个获取 SubmitImage 代码存储库的步骤,且分支是 IM-61。另请注意,有 run-sam-validate、run-sam-build、run-sam-deploy 和 run-update-script 步骤。
![IM 61 jira 事务摘要](https://wac-cdn.atlassian.com/dam/jcr:1b543c72-ceaf-45a2-807c-822457d9e630/IM-61-jira-issue-summary.png?cdnVersion=2569)
在开发管道完成运行之后,返回至 IM-61 Jira 事务,注意有一个一分钟前记录的新评论,该评论与 child.yml 中的评论字符串相匹配
总之...
本指南演示了如何设置管道,以使用 Concourse-CI 将 Golang AWS Lambda 自动部署到单个 AWS 区域中。本指南还演示了如何使用 bash shell 脚本编写与 Jira 的简单集成。通过详细阅读此处提供的 Atlassian REST API 文档,可大幅扩展集成脚本。
分享此文章
下一主题
推荐阅读
将这些资源加入书签,以了解 DevOps 团队的类型,或获取 Atlassian 关于 DevOps 的持续更新。
![Devops 示意图](https://wac-cdn.atlassian.com/dam/jcr:bd9d8b2c-ca36-444f-8595-719cb1990e64/Devops-community.png?cdnVersion=2569)
DevOps 社区
![Devops 示意图](https://wac-cdn.atlassian.com/dam/jcr:297108ea-d232-4368-af51-b53af230c4fe/Simulation-workshop.png?cdnVersion=2569)
DevOps 学习路径
![地图插图](https://wac-cdn.atlassian.com/dam/jcr:25f6330a-4191-408f-a4e5-2e24bfba67b4/Maturity-model.png?cdnVersion=2569)