Github Actions를 위한 저장소, actions 개발기

KEUN·2022년 1월 12일
3
post-thumbnail

🚪들어가며


여러 github repository에 공통적으로 들어가는 github actions workflow를 중앙에서 관리하고
배포할 수 있는 시스템(?)을 생각하고 개발하기까지의 과정을 기록한다.

아직은 모든 repository에 적용할 수 없지만, 37개 정도의 repository를 관리할 수 있게 되었다.

개발 결과물에 대한 자세한 소개는 없고, '이런 아이디어로 만들어 봤다'가 주된 내용이다.

SpaceONE github action workflow repository
https://github.com/spaceone-dev/actions

🔭배경


현재 몸담고 있는 조직은 kubernetes위에 MSA 형태로 서비스가 돌아가고 있다.

그렇기 때문에 서비스를 구성하는 위해 수많은 github repository가 있고, 별도로 plugin이 존재하기 때문에 날이 갈수록 github repository는 늘어나고 있다.

이러한 상황 속에서 repository에 들어가는 github actions의 workflow가 변경되거나
추가되기라도 한다면 그 많은 repository에 어떻게 덤벼들어야 할까 막연히 생각하고 있었다.

workflow를 중앙에서 관리할 수 있는 걸 찾아봅시다!
CI가 실행되면 workflow를 땡겨와서 최신 상태로 CI를 진행하면 좋겠어요!
- by 팀장님

그러던 와중에 개발 팀장님께서 workflow를 한 곳에 두고 CI(github actions)가 실행될때 최신 상태의 workflow를 받아가는 솔루션이 있는지 찾아보고 도입해볼 것을 부탁하셨다.

가장 먼저 떠오른 것은 github submodule이었지만 submodule이 업데이트 되면 이를 import하는 repository에서도 직접 업데이트가 필요한 듯 보였다.

github actions의 경우 workflow가 실행되면 해당 내용을 가지고 VM이 생성되어 기술된 내용을 그대로 실행하는 과정을 거치는데, CI가 실행된 후 submodule을 억지로 가져와봤자 도중에 workflow가 바뀔리가 없어 보였다.

이러한 특성이 요구조건에는 맞지 않아 보여 열심히 구글링을 해보았지만 솔루션을 찾기 힘들었다.

⚙️Actions


사실 actions는 전임자가 github api를 이용해 모든 repository를 대상으로 workflow를 배포하기위한 용도로 만들어둔 것이었다.

하지만 이걸 개량해서 workflow가 수정/추가되었을때 수동으로 배포하는 것이 아닌, 각 repository에서 CI가 실행될때 자동으로 actions로부터 workflow를 받아가도록 만들고 싶었다.

1. 개조 시작

컨셉

docker hub처럼 저장소처럼 활용하면서 필요시에 저장소로부터 workflow를 받아가는 형태를 생각했다.
또한 이러한 것은 CI가 수행되기전, 자동으로 이루어지는 것을 원했다.

아래에서 자세히 설명하겠지만, github actions은 api로도 trigger할 수 있다.
때문에 trigger되는 workflow가 어떤 내용을 포함하고 있느냐에 따라 나에게는 마치 api 플랫폼(?)처럼 보이기도 했다. 이를 잘만 이용하면 workflow 저장소처럼 활용할 수 있을 것 같았다.

요구조건

  • CI가 시작되기전 자동으로 workflow를 최신화 해야한다.
  • workflow 최신화는 actions에서 배포하는 것이 아닌, repository가 요청한다.
    • 즉, 시작은 actions가 아니라 각 repository이다.
    • 이를 통해 이후의 workflow는 오로지 action에서만 관리한다.
  • actions는 각 repository를 몰라야한다.
    • 즉, actions에서 repository 목록을 설정파일로 가지지 않는다.
  • 동일한 workflow template을 사용하는 repository가 있어 group으로 나눌 수 있다.
    - 이를 이용해, plugin, backend, console 등으로 그룹을 지정해 배포할 수 있다.

전체 흐름

CI trigger -> workflow 요청 -> workflow 전달(최신화) -> CI 진행

2. 개발 과정

workflow 요청 : github actions 활용

먼저 각 repository는 CI가 trigger되면 actions로 workflow를 요청해야한다.

github actions는 repository에서 발생하는 event를 통해 workflow를 실행시키는데 여러 event 중 workflow를 수동으로 trigger하는 Manual events가 있다.

이러한 Manual events는 api를 통해서도 trigger할 수 있는데,이를 이용해 repository에서 CI가 trigger 되었을때 actions로 workflow를 요청하는 것을 구현했다.

이를 통해 code commit이 일어나면, 먼저 workflow를 요청해 최신화하는 단계를 만족할 수 있었다.

name: "[Push] Sync_ci"

on:
  push:
    branches:
      - master
    paths-ignore:
      - '.github/**'
      - 'src/VERSION'

env:
  workflow_file_name: deploy.yaml
  owner: spaceone-dev
  repo: actions
  ref: master

jobs:
  master_push:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - name: git pull
        uses: convictional/trigger-workflow-and-wait@v1.3.0
        with:
          owner: ${{ env.owner }}
          repo: ${{ env.repo }}
          github_token: ${{ secrets.PAT_TOKEN }}
          workflow_file_name: ${{ env.workflow_file_name }}
          ref: ${{ env.ref }}
          wait_interval: 10
          inputs: '{"repository" : "${{ github.repository }}"}'
          trigger_workflow: true
          wait_workflow: true

여기서 convictional/trigger-workflow-and-wait@v1.3.0라는 action을 이용했는데
이는 api로 workflow를 trigger하고 종료될 때까지 기다리는 github actions의 module(?)이다.

이를 통해 workflow 요청이 잘 끝나는 지를 확인할 수 있도록 했다.

workflow 전달 : python script 개발

각 repository로부터 workflow 요청을 받으면, 해당 repository에 workflow를 전달해야한다.

python script로 repository로부터 요청이 오면 해당 repository에 workflow를 배포 해주는 기능을 구현했다.

단일 repository에 대해서 배포할 수 있지만, 동일한 workflow를 사용하는 repository group으로 보고, 이를 지정하면 해당 group에 속한 모든 repository에 배포할 수 있다.[code]

usage: main.py [-h] (--repo <owner/repo> | --group <group_name>) [--init]

File push to github repository

optional arguments:
  -h, --help            show this help message and exit
  --repo <owner/repo>   Select specified repository.
  --group <group_name>  Select specified group.
  --init                Deploy sync workflow only.

Examples:
    python src/main.py --repo spaceone/inventory
    python src/main.py --repo spaceone/config --init
    python src/main.py --group plugin
    python src/main.py --group plugin --init

요청을 받아주는 것은 github actions로 구현했다.

이때 repository 명을 input으로 받는다, 이를 통해 actions는 repository 목록을 가지지 않고
workflow를 요청하는 곳을 알고 이를 보낼 수 있다.

name: SpaceONE Github Action Deploy

on:
  workflow_dispatch:
    inputs:
      repository:
        description: 'target repository'
        required: true
      sync_only:
        description: 'sync only flag'
        required: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: set python
        uses: actions/setup-python@v2
        with:
          python-version: '3.8'
      - name: install requirements
        run: pip install -r requirements.txt
      - name: set PAT_TOKEN env
        run: echo "PAT_TOKEN=$(echo ${{ secrets.PAT_TOKEN }})" >> $GITHUB_ENV
      - name: deploy
        run: |
          python src/main.py --repo ${{ github.event.inputs.repository }}
...

CI 진행(trigger) : github actions 활용

사실 이 부분이 이해되기 어려운 부분일 수 있다.

workflow 요청 -> CI 시작으로 흘러가야하지만 이러한 두 이벤트를 따로 핸들링하는 것은
의미가 없다고 생각했고, 이렇게 되면 자동이 아니라 개발자가 의도적으로 두 번 이벤트를 발생시켜야 했다.

두 요청을 처리하는 것을 하나의 workflow에 담고 싶지만, 그러자니 submodule의 불합격 이유와 마찬가지로 중간에 변경되는 workflow 내용을 반영해줄리가 없었다.

때문에 actions가 workflow 전송을 끝내고, 전송된 workflow를 실행하는 방식으로 구성했다.
즉, workflow를 trigger하는 것은 각 repository가 아니라 actions가 진행한다.

그렇다 하더라도, 결국 repository에서의 event를 기반으로 어느 workflow를 trigger해야할지 판단할 수 있기 때문에 이렇게 구현해도 괜찮겠다는 생각이었다.

(아래의 workflow는 위 내용의 full 버전이다.)

name: SpaceONE Github Action Deploy

on:
  workflow_dispatch:
    inputs:
      repository:
        description: 'target repository'
        required: true
      sync_only:
        description: 'sync only flag'
        required: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: set python
        uses: actions/setup-python@v2
        with:
          python-version: '3.8'
      - name: install requirements
        run: pip install -r requirements.txt
      - name: set PAT_TOKEN env
        run: echo "PAT_TOKEN=$(echo ${{ secrets.PAT_TOKEN }})" >> $GITHUB_ENV
      - name: deploy
        run: |
          python src/main.py --repo ${{ github.event.inputs.repository }}
  master_push:
    runs-on: ubuntu-latest
    if: ${{ !github.event.inputs.sync_only }}
    needs: deploy
    steps:
      - uses: actions/checkout@v2
      - name: Repository Dispatch
        uses: peter-evans/repository-dispatch@v1
        with:
          token: ${{ secrets.PAT_TOKEN }}
          repository: ${{ github.event.inputs.repository }}
          event-type: master_push

완성

이런 과정을 거쳐 현재의 actions가 만들어졌다.

초기 설계와는 달라진 부분도 있고, 컨셉을 맞추기 위해 억지로 구현된 부분이 없지않아 있는 듯 보이지만
그래도 CI가 실행되기전 workflow를 자동으로 최신화 해주는 것은 충분히 구현되었다고 생각한다.

이렇게 보니 키메라 같다. 하지만 한번 연동이 되면

  • 개발자는 이후로 workflow 관리를 신경쓰지 않아도 된다.
  • 모든 workflow는 actions에 넣어두기만 하면 되고
    • 알아서 최신화 되며 개발자는 이전과 변함없이 Build 할 수 있다.

아래 구성도를 보면 release의 경우는 곧장 workflow를 실행시키는 것을 알 수 있는데, 이것은 개발 프로세스상

Dev Build -> Release

처럼 진행되기 때문에 dev build 단계에서 workflow가 최신화되면 release에서 한번 더 진행 될 필요가 없을 것 같아 분리했다.

구성도


1. repository에서 code commit 발생
2. repository의 syncactions에 workflow 요청(*1)
3. actions는 workflow를 요청한 repository가 어느 group에 속한지 판단(*2)
4. actions는 해당 repository에 workflow 전달
5. actions는 이후 CI를 trigger

*1) sync라는 event를 핸들링 해주는 workflow의 배포가 필요하다. (= actions와 연동 작업이 되겠다.)
*2) actions에서 workflow는 group별로 관리되고 있고, repository의 topic과 비교해 적절한 wokflow를 찾는다.

연동 방법

actions는 대상 repository와 연동 작업만 해주게 되면 이후부터 workflow가 자동으로 동기화된다.
때문에 sync라는 workflow만 repository에 넣어주기만 하면 된다.

사용 이점

action를 통해 workflow 수정/추가 이벤트가 발생했을때.

개발자 입장에서는

workflow가 바뀐지는 모르겠어,그냥 평소와 다름 없이 build하니 되더라구...

DevOps 입장에서는

repository를 찾아다니면서 workflow를 바꿔줄 필요가 없어서 편리하네...
그냥 action에 넣어두기만 하면 알아서 찾아가는구나...

라는 이점이 있지 않을까 한다.(그랬으면 좋겠다.)

👋🏼마무리


첫 포스트인 만큼 어떻게 이야기를 풀어내야할까 고민이 되기도 했고 말하고싶은 것도 많아서 말하고자 하는 것이 잘 전달 되었을지 모르겠다.

docker hub에서 착안하여 workflow를 중앙에서 관리/배포 할 수 있는 시스템을 만들어 봤다!

이런 이야기를 해보고 싶었다.

또한 이야기를 이어나가다 보니, actions에 대한 간단한 소개를 해야할지, 구체적인 구현 내용에 대해서 설명해야할지 거기에 실제 어떻게 사용해야하는지 라는 내용을 포함해야할까 라는 고민이 들었다.

포스트 작성도 공부구나 싶다.

profile
Let's make something for comfortable development

0개의 댓글