Dico

GitHub Actions, 커스텀 액션을 만들고 활용해보자

GitHub_Actions.png

   오픈 소스 저장소로 사랑을 받는 GitHub에서 GitLab CI와 유사하지만, 일부 차별화를 둔 GitHub Actions를 정식 출시 하였습니다. GitLab CI와 어떻게 다른지 살펴보고, GitHub Actions만의 특징을 활용한 응용법에 대하여 짚어 보겠습니다.

Runner: GitLab CI vs GitHub Actions

   GitLab CI와 GitHub Actions 둘 다 Runner를 기반으로 하여 CI/CD를 이루어 내는 도구입니다. 이는 CI/CD 도구라면 당연한 이야기입니다. 바로 이 Runner에서 두 서비스 간 유독 큰 차이점이 존재합니다.

  • CI/CD   CI는 개발자를 위한 자동화 프로세스인 지속적인 통합(Continuous Integration)을 의미한다. 변경 사항이 빌드와 테스트를 거쳐 repository에 병합 된다. CD는 지속적인 서비스 제공(Continuous Delivery) 또는 지속적인 배포(Continuous Deployment)를 의미하며, 고객이 사용할 서비스를 배포하는 일련의 자동화 프로세스를 말한다.

   GitLab은 자체적으로 제공하는 Runner를 그대로 사용할 수도 있고, 개인 서버에 Runner를 생성하여 사용해야 합니다. Runner설치와 연동은 어렵지 않으며, 개발자라면 누구나 손쉽게 할 수 있습니다. GitLab 서비스에서 자체적으로 제공하는 Runner를 사용할 경우 사용 방법에 따라 과금이 발생합니다. GitLab CI에 대한 내용은 다른 글에서 살펴볼 수 있으니 궁금하다면 링크를 참고하시면 됩니다.

  • GitLab CI & Runner

   Runner정책은 GitHub Actions도 동일합니다. 다만 GitLab CI처럼 완성된 Runner를 제공하는 것이 아니라는 점이 큰 차이를 일으킵니다. GitHub Actions는 일회성 인스턴스로 Runner를 제공하기 때문에 자신의 개인 서버가 없을지라도 사용자가 직접 Runner를 설정할 수 있습니다.

   한 가지 아쉬운 점은 가격 정책 변경입니다. 불과 몇 주 전까지만 하더라도 public repositories는 무료로 Runner를 제공 했었으나, 2020년 4월 15일을 기점으로 private repositories 정책이 풀리면서 공개 여부 상관 없이 2,000분/월 CI 러닝타임으로 변경 되었습니다. 물론 GitLab CI처럼 과금을 할 경우 러닝타임이 늘어나기는 합니다. 글을 쓰고 있던 와중에 GitHub 정책이 바뀌다니, 자칫하면 놓쳐서 잘못된 내용을 전달할 뻔 했습니다.

불가 몇 주 전까지만 하더라도 GitHub Actions를 무료로 제공하고 있었다. (링크)

   GitLab CI는 public repositories인 경우 50,000분/월 CI 러닝타임을 제공하고 있어 다소 대조되는 모습이 보입니다. 후발 주자였기에 초기에는 무제한 정책을 펼친 것으로 보이지만, 이제 러닝타임에 대한 장점은 GitHub Actions에 존재하지 않습니다.

  • Warning: We recommend that you do not use self-hosted runners with public repositories.Forks of your public repository can potentially run dangerous code on your self-hosted runner machine by creating a pull request that executes the code in a workflow.For more information, see "About self-hosted runners."

   GitHub Actions도 개인 Runner를 구성할 수 있지만, 경고 문구를 적어 놓았습니다. public repositories 무제한 정책일 때는 단순히 사용자를 배려하는 측면의 문구라고 생각 했지만, 오픈소스를 운영하시는 분이라면 이 점에 대하여 유의하시기 바랍니다. 굳이 번거롭게 개인 Runner를 만들 이유는 없지만, 2,000분/월 CI 러닝타임이 모자를 수도 있습니다.

Singularity: GitHub Actions

   GitHub Actions는 GitLab Ci보다 조금 더 친절하게 기본 템플릿을 제공합니다. workflow.yml를 언어 별로 샘플링하여 제공하기 때문에 이를 보고 손쉽게 워크플로우를 구성할 수 있습니다. 심지어 프로젝트에서 사용하는 언어에 어울리는 관련 템플릿을 추천해줍니다. 사용자 친화적인 기능이 돋보이는 부분입니다.

GitHub_Actions_template.png

   위에서 살펴본 GitHub Actions Runner가 가진 특이점을 보면 어떤 생각이 떠올랐을지도 모릅니다. 일회성 인스턴스를 활용하면 CI/CD 뿐만 아니라 서버리스 프로그램을 만들 수도 있다는 사실을 말이죠. AWS Lambda처럼 실행할 수 있는 별도 URL을 제공하지 않지만, 스케쥴 기반 FaaS로 활용할 수 있습니다. 간단한 크롤러나 반복 작업을 GitHub Actions를 통해 입맛에 맞추어 수행시킬 수 있죠. private repositories에 대한 제한이 풀리면서 간단한 작업을 비용 없이 운영할 수 있습니다. 이에 대한 내용은 뒤에서 다시 이야기 하고자 합니다.

  • FaaS: Function as a Service서버 관리를 신경 쓰지 않고 특정 이벤트에 대응하여 애플리케이션을 실행하도록 지원하는 추상화 기능을 제공하는 서비스를 지칭한다. 실행 당시에만 서버 자원을 활용하기 때문에 비용적인 측면에 우위가 있으며, 거의 모든 프로그래밍 언어로 기능 구현이 가능하다.

   또 다른 특징으로 마켓플레이스입니다. 무언가를 사고 파는건 아니고, 다른 개발자가 만든 커스텀 액션을 이곳에서 확인할 수 있습니다. public repository를 릴리즈 해야 마켓플레이스에 나타납니다.

release_devellany_send-mail.png

   아직은 커스텀 액션이 많이 올라오지 않았지만, 이메일이나 텔레그램을 활용한 간단한 알림 기능부터 테스팅 도구까지 여러가지 존재합니다. 필요한 기능이 있다면 먼저 마켓플레이스에서 찾아본 후 만들기를 권합니다. 커스텀 액션에 대한 코드는 공개가 원칙이기 때문에 좋은 가이드가 되기도 합니다.

커스텀 액션을 만들어보자

   만약 마켓플레이스에 원하는 기능을 지원하는 Custom Action이 없다면, 직접 만들어서 사용하면 됩니다. 어렵지 않습니다. 메일 발송을 위해 만든 간단한 Custom Action을 통해 살펴보도록 하죠. 우선 제가 해당 액션을 만든 이유는 간단합니다. 메일에 첨부 파일을 편하게 보낼 수 있는 Custom Action이 없었습니다. 이를 해결하기 위해 잠깐 시간 내서 만든 Custom Action입니다.

   Custom Action을 만들기 위해서는 action.yml 파일과 함께 README.md가 필수입니다. 앞서 릴리즈 스크린샷을 보셨을텐데요. action.ymlCustom Action을 등록 설정을, README.mdCustom Action을 사용하는 유저를 위한 설명서를 제공하라는 의미에서 필수로 지정되어 있습니다. 먼저 action.yml를 살펴볼까요?

name: Send mailer
description: Dynamically send an email with attachments.
author: devellany
branding:
  icon: mail
  color: purple
inputs:
  host:
    description: Enter SMTP host.
    required: true
  port:
    description: Enter SMTP port. (defaults to 465)
    required: false
    default: 465
  # code...
runs:
  using: node12
  main: dist/index.js

   name은 마켓플레이스에 공개될 이름이기 때문에 동일한 이름을 가진 Custom Action이 존재하면 안 됩니다. description은 노출 될 Custom Action 설명입니다. branding은 지정된 값만 넣을 수 있는데, 한눈에 파악할 수 있도록 돕기 위한 아이콘 설정입니다. 어떤 아이콘과 색상을 사용할 수 있는지는 GitHub Actions docs를 참고하길 바랍니다. 임의의 아이콘을 집어 넣을 수 없는건 아쉬웠지만, 관리 편의상 이와 같은 선택을 한 것으로 보입니다. inputCustom Action에서 받아야 되는 값을 지정할 수 있습니다. input이 존재해야 workflow.yml의 프로퍼티로 해당 정보를 받아와 읽을 수 있습니다.

   runs는 실행 환경을 의미합니다. 간단하게 만든 send mailer는 노드 기반 nodemailer 라이브러리로 만들었기에 노드로 지정하였습니다. main은 최초로 코드가 실행될 진입점입니다.

   GitHub에서는 node_module을 소스에 추가하지 말아달라고 권장하고 있지만, 생각보다 많은 Custom Action들이 이를 지키지 않고 있습니다. repository에 node_module이 포함 되지 않으면 해당 액션을 사용하고자 할 때 관련 패키지를 불러오지 못 하고 오류를 반환하기 때문입니다. 그렇다고 node_module을 저장소에 올리는건 비효율적인 구조이므로 GitHub에서는 다음과 같은 빌드를 거쳐 코드를 올려 달라고 권장합니다. GitHub에서 Docs에 해결 방법까지 적어 놓은 것으로 이 글을 보신다면 꼭 지켜주시길 바랍니다.

npm i -g @zeit/ncc
ncc build xxx.js # dist/index.js 생성

   실제 코드에서는 아래와 같은 내용만 알고 있으면 됩니다. @action/core를 통하여 workfolw.yml의 프로퍼티를 받을 수 있다는 점과 함께 오류가 발생할 때를 대비하여 core.setFailed() 에러를 표기하면 된다는 것을 말이죠. 이 외는 재량껏 코드를 작성하면 됩니다. 참 쉽죠? 나머지는 평소에 치던 코드대로 작성하면 됩니다.

const core = require('@actions/core');
try {
    const host = core.getInput("host", {required: true});
    // code...
} catch (e) {
    core.setFailed(e.message);
}

   이와 같은 요건을 지켜서 만든 GitHub Custom Action repository, Send mailer입니다. 저도 간단하게 만든거니 '이런 식으로 Custom Action이 구성 되는구나.' 정도로 살펴보시면 됩니다.

커스텀 액션을 사용해보자

   Custom Action을 사용하기 위해 추가할 부분은 workflow.yml 뿐입니다. 해당 Custom Action에서 요구하는 프로퍼티 필수값을 적어주면 필요한 기능을 마켓플레이스에서 가져와 손쉽게 사용할 수 있습니다.

steps:
    - uses: actions/checkout@v2
    - name: Send email
      uses: devellany/send-mail@v1.0.2
      with:
          host: ${{secrets.SMTP_HOST}}
          account: ${{secrets.MAIL_ACCOUNT}}
          password: ${{secrets.MAIL_PASSWORD}}
          sender: devellany
          from: ${{secrets.MAIL_ADDRESS}}
          to: devellany@dico.me
          subject: Github Actions job result
          body: file://README.md
          attachments: '[{"path":"README.md"}]'

   GitHub에 대해 익숙하지 않은 분이라면 secrets key에 대해 모르실 수도 있는데요. 링크를 걸어놓은 repository README.md에 적혀있는 것처럼 손쉽게 지정할 수 있습니다. 노출되면 안 되는 값들은 secrets key로 관리합시다!

secrets.png

위와 같은 구성으로 적용 되어 있는 샘플 코드는 이 링크를 참조하시면 됩니다.

GitHub Actions 응용, 서버리스 스케쥴러

   크롤링하고 싶은 데이터가 따로 없어 GitHub API를 끌어 쓰고자 합니다. 이 예제는 Open API를 끌어다 쓰는 것 뿐이지만, 여기에 Dom 파싱만 구현하면 크롤러가 됩니다. 간단하게 JSON 데이터를 파일로 받고자 파이썬의 Pandas를 기반으로 코드를 만들었습니다. 주력으로 쓰는 개발 언어는 아니지만, 이정도는 간단하니까요.

   파이썬이니 pip으로 라이브러리 의존성을 추가해야 합니다. GitHub에서 제공하는 일회성 인스턴스에서 라이브러리를 끌어다 쓰기 위해서는 이 작업이 필수로 진행 되어야 합니다. Pandas를 추가합시다. API는 Requests 라이브러리를 통해 가져오고자 하니 이것도 추가하죠!

steps:
    - uses: actions/checkout@v2
    - name: Set up Python 3.8
      uses: actions/setup-python@v1
      with:
        python-version: 3.8
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install pandas
        pip install requests

   실행할 코드는 일반적인 파이썬 그 자체입니다. 단순히 URI를 호출하고, 반환된 JSON를 바탕으로 csv를 만드는 코드이기에 간단합니다. 파이썬을 모르더라도 간단히 읽을 수 있을겁니다.

import requests
import pandas
from pandas.io.json import json_normalize
 
url = 'https://api.github.com/search/code'
params = {
    'q': 'KEYWORD',
}
headers = {
    'Authorization': 'AUTH_KEY'
}

jsonData = requests.get(url, params=params, headers=headers).json()
if (jsonData['total_count'] > 0):
    df = json_normalize(jsonData['items'])
    df.to_csv('result.csv')

   실행 파일이 하나 뿐이니, workflow.yml에서 실행할 코드도 한 줄 뿐입니다.

- name: Run
      run: |
        python finder.py

   워크플로우 설정에 pull request기반이 아닌 지정된 시간 마다 동작하도록 바꾸면 스케쥴 기반 FaaS가 완성됩니다. 다른 서비스도 아닌 모든 개발자들이 익숙한 GitHub를 통해서 서버리스 프로그램을 만들 수 있습니다.

on:
  schedule:
    - cron: '0 0 * * *' # UTC 00:00 == KST 09:00

   이제 지정된 시간마다 파이썬 코드를 실행합니다. UTC 기반이기에 9시간을 빼야 KST입니다. 즉, 오전 9시에 스케쥴러를 실행하고 싶다면 0시로 지정해야 합니다.

   파이썬 코드에 따라 API 결과 값이 존재하면 result.csv를 생성을 하고 끝마치는데, 위에서 만든 Custom Action을 통해 메일로 발송할 계획입니다. 파일이 생성 되지 않은 경우 Send mailer는 메일을 발송하지 않고 종료 되므로 result.csv가 생성 되지 않는다면 불필요한 메일을 받지 않을 수 있습니다.

- name: Send email
      uses: devellany/send-mail@v1.0.2
      with:
          host: ${{secrets.SMTP_HOST}}
          account: ${{secrets.MAIL_ACCOUNT}}
          password: ${{secrets.MAIL_PASSWORD}}
          sender: devellany
          from: ${{secrets.MAIL_ADDRESS}}
          to: devellany@dico.me
          subject: GitHub search mail
          contentType: text/plain
          body: GitHub code result data
          attachments: '[{"path":"result.csv"}]'

   Workflows 메뉴를 확인해 보면 지정한 시간 마다 코드가 실행 되는 모습을 볼 수 있습니다. 또한 위에서 만든 Send mailer을 통해 메일이 오는 것도 확인할 수 있습니다.

workflows.pnggmail.png

마치며

   GitHub Actions의 베타 버전이 공개 되었을 당시에는 관심을 가지지 않았습니다. 단순한 소개 페이지만 봐서는 GitLab CI와 차이를 알 수 없었습니다. 과금 정책 또한 사실상 큰 차이 없이 동일 했었고, 특색을 느낄 수 없었습니다. GitHub Actions가 정식 출시가 되고 나서 기회가 닿아 써봤는데 웬걸? 꽤나 매력적인 부분이 있었습니다. Custom Action 마켓플레이스는 물론이고, 일회성 인스턴스 Runner는 개발자로 하여금 다양한 접근을 할 수 있게 만들어 냈습니다. public repositories의 GitHub Actions 과금 정책이 변경된 점은 아쉽지만, private repositories가 무제한으로 풀려 이러한 이유로 GitLab을 쓰고 있던 입장에서는 좋은 일입니다.

   위에서 살펴 보았듯이 GitHub Actions를 통해 모든 개발자들이 서버리스 스케쥴러를 큰 제약 없이 만들 수 있게 되었습니다. 아무리 개발자라고 할지라도 저처럼 개인 서버를 가지고 있는 경우는 많지 않으니 다른 개발자들에게 꽤나 희소식으로 다가올 수 있으리라 봅니다. 애써서 AWS free tier를 쓸 이유가 없는거죠. 굳이 GitHub Actions를 활용하여 CI/CD를 만들지 않아도 됩니다. 제법 매력적으로 다가오지 않나요?