테스트를 자동으로 실행할 수 있게 되면 다음 질문은 “배포도 버튼 하나로 끝낼 수 있을까?”가 됩니다. 가능하지만, 테스트와 배포에 같은 권한을 줄 필요는 없습니다. 자동화할 일과 승인받을 일, 실패했을 때 멈출 위치를 먼저 정하는 편이 운영하기 쉽습니다.
Jenkins 시작하기 · 01 Ubuntu 설치 · 02 Rocky Linux 9 설치 · 03 첫 작업과 Pipeline · 04 자동화 활용
앞의 03 첫 작업과 Pipeline은 준비된 Linux 에이전트에서 테스트하고 산출물을 남기는 실습이었습니다. 여기서는 그 위에 붙일 수 있는 활용 사례 여섯 가지를 다룹니다. 아래의 레지스트리·클러스터·계정 이름은 예시이므로 실제 대상으로 바꿔야 합니다.
이 글의 목차
1. Git 변경이 있을 때 자동으로 테스트하기
외부에서 접근할 수 없는 SSH 터널 실습 환경이라면 Poll SCM으로 시작할 수 있습니다. Jenkins가 Git 저장소에 변경이 있는지 확인하는 방식이라 Git 서비스가 Jenkins에 직접 접속할 필요가 없어요. Jenkinsfile의 pipeline 안에 다음 triggers를 추가합니다.
triggers {
pollSCM('H/15 * * * *')
}
이는 변경 유무를 약 15분 간격으로 확인하는 설정입니다. 매번 빌드하는 일반 cron과는 다릅니다. H는 작업마다 시간을 분산하는 값이며, 지정한 정각을 뜻하지 않습니다. Jenkinsfile을 처음 읽어 이 설정을 등록하도록 한 번 수동 실행한 뒤 변경 감지를 확인하세요. 문법과 동작은 Pipeline의 triggers·cron 설명을 참고했습니다.
팀 환경에서 응답 시간을 줄이려면 GitHub·GitLab 등 서비스에 맞는 웹훅을 사용할 수 있습니다. HTTPS로 도달할 수 있는 실제 Jenkins 주소, 이벤트 종류, 인증과 플러그인 구성이 필요합니다. GitHub 플러그인의 일반적인 수신 경로는 /github-webhook/이며, 플러그인 문서의 설정을 함께 적용합니다. localhost SSH 터널 주소는 외부 웹훅의 수신 주소로 쓸 수 없습니다.
외부 기여자의 Pull Request는 신뢰 경계도 다릅니다. 코드를 실행할 수 있는 사람에게 운영용 토큰을 함께 노출하지 않도록, 신뢰하지 않는 PR 테스트와 배포용 작업·에이전트를 나눕니다.
2. 테스트를 통과한 컨테이너 이미지만 올리기
이 사례는 docker-ci 라벨의 별도 빌드 에이전트에 Python·Git·Docker CLI와 접근 가능한 빌드 엔진이 준비되어 있다는 전제입니다. 레지스트리는 실제 HTTPS 주소로 바꾸고, Credentials에 해당 저장소만 push할 수 있는 registry-push 사용자명·토큰을 등록합니다.
Dockerfile은 3편의 저장소 루트에 추가합니다. 이 이미지는 애플리케이션 패키징 구조를 보기 위한 예시이며, 네트워크 서버를 실행하거나 Kubernetes의 정상적인 서비스 응답을 제공하는 샘플은 아닙니다.
FROM python:3.12-slim
WORKDIR /app
COPY app.py ./app.py
USER 10001:10001
CMD ["python3", "-c", "from app import add; print(add(2, 3))"]
pipeline {
agent { label 'docker-ci' }
options {
skipDefaultCheckout(true)
disableConcurrentBuilds()
timeout(time: 20, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '20'))
}
environment {
REGISTRY = 'registry.example.com'
IMAGE_REPO = 'registry.example.com/team/demo'
}
stages {
stage('Checkout and test') {
steps {
deleteDir()
checkout scm
sh 'python3 -m unittest discover -s tests -v'
}
}
stage('Build image') {
steps {
script {
env.IMAGE_TAG = sh(
script: 'git rev-parse HEAD',
returnStdout: true
).trim()
}
sh 'docker build --pull -t "$IMAGE_REPO:$IMAGE_TAG" .'
}
}
stage('Push image') {
steps {
withCredentials([usernamePassword(
credentialsId: 'registry-push',
usernameVariable: 'REGISTRY_USER',
passwordVariable: 'REGISTRY_PASSWORD'
)]) {
sh '''
set +x
set -eu
DOCKER_CONFIG=$(mktemp -d)
export DOCKER_CONFIG
trap 'rm -rf "$DOCKER_CONFIG"' EXIT
printf '%s' "$REGISTRY_PASSWORD" | docker login \
"$REGISTRY" --username "$REGISTRY_USER" --password-stdin
docker push "$IMAGE_REPO:$IMAGE_TAG"
'''
}
}
}
}
post { cleanup { deleteDir() } }
}
커밋 SHA를 태그로 사용하면 어떤 소스를 빌드했는지 추적하기 편합니다. 다만 태그 자체는 레지스트리에서 다시 가리킬 수 있어요. 운영 배포 기록에는 push 결과의 이미지 digest도 남깁니다. 베이스 이미지까지 같은 입력으로 재현해야 한다면 FROM도 검증한 digest로 고정하고 업데이트 절차를 별도로 둡니다.
비밀번호는 셸이 환경 변수로 읽게 했고 Groovy 문자열에 직접 끼워 넣지 않았습니다. 임시 DOCKER_CONFIG를 사용해 로그인이 남긴 파일도 종료 시 정리합니다. Credentials Binding 주의사항과 Docker의 password-stdin 안내를 참고한 구성입니다. 로그 마스킹은 자격 증명 오용까지 막아 주는 접근 제어가 아닙니다.
Docker 데몬에 접근하는 권한은 호스트를 강하게 제어할 수 있는 권한입니다. 컨트롤러에 Docker 소켓을 마운트하는 식으로 편의상 권한을 넓히지 말고, 전용 에이전트나 관리되는 빌드 환경에서 실행 범위를 정합니다.
3. 승인받은 이미지를 Kubernetes에 배포하기
이 예제는 앞의 일회성 Python 이미지를 배포하는 실습과 분리된 배포 작업의 골격입니다. 이미 검증한 웹 애플리케이션 이미지, demo 네임스페이스의 demo Deployment, 그 안의 app 컨테이너가 있어야 합니다. readinessProbe와 레지스트리 인증도 사전에 준비합니다.
k8s-deploy 에이전트에 호환되는 kubectl과 API 서버 연결을 준비합니다. Jenkins Secret file 자격 증명 demo-kubeconfig는 해당 네임스페이스·Deployment 조회/패치/롤아웃 확인에 필요한 범위만 허용하도록 만듭니다. 개인의 cluster-admin kubeconfig를 그대로 올리는 예제는 사용하지 않습니다.
pipeline {
agent { label 'k8s-deploy' }
options {
disableConcurrentBuilds()
skipDefaultCheckout(true)
timeout(time: 10, unit: 'MINUTES')
}
parameters {
string(name: 'IMAGE_DIGEST', defaultValue: '',
description: '검증된 이미지의 sha256:... 값')
}
stages {
stage('Validate digest') {
steps {
script {
if (!(params.IMAGE_DIGEST ==~ /sha256:[0-9a-f]{64}/)) {
error('Use a verified sha256 image digest.')
}
}
}
}
stage('Approval') {
steps {
input message: 'demo 네임스페이스에 배포할까요?',
submitter: 'deploy-approvers'
}
}
stage('Deploy') {
steps {
withCredentials([file(
credentialsId: 'demo-kubeconfig',
variable: 'KUBECONFIG'
)]) {
withEnv(["IMAGE_DIGEST=${params.IMAGE_DIGEST}"]) {
sh '''
set +x
set -eu
kubectl -n demo set image deployment/demo \
app="registry.example.com/team/demo@$IMAGE_DIGEST"
kubectl -n demo rollout status deployment/demo --timeout=120s
'''
}
}
}
}
}
}
deploy-approvers는 실제로 구성한 승인자 그룹 예시입니다. 그룹 이름만 적는다고 사용자가 새로 생기는 것은 아닙니다. 승인할 사람·권한을 먼저 준비해야 하며 Jenkins 관리자는 승인할 수 있습니다. 웹훅으로 누구나 이 작업을 호출하게 만들지 말고, 검증된 산출물과 승인 절차를 연결합니다.
rollout status가 실패하면 Jenkins 작업은 실패하지만, 이미 요청한 클러스터 변경이 자동으로 원복되지는 않습니다. 새 ReplicaSet의 상태·Pod 이벤트·로그를 보고, 검증된 이전 digest로 되돌릴지 판단합니다. Deployment의 revision과 rollback 동작은 Kubernetes 공식 Deployment 문서에서 확인할 수 있습니다.
kubectl -n demo rollout history deployment/demo
kubectl -n demo get pods
kubectl -n demo describe deployment/demo
# 복구 대상으로 이전 리비전을 확인하고 승인받은 뒤 실행
kubectl -n demo rollout undo deployment/demo --to-revision=2
kubectl -n demo rollout status deployment/demo --timeout=120s
리비전 2는 예시입니다. 실제 이력의 정상 리비전으로 바꿔야 합니다. 애플리케이션 이미지 롤백은 DB 스키마나 외부 시스템 변경까지 되돌려 주지 않으므로, 이런 변경은 배포 전에 복구 가능성을 따로 검토합니다.
4. Ansible로 서버 설정 변경을 사전 점검하기
서버 여러 대에 같은 패키지나 설정을 적용한다면 Jenkins가 Ansible 실행의 입구가 될 수 있습니다. 저장소에 inventory·playbook을 두고, 전용 에이전트와 대상 서버에 필요한 최소 SSH 권한을 준비합니다. 아래 명령은 기존 Jenkinsfile stage의 셸 명령으로 넣을 수 있는 예시입니다.
ansible-playbook -i inventories/lab/hosts.yml site.yml --syntax-check
ansible-playbook -i inventories/lab/hosts.yml site.yml --check --diff
--check는 모든 모듈에서 동일하게 동작하는 완전한 시뮬레이터가 아닙니다. 지원하지 않는 태스크는 건너뛰거나 충분한 결과를 주지 않을 수 있어요. --diff에는 설정 파일의 민감한 내용이 나타날 수 있으므로 비밀을 다루는 태스크는 출력 정책도 검토합니다. Ansible check·diff 모드 안내를 확인한 뒤, 실제 적용은 별도 승인 단계로 분리하세요.
5. 정기 점검을 실행하고 실패를 남기기
예를 들어 매일 테스트 환경의 TLS 인증서 만료일이나 복원용 파일 존재 여부를 점검할 수 있습니다. 일정 실행에는 pollSCM이 아니라 cron을 사용합니다. 아래 코드는 Jenkinsfile의 triggers 부분 예시입니다.
triggers {
cron("""TZ=Asia/Seoul
H 2 * * *""")
}
이 설정은 서울 시간 02시대에 작업별로 분산된 분에 실행합니다. 매일 02:00 정각이라는 의미는 아닙니다. 정기 점검 스크립트도 문제가 있으면 종료 코드 0이 아닌 값으로 끝나야 Jenkins가 실패로 기록할 수 있습니다.
알림은 이미 팀이 쓰는 채널에 연결하되, 수신 대상·SMTP나 웹훅 자격 증명·연동 플러그인을 먼저 준비합니다. 알림을 보냈다는 사실과 담당자가 장애를 확인했다는 사실을 구분해 운영 절차를 정하는 편이 좋습니다.
6. Jenkins 자체의 백업을 복원해 보기

Jenkinsfile은 Git에 있어도 사용자 설정·자격 증명·작업 이력은 Jenkins 홈에 남습니다. 패키지 설치에서는 보통 /var/lib/jenkins가 그 위치예요. 실제 값은 systemctl cat jenkins의 JENKINS_HOME에서 확인합니다.
먼저 신규 작업 실행을 멈추고 진행 중인 작업이 끝나는 시간을 잡습니다. 일관성이 필요한 백업은 서비스를 정지하거나 이에 준하는 일관된 스냅샷 방식으로 확보합니다. 서비스가 쓰는 중인 디렉터리를 단순 복사한 파일을 곧바로 복구 가능한 백업이라고 부르기는 어렵습니다.
설정·작업 이력과 함께 자격 증명 복호화에 필요한 키를 어떻게 복구할지 설계해야 합니다. 특히 secrets/master.key 등 핵심 키는 일반 백업과 분리해 별도로 보호·보관하고, 백업 본문도 암호화합니다. 자격 증명과 복호화 키가 함께 유출되면 비밀번호 보호가 무력해질 수 있습니다. Jenkins 백업·복원 문서의 키 취급 안내를 먼저 읽어 보세요.
- Jenkins·Java·플러그인 버전과 설치 목록을 기록합니다.
- 설정·이력 백업과 별도로 보호한 복구용 키의 위치·접근 권한을 확인합니다.
- 운영과 분리된 환경에 복원합니다. 웹훅·정기 작업·배포·알림이 실제 대상으로 실행되지 않게 막습니다.
- 로그인, 작업 설정, 자격 증명 복호화, 에이전트 연결을 확인합니다.
- 실제 복원 시간을 기록하고 업그레이드 전에 다시 점검합니다.
자동화 범위를 넓히기 전에 정할 것
| 자동화 | 실패했을 때 멈출 위치 |
|---|---|
| 애플리케이션 테스트 | 테스트가 실패하면 새 결과물을 배포 단계로 넘기지 않음 |
| 이미지 빌드·push | push 실패 시 이전 latest 태그로 배포를 계속하지 않음 |
| Kubernetes 배포 | 롤아웃 실패를 기록하고 원인 확인·승인된 복구로 진행 |
| 서버 설정 변경 | 대상 inventory와 변경 결과를 확인한 뒤 실제 적용 승인 |
| 정기 점검 | 종료 코드·보관 기간·알림 수신자를 함께 관리 |
| Jenkins 업그레이드 | 복원 가능한 백업과 호환성 점검이 준비된 뒤 진행 |
이 연재의 설치·설정은 2026년 10월 18일의 공식 문서와 패키지 정보를 대조했습니다. Python 테스트와 tar 산출물 생성은 별도 디렉터리에서 실행해 확인했습니다. Ubuntu·Rocky VM의 전체 설치, Jenkins 내부 Pipeline 실행, 실제 레지스트리 push·클러스터 배포·Ansible 적용까지 재현한 결과를 제시하는 글은 아닙니다. 해당 단계는 자신의 격리된 학습 환경에서 검증한 뒤 운영 대상으로 확장하세요.

![[Jenkins 시작하기 03] 첫 작업부터 Jenkinsfile까지: Git·에이전트·Pipeline](https://blog.maruops.com/wp-content/uploads/2026/10/maruops-jenkins-pipeline-768x512.webp)
![[Jenkins 시작하기 02] Rocky Linux 9에 Jenkins 설치하기: DNF·SELinux·방화벽](https://blog.maruops.com/wp-content/uploads/2026/10/maruops-jenkins-rocky-install-768x512.webp)
![[Jenkins 시작하기 01] Ubuntu에 Jenkins LTS 설치하기: Java·초기 설정·접속](https://blog.maruops.com/wp-content/uploads/2026/10/maruops-jenkins-ubuntu-install-768x512.webp)
답글 남기기