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 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