과거에는 젠킨스가 빌드한 결과물(예: .jar, .war, 빌드된 React 정적 파일)을 운영 서버에 scp나 rsync로 전송하여 덮어씌우는 방식이 주류였습니다. 하지만 바야흐로 컨테이너(Container)의 시대가 도래했습니다. 이제는 빌드 산출물을 압축해서 보내는 것이 아니라, OS와 런타임 환경까지 통째로 얼려버린 도커 이미지(Docker Image)를 만들어 굽고(Build), 원격 이미지 저장소(Registry)로 밀어 넣는(Push) 작업이 현대 CI/CD의 알파이자 오메가가 되었습니다.
이번 [Jenkins 실무 완벽 가이드] 시리즈에서는 인프라 불변성(Immutable Infrastructure)을 실현하는 Jenkins Pipeline 내에서의 우아한 Docker 제어 기법과 플러그인 100% 활용 노하우에 대하여 3,000자 분량으로 아주 치밀하게 딥다이브 해보겠습니다.

🐳 1. 사전 준비: Docker in Docker (DinD) 권한 해결
젠킨스 파이프라인 안에서 docker build 명령어를 치려면 가장 먼저 해결해야 할 거대한 장벽이 있습니다. 만약 여러분의 젠킨스 자체가 도커 컨테이너로 떠 있다면, 컨테이너 내부에서 또 도커 명령어를 실행해야 하는 'Docker in Docker' 상황에 놓이게 됩니다.
가장 대중적이고 안정적인 실무 해결책은 Docker Socket Binding (도커 소켓 바인딩)입니다. 젠킨스 컨테이너를 최초로 띄울 때 호스트 머신의 도커 데몬 소켓(/var/run/docker.sock)을 젠킨스 컨테이너 안으로 볼륨 마운트 시켜줍니다. 이렇게 하면 젠킨스 내부에서 치는 docker 명령어가 사실은 호스트 머신의 도커 엔진에게 전달되어 실행되므로, 권한 문제와 성능 저하를 동시에 해결할 수 있습니다.
📦 2. Docker Pipeline Plugin: 객체 지향적 접근
소켓이 연결되었다면 파이프라인의 steps 안에 무식하게 sh 'docker build -t my-app .' 쉘 스크립트를 도배할 수도 있습니다. 하지만 도커 허브(Docker Hub)나 AWS ECR에 로그인(Login)하기 위해 비밀번호를 치고, 태그를 여러 개 달아서 푸시(Push)하는 과정을 생 쉘 스크립트로 짜면 코드가 매우 지저분해지고 보안 토큰이 노출될 위험이 큽니다.
따라서 실무에서는 젠킨스가 공식 지원하는 Docker Pipeline Plugin을 활용합니다. 이 플러그인을 사용하면 도커 명령어를 그루비(Groovy)의 객체 지향 문법처럼 캡슐화하여 아주 우아하게 다룰 수 있습니다. (참고: Declarative Pipeline의 script { ... } 블록 안에서 사용해야 제맛이 납니다.)
💻 3. 실무 적용 코드 파헤치기: Build & Push
단순히 latest 태그만 덮어씌우면 롤백(Rollback)이 불가능합니다. 실무에서는 이번 빌드의 고유 번호(env.BUILD_NUMBER)를 태그로 달아 Push하고, 동시에 latest 태그도 업데이트해 주는 '이중 태깅(Double Tagging)' 전략을 씁니다.
pipeline {
agent any
environment {
// 도커 허브 계정명과 앱 이름 정의
IMAGE_NAME = "my-dockerhub-id/my-awesome-api"
// 실무 핵심: 고유한 버저닝을 위해 젠킨스 빌드 번호를 태그로 사용
IMAGE_TAG = "${env.BUILD_NUMBER}"
}
stages {
stage('Docker Build & Push') {
steps {
script {
// 1. withRegistry 블록: 로그인 자격 증명을 젠킨스 Credentials 금고에서 안전하게 꺼내옵니다.
// 첫 번째 인자: 레지스트리 URL (Docker Hub는 기본값 생략 가능하나 명시 권장)
// 두 번째 인자: Credentials에 등록해둔 ID ('dockerhub-login-creds')
docker.withRegistry('https://registry.hub.docker.com', 'dockerhub-login-creds') {
// 2. 이미지 빌드 (Dockerfile이 있는 현재 경로 '.' 에서 빌드)
// 반환된 customImage 객체를 변수에 담아둡니다.
echo "도커 이미지를 굽고 있습니다..."
def customImage = docker.build("${IMAGE_NAME}:${IMAGE_TAG}")
// 3. 고유 태그(예: 42번 빌드)로 Push
echo "버전 ${IMAGE_TAG} 이미지를 푸시합니다..."
customImage.push()
// 4. latest 태그로도 한 번 더 Push (서버에서 편하게 당겨 쓰기 위함)
echo "latest 태그를 업데이트합니다..."
customImage.push('latest')
}
}
}
}
}
}
이 플러그인의 withRegistry 블록을 통과하면, 블록이 끝나는 순간 젠킨스가 알아서 docker logout 명령어를 수행해 주어 호스트 서버에 로그인 정보 찌꺼기가 남는 보안 취약점을 완벽하게 방어해 줍니다.
🎯 4. 마무리 및 다음 단계
지금까지 현대 배포의 표준인 도커 이미지를 젠킨스 내부에서 굽고, 플러그인을 활용하여 가장 우아하고 안전하게 레지스트리(Docker Hub)에 밀어 넣는(Push) 전 과정에 대해 3,000자 분량으로 밀도 높게 다루어 보았습니다. 이제 여러분의 결과물은 가상화된 이미지 형태로 둥둥 떠다니며, 세상 어떤 서버에서든 docker pull 한 방에 동일한 환경으로 실행될 준비를 마쳤습니다.
이미지 저장소에 무기(Image)를 완벽하게 장전해 두었으니, 이제 이 무기를 발사하여 실제 전쟁터(Production)에 투입할 차례입니다. 서버가 한두 대라면 docker-compose로 충분하겠지만, 서버가 수십 대라면 오케스트레이션의 제왕인 Kubernetes(쿠버네티스)의 조종석에 앉아야 합니다. 이어지는 23단계 포스팅에서는 방금 푸시한 따끈따끈한 도커 이미지를 K8s 클러스터로 쏘아 보내 무중단 배포(Rolling Update)를 수행하는 궁극의 'Jenkins & Kubernetes 연동 자동화 전략'에 대해 아주 치밀하게 파헤쳐 보겠습니다!
'CI.CD > Jenkins' 카테고리의 다른 글
| [Jenkins] Input Step: 상용 배포 전 수동 승인(결재) 결계 치기 (0) | 2026.07.22 |
|---|---|
| [Jenkins] Kubernetes(K8s) 완벽 연동: 무중단 배포 자동화의 끝판왕 (0) | 2026.07.22 |
| [Jenkins] SonarQube 연동: 불량 코드 자동 차단 시스템 (Quality Gate) (0) | 2026.07.22 |
| [Jenkins] Shared Library: 전사 파이프라인 중복 제거의 마법 (0) | 2026.07.22 |
| [Jenkins] 병렬(Parallel) 빌드로 파이프라인 속도 혁신하기 (0) | 2026.07.22 |