[인프라] CI/CD와 깃허브 액션

2026. 5. 7. 01:07·CS/인프라

 

CI/CD

수동배포에서의 업데이트

https://dev-dx2d2y-log.tistory.com/240

 

[인프라] AWS란? (AWS 왕왕기초) + 수동배포

AWS개발이 끝났다면 내가 만든 애플리케이션을 사용자들이 사용할 수 있도록 배포를 해야한다.물론 개인PC를 통해 배포해도 큰 문제는 되지 않지만... 24시간 내내 서버를 돌리고 있어야하고, 학

dev-dx2d2y-log.tistory.com

저번에는 AWS의 기초와 도커와 AWS를 사용한 수동배포 방식에 대해서 배웠다.

수동배포 환경에서 첫 번째 배포에 성공했다고할 때, 업데이트를 어떻게 반영할까?

 

몇 가지를 지피티에게 물어보니 로컬에서 다시 도커 이미지를 빌드한 후 push하고, EC2에서 이 이미지를 pull해서 도커이미지를 run하는 구조다. (이 과정에서 잠깐 도커 컨테이너를 갈아끼우면서 서버가 멈춘다)

 

https://dawonny.tistory.com/492

 

수동 배포에서 CI/CD 파이프라인 구축까지 (feat. Github Actions)

들어가며저는 웹 외주 프로젝트에 프론트엔드 개발로 참여하며 팀원과 공유할 테스트용 배포 사이트가 필요했습니다.좀 더 구체적으로는 다음과 같은 프로세스를 가졌는데요.develop 브랜치에

dawonny.tistory.com

몇 가지 글을 보면 알 수 있듯이 매번 터미널에 명령어를 치는게 귀찮기도하고, 몇 가지 과정도 빼먹을 수 있어서 매 배포 때마다 자동으로 빌드도하고, 테스트도하고, 도커 이미지도 빌드하고, EC2에서 컨테이너도 갈아끼우는 과정을 자동으로 수행하도록한 것이 CI/CD다.


CI/CD?

CI는 Continuous Integration (지속적 통합), CD는 Continuous Deployment/Delivery (지속적 제공/배포)의 약어다.

 

CI/CD가 한 동작처럼 수행하는 경우도 있지만 CI와 CD는 별개의 개념인데,

CI는 여러 개발자가 작업한 코드를 병합 및 테스트를 수행하는 과정

CD는 적용된 변경사항이 자동테스트를 거쳐 기존 코드에 병합되는 과정(Delivery)와 자동으로 기존 운영환경에 배포되는 과정(Deployment)이다. Delivery의 경우에는 수동으로 승인을 거쳐서 배포가되지만 Deployment의 경우에는 자동승인이 된다고한다.


 

파이프라이닝

CI/CD 과정은 파이프라인으로 이루어져있다. CI와 CD 과정을 분리한 파이프라인을 Split Pipline이고, 모든 과정을 한 번에 처리하면 All-in-One Pipeline이라고 칭한다. 후자는 CD를 세분화 (Deployment / Delivery) 할 수 없는 등 배포 환경에서 디테일한 처리가 불가능한데다 빌드 / 배포 실패 시 부분롤백이 불가능하다. 그래서 전자를 사용하는게 더 좋다고

 

 

CI 파이프라이닝

코드 커밋 → 자동 빌드 → 자동 테스트

커밋과 빌드는 소스코드 컴파일, 라이브러리 다운로드, 애플리케이션 패키징 등, 이전에 도커와 AWS 수동배포에서 한 번 다뤘던 패키징에 대한 내용이다.

테스트는 CI 과정에서는 수행한다. 이 때 테스트는 gradlew로 빌드할 때의 테스트로, ApplicationTest 코드가 돌아가는 과정이다. 보통은 로컬에서 테스트를 돌리고 올리더라도 CI 과정에서 테스트를 한 번씩 꼭 해봐야한다고한다.

 

CD 파이프라이닝

코드리뷰 → 스테이징 → 배포

CI를 통해서 코드가 맞춰졌으면 CD과정을 거치는데, 먼저 코드리뷰를 진행하고 스테이징 과정으로 넘어간다.

스테이징 과정이란 테스트 코드가 모두 통과하였더라도 예상치 못한 운영환경에서의 장애 (DB 연결 실패 등...) 를 미리 탐지해내기 위하여 실제 사용자 없이 EC2, RDS 등에 배포해보는 과정이다. 이 과정에서 문제가 없다면 배포로 이어지게된다.

 

CI/CD 과정에서 가장 많이 쓰이는 툴은 깃허브액션과 젠킨스가 있다.


 

Github Actions로 CI/CD 구성하기

Github Actions는 CI/CD 구성하기 단순한 편인데, 레포지토리의 github/workflows 파일에 YAML 파일로 정의하면 알아서 CI/CD가 동작하기 때문이다.

이렇게 깃허브 창에서 바로 결과를 확인할 수도 있다.

 

CI

CI의 목적은 push하면 자동으로 빌드 및 테스트 검증 끝내기가 목적이다.

name: CONX server CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - dev
      - main

jobs:
  build:

    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK21
        uses: actions/setup-java@4
        with:
          java-version: '21'
          distribution: 'temurin'

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582

      - name: Build with Gradle Wrapper
        run: ./gradlew build

CI는 이렇게 구성된다.

name 속성으로 문서의 이름을 지정하고,

on 속성으로 언제 CI를 동작시킬 것인지를 지정하면된다. 내가 팀프로젝트에서 적용시킬 CI 파일이기 때문에 main 브랜치에 최종 push 될 때, 그리고 main 브랜치로 PR이 날아가거나 feature 브랜치에서 개발을 끝내고 dev 브랜치에 PR을 날릴 때 한 번 CI가 발생하게 했다.

 

환경은 gradle을 작동시키고 JDK21을 다운받는 과정까지 거쳤다.

이렇게 CI 파일을 작성하면 CI파일이 동작할 때 깃허브 임시서버에서 JDK21이 다운받아지고, gradle이 다운 받아지고 build가 일어나며 테스트까지 진행된다. 만약 문제가 발생하지 않은 경우 임시서버를 비우는 방식

 

무튼 그래서 CI 과정에서는 코드커밋 이후 자동 빌드 및 자동 테스트까지 거쳤다. 이를 통해서 코드가 커밋 되었을 때 별다른 문제가 없는지 자동으로 판별하게된다. 만약 에러가 발생하면 코드가 올라가지 않는다.


CD

name: CONX server CD
on:
  workflow_run:
    workflows:
      - "CI"
    types:
      - completed

jobs:
  deploy:
    if: >
      github.event.workflow_run.conclusion == 'success' &&
      github.event.workflow_run.event == 'push' &&
      github.event.workflow_run.head_branch == 'main'
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.workflow_run.head_sha }}

      - name: Setting Buildx Docker
        uses: docker/setup-buildx-action@v3

      - name: Login Docker
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: Build and Push Docker Image
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./Dockerfile
          push: true
          platforms: linux/amd64, linux/arm64
          tags: |
            kdhyun422/conx-server:latest
            kdhyun422/conx-server:${{ github.event.workflow_run.head_sha }}
      
      - name: Deploy to EC2
        uses: appleboy/ssh-action@v1.0.3
        with:
            host: ${{ secrets.EC2_HOST }}
            username: ubuntu
            key: ${{ secrets.EC2_KEY }}
            script: |
              sudo docker pull kdhyun422/conx-server:${{ github.event.workflow_run.head_sha }}
              
              sudo docker stop conx || true
              sudo docker rm conx || true
              
              sudo docker run -d -p 80:8080 \
              --name conx \
              --restart unless-stopped \
              kdhyun422/conx-server:${{ github.event.workflow_run.head_sha }}
              
              for i in {1..10}; do
                if curl -f http://localhost:80/health/ready; then
                  echo "Health Check passed"
                  break
                fi
                echo "Waiting for Service..."
                sleep 2
              done
  
              curl -f http://localhost:80/health/ready || exit 1
              
              sudo docker image prune -f

CD도 CI와 같이 구성된다.

그래도 약간 다른 점이 있는데, on 속성을 특정 브랜치에 대한 push나 PR을 기준으로 삼는게 아니라 "CI"라는 워크플로우가 완성된 후에 동작하겠다는 것을 정의하였다.

 

이후 deploy 과정을 통해 본격적인 CD 과정을 거친다. 대부분 위에서 다룬 파이프라인과 크게 다르지 않다.

${{ github.event.workflow_run.head_sha }}

github.event.workflow_run 에는 다른 워크플로우가 끝났을 때의 정보가 들어있다. 여기서는 CI가 끝났을 때 정보가 들어있다. head_sha 속성을 통해서 CI 커밋의 SHA를 가져온다. 여기서 커밋은 깃허브의 커밋이다. 그 커밋ID의 SHA를 가져온다.

 

그래서 매 이미지를 latest로 덮어씌우는게 아니라 고윳값을 토대로 이미지에 태그를 붙일 수 있다. 문제가 발생한 경우 어떤 커밋이 문제였는지를 파악할 수도 있고.

 

deploy 과정에서는 CI 과정이 main에서 성공적으로 push된 것인지를 확인한 후에 CD를 진행한다. 이후 과정은 actions/checkout, 도커 세팅, 도커 로그인, 도커 이미지 푸시, EC2에 적용 등이 있다. CI와 CD가 완벽하게 분리되어있다면 CD 과정에서도 JDK를 설치하고 gradle도 재설치하고 빌드해야하지만 이 CD는 CI 직후에 동작하기 때문에 이미 깔려있다.

 

무튼 도커 이미지 빌드까지는 크게 어려울 게 없고, 자세히 알아볼 것은 EC2에 적용. 주목할 것은 script 부분.

수동배포에서 ssh ~~ 명령어를 통해 EC2 인스턴스로 원격접속을 한 다음에 명령어를 입력했듯이, YAML 파일에서 Deploy to EC2 부분의 script 속성은 EC2 인스턴스에 원격접속을 한 다음 수행할 명령어들이다.

 

크게 도커이미지를 pull해오고 → 만약 동일한 도커 컨테이너가 이미 동작 중이라면 끄고 → 컨테이너를 동작시킨다.

-p 속성은 포트를 지정하고, --name 속성으로 도커 컨테이너의 이름을 지정하고, --restart 부분의 속성은 도커 컨테이너가 개발자에 의해 수동으로 중지되지 않는한 어느 이유로 컨테이너가 뻗으면 재시작하는 속성이다.

 

package com.conx.server.Global;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Map;

@RestController
@RequiredArgsConstructor
@RequestMapping("/health")
public class health {

    private DataSource dataSource;

    /**
     * CD 이후 서버가 정상적으로 동작하는지 파악하기 위한 엔드포인트입니다.
     */
    @GetMapping("/ready")
    public ResponseEntity<?> healthChecking() {
        try (Connection conn = dataSource.getConnection()) {
            if (!conn.isValid(2)) {
                throw new RuntimeException("DB not valid");
            }
        } catch (Exception e){
            return ResponseEntity.status(500)
                    .body(Map.of("status", "DOWN"));
        }

        return ResponseEntity.ok(Map.of("status", "UP"));
    }
}

이 이후 코드는 도커 컨테이너가 제대로 빌드 및 실행되었는지 확인하는 코드다.

위처럼 미리 health 체크를 위한 엔드포인트를 마련해두고 요청을 보내게한다. 만약 제대로 응답했다면 서버도 살고 DB도 정상적인 반면, DB 연결이 안되거나 서버가 뻗어버리면 연결자체가 안된다. 즉, CD에 실패한다.

 

그리고 마지막으로 안쓰는 도커 이미지들을 제거해 메모리 효용성을 높인다.


이렇게 깃허브 액션을 통한 CI/CD 과정에 대해서 알아보았다. 아직 AWS 기술들이 많이 남아있기 때문에 올릴 글도 참 많다. 무중단배포, 로드밸런싱, 추가로 부하테스트, 도커컴포즈.. 정말 다룰게 많다. 인프라는 확실히 개발과는 다른 새로운 영역인 것 같다.

'CS > 인프라' 카테고리의 다른 글

[인프라] 도전! 실전 깃허브 액션  (0) 2026.05.12
[인프라] CI/CD 깃허브 액션에 도커컴포즈 적용하기  (0) 2026.05.07
[인프라] AWS란? (AWS 왕왕기초) + 수동배포  (0) 2026.05.06
[인프라] 도커란? (도커왕왕기초)  (0) 2026.05.06
'CS/인프라' 카테고리의 다른 글
  • [인프라] 도전! 실전 깃허브 액션
  • [인프라] CI/CD 깃허브 액션에 도커컴포즈 적용하기
  • [인프라] AWS란? (AWS 왕왕기초) + 수동배포
  • [인프라] 도커란? (도커왕왕기초)
컬러잇
컬러잇
탄천러너지망생
  • 컬러잇
    Color it
    컬러잇
  • 전체
    오늘
    어제
    • 분류 전체보기 (235)
      • 신년사 (3)
        • 2025년 (2)
        • 2026년 (1)
      • CS (72)
        • JVM (12)
        • 인프라 (5)
        • 백엔드 (22)
        • 논리회로 (5)
        • 언어구현 (1)
        • 인공지능 (1)
        • 코드설계 (3)
        • 컴퓨터구조 (9)
        • 데이터베이스 (4)
        • 컴퓨터 네트워크 (10)
      • 언어공부 (65)
        • Java | Kotlin (49)
        • JavaScript | TypeScript (9)
        • C | C++ (6)
      • 개인 프로젝트 (11)
        • [2025] Happy2SendingMails (3)
        • [2026] 골든리포트! (8)
        • [2026] 순수자바로 개발하기 (0)
        • 기타 이것저것 (0)
      • 팀 프로젝트 (29)
        • [2025][GDG]홍대 맛집 아카이빙 프로젝트 (29)
      • 알고리즘 (13)
        • 백준풀이기록 (11)
      • 놀이터 (0)
      • 에러 수정일지 (4)
      • 고찰 (35)
        • CEOS 23기 회고록 (9)
  • 링크

    • 교양있는컬러잇
  • 최근 글

  • 인기 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.5
컬러잇
[인프라] CI/CD와 깃허브 액션
상단으로

티스토리툴바