공공 SI 환경의 CI/CD 배포·백업·롤백 자동화

· 약 11분 읽기 · 조회 1 #CI/CD#공공 SI#배포 자동화#롤백#Jenkins#망분리

공공 SI의 운영 배포는 망분리, 변경 승인, 정해진 작업 시간 안에서 이뤄진다. 이 조건에서 사람이 WAR를 복사하고 로그를 눈으로 보는 배포는 실수 한 번에 서비스가 멈추고, 무엇이 언제 바뀌었는지도 남지 않는다. 이 글은 빌드부터 반입, 승인, 백업, 노드별 배포, 헬스체크, 자동 롤백, 배포 이력까지를 하나의 파이프라인으로 묶는 방법과 그대로 고쳐 쓸 수 있는 스크립트를 정리한 것이다.

정부 데이터센터에서 엔지니어가 봉인된 상자를 컨베이어로 서버 랙에 보내고, 뒤편 보관 선반 옆 되돌림 통로로 상자 하나가 돌아오는 장면

한눈에 보기

공공 사업의 배포는 개발망에서 만든 것을 운영망으로 옮기는 일이다. 운영망은 인터넷이 막혀 있고, 빌드 도구가 없는 경우가 많으며, 배포마다 변경 승인이 필요하다. 그래서 "운영 서버에서 빌드하지 않는다"와 "운영에 올라간 바이너리는 검증한 바이너리와 같다"가 출발점이다.

단계 자동화할 일 남는 기록
빌드 태그 기준 빌드, 정적 분석, 테스트, 해시 계산 버전, 커밋, SHA-256
반입·승인 반입 파일 해시 대조, 승인 번호 확인 반입 일시, 승인 번호
백업 현재 아티팩트·설정 복사, DB 변경 대상 백업 백업 경로, 보관 기한
배포 노드 하나씩 제외 → 배포 → 헬스체크 → 복귀 노드별 결과
롤백 헬스체크 실패 시 이전 아티팩트로 되돌림 실패 노드, 되돌린 버전
이력 배포 한 건을 한 줄로 기록 누가·언제·무엇을·결과

빌드, 정적 분석과 테스트, 변경 승인, 백업, 배포를 거쳐 헬스체크에 통과하면 이력을 남기고, 실패하면 이전 아티팩트로 자동 롤백한 뒤 이력을 남기는 배포 흐름

백업이 배포보다 앞에 있다는 점이 핵심이다. 되돌릴 대상이 없으면 롤백은 자동화할 수 없다.

1단계: 빌드는 한 번, 반입은 해시로 확인한다

할 일

  • 개발망의 내부 Git(GitLab 등)에 태그를 달면 Jenkins가 그 태그로 빌드한다. 브랜치 최신 상태가 아니라 태그로 빌드해야 같은 버전을 다시 만들 수 있다.
  • 빌드 결과물 이름에 버전을 넣는다. minwon-api-1.4.2.war처럼 쓰고, app.war 하나를 덮어쓰지 않는다. 이름에 버전이 없으면 롤백할 파일을 찾을 수 없다.
  • 정적 분석과 단위 테스트를 빌드 단계에서 돌리고, 기준을 넘지 못하면 아티팩트를 만들지 않는다.
  • 아티팩트의 SHA-256을 계산해 .sha256 파일로 함께 둔다. 결과물은 내부 저장소(Nexus 같은 아티팩트 저장소)에 올린다.
  • 운영망으로는 망연계 솔루션이나 승인된 매체로 아티팩트와 해시 파일을 함께 반입한다. 운영망에서는 반입한 파일의 해시를 다시 계산해 대조한다. 다르면 배포하지 않는다.

산출물

  • 버전별 아티팩트와 해시 파일
  • 빌드 기록: 태그, 커밋 ID, 정적 분석·테스트 결과

검증 서버와 운영 서버에 같은 파일을 올리는 것이 원칙이다. 운영용으로 다시 빌드하면 검증한 것과 다른 바이너리가 나갈 수 있다. 환경별 차이는 아티팩트 밖의 설정 파일로 둔다.

2단계: 변경 승인을 파이프라인에 넣는다

공공 사업에서는 운영 반영 전에 변경 요청서와 승인 절차를 거친다. 승인을 메일이나 문서로만 받으면 승인 없이 배포한 건을 나중에 가려낼 수 없다. 승인 번호를 배포 입력값으로 받고, 승인권자만 다음 단계를 누를 수 있게 한다.

pipeline {
  agent any
  parameters {
    string(name: 'VERSION', description: '배포할 버전 (예: 1.4.2)')
    string(name: 'CHANGE_ID', description: '변경 승인 번호')
  }
  stages {
    stage('승인') {
      input {
        message "${params.VERSION} 운영 배포를 승인합니까? (승인 번호 ${params.CHANGE_ID})"
        submitter 'ops-approvers'
      }
      steps { echo '승인됨' }
    }
    stage('배포') {
      steps { sh "./deploy.sh ${params.VERSION} ${params.CHANGE_ID}" }
    }
  }
}

submitter에 승인 그룹을 지정하면 개발자가 스스로 승인하고 배포할 수 없다. 승인 번호가 비어 있으면 실패하도록 스크립트 첫 줄에서 다시 검사한다.

3단계: 배포 전에 백업을 자동으로 남긴다

애플리케이션

  • 지금 운영 중인 버전의 아티팩트를 백업 폴더로 복사한다. 운영 중인 버전은 CURRENT 파일처럼 한곳에 적어 둔다.
  • 서버의 설정 파일(접속 정보 제외한 환경 설정, WAS 설정)도 함께 복사한다. 설정 변경이 섞인 배포는 아티팩트만 되돌려서는 원래대로 돌아가지 않는다.

데이터베이스

  • 스키마나 기준 데이터를 바꾸는 배포라면 변경 대상 테이블을 먼저 백업한다. DBMS가 주는 도구(Oracle Data Pump, PostgreSQL pg_dump 등)를 쓰고, 백업이 끝났는지 확인한 뒤에 다음 단계로 간다.
  • 백업 파일에는 배포 버전과 시각을 이름에 넣고, 보관 기한을 정해 오래된 것은 지운다. 기한이 없으면 디스크가 찬 날 백업이 조용히 실패한다.

산출물

  • 배포 건마다 하나의 백업 폴더: 이전 아티팩트, 설정, DB 백업 파일

4단계: 노드를 하나씩 배포하고 헬스체크로 판정한다

이중화된 WAS라면 노드를 하나씩 바꾼다. L4나 Apache에서 노드를 빼고, 새 버전을 올리고, 헬스체크를 통과하면 다시 넣는다. 전체를 한 번에 올리면 실패했을 때 서비스 전체가 멈춘다.

방식 필요한 것 되돌리는 법 공공 SI에서
롤링 노드 2대 이상 바꾼 노드만 이전 버전으로 재배포 가장 흔하다
블루그린 같은 규모의 대기 환경 한 벌 트래픽을 이전 환경으로 돌림 서버 예산이 있을 때
일괄 없음 전체 재배포 점검 공지를 내고 서비스를 멈출 때

헬스체크는 "프로세스가 떴다"가 아니라 "요청을 받을 수 있다"를 확인해야 한다. Spring Boot라면 management.endpoint.health.probes.enabled=true로 /actuator/health/readiness를 켜고, DB 연결 같은 필수 의존성을 readiness에 넣는다. 판정에는 시간 제한을 둔다. 60초 안에 UP이 아니면 실패로 본다. 제한이 없으면 파이프라인이 끝없이 기다린다.

5단계: 헬스체크가 실패하면 자동으로 되돌린다

롤백 대상은 그 배포에서 이미 바꾼 노드 전부다. 두 번째 노드에서 실패했는데 첫 번째 노드만 새 버전으로 남겨 두면 두 버전이 섞여 돈다. 아래 스크립트는 노드를 하나씩 배포하고, 실패하면 이미 바꾼 노드까지 백업한 이전 버전으로 되돌린 뒤 이력을 남긴다.

#!/usr/bin/env bash
# deploy.sh <버전> <변경승인번호>
# 노드를 하나씩 배포하고, 헬스체크 실패 시 바꾼 노드 전부를 이전 버전으로 되돌린다
set -euo pipefail

VERSION="${1:?버전 필요}"; CHANGE_ID="${2:?변경 승인 번호 필요}"
APP=minwon-api
REPO=/data/artifacts/$APP                 # 반입·검증한 아티팩트 보관 위치
BACKUP=/data/backup/$APP/$(date +%Y%m%d-%H%M%S)
NODES=(was1 was2)
HISTORY=/data/deploy-history.csv

NEW_WAR=$REPO/$APP-$VERSION.war
(cd "$REPO" && sha256sum -c "$APP-$VERSION.war.sha256")   # 반입 때 해시와 다르면 중단

CURRENT=$(cat "$REPO/CURRENT")            # 지금 운영 중인 버전
mkdir -p "$BACKUP"
cp "$REPO/$APP-$CURRENT.war" "$BACKUP/"
cp -r "/data/config/$APP" "$BACKUP/config"

lb_out() { :; }   # L4·Apache에서 노드 제외: 사용하는 장비의 명령으로 채운다
lb_in()  { :; }   # 노드 복귀

deploy() {        # deploy <노드> <war 경로>
  lb_out "$1"
  jboss-cli.sh --connect --controller="$1:9990" \
    --command="deploy $2 --name=$APP.war --force"
}

health() {        # 60초 안에 readiness가 UP이면 성공
  for _ in $(seq 1 12); do
    curl -fs "http://$1:8080/actuator/health/readiness" | grep -q '"UP"' && return 0
    sleep 5
  done
  return 1
}

record() { echo "$(date -Iseconds),$APP,$CURRENT,$VERSION,$CHANGE_ID,${BUILD_USER:-manual},$1" >> "$HISTORY"; }

DONE=()
for node in "${NODES[@]}"; do
  DONE+=("$node")
  if deploy "$node" "$NEW_WAR" && health "$node"; then
    lb_in "$node"
    continue
  fi
  echo "헬스체크 실패: $node, $CURRENT 버전으로 되돌림"
  for n in "${DONE[@]}"; do
    if deploy "$n" "$BACKUP/$APP-$CURRENT.war" && health "$n"; then
      lb_in "$n"
    else
      echo "롤백 실패: $n 수동 조치 필요"
    fi
  done
  record "ROLLBACK:$node"
  exit 1
done

echo "$VERSION" > "$REPO/CURRENT"
record "SUCCESS"

jboss-cli.sh의 deploy --force는 JBoss EAP·WildFly 기준이다. 다른 WAS라면 deploy 함수만 그 WAS의 배포 명령으로 바꾼다. 롤백까지 실패한 노드는 서비스에서 빠진 채로 남으므로, 이 메시지는 담당자에게 알림으로 보낸다.

DB는 자동으로 되돌리지 않는다. 데이터가 이미 바뀐 뒤에 백업을 덮어쓰면 그사이 들어온 민원·신청 데이터가 사라진다. 대신 스키마 변경을 하위 호환으로 나눈다. 컬럼 추가와 새 테이블은 이번 배포에, 기존 컬럼 삭제는 다음 배포에 한다. 이렇게 하면 앱만 이전 버전으로 되돌려도 동작하고, DB 복원은 사람이 판단해서 할 일로 남는다.

6단계: 배포 한 건을 한 줄로 남긴다

배포 이력은 운영 이관, 장애 분석, 감리에서 모두 찾는 기록이다. 스크립트가 남기는 한 줄에는 최소한 다음이 들어가야 한다.

  • 시각, 시스템, 이전 버전, 새 버전
  • 변경 승인 번호, 실행한 사람(Jenkins 사용자)
  • 결과: 성공, 롤백(실패한 노드), 롤백 실패
  • 아티팩트 해시와 백업 경로(별도 열로 두거나 빌드 기록에서 찾을 수 있게)

CSV 한 파일로 시작해도 충분하다. 중요한 것은 사람이 손으로 쓰지 않는다는 점이다. 수기 대장은 바쁜 날 빠지고, 빠진 날에 장애가 난다.

무엇을 자동화하고 왜 남기나

무엇을 누가 쓰나 없으면 생기는 일
버전이 붙은 아티팩트 배포 담당 되돌릴 파일을 찾지 못함
반입 해시 대조 운영·보안 담당 검증하지 않은 파일이 운영에 올라감
파이프라인 승인 단계 승인권자, 감리 승인 없는 배포를 가려낼 수 없음
배포 전 자동 백업 배포 담당 롤백을 사람이 기억에 의존해 함
시간 제한 있는 헬스체크 파이프라인 실패를 늦게 알거나 끝없이 기다림
바꾼 노드 전체 롤백 파이프라인 두 버전이 섞여 돌아감
자동 배포 이력 운영팀, 감리 장애 시점에 무엇이 바뀌었는지 모름

자주 하는 실수

  • 운영망에서 다시 빌드한다. 의존성이나 빌드 옵션이 달라져 검증한 것과 다른 바이너리가 나간다.
  • app.war 하나를 덮어쓴다. 이전 버전이 사라져 롤백할 대상이 없다.
  • 헬스체크로 포트만 본다. WAS가 떠도 DB 연결이 실패하면 요청은 전부 에러인데 배포는 성공으로 기록된다.
  • 실패한 노드만 되돌린다. 먼저 배포한 노드는 새 버전으로 남아 사용자가 어느 노드에 붙느냐에 따라 화면이 달라진다.
  • DB를 백업으로 덮어써서 되돌린다. 배포 이후 들어온 업무 데이터가 함께 사라진다.

점검표

  • 운영 배포 아티팩트는 태그로 빌드했고 이름에 버전이 있다
  • 반입한 아티팩트의 SHA-256을 운영망에서 다시 대조한다
  • 변경 승인 번호 없이는 배포가 시작되지 않는다
  • 승인 단계는 승인 그룹만 통과시킬 수 있다
  • 배포 전에 이전 아티팩트와 설정이 자동으로 백업된다
  • DB 변경이 있는 배포는 변경 대상 백업을 확인한 뒤 진행한다
  • 헬스체크가 readiness를 보고 시간 제한이 있다
  • 헬스체크 실패 시 이미 바꾼 노드까지 이전 버전으로 돌아간다
  • 스키마 변경은 이전 버전 앱과 함께 동작하도록 나눠 배포한다
  • 배포마다 이력 한 줄이 자동으로 남는다

공공 SI의 배포 자동화는 배포를 빠르게 하는 일보다, 같은 바이너리를 승인받은 대로 올리고 실패하면 기록과 함께 원래대로 되돌아가게 만드는 일이다.

댓글 0개

아직 댓글이 없습니다. 첫 댓글을 남겨보세요.

댓글 쓰기

← 목록으로 돌아가기