Close

集成 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_KEYCONCOURSE_AWS_SECRETSMANAGER_SECRET_KEYCONCOURSE_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 密钥

使用 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 令牌帐户设置

单击“安全

帐户安全

单击“创建和管理 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 客户端图片

使用 parent.yml 运行 fly set-pipline 后,会有一个 wm 管道。该管道用于监控 SubmitImage 中是否存在任何新功能分支,并为与 parent.yml 中的正则表达式相匹配的每个功能分支创建管道。

fly set pipeline

单击 wm 管道,查看正在运行的步骤。请注意,功能分支步骤列出了 IM-61 分支。这是目前 SubmitImage 中存在的唯一与 parent.yml 中的正则表达式相匹配的分支。点击自动启动的开发管道,以查看其运行的步骤。

VM 管道在 fly set 管道之后

请注意,有一个获取 SubmitImage 代码存储库的步骤,且分支是 IM-61。另请注意,有 run-sam-validate、run-sam-build、run-sam-deploy 和 run-update-script 步骤。

IM 61 jira 事务摘要

在开发管道完成运行之后,返回至 IM-61 Jira 事务,注意有一个一分钟前记录的新评论,该评论与 child.yml 中的评论字符串相匹配

总之...

本指南演示了如何设置管道,以使用 Concourse-CI 将 Golang AWS Lambda 自动部署到单个 AWS 区域中。本指南还演示了如何使用 bash shell 脚本编写与 Jira 的简单集成。通过详细阅读此处提供的 Atlassian REST API 文档,可大幅扩展集成脚本。

Warren Marusiak
Warren Marusiak

Warren is a Canadian developer from Vancouver, BC with over 10 years of experience. He came to Atlassian from AWS in January of 2021.

分享此文章

推荐阅读

将这些资源加入书签,以了解 DevOps 团队的类型,或获取 Atlassian 关于 DevOps 的持续更新。

Devops 示意图

DevOps 社区

Devops 示意图

DevOps 学习路径

地图插图

免费试用

注册以获取我们的 DevOps 新闻资讯

Thank you for signing up