[CI/CD] Jenkins 기반 CI 파이프라인 구성 (실습)

우유·2026년 4월 8일

[Cloud] CI/CD

목록 보기
5/13

실습 목표

이 실습에서는 Jenkins를 이용해 GitHub 저장소의 코드 변경을 감지하고, 자동으로 소스코드를 가져와 Docker 이미지를 빌드한 뒤 컨테이너 레지스트리에 업로드하는 기본 CI 파이프라인을 구성한다.

또한 Jenkins가 단순 빌드 도구가 아니라, 코드 변경 → 자동 실행 → 이미지 생성 → 결과물 배포 준비까지 연결하는 자동화 서버라는 점을 실습으로 확인한다.

이 실습을 끝내면 다음이 가능해진다.

  • Jenkins 설치 및 초기 접속
  • GitHub 저장소 연동
  • Jenkins Pipeline Job 생성
  • GitHub Webhook 기반 자동 실행
  • Docker 이미지 빌드
  • 컨테이너 레지스트리 Push
  • 전체 CI 흐름 검증

실습 개요

이번 실습은 앞에서 나눠서 보았던 Jenkins 관련 세부 실습을 하나의 흐름으로 묶어서 수행하는 통합 실습이다.

실습 전체 흐름은 다음과 같다.

Jenkins 설치
→ 초기 관리자 설정
→ GitHub 저장소 준비
→ Jenkins Pipeline Job 생성
→ GitHub Webhook 등록
→ 코드 push 시 자동 실행
→ Docker 이미지 빌드
→ 컨테이너 레지스트리 Push
→ 결과 확인

즉, 이번 실습은 Jenkins를 "설치만 해보는 것"이 아니라

실제로 CI 파이프라인 역할을 수행하게 만드는 것이 목적이다.


전체 아키텍처

개발자
   ↓ git push
GitHub Repository
   ↓ Webhook
Jenkins Server
   ├─ Pipeline Job 실행
   ├─ Git checkout
   ├─ Docker 이미지 빌드
   └─ Registry Push
        ↓
Container Registry

이 구조에서 각 구성요소 역할은 다음과 같다.

GitHub

소스코드 저장소 역할을 하며, 코드 변경 이벤트를 Jenkins에 전달한다.

Jenkins

CI 파이프라인 실행 서버 역할을 하며, 코드를 받아 이미지 빌드와 업로드를 수행한다.

Container Registry

Jenkins가 생성한 이미지를 저장하는 중앙 저장소 역할을 한다.


실습 환경

서버 환경

  • OS: Ubuntu 22.04 LTS
  • CPU: 2 vCPU 이상
  • Memory: 4GB 이상
  • Disk: 20GB 이상

소프트웨어

  • Jenkins LTS
  • OpenJDK 17
  • Git
  • Docker
  • 브라우저
  • GitHub 계정
  • Docker Hub 계정 또는 다른 컨테이너 레지스트리 계정

네트워크 조건

  • Jenkins 서버에서 인터넷 접근 가능
  • GitHub에서 Jenkins로 접근 가능한 주소 보유
  • Jenkins 8080 포트 외부 접근 가능
  • Docker Hub 또는 사용하는 레지스트리 접근 가능

사전 준비사항

실습 전에 다음을 준비한다.

  • GitHub 저장소 1개
  • Docker Hub 계정 1개
  • Jenkins 서버 1대
  • 공인 IP 또는 외부 접근 가능한 도메인 (EC2)
  • Ubuntu 서버 sudo 권한 계정

실습용 GitHub 저장소 준비

이번 실습에서는 Docker 이미지 빌드를 위해 간단한 정적 웹 예제를 사용한다.

저장소 예시 이름:

jenkins-ci-lab

저장소 구조는 다음처럼 구성한다.

jenkins-ci-lab/
 ├─ Dockerfile
 ├─ index.html
 └─ README.md

index.html

<!DOCTYPE html>
<html>
<head>
<metacharset="UTF-8">
<title>Jenkins CI Lab</title>
</head>
<body>
<h1>Jenkins CI Pipeline Success</h1>
<p>This page is served from an image built by Jenkins.</p>
</body>
</html>

Dockerfile

FROM nginx:alpine

RUN rm /usr/share/nginx/html/index.html

COPY index.html /usr/share/nginx/html/index.html

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

Dockerfile 설명

FROM nginx:latest

Nginx 공식 이미지를 기반 이미지로 사용한다.

COPY index.html ...

현재 저장소의 index.html 파일을 Nginx 웹 루트 위치에 복사한다.

즉, 이 이미지는 Nginx + 사용자 정의 정적 페이지 구조를 가진다.


1단계. Jenkins 설치

먼저 Ubuntu 서버에 접속한다.

1-1. 패키지 목록 갱신

sudo apt update
sudo apt upgrade -y

설명

  • apt update는 설치 가능한 패키지 목록을 최신화한다.
  • apt upgrade -y는 현재 설치된 패키지를 최신 버전으로 갱신한다.

1-2. Java 설치

sudo apt install -y openjdk-21-jdk

설치 확인:

java-version

설명

Jenkins는 Java 기반 애플리케이션이다.

즉, Java 런타임이 없으면 Jenkins 자체가 동작할 수 없다.


1-3. Jenkins 저장소 키 등록

sudo wget -O /etc/apt/keyrings/jenkins-keyring.asc \
  https://pkg.jenkins.io/debian-stable/jenkins.io-2026.key

설명

Jenkins 공식 저장소의 서명 키를 시스템에 등록한다.

이 키는 이후 Jenkins 패키지를 신뢰 가능한 출처에서 받았는지 검증하는 데 사용된다.


1-4. Jenkins 저장소 등록

echo "deb [signed-by=/etc/apt/keyrings/jenkins-keyring.asc]" \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null

다시 패키지 목록을 갱신한다.

sudo apt update

1-5. Jenkins 설치

sudo apt install -y jenkins

설치 후 서비스 상태 확인:

sudo systemctl status jenkins

정상 확인 포인트

  • Active: active (running) 이 보이면 정상 실행 상태다.

1-6. 방화벽 및 포트 확인

sudo ufw status
sudo ufw allow 8080/tcp
  • Amazon Linux 2023에 설치
sudo wget -O /etc/yum.repos.d/jenkins.repo \
    https://pkg.jenkins.io/rpm-stable/jenkins.repo
sudo dnf upgrade
# Add required dependencies for the jenkins package
sudo dnf install java-21-amazon-corretto-devel -y
sudo dnf install jenkins
sudo systemctl daemon-reload

설명

Jenkins 기본 웹 포트는 8080이다.

외부 브라우저에서 접속하려면 서버 방화벽과 클라우드 보안그룹 모두 8080 허용이 필요하다.


1-7. 초기 관리자 비밀번호 확인

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

이 값을 복사해둔다.


1-8. 브라우저 접속 및 초기 설정

브라우저에서 접속:

http://서버IP:8080

또는

http://도메인:8080

초기 설정 절차:

  • Unlock Jenkins 화면에서 초기 비밀번호 입력
  • Install suggested plugins 선택
  • 관리자 계정 생성
  • Jenkins URL 확인
  • 대시보드 진입

2단계. Git 및 Docker 설치

Jenkins 서버에서 Git과 Docker가 필요하다.

2-1. Git 설치

sudo apt install -y git | sudo dnf install -y git
git --version

설명

Jenkins가 GitHub 저장소를 checkout 하려면 Git CLI가 필요하다.


2-2. Docker 저장소 추가

# Add Docker's official GPG key:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update

2-3. Docker 설치

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo dnf install -y docker

서비스 상태 확인:

sudo systemctl status docker

버전 확인:

sudo docker version

3단계. Jenkins가 Docker 명령을 실행할 수 있도록 권한 설정

Jenkins Job 안에서 Docker build를 하려면 jenkins 사용자에게 Docker 접근 권한이 필요하다.

3-1. jenkins 사용자를 docker 그룹에 추가

sudo usermod -aG docker ec2-user
sudo usermod -aG docker jenkins

설명

  • usermod -aG docker jenkinsjenkins 사용자를 docker 그룹에 추가한다.
  • Docker 소켓 접근 권한을 위해 필요한 설정이다.

3-2. Jenkins 재시작

sudo systemctl restart jenkins

필요 시 Docker도 재시작한다.

sudo systemctl restart docker

그룹 반영 확인:

id jenkins

확인 포인트

출력 결과에 docker 그룹이 포함되어 있어야 한다.


4단계. Docker Hub 자격증명 준비

4-1. Docker Hub 계정 확인

예시 사용자명:

mydockerid

4-2. Access Token 준비

실습용 비밀번호 대신 Docker Hub Access Token 사용을 권장한다.

이유

  • 비밀번호 직접 사용보다 안전함
  • 자동화 도구에 적합함
  • 추후 폐기와 교체가 쉬움

5단계. Jenkins Credentials 등록

Jenkins에 레지스트리 인증정보를 안전하게 저장한다.

이동 경로

  • Manage Jenkins
  • Credentials

환경에 따라 System → Global credentials 구조가 보일 수 있다.

5-1. Add Credentials 클릭

다음 값 입력:

  • Kind: Username with password
  • Username: Docker Hub 사용자명
  • Password: Docker Hub 토큰
  • ID: dockerhub-creds
  • Description: Docker Hub registry credentials

저장한다.

설명

이 Credential은 이후 Pipeline에서 참조한다.

절대로 Jenkinsfile이나 Shell 스크립트 안에 비밀번호를 직접 적지 않는다.


6단계. GitHub 저장소를 Jenkins와 연동할 Pipeline Job 생성

6-1. New Item 생성

  • Item name: jenkins-ci-pipeline
  • Type: Pipeline
  • OK 클릭

6-2. Pipeline Job이 필요한 이유

이번 실습 전체 CI 흐름

  • Git checkout
  • Docker build
  • 태그 지정
  • 레지스트리 로그인
  • 이미지 Push

이런 흐름은 Freestyle보다 Pipeline으로 관리하는 편이 훨씬 구조적이다.

즉, Stage 단위로 나눠서 실행 흐름을 보기 쉽게 만들 수 있다.


7단계. Pipeline Script 작성

이번 실습에서는 Jenkins UI 안에 직접 Script를 넣는 방식으로 시작한다.

실무에서는 Git 저장소 안 Jenkinsfile로 분리하는 것이 일반적이지만, 지금은 흐름 이해가 우선이다.

Pipeline 섹션에서 Pipeline script 선택 후 아래 코드를 입력한다.

pipeline {
    agent any

    environment {
        // Docker Hub 계정 ID와 Credential ID를 환경변수로 빼두면 관리가 편합니다.
        DOCKERHUB_USER = '사용자명' // 실제 본인의 ID로 수정하세요
        DOCKERHUB_CREDS = 'dockerhub-creds'
        
        IMAGE_NAME = 'sample-docker-app'
        IMAGE_TAG  = 'latest'
        
        // 최종 이미지 전체 경로 (변수 조합)
        REGISTRY_IMAGE = "${DOCKERHUB_USER}/${IMAGE_NAME}:${IMAGE_TAG}"
    }

    stages {
        stage('Checkout') {
            steps {
                git branch: 'main', url: 'https://github.com/사용자명/jenkins-ci-lab.git'
            }
        }

        stage('Build & Tag Image') {
            steps {
                script {
                    // docker DSL을 사용하여 객체지향적으로 빌드
                    def myApp = docker.build("${REGISTRY_IMAGE}")
                    
                    // (선택 사항) 빌드된 이미지의 ID 등을 확인하고 싶을 때
                    sh "docker images | grep ${IMAGE_NAME}"
                }
            }
        }

        stage('Push Image') {
            steps {
                script {
                    // 강사님이 말씀하신 세련된 방식! 
                    // 로그인-푸시-로그아웃을 한 번에 처리합니다.
                    docker.withRegistry('', DOCKERHUB_CREDS) {
                        sh "docker push ${REGISTRY_IMAGE}"
                        
                        // 만약 빌드 번호를 태그로 추가하고 싶다면?
                        // sh "docker tag ${REGISTRY_IMAGE} ${DOCKERHUB_USER}/${IMAGE_NAME}:${env.BUILD_NUMBER}"
                        // sh "docker push ${DOCKERHUB_USER}/${IMAGE_NAME}:${env.BUILD_NUMBER}"
                    }
                }
            }
        }

        stage('Verify') {
            steps {
                echo "Successfully pushed to: ${REGISTRY_IMAGE}"
                // 빌드 후에 로컬 이미지를 정리하고 싶다면 아래 주석 해제
                // sh "docker rmi ${REGISTRY_IMAGE}"
            }
        }
    }

    post {
        success {
            echo "✅ Jenkins CI Pipeline 실행 성공: ${REGISTRY_IMAGE}"
        }
        failure {
            echo '❌ Jenkins CI Pipeline 실행 실패'
        }
        always {
            echo '🏁 Pipeline 종료'
        }
    }
}

8단계. Pipeline 코드 상세 설명

이 코드는 길어 보이지만, 흐름은 매우 단순하다.


8-1. agent any

이 Pipeline을 실행 가능한 Jenkins 환경에서 돌리겠다는 뜻이다.

현재 실습 구조에서는 Jenkins Controller 서버에서 직접 실행된다고 이해하면 된다.


8-2. environment

environment {
    IMAGE_NAME = 'sample-docker-app'
    IMAGE_TAG  = 'latest'
    REGISTRY_IMAGE = ''
}

의미

Pipeline 안에서 반복해서 사용할 값을 변수처럼 정의한다.

  • IMAGE_NAME: 로컬 이미지 이름
  • IMAGE_TAG: 태그
  • REGISTRY_IMAGE: 최종 레지스트리용 이미지 이름

8-3. Checkout Stage

git branch: 'main', url: 'https://github.com/사용자명/jenkins-ci-lab.git'

의미

GitHub 저장소의 main 브랜치를 Jenkins Workspace로 가져온다.

즉, 이 Stage가 성공해야 다음 Build 단계에서 Dockerfile과 index.html 파일을 사용할 수 있다.


8-4. Prepare Stage

이 Stage에서는 다음을 확인한다.

  • 현재 작업 디렉터리
  • 파일 목록
  • Docker CLI/daemon 접근 가능 여부

특히 docker version은 매우 중요하다.

여기서 실패하면 권한 또는 Docker 서비스 문제일 가능성이 높다.


8-5. Build Image Stage

docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${REGISTRY_IMAGE}

의미

  1. 로컬 이미지를 빌드한다.
  2. Docker Hub Push용 태그를 다시 붙인다.

즉, 로컬 빌드 이미지와 레지스트리용 이미지 이름은 구분해서 이해하는 것이 좋다.


8-6. Push Image Stage

이 Stage는 가장 중요하다.

withCredentials([usernamePassword(...)]) {
    sh '''
        echo "${DOCKERHUB_PASS}" | docker login -u "${DOCKERHUB_USER}" --password-stdin
        docker push ${DOCKERHUB_USER}/${IMAGE_NAME}:${IMAGE_TAG}
    '''
}

핵심 포인트

withCredentials

Jenkins에 저장해둔 Credential을 실행 시점에만 환경변수로 주입한다.

docker login --password-stdin

비밀번호를 명령행에 직접 쓰지 않고 안전하게 전달한다.

docker push

최종 이미지를 Docker Hub에 업로드한다.

즉, 이 Stage가 성공해야 비로소 다른 환경에서도 사용할 수 있는 배포 가능한 이미지가 된다.


8-7. Verify Stage

이 Stage에서는 빌드 결과를 다시 확인한다.

  • 로컬 Docker 이미지 목록에 존재하는지
  • 어떤 이름으로 Push했는지

즉, 빌드 결과를 다시 한 번 명시적으로 보여주는 검증 단계다.


8-8. post

이 블록은 전체 Pipeline 종료 후 실행된다.

  • success: 전체 성공 시 메시지 출력
  • failure: 전체 실패 시 메시지 출력
  • always: 성공/실패와 관계없이 항상 실행

이 구조는 나중에 Slack 알림, 메일, 리포트 업로드 등으로 확장 가능하다.


9단계. 수동 실행으로 1차 검증

먼저 Webhook 없이 수동으로 한 번 실행해본다.

실행 방법

  • Job 화면
  • Build Now 클릭

기대 결과

Stage가 순서대로 실행된다.

Checkout
→ Prepare
→ Build Image
→ Push Image
→ Verify

10단계. Console Output 확인

성공 시 로그에서 다음을 확인한다.

10-1. checkout 성공

  • Git clone/fetch 로그
  • Workspace 경로 확인

10-2. docker version 정상 출력

  • Docker Client/Server 정보 보임

10-3. docker build 성공

  • load build definition from Dockerfile
  • COPY index.html ...
  • naming to ... sample-docker-app:latest

10-4. docker login 성공

  • Login Succeeded

10-5. docker push 성공

  • The push refers to repository ...
  • digest: sha256: ...

10-6. Finished: SUCCESS

최종 성공 상태 확인


11단계. Docker Hub 업로드 결과 확인

Docker Hub 웹사이트에서 다음을 확인한다.

  • Repository 생성 여부
  • sample-docker-app 저장 여부
  • latest 태그 존재 여부
  • 최근 push 시간

이 단계는 매우 중요하다.

Jenkins 콘솔 로그만으로 끝내지 않고, 실제 레지스트리에 결과물이 올라갔는지 외부 시점에서 확인해야 한다.


12단계. GitHub Webhook 연동

이제 코드 push 시 Jenkins가 자동 실행되도록 연결한다.


12-1. Jenkins Job Trigger 설정

Job 설정으로 들어간다.

Build Triggers 섹션에서 아래 항목 체크:

GitHub hook trigger for GITScm polling

저장한다.


12-2. GitHub Webhook 등록

GitHub 저장소로 이동:

  • Settings
  • Webhooks
  • Add webhook

입력값:

Payload URL

http://JENKINS주소/github-webhook/

예:

http://203.0.113.10:8080/github-webhook/

Content type

application/json

Events

Just the push event

Active

체크 상태 유지

저장한다.


13단계. Webhook 기반 자동 실행 검증

이제 로컬 저장소에서 파일을 하나 수정하고 push 한다.

예:

echo"<p>Webhook triggered build</p>" >> index.html
git add index.html
git commit-m"test: trigger jenkins webhook build"
git push origin main

기대 동작

  • GitHub push 발생
  • GitHub가 Jenkins Webhook URL 호출
  • Jenkins Pipeline 자동 실행
  • Build History에 새 실행 번호 생성

14단계. 자동 실행 여부 확인

Jenkins Build History에서 새 Build를 클릭한다.

확인 포인트

  • 수동 실행이 아니라 자동 실행인가
  • 시작 원인에 GitHub push 관련 메시지가 있는가
  • 전체 Stage가 다시 정상 수행되는가

즉, 지금부터 Jenkins는 사람이 버튼을 누르는 시스템이 아니라

코드 변경에 반응하는 이벤트 기반 CI 서버가 된다.


15단계. 최종 검증 포인트

이번 실습이 정상적으로 끝났다면 다음이 모두 충족되어야 한다.

  • Jenkins 웹 UI 접속 가능
  • Pipeline Job 생성 완료
  • GitHub 저장소 checkout 성공
  • Docker build 성공
  • Docker Hub Push 성공
  • GitHub Webhook 등록 완료
  • 코드 push 시 Jenkins 자동 실행 성공
  • Docker Hub에 최신 이미지 반영 확인

자주 발생하는 오류

오류 1. Jenkins 접속 불가

원인 가능성

  • Jenkins 서비스 미실행
  • 8080 포트 차단
  • 보안그룹 미허용
  • 방화벽 문제

점검 명령

sudo systemctl status jenkins
sudo ss-tulnp |grep8080
sudo ufw status

오류 2. Docker build 시 권한 오류

예:

permission denied while trying to connect to the Docker daemon socket

원인

  • jenkins 사용자가 docker 그룹에 없음
  • 그룹 변경 후 Jenkins 재시작 안 함

해결

sudo usermod-aG docker jenkins
sudo systemctlrestart jenkins
id jenkins

오류 3. Docker login 실패

예:

unauthorized: incorrect username or password

원인

  • Docker Hub 사용자명 오류
  • 토큰 오류
  • Credential ID 잘못 참조
  • Credential 값 오입력

해결

  • Jenkins Credentials 값 재확인
  • Docker Hub 토큰 재생성
  • credentialsId: 'dockerhub-creds' 오타 확인

오류 4. Push는 되는데 Docker Hub에서 안 보임

원인 가능성

  • 잘못된 사용자명 namespace로 Push
  • 다른 태그로 Push
  • private/public 혼동

해결

  • Console Output의 repository 이름 확인
  • Docker Hub에서 해당 계정 repository 목록 확인
  • 이미지 이름 형식 재확인

오류 5. GitHub Webhook이 동작하지 않음

원인 가능성

  • Jenkins 주소가 외부에서 접근 불가
  • /github-webhook/ 경로 누락
  • Job Trigger 미설정
  • GitHub Delivery 실패

확인 포인트

  • GitHub Webhooks 화면의 Recent Deliveries
  • 응답 코드 200 여부
  • Jenkins Job Trigger 체크 여부

오류 6. Checkout 실패

원인 가능성

  • GitHub 저장소 URL 오타
  • main 브랜치 오타
  • private 저장소 접근 권한 없음

해결

  • 저장소 URL 다시 확인
  • 브랜치명 확인
  • 필요 시 GitHub Credential 등록 후 사용

실습 후 정리해야 할 핵심 개념

1) Jenkins는 설치형 CI 서버다

즉, 직접 설치하고 운영해야 한다.

2) CI 파이프라인은 단순 빌드가 아니라 전체 자동화 흐름이다

이번 실습에서는 checkout, build, push까지 연결했다.

3) Docker 이미지는 CI 결과물로 매우 중요하다

운영에 반영 가능한 표준 배포 단위 역할을 한다.

4) 레지스트리 Push가 되어야 결과물이 다른 환경에서 사용 가능해진다

즉, 빌드 성공과 배포 준비 완료는 다르다.

5) Jenkins Credentials는 필수다

인증정보를 Job 스크립트에 하드코딩하면 안 된다.

6) GitHub Webhook을 연결하면 Jenkins가 이벤트 기반으로 동작한다

즉, 사람이 수동으로 빌드 버튼을 누르지 않아도 된다.


실습 결과 요약

이번 실습을 통해 다음을 완료했음.

  • Jenkins 설치 및 초기 설정
  • Git, Docker 설치
  • Jenkins Docker 권한 구성
  • Docker Hub Credential 등록
  • Jenkins Pipeline Job 생성
  • GitHub 저장소 checkout
  • Docker 이미지 빌드
  • Docker Hub Push
  • GitHub Webhook 등록
  • 코드 push 시 Jenkins 자동 실행 확인

profile
Front-end Developer, Cloud Engineer

0개의 댓글