Profile picture

[Jenkins] 젠킨스(Jenkins)와 깃랩(Gitlab) 웹훅(Webhook) 연동하는 방법

JaehyoJJAng2025년 12월 26일

개요

팀원이 GitLab에 push만 하면,

  1. GitLab Webhook이 Jenkins 빌드를 트리거하고
  2. Jenkins가 Frontend(Vue3) / API(Flask) Docker 이미지를 빌드하여 GitLab Container Registry에 푸시한 뒤
  3. Jenkins가 운영 서버에 SSH 접속해 docker compose pull && up -d로 안전하게 교체

위 시나리오를 이행하기 위한 Jenkins <-> Gitlab 연동 작업을 기록




배포 구조

  1. Dev → GitLab Push
  2. GitLab Webhook → Jenkins Job Trigger
  3. Jenkins CI: Checkout → Docker build → Registry push
  4. Jenkins CD: SSH 원격 접속 → Registry login → docker compose pull/up → 정리(prune)



사전 준비

이 배포 파이프라인을 제대로 실습하기 위해서는 몇 가지 준비사항이 있습니다.


1) GitLab 프로젝트 준비

  • GitLab 프로젝트가 있고, Container Registry가 활성화되어 있어야 합니다.
  • 프로젝트 경로(예: group/project) 확인 필요

2) Jenkins 요구사항

  • Jenkins 서버(또는 Jenkins 컨테이너)가 다음을 수행할 수 있어야 합니다.
    • GitLab 레포지토리 접근(HTTPS 또는 SSH)
    • Docker build & push 가능
    • 운영 서버로 SSH 접속 가능
  • Jenkins에서 Docker build를 하려면 다음 중 하나 필요:
    • Jenkins가 Docker가 설치된 머신에서 동작
    • Jenkins 컨테이너라면 Docker-in-Docker 또는 호스트 docker.sock 마운트 방식



[준비] Jenkins <-> Gitlab 연동 작업

GitLab 설정

GitLab Access Token 발급

  1. 프로젝트 → Settings > Access Tokens
  2. Token 이름: jenkins-project-token
  3. Role(권한): 최소 Maintainer 필요할 수 있음(Registry push 권한)
  4. Scope 체크:
    • read_registry
    • write_registry
    • (필요시) read_api
  5. Create → 토큰 복사

팀 프로젝트면 “개인 계정 PAT”보다 “Project Access Token”이 권장됩니다. 담당자 퇴사/권한 변경 리스크가 줄어듭니다.

image


GitLab Webhook 설정

Webhook을 연결하려면 먼저 Jenkins Job에서 GitLab 트리거 URL을 만들어야 합니다.


따라서 Jenkins Job을 먼저 만들고 그 URL을 GitLab에 등록합니다.
(Jenkins pipeline job 생성에서 Webhook URL을 생성한 후 해당 챕터를 진행해주세요.)


GitLab쪽 설정 위치

  • 프로젝트 -> Settings -> Webhooks
  • Jenkins에서 받은 Webhook URL 입력
  • Secret Token 입력 (반드시 설정 권장)
  • Trigger Events 체크
    • Push events (필수)
    • Merge request events (선택)
  • Save
  • 아래 Test 버튼으로 Push events 테스트 -> Jenkins 빌드가 시작되는지 확인!

GitLab Webhook 테스트

위 과정이 모두 진행되었다면 Push events가 정상적으로 발동하는지 테스트해봅시다.


GitLab Project -> Settings -> Webhooks로 이동하면 저희가 등록한 웹훅이 보일 겁니다.


여기서 Test -> Push events를 클릭하면 Push events가 발생합니다.
image


그 다음에 Jenkins Project로 이동하여 빌드가 발생되었는지 확인합니다.
image


Jenkins 설정

Jenkins 필수 플러그인

Jenkins 관리(Manage Jenkins) → Plugins에서 아래 플러그인 확인/설치:

  • GitLab Plugin (Webhook/트리거 연동)
  • Git Plugin (SCM 체크아웃)
  • Pipeline (Jenkinsfile 실행)
  • Credentials Binding Plugin (withCredentials)
  • SSH Agent Plugin (sshagent 사용)
  • (권장) Docker Pipeline (docker 관련 편의)

설치 후 Jenkins 재시작 필요할 수 있습니다.


GitLab 연결용 Credentials 등록


Credential 추가 위치

  • Jenkins → Manage Jenkins → Credentials
  • (Global 또는 폴더 스코프) → Add Credentials

GitLab 토큰 등록 (Username with password)

  • Kind: Username with password
  • Username: GitLab 사용자명(또는 Project Access Token이면 표시된 username)
  • Password: gitlab access token 발급에서 발급받았던 Token 값
  • ID: 예) gitlab-auth-id
  • Description: GitLab Registry token

이 ID를 Jenkinsfile에서 그대로 참조합니다.

image


Gitalb <-> Jenkins 연동 테스트

Jenkins에서 새 파이프라인 생성 시,

SCM 설정에서 레포지토리 URL과 위에서 만든 Credential을 선택했을 때 에러 없이 연결되는지 확인합시다.

image
이때 연결이 실패한다면 오류 메시지가 발생합니다.




Jenkins pipeline job 생성

1. 새 Item 생성

Jenkins -> New Item

  • 이름: vue-flask-deploy
  • 유형: Pipeline

image


2. SCM에서 Jenkinsfile 가져오기

Pipeline 섹션에서

  • Definition: Pipeline script from SCM
  • SCM: Git
  • Repository URL: https://gitlab.com/<group>/<project>.git
  • Credentials: gitlab-auth-id 선택
  • Branch Specifier: /main
  • Script Path: Jenkinsfile (프로젝트 루트에 Jenkinsfile이 있다면 기본값)

image


3. GitLab Webhook 트리거 활성화

Build Triggers에서

  • Build when a change is pushed to GitLab 체크
  • 고급(Advanced)에서:
    • Secret token 생성/입력(이 값을 GitLab Webhook에도 동일하게 넣음)
  • 여기서 Webhook URL이 표시됩니다 → 복사해서 GitLab Webhooks에 등록

이 단계가 되어야 GitLab에서 Jenkins를 “콜백 호출”할 수 있습니다!

image




[실전] 자동 배포 작업

Jenkins <-> GitLab 연동이 성공적으로 끝났셨나요?

그렇다면 이제 Jenkinsfile을 작성하여 CI/CD를 직접 테스트해보도록 하죠!


[준비] SSH 보안 연결

배포 서버에 안전하게 명령을 내리기 위해 SSH 비대칭 키 방식을 사용합니다!

관련해서 아래 게시글을 참조하시면 더 수월합니다!


SSH 키 생성 및 운영 서버 등록

1. 키 생성: Jenkins 컨테이너 내부에서 실행합니다.

ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_jenkins

2. 공개키 전송: 운영 서버의 ~/.ssh/authorized_keysid_rsa_jenkins.pub 내용을 추가합니다.

# 운영 서버에서 권한 설정 필수~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

3. Jenkins Credentials 등록: SSH Username with private key 타입으로 추가합니다.

  • ID: server-ssh-key
  • Private Key: 위에서 생성한 id_rsa_jenkins (비밀키) 내용 붙여넣기.
  • Username: 접속하고자 하는 원격 서버의 계정명

image


4. 접속 테스트

Jenkins 파이프라인을 다음과 같이 작성한 후, 실행하여 로그에 Hello, jhlee37!가 정상 출력되는지 테스트합시다.

pipeline {
    agent any

    stages {
        stage('Test echo') {
            steps {
                // SSH Agent를 사용하여 원격 서버에 접속
                sshagent(credentials: ['server-ssh-key']) {
                    // 원격 서버에서 명령어 실행
                    sh """
                        ssh -o StrictHostKeyChecking=no jhlee37@192.168.219.111 '
                            echo Hello, jhlee37!
                        '
                    """
                }
            }
        }
    }
}

image
출력에 성공했네요.


[배포] Jenkinsfile 작성

이제 Jenkins와 연동된 GitLab 프로젝트에 Jenkinsfile을 작성하여 배포해줄겁니다.

제 GitLab 프로젝트는 다음과 같아요.
image


pipeline {
  agent any

  environment {
    REGISTRY     = "registry.gitlab.com/your-team/project"
    IMAGE_FRONT  = "${REGISTRY}/vue_front"
    IMAGE_API    = "${REGISTRY}/vue_api"
    TARGET_SERVER= "deploy-user@1.2.3.4"
    DEPLOY_DIR   = "/home/user/app"
    // 태그: latest + 커밋태그 같이 운용 권장
    GIT_TAG      = "${env.GIT_COMMIT}"
  }

  stages {
    stage('Checkout') {
      steps {
        checkout scm
      }
    }

    stage('Build & Push') {
      steps {
        script {
          withCredentials([usernamePassword(credentialsId: 'gitlab-auth-id',
                                            usernameVariable: 'GL_USER',
                                            passwordVariable: 'GL_PASS')]) {

            sh """
              docker login registry.gitlab.com -u ${GL_USER} -p ${GL_PASS}

              docker build -t ${IMAGE_FRONT}:latest -t ${IMAGE_FRONT}:${GIT_TAG} ./vue_front
              docker push ${IMAGE_FRONT}:latest
              docker push ${IMAGE_FRONT}:${GIT_TAG}

              docker build -t ${IMAGE_API}:latest -t ${IMAGE_API}:${GIT_TAG} ./vue_api
              docker push ${IMAGE_API}:latest
              docker push ${IMAGE_API}:${GIT_TAG}
            """
          }
        }
      }
    }

    stage('Remote Deploy') {
      steps {
        script {
          withCredentials([usernamePassword(credentialsId: 'gitlab-auth-id',
                                            usernameVariable: 'GL_USER',
                                            passwordVariable: 'GL_PASS')]) {
            sshagent(['server-ssh-key']) {
              sh """
                ssh -o StrictHostKeyChecking=no ${TARGET_SERVER} '
                  set -e
                  cd ${DEPLOY_DIR}
                  docker login registry.gitlab.com -u ${GL_USER} -p ${GL_PASS}
                  docker compose pull
                  docker compose up -d
                  docker image prune -f
                '
              """
            }
          }
        }
      }
    }
  }

  post {
    success {
      echo "Deployment succeeded."
    }
    failure {
      echo "Deployment failed. Check console output."
    }
  }
}
  • USER/PASS 변수가 Build&Push 단계 스코프에만 있고 Remote Deploy에서 못 쓰는 경우
  • 원격 서버에서 registry login을 하려면 credential을 다시 바인딩해야 함
  • latest 태그만 쓰면 롤백/추적이 어려움 → commit sha 태그 병행 권장
  • StrictHostKeyChecking=no는 편하지만 운영보안 상 known_hosts 등록이 더 권장


실무 운영 팁(현장에서 자주 터지는 포인트 중심)

1. Jenkins가 Docker 빌드를 못 하는 경우(가장 흔함)

  • Jenkins 컨테이너에서 docker: command not found 또는 permission denied 발생
  • 해결: Jenkins 실행 구조를 “Docker 빌드 가능”하도록 구성 필요
    • 호스트의 /var/run/docker.sock 마운트 방식 또는 DinD 구성

2. 운영 서버에서 docker compose가 없거나 버전 이슈

  • docker-compose(v1) vs docker compose(v2) 차이
  • Jenkinsfile에서 운영 서버 커맨드가 어느 것을 쓰는지와 서버 설치 상태를 일치

3. latest만 쓰면 롤백이 어려움

  • 운영에서는 latest + commit sha(또는 build number) 병행 권장
  • 롤백 시 compose 파일에서 태그만 바꿔도 즉시 복구 가능

4. SSH 보안

  • StrictHostKeyChecking=no는 편의상 예시로 쓰되, 운영에서는 known_hosts 등록 권장
  • Jenkins 노드의 ~/.ssh/known_hosts에 운영 서버 fingerprint 고정

트러블슈팅

GitLab 내부망 Webhook 차단 이슈

Jenkins로부터 생성된 Webhook URL을 GitLab에 등록했을 때,

Invalid URL와 같이 웹훅 URL이 등록되지 않는 문제가 발생함.


이러한 문제가 발생하는 원인을 검색해 본 결과 아래 내용처럼 정리가 됨.


GitLab이 192.168.x.x 같은 사설망(Local network) 주소로 Webhook을 보내는 것을

보안상 차단해서 저장 단계에서 “Invalid URL”류로 막히는 경우가 매우 흔합니다. (SSRF 방지 목적)
(GitLab 문서)


즉, 문제의 핵심은 웹훅 대상이 사설 IP(내부망)라서 GitLab이 허용하지 않은 것!


사내 GitLab(Self-managed)을 쓰는 경우

저는 GitLab을 사내 테스트 서버에서 도커로 배포하여 운영 중입니다.


이러한 경우에는 GitLab 관리자 설정으로 내부망 Webhook을 허용할 수 있습니다.

관리자 권한으로 GitLab에서 설정

  • GitLab 상단/좌측의 Admin
  • Settings > Network
  • Outbound requests 펼치기
  • “Allow requests to the local network from webhooks and integrations”(웹훅/통합의 로컬 네트워크 요청 허용) 체크 (GitLab 문서)

이 설정을 켜면 192.168 같은 내부 주소도 Webhook URL로 저장/호출이 가능해집니다(보안 리스크는 증가).
image


Jenkins Docker Pipeline 에러

Jenkins를 DooD(Docker-outside-of-Docker) 방식으로 운영하며

사내 GitLab Container registry에 접근할 때 발생했던 문제들과 해결 과정을 정리.


1. 사내 HTTP 레지스트리 접근 문제

  • 에러 메시지: http: server gave HTTP response to HTTPS client
  • 원인: Docker 클라이언트는 기본적으로 레지스트리와 HTTPS 통신을 시도하지만, 내부망의 GitLab 레지스트리는 HTTP로 운영 중이었음.
  • 해결: 호스트 머신의 /etc/docker/daemon.json에 해당 레지스트리를 신뢰할 수 있는 목록으로 등록.
{
  "insecure-registries": ["192.168.219.111:5050"]
}

2. Docker API 버전 불일치 문제

  • 증상: client version 1.43 is too old. Minimum supported API version is 1.44
  • 원인: 호스트의 Docker 엔진은 최신인데, 마운트해서 쓰는 클라이언트 바이너리나 컨테이너 내 버전이 낮아 엔진이 연결을 거부함.
  • 해결: Dockerfile에서 최신 버전의 Docker CLI 정적 바이너리를 직접 다운로드하여 설치.
# Dockerfile 예시
RUN curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz | tar xvz && \
    mv docker/docker /usr/local/bin/

3. 계정 권한 문제

  • 증상: permission denied while trying to connect to the Docker daemon socket
  • 상황
    • USER root 사용 시: Docker는 되지만, Jenkins의 Git 설정/권한이 꼬임.
    • USER jenkins 사용 시: Git은 되지만, Docker 소켓(/var/run/docker.sock) 접근 권한이 없음.
  • 최종 해결: GID 매핑 전략
  • Dockerfile: 마지막에 USER jenkins를 선언하여 Jenkins 본연의 환경 유지.
  • docker-compose.yml: 호스트의 docker 그룹 GID(예: 998)를 확인하여 컨테이너에 주입.
services:
  jenkins:
    group_add:
      - "998" # 호스트의 getent group docker | cut -d: -f3 결과값
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"

마무리

처절한 빌드 흔적 .... image

    Tag -

Loading script...