sudo 권한과 Jenkins 공식 패키지 저장소·업데이트 센터에 접근 가능한 네트워크
CI/CD CONTROLLER
Jenkins LTS 설치와 초기 보안 설정
Ubuntu 24.04에 Java 21을 먼저 설치하고 Jenkins LTS 공식 저장소를 등록한 뒤 systemd, localhost 바인딩, 초기 관리자, 접근 제어와 컨트롤러 격리를 검증합니다.BEFORE YOU START
시작 전에 준비하세요
소규모 팀 기준 권장 4GB 이상 메모리와 50GB 이상 가용 디스크 검토
기본 8080 또는 대체 포트와 NGINX·VPN·SSH 터널 중 관리 접속 경로 계획
기존 Jenkins가 있다면 JENKINS_HOME과 플러그인·작업 백업 및 중단 승인
권장 대상 소규모 팀의 Jenkins 컨트롤러를 새로 설치하고 기본 노출·권한 위험을 줄이려는 개발자·DevOps 운영자
FOLLOW THE RECIPE
8단계 구성·점검 레시피
OS·Java·자원 환경 확인
Jenkins 현재 Linux 설치 문서는 Java 21 이상을 요구합니다. OS, 아키텍처, 메모리, 디스크와 Java 상태를 확인합니다.
cat /etc/os-release
dpkg --print-architecturefree -h
df -hT / /var/lib/jenkins/var/lib/jenkins가 아직 없다는 메시지는 신규 설치에서는 정상입니다.java -version
readlink -f /usr/bin/javaJava 미설치 환경에서는 not found가 정상입니다.ps -p 1 -o comm=
systemctl --version- Ubuntu 24.04 LTS와 지원 아키텍처임을 확인했습니다.
- Jenkins 홈과 빌드 산출물을 수용할 메모리·디스크를 검토했습니다.
- systemd로 서비스를 관리할 수 있습니다.
메모리·디스크가 최소치에 가까우면 설치는 되더라도 플러그인 로드와 빌드에서 불안정할 수 있습니다. 컨트롤러와 빌드 에이전트 자원을 분리해 계획합니다.
기존 Jenkins, Java, 8080 포트와 저장소 설정을 조사합니다.
기존 설치·포트·데이터 사전 점검
기존 Jenkins 서비스와 JENKINS_HOME을 신규 설치로 덮지 않도록 패키지, unit, 데이터, 포트, 프록시 상태를 확인합니다.
dpkg -l | grep -E 'jenkins|openjdk'
apt-cache policy jenkinssystemctl status jenkins.service --no-pager
systemctl cat jenkins.serviceunit 없음은 신규 설치에서 정상입니다.sudo ls -ld /var/lib/jenkins
sudo du -sh /var/lib/jenkins경로가 이미 있으면 소유자, 작업, 플러그인, 백업을 확인하기 전 설치하지 않습니다.sudo ss -lntp | grep -E ':(8080|<JENKINS_PORT>)\b'sudo ls -l /etc/apt/sources.list.d/jenkins.list /etc/apt/keyrings/jenkins-keyring.asc파일이 없다는 메시지는 신규 설치에서 정상입니다.- 기존 Jenkins 데이터와 서비스가 없는 신규 설치인지 확인했습니다.
- 기존 설치라면 JENKINS_HOME 전체와 복원 절차를 검증했습니다.
- 8080 또는 선택 포트를 점유한 프로세스와 프록시 구성을 확인했습니다.
기존 /var/lib/jenkins가 있거나 서비스가 설치돼 있으면 신규 설치가 아니라 업그레이드입니다. Java·플러그인 호환성과 백업을 별도 검토합니다.
Java 21을 먼저 설치한 다음 Jenkins LTS 공식 저장소와 패키지를 설치합니다.
Java 21과 Jenkins LTS 설치
Jenkins보다 Java를 먼저 설치하고, 신규 jenkins.service를 미리 mask해 패키지가 0.0.0.0:8080으로 자동 기동하지 못하게 합니다. localhost drop-in 적용 뒤에만 통제해 시작합니다.
sudo apt update
sudo apt install fontconfig openjdk-21-jre wget gnupg
java -versionsudo install -m 0755 -d /etc/apt/keyringssudo cp --archive --no-clobber /etc/apt/keyrings/jenkins-keyring.asc /etc/apt/keyrings/jenkins-keyring.asc.<BACKUP_SUFFIX>기존 파일이 있을 때만 실행하고 고유한 백업 이름을 사용합니다.sudo wget -O /etc/apt/keyrings/jenkins-keyring.asc https://pkg.jenkins.io/debian-stable/jenkins.io-2026.key기존 키가 있었다면 바로 앞 단계에서 백업한 뒤 실행합니다.gpg --show-keys --with-fingerprint /etc/apt/keyrings/jenkins-keyring.ascsudo cp --archive --no-clobber /etc/apt/sources.list.d/jenkins.list /etc/apt/sources.list.d/jenkins.list.<BACKUP_SUFFIX>기존 파일이 있을 때만 실행합니다.sudoedit /etc/apt/sources.list.d/jenkins.listsudo apt update
apt-cache policy jenkinssudo systemctl mask jenkins.service
systemctl is-enabled jenkins.service사전 점검에서 Jenkins 패키지·unit·JENKINS_HOME이 모두 없던 신규 설치에서만 실행합니다. 출력이 masked인지 확인하고, 기존 Jenkins에는 적용하지 않습니다.sudo apt install jenkinssystemctl is-enabled jenkins.service
systemctl is-active jenkins.service
sudo ss -lntp | grep ':8080\b'unit은 masked, 서비스는 inactive이고 8080 리슨 출력은 없어야 합니다. 다르면 localhost 설정 전에 접근을 열지 말고 즉시 원인을 조사합니다.deb [signed-by=/etc/apt/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/Weekly 저장소가 아닌 LTS debian-stable 한 줄만 사용합니다. apt-cache policy에서 pkg.jenkins.io 출처를 확인합니다.- java -version이 OpenJDK 21 이상을 표시합니다.
- Jenkins 키와 저장소가 pkg.jenkins.io의 LTS 경로입니다.
- apt-cache policy의 설치 후보와 출처를 확인했습니다.
jenkins: failed to find a valid Java installation 오류가 나면 Jenkins를 반복 재시작하지 말고 Java 경로와 버전을 바로잡습니다.
기본 unit을 직접 편집하지 않고 systemd drop-in으로 포트와 리슨 주소를 제한합니다.
systemd drop-in과 관리 접속 경로 구성
패키지가 제공한 unit은 직접 수정하지 않습니다. 운영 권장안은 Jenkins를 localhost에 바인딩하고 SSH 터널 또는 같은 호스트의 NGINX를 통해 접근하는 것입니다.
systemctl cat jenkins.service
sudo ls -l /etc/systemd/system/jenkins.service.d/override.confsudo cp --archive --no-clobber /etc/systemd/system/jenkins.service.d/override.conf /etc/systemd/system/jenkins.service.d/override.conf.<BACKUP_SUFFIX>기존 파일이 있을 때만 실행하고 고유한 백업 이름을 사용합니다.sudo install -d -m 0755 /etc/systemd/system/jenkins.service.d
sudoedit /etc/systemd/system/jenkins.service.d/override.conf서비스가 아직 masked인 상태에서 drop-in 파일을 직접 준비합니다. 아래 예제는 같은 서버의 NGINX 또는 SSH 터널을 사용할 때 권장됩니다.sudo cat /etc/systemd/system/jenkins.service.d/override.conf
systemctl is-enabled jenkins.servicelocalhost 옵션이 정확하고 서비스가 아직 masked인지 확인합니다.ssh -L 8080:127.0.0.1:<JENKINS_PORT> <ADMIN_USER>@<SERVER_IP>서버가 아니라 관리자 로컬 PC에서 실행하고 브라우저로 http://127.0.0.1:8080을 엽니다.[Service]
Environment="JENKINS_PORT=<JENKINS_PORT>"
Environment="JENKINS_OPTS=--httpListenAddress=127.0.0.1"<JENKINS_PORT>를 8080 또는 확인한 빈 포트로 바꿉니다. 다른 JENKINS_OPTS가 기존에 있다면 통째로 대체하지 말고 같은 값에 필요한 옵션을 합칩니다.- 패키지 원본 unit을 직접 수정하지 않았습니다.
- 기존 override가 있었다면 백업하고 옵션을 병합했습니다.
- Jenkins를 인터넷 전체 주소에 직접 노출하지 않는 관리 경로를 정했습니다.
localhost 바인딩 뒤 원격 8080 직접 접속은 실패하는 것이 정상입니다. SSH 터널이나 검증된 NGINX reverse proxy를 사용합니다.
Jenkins 서비스를 시작하고 systemd 상태와 journal로 기동 완료를 확인합니다.
Jenkins 시작과 systemd 활성화
drop-in을 반영해 서비스를 시작하고 enabled·active 상태와 기동 로그를 확인합니다. 플러그인 초기화에는 시간이 걸릴 수 있습니다.
sudo systemctl unmask jenkins.service
sudo systemctl daemon-reload
systemctl cat jenkins.service출력에 localhost drop-in이 포함됐는지 확인합니다. 포함되지 않으면 서비스를 시작하지 말고 다시 mask합니다.sudo ss -lntp | grep ':<JENKINS_PORT>\b'
sudo systemctl enable jenkins.service
sudo systemctl start jenkins.service첫 명령에 출력이 없어 선택 포트가 비어 있고, 직전 systemctl cat에서 127.0.0.1 설정을 확인한 경우에만 enable·start를 실행합니다.systemctl is-enabled jenkins.service
systemctl is-active jenkins.service
systemctl status jenkins.service --no-pagersudo journalctl -u jenkins.service -n 120 --no-pagersudo ss -lntp | grep ':<JENKINS_PORT>\b'- jenkins.service가 enabled·active입니다.
- journal에 Completed initialization 또는 정상 기동 흐름이 표시됩니다.
- 선택 포트가 127.0.0.1에만 바인딩됐습니다.
activating이 길어도 journal의 초기화 진행이 계속되면 기다립니다. 즉시 재시작을 반복하면 원인 로그가 흐려질 수 있습니다.
로컬 HTTP, SSH 터널, 초기 관리자 설정과 systemd 활성 상태를 실제로 검증합니다.
초기 관리자와 서비스 활성 상태 검증
localhost 응답과 SSH 터널을 확인하고 일회용 초기 비밀번호로 설정 마법사를 완료합니다. 비밀번호를 로그·채팅·스크린샷에 남기지 않습니다.
curl -I http://127.0.0.1:<JENKINS_PORT>/loginsudo cat /var/lib/jenkins/secrets/initialAdminPassword설정 마법사에서 즉시 사용하고 터미널 출력·클립보드·화면 공유에 남기지 않습니다.id jenkins
sudo stat -c '%U:%G %a %n' /var/lib/jenkinssystemctl is-enabled jenkins.service
systemctl show jenkins.service -p ActiveState -p SubState -p ExecMainStatussudo journalctl -u jenkins.service -p warning -n 80 --no-pager- SSH 터널 또는 reverse proxy를 통해 설정 마법사에 접근했습니다.
- 초기 비밀번호를 공유하지 않고 별도 관리자 계정을 생성했습니다.
- Jenkins 기본 URL과 실제 HTTPS URL을 일치시켰습니다.
- 서비스가 enabled·active이며 localhost에만 리슨하는지 확인했습니다.
curl 응답이 200·302·403 중 하나여도 Jenkins가 응답하는지는 확인할 수 있습니다. 브라우저 동작은 터널·프록시 헤더와 Jenkins URL까지 함께 봅니다.
인증·권한·플러그인·빌드 실행 위치와 노출 포트를 운영 기준으로 잠급니다.
접근 제어와 컨트롤러 격리
설정 마법사가 제공하는 보안 기본값을 유지하고 익명 접근, 과도한 관리자, 내장 노드 빌드, 불필요한 에이전트 포트를 제한합니다.
sudo ss -lntp | grep ':<JENKINS_PORT>\b'sudo ufw status numberedps -o user,group,pid,cmd -C java
sudo namei -l /var/lib/jenkins/secretscurl -I http://127.0.0.1:<JENKINS_PORT>/pluginManager/인증 전 403 또는 로그인 리다이렉트가 나오면 접근 제어가 동작하는 것입니다.sudo du -sh /var/lib/jenkins- 익명 사용자는 관리·읽기 권한을 갖지 않으며 인증과 권한 전략을 명시했습니다.
- 관리자 계정을 최소화하고 개인별 계정·다중요소 인증 연계를 검토했습니다.
- 인터넷에 8080을 직접 열지 않고 NGINX·VPN·SSH 터널 등 통제된 경로를 사용합니다.
- 내장 노드 executors를 0으로 설정하고 빌드는 격리된 에이전트에서 실행합니다.
- Agent → Controller Access Control을 비활성화하지 않습니다.
- 사용하지 않는 inbound agent TCP 포트는 비활성화하고 필요한 포트만 고정·제한합니다.
- 플러그인은 최소한으로 설치하고 Jenkins core와 함께 정기 업데이트·백업합니다.
- 파이프라인 비밀정보는 Jenkins Credentials에 저장하고 코드·로그에 하드코딩하지 않습니다.
Jenkins 컨트롤러에서 빌드하면 작업 코드가 JENKINS_HOME과 컨트롤러 권한에 접근할 수 있습니다. 초기 테스트 뒤에는 에이전트 분리를 우선합니다.
문제가 생기면 데이터 홈을 지우지 말고 systemd override 또는 패키지만 단계적으로 되돌립니다.
systemd 설정 복원 또는 신규 설치 제거
A는 기존 override의 확인된 백업을 복원한 뒤에만 서비스를 다시 시작합니다. B 신규 설치 철회는 서비스를 중지·disable·mask한 상태로 유지하며 localhost override를 비활성화한 뒤 절대 재시작하지 않습니다.
sudo du -sh /var/lib/jenkins
systemctl status jenkins.service --no-pager
systemctl cat jenkins.service
sudo find /etc/systemd/system/jenkins.service.d -maxdepth 1 -type f -name 'override.conf*' -print사전 점검에서 override.conf가 있었는지와 이번 작업에서 만든 고유 백업 파일을 변경 기록과 대조합니다. JENKINS_HOME은 제거 후보가 아닙니다.sudo systemctl stop jenkins.service
sudo test -f /etc/systemd/system/jenkins.service.d/override.conf.<BACKUP_SUFFIX> && sudo cp --archive /etc/systemd/system/jenkins.service.d/override.conf.<BACKUP_SUFFIX> /etc/systemd/system/jenkins.service.d/override.conf실행 중 빌드가 없고 중단이 승인됐으며, 사전 점검에서 기존 override가 있었고 실제 백업의 내용·시각을 확인한 경우에만 실행합니다. test가 실패하면 복원하지 않습니다.sudo systemctl daemon-reload
systemctl cat jenkins.service
sudo systemctl start jenkins.service
systemctl status jenkins.service --no-pager확인된 기존 백업 복원이 성공했고 systemctl cat 결과가 작업 전 unit·노출 정책과 일치할 때만 실행합니다. B 경로에서는 절대 실행하지 않습니다.sudo systemctl stop jenkins.service
sudo systemctl disable jenkins.service
sudo systemctl mask jenkins.service사전 점검에서 Jenkins가 없었고 이번 레시피의 신규 설치를 철회할 때만 A 대신 실행합니다. 이후 이 롤백 단계에서는 Jenkins를 다시 시작하거나 unmask하지 않습니다.sudo test -f /etc/systemd/system/jenkins.service.d/override.conf && sudo test ! -e /etc/systemd/system/jenkins.service.d/override.conf.disabled && sudo mv /etc/systemd/system/jenkins.service.d/override.conf /etc/systemd/system/jenkins.service.d/override.conf.disabled
sudo systemctl daemon-reload
systemctl is-enabled jenkins.service
systemctl is-active jenkins.service
sudo ss -lntp | grep ':<JENKINS_PORT>\b'B 경로에서만 실행합니다. 결과는 masked와 inactive이며 마지막 포트 검사에는 출력이 없어야 합니다. 127.0.0.1 제한 파일을 비활성화한 상태에서는 어떤 이유로도 Jenkins를 시작하지 않습니다.dpkg-query -W jenkins
sudo apt remove jenkins사전 점검에서 Jenkins 패키지와 /var/lib/jenkins가 없었고 이번 레시피가 설치했으며 완전 제거가 승인된 경우에만 두 번째 명령을 실행합니다. JENKINS_HOME, 작업, 플러그인, 자격증명은 절대 자동 삭제하지 않습니다.sudo test -f /etc/apt/sources.list.d/jenkins.list && sudo test ! -e /etc/apt/sources.list.d/jenkins.list.disabled && sudo mv /etc/apt/sources.list.d/jenkins.list /etc/apt/sources.list.d/jenkins.list.disabled
sudo apt update사전 점검에서 저장소 파일이 없었고 이번 레시피가 만든 파일일 때만 실행합니다. 기존 파일이었다면 확인된 .<BACKUP_SUFFIX> 파일을 복원합니다.- 실행 중 빌드와 사용자 영향을 확인한 뒤 중지했습니다.
- A에서는 확인된 기존 override 복원과 unit 검증 후에만 서비스를 시작했습니다.
- B에서는 Jenkins가 inactive·disabled·masked이며 리슨 포트가 없고 서비스를 다시 시작하지 않았습니다.
- A와 B 중 사전 점검 기록에 맞는 한 경로만 실행했으며 공통 재시작 단계는 없습니다.
- JENKINS_HOME과 플러그인·작업·자격증명을 삭제하지 않았습니다.
Jenkins 패키지와 JENKINS_HOME은 분리해 취급합니다. B에서 localhost override를 비활성화한 뒤 시작하면 기본 0.0.0.0:8080 노출 위험이 있으므로 mask를 유지합니다.
A는 Jenkins URL·프록시·로그인을 확인하고, B는 masked·inactive·무리스닝 상태와 보존된 JENKINS_HOME을 기록합니다.
SECURITY CHECK
운영 전 마지막 보안 점검
- Jenkins보다 먼저 공식 지원 Java 21 이상을 설치하고 실제 런타임 버전을 확인합니다.
- 8080을 인터넷에 직접 노출하지 않고 localhost·VPN·reverse proxy 등 통제된 경로를 사용합니다.
- 설정 마법사를 완료해 인증·권한을 활성화하고 익명·과도한 관리자 권한을 허용하지 않습니다.
- 내장 노드에서 빌드하지 않고 격리된 에이전트를 사용합니다.
- Jenkins core·플러그인 업데이트 전 JENKINS_HOME 백업과 복원 테스트를 수행합니다.
- 자격증명은 Jenkins Credentials에 저장하고 파이프라인·환경 출력·로그에 평문으로 남기지 않습니다.
COMMON ERRORS
자주 막히는 지점
유효한 Java를 찾지 못해 시작 실패
- 증상
- jenkins.service가 failed이고 failed to find a valid Java installation이 보입니다.
- 가능한 원인
- Java보다 Jenkins를 먼저 설치했거나 지원하지 않는 Java가 기본 경로로 선택됐을 수 있습니다.
- 확인 순서
- OpenJDK 21을 설치하고 java -version과 실제 경로를 확인한 뒤 Jenkins를 다시 시작합니다.
8080 포트 충돌
- 증상
- Address already in use와 함께 Jenkins가 시작되지 않습니다.
- 가능한 원인
- 다른 Java 프로세스·서비스가 기본 8080을 사용 중일 수 있습니다.
- 확인 순서
- ss로 소유 프로세스를 확인하고 임의 종료하지 말고, systemctl edit jenkins의 JENKINS_PORT를 확인된 빈 포트로 변경합니다.
localhost 설정 후 원격에서 접속되지 않음
- 증상
- 서버 내부 curl은 되지만 원격 브라우저에서 8080 연결이 실패합니다.
- 가능한 원인
- 권장 보안 설정으로 Jenkins가 127.0.0.1에만 바인딩된 정상 상태일 수 있습니다.
- 확인 순서
- 8080을 전체 주소로 열지 말고 SSH 터널 또는 올바른 NGINX reverse proxy를 구성합니다.
reverse proxy setup is broken 경고
- 증상
- Jenkins 화면에 reverse proxy 설정 오류가 나오거나 저장 후 URL이 어긋납니다.
- 가능한 원인
- Host·X-Forwarded 헤더, 경로 prefix, Jenkins URL 또는 응답 재작성 설정이 맞지 않을 수 있습니다.
- 확인 순서
- Jenkins 공식 reverse proxy 예제와 NGINX 설정을 대조하고 Jenkins URL을 실제 외부 HTTPS 주소와 맞춥니다.
플러그인 설치 중 디스크·메모리 부족
- 증상
- 초기화가 느려지거나 OOM, No space left, 플러그인 로드 실패가 발생합니다.
- 가능한 원인
- 컨트롤러 자원이 부족하거나 빌드와 플러그인이 같은 호스트 자원을 과도하게 사용할 수 있습니다.
- 확인 순서
- JENKINS_HOME 용량과 JVM 메모리, journal을 확인하고 빌드를 에이전트로 분리합니다.
PRIMARY REFERENCES
공식 문서
- Jenkins · Installing Jenkins on Linux
- Jenkins · Managing systemd services
- Jenkins · Initial Settings
- Jenkins · Securing Jenkins
- Jenkins · Controller Isolation
설치 저장소와 지원 버전은 바뀔 수 있습니다. 검토일 이후에는 링크된 공식 문서와 현재 서버의 패키지 후보 버전을 함께 확인하세요.