1. CI/CD와 GitHub Actions

- 어떤 앱의 소스코드를 담고 있는 레포지토리가 있고 여러 개발자가 그 레포지토리의 소스코드를 가져와 각자 조금씩 수정을 한다 할 때, 수정을 완료한 결과물을 배포하려면 결국 각 개발자가 수정한 결과물을 하나의 레포지토리에 통합을 하는 절차가 필요하다. 이때 본래 레포지토리에서 소스코드를 가져와 수정한 후 그 결과를 빌드-테스트 해보고 이를 다시 본래 레포지토리에 통합하는 사이클은 짧을수록 좋다. 여러 개발자가 각각 따로 수정하는 만큼, 이 사이클의 간격이 길어지면 한 개발자가 어떤 부분을 수정할 때 참조한 부분을 다른 개발자가 수정해버려 결국 그 개발자가 했던 수정이 의미가 없어지고 다시 수정해야 하는 일이 생길 수 있기 때문이다. 이처럼 소스코드 수정에서 통합에 이르는 일련의 절차가 짧은 간격으로 해야 한다는 개념을 소프트웨어 공학에서는 지속적 통합(continuous integration, CI)이라 한다.

  • 메인 레포지토리에 통합하기 위해서는 반드시 빌드-테스트를 거쳐 그 수정사항이 정상적으로 작동한다는 사실을 확인해야 한다. 한편 CI는 수정-통합의 간격을 짧게 한다는 개념이므로, 이를 위한 빌드-테스트 또한 자동화하여 빠른 시간 내에 수행한다는 개념을 포함한다.

- 지속적 배포(continuous delivery/deployment, CD)는 CI의 확대된 개념으로서, CI를 마쳐 실제 서비스 환경으로 배포될 코드가 메인 레포지토리를 거쳐 실제 서비스 환경으로 배포되는 일련의 과정을 자동화해 코드 수정에서 배포까지의 간격을 짧게 줄이는 소프트웨어 공학의 개념을 말한다. (continuous delivery와 deployment를 구분하지 않는 경우도 많으나, continuous deployment를 보다 확대된 개념으로서 칭하기도 한다. 보통 메인 레포지토리로 코드를 release하는 단계까지를 delivery, 실제 서비스 환경에 deploy하는 단계까지를 deployment라 한다.)

- CI에서부터 CD까지 이르는 일련의 과정을 자동화한 것을 통칭하여 CI/CD 파이프라인이라 하며, 이를 수행하는 서버를 CI 서버라 한다. 이를 수행하는 툴로서 Jenkins, Travis CI 등이 있으며, GitHub의 GitHub Actions 서비스 또한 별도 서버 없이 GitHub의 레포지토리를 통해 CI/CD 파이프라인을 작동시킬 수 있는 서비스로 널리 쓰이고 있다.

* GitHub Actions 관련 팁

  • GitHub Actions로 구동한 가상머신을 웹서버의 호스트로 사용하는 것은 허용되지 않는다.

2. GitHub Actions의 구성

1) workflow

- GitHub Actions에서 설정할 수 있는 CI/CD 파이프라인 한 단위를 workflow라 하며, 한 레포지토리에 여러 workflow를 두어 하나의 앱에 대해 여러 개의 CI/CD 파이프라인을 설정할 수 있다.

- 하나의 workflow는 크게 (1)조건이 발동되면 (2)그 workflow 내에 설정된 여러 작업(job)들이 실행된다는 구조로 이루어져 있다.

  • workflow가 실행될 조건을 충족하는 사건(event)은 하나 이상일 수 있으며, 그 레포지토리에 푸시/풀이 일어날 때, 특정 시간이 됐을 때, 다른 workflow가 실행됐을 때 등 여러 사건을 workflow의 실행 조건으로 설정할 수 있다.

- workflow의 생성은 그 레포지토리의 Actions 페이지에서 제공되는 템플릿을 활용하여 할 수 있고, 그 레포지토리 내 .github/workflows 폴더에 일정 형식을 갖춘 YAML 파일을 추가하여 할 수도 있다.

- 하나의 workflow를 정의하는 YAML 파일의 형식은 크게 다음 세 부분으로 나뉜다.

name: # workflow의 이름을 적는 프로퍼티.
on: # workflow가 실행될 조건을 정의하는 프로퍼티.
    ...
jobs: # workflow 실행 시 수행할 작업들의 목록을 나타내는 프로퍼티.
    ...

2) job의 개념 및 job을 구성하는 작업 단계들

- 한 workflow에서는 서로 독립적인 가상머신에서 동작하는 job이 하나 이상 있어, 그 workflow의 조건이 발동되면 그 workflow에 속하는 job들이 각각 따로 동시에(=병렬적으로) 실행된다. (단, 설정에 따라 각 job 사이 순서를 정해 실행되게 할 수는 있다.)

- 하나의 job은 여러 작업들이 순서대로 배치되는 구조를 갖는다. 이때, 그 레포지토리 내 폴더에 workflow를 생성했다 해서 아무 작업 명령이 없어도 가상머신에 그 레포지토리의 소스코드를 다운 받는 게 아니므로, 모든 job의 첫 단계 작업으로는 그 레포지토리의 소스코드를 가상머신으로 다운받는다는 작업 명령이 들어가야 한다.

- YAML 파일에서 하나의 job은 다음과 같은 형식으로 쓰인다.

...
jobs:
    job1:
        ...
    job2:
        ...
    job3: # jobs의 자식으로 들어가는 프로퍼티의 이름이 그에 해당하는 job의 변수명이 되며, 그 하위 프로퍼티들이 그 job을 이루는 내용이 된다.
        needs: [job1, job2] # 그 job을 수행하기에 앞서 어떤 job을 수행해야 하는지 의존관계를 명시하는 프로퍼티.
        name: my new job # 이 작업 단계의 이름을 적는 프로퍼티. 각 작업 단계에 그 작업에 해당하는 이름을 지을 수 있다. 상기한 job의 변수명은 다른 job에서 이 job을 호출할 때의 이름이 되고, 이 프로퍼티에 적는 이름이 이 job의 정식 명칭이 된다.
        runs-on: ubuntu-latest # 이 job이 실행될 OS 환경을 적는 프로퍼티.
        env:
            env1: 3.1415
        steps: # 이 job을 구성하는 작업들을 순서대로 적는 프로퍼티.
            - name: download source code # 각 작업 단계를 쓸 땐 프로퍼티 앞에 작업 단계를 구분하는 기호인 -를 반드시 써줘야 한다. 
              uses: actions/checkout@v3 # 현재 레포지토리의 소스코드를 가상머신으로 다운받는, GitHub에서 공식적으로 제공하는 액션.
            - uses: actions/setup-python@v2 # 가상머신에 파이썬을 설치하는 액션.
            - run: pip install -r requirements.txt # 콘솔창에서 직접 실행시키고자 하는 명령어를 쓰는 프로퍼티.
            - run: python main.py
              env: 
                env2: $\ # 레포지토리 설정의 secrets 탭에, 특정 변수명에 대응되는 값을 저장하여 YAML 파일에서 이런 식으로 불러올 수 있다. 레포지토리의 소스코드에 직접 담기 어려운 값을 이렇게 쓰면 그 값을 레포지토리에 공개적으로 나타내지 않으면서도 이처럼 workflow를 수행하는 소스코드에서 사용할 수 있다.
            ...
  • uses: 구체적 작업 단계의 내용을 이 YAML 파일에 세세히 적는 대신, GitHub 내 다른 레포지토리의 소스코드에 담긴 작업 단계 묶음(이를 action이라 한다)을 그대로 실행시키도록 할 수 있다. 이 이름을 한 프로퍼티에 그 레포지토리가 속한 계정명, 레포지토리의 이름, 그 레포지토리에서 가져올 소스코드가 담긴 브랜치명을 적으면 된다.

  • env: job의 자식으로 들어가거나 run 프로퍼티와 함께 쓰는 프로퍼티로, 거기서 실행되는 앱에서 호출할 수 있는 커스텀 환경변수를 전달하는 프로퍼티.