Close

Concourse-ci와 Atlassian Open DevOps 통합

많은 팀은 요구 사항을 처리하는 데 자체 도구를 활용하거나 오랫동안 사용해 온 레거시 도구를 사용합니다. 팀이 따르는 개발 프로세스에는 이 도구가 꼭 필요합니다. 그러나 해당 도구에는 Jira와의 상용 통합이 없습니다. 다행히 Cloud 개발자 설명서 - Atlassian 개발자에 있는 Atlassian REST API를 사용하면 사용자 지정 통합을 간단하게 만들 수 있습니다. Concourse-CI는 (문서 작성일 기준) Atlassian Marketplace에 통합이 없는 CI/CD 제품입니다. 본 문서에서는 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_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

지금은 정의된 파이프라인이 없습니다.

hello world 파이프라인

Concourse-ci 설명서 https://concourse-ci.org/tutorial-hello-world.html에 따라 hello world 파이프라인을 설정합니다. Fly cli를 소개하고 명령줄에서 Concourse-ci 작업에 익숙해지기 위해 필요한 단계입니다.

다음 섹션에서는 Concourse-ci를 사용하여 Golang으로 작성된 AWS Lambda를 단일 AWS 리전에 배포하는 방법과 프로세스의 단계인 Jira 이슈의 업데이트를 작성하는 방법을 살펴봅니다.

Concourse-ci로 SubmitImage 배포

SubmitImage Lambda를 Concourse-ci로 배포하는 방법은 세 단계로 구성됩니다. 첫 번째 단계는 Jira Cloud REST API를 사용하여 Jira 이슈에 댓글을 작성하는 간단한 Bash 스크립트를 작성하는 것입니다. 만들 수 있는 가장 간단한 통합입니다. 두 번째 단계는 Golang AWS Lambda를 빌드하고 배포하는 데 필요한 도구를 사용하여 Docker 이미지를 만드는 것입니다. 마지막 단계는 한 쌍의 Concourse-ci 구성 파일을 작성하는 것입니다. 첫 번째 구성 파일인 parent.yml은 SubmitImage 리포지토리에 새 브랜치가 있는지 모니터링하고 새 파이프라인을 스풀링하여 해당 브랜치의 커밋을 배포합니다. 두 번째 구성 파일인 child.yml은 변경 사항을 배포하는 데 필요한 단계를 정의합니다.

1단계 - REST API를 통해 Jira 이슈 업데이트

이 업데이트 스크립트는 Jira Cloud 플랫폼 REST API를 사용하여 특정 Jira 이슈에 댓글을 작성합니다. 스크립트를 실행할 때마다 설정해야 하는 필수 매개 변수가 다섯 개 있습니다. jiraUsername, jiraApiToken, workspace는 보통 특정 파이프라인을 각각 실행하는 것과 같습니다. issueKey는 배포 중인 특정 브랜치의 브랜치 이름에 따라 달라집니다. 브랜치 이름에 Jira 이슈 ID를 입력하고 특정 이슈 작업을 할 때는 메시지를 커밋하는 것이 Jira의 모범 사례입니다. 본 문서에서는 사용자가 모범 사례를 따르며, 브랜치 이름이 Jira 이슈 ID와 같다고 가정합니다.

매개 변수를 찾는 방법
Jira 사용자 이름


Jira 사용자 이름은 Jira에 로그인할 때 사용하는 이메일 주소입니다.

Jira API 토큰
계정 설정으로 이동합니다

JIRA API 토큰 계정 설정

보안을 클릭합니다

계정 보안

API 토큰 만들기 및 관리

작업 영역
을 클릭합니다

https://pmmquickstartguides01.atlassian.net/jira/software/projects/IM/boards/1?selectedIssue=IM-203과 같은 Jira 인스턴스 URL의 경우 작업 영역은 pmmquickstartguide01입니다.

이슈 키

https://pmmquickstartguides01.atlassian.net/jira/software/projects/IM/boards/1?selectedIssue=IM-203과 같은 Jira 이슈 URL의 경우 Jira 이슈 키는 IM-203입니다. 통합에서 올바른 위치에 업데이트를 작성할 수 있도록 커밋 메시지와 브랜치 이름에 Jira 이슈 ID를 입력하세요.

댓글

댓글에는 무엇이든 입력할 수 있습니다.

업데이트 스크립트

업데이트 스크립트는 Jira 이슈 댓글 REST API 엔드포인트를 사용하는 간단한 Bash 셸 스크립트입니다. 이 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은 1단계에서 Bitbucket 리포지토리에서 만든 업데이트 스크립트를 복제합니다. 이 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-*입니다. parent.yml은 정규식 IM-*와 일치하는 새 브랜치가 만들어지면 child.yml의 구성을 사용하여 새 파이프라인을 만듭니다. SubmitImage 리포지토리의 자체 버전을 가리키도록 이 파일을 업데이트해야 합니다. 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 웹 클라이언트로 이동하여 wm 파이프라인이 시작되고 실행 중인지 확인합니다.

concourse ci 웹 클라이언트 이미지

parent.yml의 fly set-pipeline을 실행하면 wm 파이프라인이 있습니다. 이 파이프라인은 SubmitImage에 새로운 기능 브랜치가 있는지 모니터링하고, parent.yml의 정규식과 일치하는 각 기능 브랜치에 대한 파이프라인을 만듭니다.

fly set 파이프라인

실행되는 단계를 보려면 wm 파이프라인을 클릭합니다. 기능 브랜치 단계에 IM-61 브랜치가 나열되는 것에 주목하세요. parent.yml의 정규식과 일치하면서 현재 SubmitImage에 있는 유일한 브랜치입니다. 자동으로 시작된 dev 파이프라인을 클릭하면 파이프라인이 실행하는 단계를 볼 수 있습니다.

fly set 파이프라인 이후의 WM 파이프라인

SubmitImage 리포지토리를 가져오는 단계가 있으며 브랜치가 IM-61이라는 것에 주목하세요. 또한 un-sam-validate, run-sam-build, run-sam-deploy, and run-update-script 단계가 있다는 것도 확인할 수 있습니다.

IM 61 jira 이슈 요약

dev 파이프라인 실행이 끝난 후, IM-61 Jira 이슈로 돌아가면 1분 전에 기록된 child.yml의 댓글 문자열과 일치하는 새 댓글을 확인할 수 있습니다

결론...

본 가이드에서는 Concourse-ci를 사용하여 Golang AWS Lambda를 단일 AWS 리전에 자동으로 배포하는 파이프라인을 설정하는 방법을 살펴봅니다. 또한 Bash 셸 스크립트를 사용하여 Jira와의 간단한 통합을 작성하는 방법도 알아봅니다. 여기에 나와 있는 Atlassian REST API 설명서를 자세히 살펴보면 통합 스크립트를 폭넓게 확장할 수 있습니다.

Warren Marusiak
Warren Marusiak

개발자 출신인 Warren은 2021년 기술 에반젤리스트로 Atlassian에 합류했습니다. 메인프레임에서 실행되는 COBOL 통신 소프트웨어부터 AWS의 최신 클라우드 인프라에 이르기까지 모든 분야에서 활약했습니다. 기술에 대한 관심이 지대하며 머신러닝 분야를 연구한 경력이 있습니다. 기술 에반젤리스트로서 Warren은 데모, 글쓰기 및 동영상을 통해 Atlassian 제품 및 파트너 통합 기능에 대한 인지도를 높이고 있습니다. 또한 Developer’s Edge 동영상 시리즈도 큐레이션합니다. 여가 시간에는 브라질 주짓수를 열정적으로 연습합니다.

이 문서 공유

여러분께 도움을 드릴 자료를 추천합니다.

이러한 리소스에 책갈피를 지정하여 DevOps 팀의 유형에 대해 알아보거나 Atlassian에서 DevOps에 대한 지속적인 업데이트를 확인하세요.

DevOps 일러스트레이션

DevOps 커뮤니티

DevOps 일러스트레이션

DevOps 학습 경로

맵 일러스트레이션

무료로 사용해보기

DevOps 뉴스레터 신청

Thank you for signing up