WIKIJenkins
플레이북

Jenkins

빌드를 내 PC가 아니라 정해진 곳에서 매번 똑같이 돌린다.

JenkinsCIPipelineJenkinsfile빌드인프라 기초
인프라백엔드 입문6분

한눈에

  • CI 도구 — 코드를 합쳐 빌드하고 검증하는 데까지가 담당이다
  • Jenkinsfile = 손으로 하던 순서를 적어 둔 스크립트. 레포 안에 같이 산다
  • 파이프라인을 토막 내면 실패한 단계만 다시 돌릴 수 있다
  • 이미지 태그에 브랜치·날짜·커밋을 넣으면 지금 도는 서버를 역추적할 수 있다
  • 클러스터에 접속하지 않는다. 이미지를 창고에 넣고 values.yaml 한 줄 고치고 끝
  • Credentials 는 Jenkins가 들고 코드에는 절대 안 적는다

Jenkins가 없앤 문제는 하나다. "내 PC에선 되는데."

빌드를 내 노트북이 아니라 정해진 서버에서 한다. 같은 자바, 같은 라이브러리, 같은 명령으로 매번 똑같이 돈다. 그리고 그 순서를 사람이 기억하는 대신 파일로 적어 둔다.

Jenkinsfile은 레포 안에 산다

Jenkins 화면에서 클릭으로 설정할 수도 있지만, 그러면 그 설정이 어디에도 기록되지 않는다. 누가 언제 뭘 바꿨는지 모르고, 서버가 날아가면 같이 날아간다.

그래서 파이프라인을 코드로 적어 소스와 같은 레포에 둔다.

pipeline {
  agent any
  stages {
    stage('빌드') {
      steps { sh './gradlew clean build -x test' }
    }
    stage('테스트') {
      steps { sh './gradlew test' }
    }
    stage('이미지') {
      steps {
        sh 'docker build -f DockerfileK8s -t $REGISTRY/my-app:$TAG .'
        sh 'docker push $REGISTRY/my-app:$TAG'
      }
    }
  }
}

코드가 바뀌면 파이프라인도 같은 커밋에서 같이 바뀐다. 이게 화면 설정과의 결정적 차이다.

파이프라인을 토막 내는 이유

한 파일에 다 넣어도 돌아간다. 그런데 실무에서는 대개 토막을 낸다.

파일하는 일결과물
Jenkinsfile.build-javaMaven·Gradle 빌드.jar
Jenkinsfile.build-dockerbase 이미지 + jar → 앱 이미지, 레지스트리에 push이미지
Jenkinsfile.update-manifestmanifest 레포의 values.yaml 태그를 고쳐 커밋커밋
Jenkinsfile.all위 셋을 한 번에

토막을 내는 값어치는 실패했을 때 나온다. 이미지 push가 네트워크 문제로 실패했을 때 자바 빌드부터 다시 돌릴 이유가 없다. 그리고 각 단계의 소요 시간이 따로 찍혀서 어디가 느린지가 보인다.

태그를 어떻게 짓나

여기가 나중에 크게 갈린다.

main-250827-79f1
 │      │      └ 커밋 해시 앞자리
 │      └ 날짜
 └ 브랜치

latest 하나만 쓰면 안 된다. 지금 서버에 떠 있는 게 어느 코드인지 알 방법이 없고, 되돌릴 지점도 없다. 위처럼 지으면 태그 하나로 어느 브랜치의 어느 커밋이 언제 구워졌는지가 역추적된다.

Jenkins는 배포하지 않는다

가장 많이 어긋나는 지점이다. "젠킨스가 dev 서버에 배포한다"고 기억하기 쉬운데, 컨테이너 기반 파이프라인에서 Jenkins의 일은 여기서 끝난다.

빌드
jar
이미지 굽기
레지스트리에 push
values.yaml 태그 한 줄 고쳐 커밋
여기까지가 Jenkins
그 다음은 ArgoCD
Git을 보고 클러스터를 맞춘다

클러스터 접속 정보를 안 갖고 있다. 이게 불편해 보이지만 보안 심사에서는 장점이다. CI 서버가 뚫려도 운영 클러스터로 바로 이어지지 않는다.

전체 그림은 DevOps와 CI/CD, 그 다음 단계는 ArgoCD에 있다.

비밀값은 코드에 안 적는다

레지스트리 비밀번호, Git 토큰, 서명 키. 전부 Jenkins의 Credentials에 등록하고 파이프라인에서는 이름으로만 부른다.

withCredentials([usernamePassword(
  credentialsId: 'registry-cred',
  usernameVariable: 'U', passwordVariable: 'P')]) {
  sh 'echo $P | docker login $REGISTRY -u $U --password-stdin'
}

로그에 값이 찍히면 그 순간 유출이다. Jenkins가 등록된 비밀값은 로그에서 마스킹해 주지만, echo로 직접 찍는 것까지 막아 주지는 않는다.

자주 걸리는 것

에이전트에 도구가 없다 — 빌드 서버에 JDK·Docker가 깔려 있어야 한다. 컨테이너 에이전트를 쓰면 이 문제가 준다
작업 공간이 안 지워진다 — 옛 산출물이 남아 빌드가 통과하거나 실패한다. cleanWs() 를 앞에 둔다
docker build 가 느리다 — 레이어 캐시가 안 먹는 순서로 Dockerfile 을 짰을 때다. 자주 바뀌는 것을 아래로
동시 실행이 서로를 밟는다 — 같은 태그로 굽는 잡 둘이 동시에 돌면 결과가 섞인다
실패했는데 초록불 — sh 스크립트의 종료 코드를 안 보는 구조일 때. 파이프로 이으면 앞 명령의 실패가 묻힌다

대안도 알아 둘 것

Jenkins가 유일한 답은 아니다. GitLab CI, GitHub Actions는 형상관리와 한 몸이라 설정이 훨씬 적다. Jenkins를 계속 쓰는 이유는 대개 폐쇄망이다. 외부로 못 나가는 환경에서는 사내에 세울 수 있는 것이 선택지가 된다.

개념은 셋 다 같다. 순서를 파일로 적고, 정해진 곳에서 매번 똑같이 돌린다.