CI/CD 파이프라인의 중심이자 가장 널리 쓰이는 오픈소스 자동화 서버인 Jenkins를 처음 도입하기로 결정하셨다면, 가장 먼저 마주하는 난관은 바로 "어떻게 설치하고 세팅할 것인가"입니다.
운영 서버나 개발자의 로컬 PC에 직접 설치하는 방법도 있지만, 최근 클라우드 네이티브(Cloud Native) 환경과 컨테이너 기술의 발달로 인해 설치 패러다임이 완전히 바뀌었습니다.
이번 [Jenkins 실무 완벽 가이드]에서는 현재 IT 업계에서 가장 강력하게 권장되는 표준 방식인 Docker와 Docker Compose를 활용하여 Jenkins를 단 5분 만에 완벽하게 설치하고 격리된 환경을 구축하는 방법에 대하여 아주 상세하고 깊이 있게 파헤쳐보겠습니다. 이 글을 정독하시면 단순한 설치를 넘어 컨테이너 인프라의 핵심 동작 원리까지 마스터하실 수 있습니다.

📌 1. 왜 로컬 환경(Host OS)에 직접 설치(Native Install)하지 않는가?
과거에는 리눅스 패키지 매니저(CentOS의 yum, Ubuntu의 apt-get)를 사용하거나, Java의 .war 아카이브 파일을 톰캣(Tomcat)에 올려 직접 구동하는 방식을 많이 썼습니다. 하지만 실무에서 이런 직접 설치 방식을 점차 기피하는 명확한 이유가 존재합니다. 바로 '의존성 꼬임(Dependency Conflict)'과 '환경 격리 실패' 때문입니다.
1. 심각한 버전 충돌 문제: Jenkins 코어 모듈을 구동하려면 특정 버전의 Java(JDK 11 혹은 17)가 필수적입니다. 만약 동일한 호스트 서버에서 구동 중인 다른 사내 애플리케이션이나 모듈이 구버전인 JDK 8을 요구한다면 어떻게 될까요? 환경 변수(`JAVA_HOME`)가 충돌하면서 둘 중 하나는 작동을 멈추게 됩니다. 더불어 Jenkins 플러그인들이 요구하는 수많은 OS 레벨의 라이브러리 패키지들이 엉키게 되면, 최악의 경우 서버 전체를 포맷하고 OS부터 다시 깔아야 하는 대참사가 발생합니다.
2. 마이그레이션과 백업의 어려움: 서버가 노후화되어 더 스펙이 좋은 신규 서버로 젠킨스를 통째로 이전(Migration)해야 하는 상황을 가정해봅시다. 직접 설치된 환경에서는 설정 파일이 OS 여기저기에 흩어져 있고, 호스트 OS에 깔아둔 수많은 패키지들을 새 서버에 똑같이 수동으로 설치해야 합니다. 이는 엄청난 인적 리소스 낭비와 휴먼 에러(Human Error)를 유발합니다.
결론적으로, 이러한 모든 문제를 단번에 해결하는 은탄환(Silver Bullet)이 바로 Docker 컨테이너를 통한 격리(Isolation)입니다. 젠킨스 실행에 필요한 모든 OS 환경, JDK, 라이브러리가 컨테이너 이미지 안에 완벽히 패키징되어 있으므로, 호스트 OS를 전혀 오염시키지 않고 깔끔하게 실행할 수 있습니다. 이것이 현대 인프라의 확고한 실무 표준(Best Practice)입니다.
🐳 2. Docker Compose를 활용한 Infrastructure as Code (IaC) 구현
Docker를 쓴다고 해서 터미널에 docker run -d -p 8080:8080 -v ... 처럼 매우 길고 복잡한 명령어를 텍스트 파일에 메모해두고 복사해서 쓰는 것은 하수들의 방식입니다. 인프라의 형상을 코드로 관리하는 IaC(Infrastructure as Code) 사상에 맞게, 설정 파일인 docker-compose.yml을 작성하여 Git으로 형상 관리하는 것이 유지보수에 압도적으로 유리합니다.
아래는 수많은 대기업과 유니콘 스타트업 실무에서 실제로 사용되는, 보안과 확장성이 최적화된 가장 이상적인 docker-compose.yml 스니펫입니다. 젠킨스를 설치할 빈 디렉토리를 하나 생성하고 아래 코드를 복사하여 저장하십시오.
version: '3.8'
services:
jenkins:
# 가장 안정적인 최신 LTS(Long Term Support) 버전을 사용합니다.
image: jenkins/jenkins:lts
container_name: jenkins_server
# 권한 문제를 피하기 위해 임시로 root를 사용하지만, 실무 상용 환경에서는 호스트 uid:gid를 매핑하는 것이 안전합니다.
user: root
privileged: true # 컨테이너 내에서 시스템 주요 기능 제어 권한 부여
ports:
- "8080:8080" # 개발자나 관리자가 접속할 웹 UI HTTP 포트
- "50000:50000" # JNLP (Java Web Start) 기반의 Agent(Worker Node) 통신용 포트
volumes:
# 호스트의 ./jenkins_data 폴더와 컨테이너 안의 /var/jenkins_home을 동기화하여 데이터 영속성 보장
- ./jenkins_data:/var/jenkins_home
# DooD(Docker out of Docker) 구성을 위해 호스트의 도커 데몬 소켓을 공유
- /var/run/docker.sock:/var/run/docker.sock
# 예상치 못한 서버 재부팅이나 크래시 발생 시 컨테이너를 자동으로 다시 띄움
restart: always
environment:
- TZ=Asia/Seoul # 컨테이너 내부 시간을 한국 시간(KST)으로 맞춤
🔑 3. 실무를 위한 핵심 설정 딥다이브 (Deep Dive)
위 docker-compose.yml 코드에는 CI/CD 인프라 엔지니어들의 피와 땀이 서린 중요한 실무 노하우들이 녹아있습니다. 각 설정이 왜 들어갔는지 정확히 이해해야 합니다.
- image (jenkins/jenkins:lts): 도커 허브(Docker Hub)에서 이미지를 당겨올 때 절대
latest태그를 쓰지 마십시오. latest는 검증되지 않은 최신 기능이 포함되어 있어 갑자기 빌드가 터지는 예기치 않은 버그에 노출될 수 있습니다. 반드시 장기 지원이 보장되고 버그 픽스가 완료된 lts(Long Term Support) 버전을 사용해야 합니다. - volumes (데이터 영속성, Persistence): 컨테이너 기술의 가장 큰 특징 중 하나는 "컨테이너가 지워지면 그 안의 데이터도 모두 날아간다"는 휘발성입니다. 이를 막기 위해 호스트 머신의 폴더(
./jenkins_data)를 컨테이너 내부의 젠킨스 홈 디렉토리(/var/jenkins_home)와 마운트(Mount) 시킵니다. 이렇게 하면 컨테이너를 아예 삭제하고 새 버전으로 다시 띄워도, 모든 계정 정보, 설치한 플러그인, 빌드 히스토리가 그대로 유지됩니다. - docker.sock 마운트 (DooD 구성): 현대의 파이프라인은 거의 100% 도커 이미지를 빌드(
docker build)하여 배포하는 과정을 거칩니다. 즉, 젠킨스 파이프라인 안에서 도커 명령어를 실행해야 한다는 뜻입니다. 하지만 젠킨스는 이미 도커 '컨테이너 내부'에서 돌고 있습니다. 컨테이너 안에서 또 도커 데몬을 띄우는 DinD(Docker in Docker) 방식은 보안과 성능 이슈가 커서 지양됩니다. 대신 호스트 머신의 도커 데몬 소켓(docker.sock)을 컨테이너 안으로 연결해주면, 컨테이너 내부에서 도커 명령어를 치더라도 실제로는 호스트 머신의 도커가 일을 대신 해줍니다. 이를 DooD(Docker out of Docker)라고 부르며 실무에서 가장 권장되는 아키텍처입니다. - timezone (TZ=Asia/Seoul): 컨테이너의 기본 시간은 UTC(협정 세계시) 기준입니다. 이를 설정하지 않으면 파이프라인 빌드 타임스탬프와 예약된 크론(Cron) 스케줄 빌드가 한국 시간과 9시간 차이가 나게 되어 엄청난 혼란을 야기합니다. 환경 변수로
TZ를 반드시 주입해야 합니다.
🚀 4. 컨테이너 실행 및 트러블슈팅 (Troubleshooting)
설정 파일의 의미를 완벽히 이해했다면, 터미널을 열고 docker-compose.yml 파일이 있는 디렉토리로 이동하여 아래 명령어로 젠킨스를 백그라운드(데몬) 모드로 실행합니다.
$ docker-compose up -d
명령어를 치면 이미지를 다운로드받고 컨테이너가 뜹니다. 정상적으로 떴는지 확인하려면 docker ps 명령어를 치면 됩니다. 하지만 실무에서는 항상 한 번에 성공하지 않고 아래와 같은 권한(Permission) 문제가 자주 발생합니다.
🔥 자주 발생하는 치명적 에러: Permission Denied (디렉토리 쓰기 권한 없음)
컨테이너는 실행되었다고 나오는데 접속이 안 되어 docker-compose logs -f jenkins 명령어로 로그를 살펴보면, "/var/jenkins_home/copy_reference_file.log: Permission denied"라는 에러를 뱉고 컨테이너가 죽어버리는 경우가 몹시 흔합니다.
발생 원인: 호스트 머신에서 생성된 ./jenkins_data 폴더의 소유자는 보통 root나 현재 로그인된 사용자(예: ubuntu)입니다. 하지만 컨테이너 내부에서 Jenkins 프로세스를 실행하는 기본 사용자의 UID는 1000(jenkins)입니다. 따라서 UID 1000이 호스트가 만든 폴더에 쓰기 작업을 시도하다가 권한이 없어 튕겨내는 것입니다.
해결책 (Solution): 크게 두 가지 완벽한 해결책이 있습니다.
- 위
docker-compose.yml예제처럼user: root옵션을 주어 젠킨스를 최상위 관리자 권한으로 강제로 실행시키는 방법입니다. 세팅이 가장 편하지만 깐깐한 보안 감사가 있는 기업에서는 컨테이너를 root로 띄우는 것을 금지하기도 합니다. - 호스트 서버에서 터미널을 열고 마운트할 폴더의 소유권을 강제로 UID 1000으로 맞춰버리는 방법입니다.
sudo chown -R 1000:1000 ./jenkins_data명령어를 치면 완벽하게 권한 충돌이 해결되며, 가장 권장되는 보안 팁입니다.
🎯 5. 마무리 및 다음 단계
지금까지 Docker 컨테이너 기술을 활용하여 Jenkins를 안전하고 효율적으로 5분 만에 구축하는 방법에 대해 3,000자 이상의 매우 상세한 설명과 함께 알아보았습니다. 로컬 OS에 덕지덕지 패키지를 깔아 서버를 오염시키던 방식에서 벗어나, 언제 어디서든 docker-compose up -d 명령어 한 줄만 치면 동일한 환경의 CI/CD 인프라가 마법처럼 구성되는 것을 체감하셨을 것입니다. 이것이 바로 IaC(인프라의 코드화)의 진정한 매력입니다.
이제 Jenkins 서버 구동 자체는 성공적으로 끝났습니다. 하지만 브라우저를 열어 접속해 보면 곧바로 사용할 수 있는 것이 아니라 굳게 잠긴 자물쇠 화면이 여러분을 반길 것입니다. 이어지는 3단계 포스팅에서는 설치 직후 무조건 거쳐야만 하는 통과의례인 '초기 관리자 비밀번호 확인 및 필수 플러그인 세팅 전략'에 대해 아주 상세하게 살펴보도록 하겠습니다. 감사합니다.
'CI.CD > Jenkins' 카테고리의 다른 글
| [Jenkins] Freestyle Project로 첫 자동화 빌드 만들기 (초보자 완벽 가이드) (0) | 2026.07.22 |
|---|---|
| [Jenkins] Github Webhook과 Jenkins 연동 (완전 자동화의 꽃) (0) | 2026.07.22 |
| [Jenkins] 관리자 계정 생성 및 권한 분리 (보안 마스터 가이드) (0) | 2026.07.22 |
| [Jenkins] 초기 비밀번호 확인 및 기본 플러그인 설정 (완벽 가이드) (0) | 2026.07.22 |
| [Jenkins] 입문: CI/CD의 중심 (0) | 2026.07.22 |