배포 자동화가 되어 있지 않은 회사에 젠킨스 도입하기 (1)

신입으로 입사한 후 제가 개발보다도 먼저 진행하게 된 업무는 다름 아닌 배포 자동화 구축이었습니다. 기존에 사용하던 블로그에 삽질했던 순간들을 기록했었지만, 최근에 도커 컨테이너 환경으로 마이그레이션 하면서 신입 때의 기억도 더듬어보고 포스팅도 현재의 기록으로 리팩터링 하기 위해서 다시 작성하려고 합니다. 대표적인 배포 자동화 구축(CI/CD) 툴로는 젠킨스와 Git Action 등이 있지만, 회사에서 Git 프라이빗 저장소로 요나를 쓰고 있어서 젠킨스를 도입하게 되었습니다. 구축 당시에 배포 방식으로 BLUE/GREEN 무중단 배포 방식을 채택하고 싶었으나, 인프라 환경이 적절치 않아 단순한 구조의 배포 자동화임을 감안해서 봐주시면 감사하겠습니다!


젠킨스란?

젠킨스(Jenkins)는 배포 혹은 수행할 작업을 자동화 해주는 도구입니다.
흔히 배포 자동화 툴로 많이 사용하지만, Spring 생태계에선 Batch와 같은 대용량 작업에서도 사용됩니다.


도입을 고려하게 된 계기

제가 지금 다니는 회사에 입사했을 때 가장 놀랐던 점 중 하나가 바로 배포하는 방식이었습니다. Maven을 사용하는 회사들이라면 대부분 빌드된 War 파일을 톰캣 경로에 배포하는 방식을 사용할 텐데요, 저희 회사는 Exploded 파일들을 하나하나 배포 서버에 덮어 씌우는 방식으로 진행하고 있었습니다. 그리고 당연히 이런 배포 방식엔 뒤따라오는 문제점들이 많았습니다.

첫째로는, FTP 프로그램을 통해 파일 하나하나를 배포하기 때문에 긴 시간이 걸린다는 것입니다. 저희 회사가 운영 중인 서비스는 개발/스테이징/운영 서버로 나뉘어져 있습니다. 배포를 하기 위해선 이 3개의 서버에 배포해야 하는데, 이런 배포 방식에서는 당연히 시간이 많이 소요될 수 밖에 없겠죠. 배포할 파일들이 많을 경우엔 그냥 상위 디렉토리를 벌크로 덮어씌워서 배포하고 있었습니다. 두 번째로는, 배포 되어야 할 파일들이 누락되는 경우가 종종 생겼습니다. 문제는 정적 파일이 아니라 클래스 파일이나 설정 파일이 누락되는 경우엔 원인 파악하는데 또 시간이 소요된다는 점입니다. 세 번째로는, WAS 여러 대를 운용 중인 경우 개발자가 직접 각 WAS를 모두 재구동 시켜줘야 했습니다. 마지막으론, 배포에 대한 아무런 히스토리를 남기지 않아서 최근 배포된 파일들이 무엇인지 알 수 없었습니다. 히스토리를 남기지 않을 거라면 최근 배포된 커밋 로그가 무엇인지 공유가 되어야 하는데 그렇지도 못한 상황이었습니다.


젠킨스에 필요한 환경 세팅

초기엔 도커를 사용하지 않고 직접 개발 환경을 세팅했습니다(제발 도커쓰세요).
직접 개발 환경을 세팅한다면 몇 가지 유의해야 할 사항이 있습니다.

Java 특정 버전 미지원

젠킨스는 특정 버전부터 Java 8을 지원하지 않고 있습니다. 따라서 Java 11 버전을 사용하거나 그 이상의 버전을 사용해야 합니다(최근엔 Java 11 버전도 지원을 종료할 예정이라고 하네요!). 저는 이미 회사 서비스 프로젝트 환경이 Java 8로 되어 있어서 Java 8을 그대로 사용하기로 했습니다. 대신 반드시 Java 8을 지원하는 젠킨스 버전으로 다운그레이드해야 합니다. 이런 히스토리를 모른 채 최신 버전의 젠킨스를 설치했다가 많은 시간을 날려 먹었습니다…🥲

리버스 프록시 이슈

위 사진은 제가 겪었던 리버스 프록시 이슈를 도식화한 사진입니다. 저희 회사 서버 구조는 대략 웹 서버 LB(로드 밸런서)를 통해서만 접근할 수 있도록 되어 있는 프라이빗 구조입니다. 사진에서 보시는 것과 같이 jenkins.web.co.kr으로 요청이 들어오면 젠킨스가 구동되고 있는 jenkins-server-ip:9090 서버에 접근하게 됩니다. 하지만 맨 오른쪽 빨간 노드를 보시면 요청 도메인에 한 번 더 9090 포트가 덧붙여지는 것을 확인하실 수 있죠. 바로 리버스 프록시 이슈입니다. 해결 방안은 젠킨스 공식 문서에 웹 서버 별로 잘 정리되어 있습니다.

1
2
3
4
5
6
7
8
9
<Location />
     ProxyPassReverse /
     Order deny,allow
     Allow from all
</Location>
# url: 젠킨스 도메인
# port: 추가되는 포트 넘버
# => Header Location 값 수정
Header edit Location ^${url}:${port}/ ${url}/

만약 아파치 환경에서 저와 비슷한 이슈를 만나셨다면 위 설정을 참고해 보세요!


Docker 마이그레이션

최근에 마이그레이션을 하면서 머리로만 알고 있었던 도커의 편리함을 체감할 수 있었습니다(이걸 왜 이제). 혹여나 익숙지 않더라도 도커를 사용하는 것을 무적권 권장해 드립니다. 저는 docker compose를 이용했는데 하나의 yaml 파일로 여러 개의 컨테이너를 관리할 수 있다는 이점이 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    ports:
      - 8080:8080
      - 50000:50000
    volumes:
      - ~/jenkins:/var/jenkins_home
    networks:
      - jenkins-net

networks:
  jenkins-net:
    driver: bridge
  • image: 최신 버전의 젠킨스 이미지 다운로드
  • container_name: 컨테이너 네임 지정
  • ports: 호스트 측 포트와 도커 포트 연결
  • voluems: 호스트 경로와 도커 경로 연결

volumes 설정 시 주의 사항

AWS EC2 리눅스 환경은 도커 사용 시 컨테이너 상의 파일들이 /var/lib/docker/${container_hash_value} 경로 하위에 생성되는 것을 확인할 수 있지만, Mac 환경일 경우엔 해당 디렉토리 경로를 찾을 수 없습니다. Mac 환경에서는 Mac OS 상에서 도커 컨테이너가 실행되는 것이 아니라 도커가 리눅스 가상머신을 생성하고 그 가상머신 안에서 도커 컨테이너를 실행시키기 때문입니다. 그래서 Mac OS 환경에서 도커를 사용할 땐 volume 설정을 통해 내 경로(호스트 경로)와 도커 경로(여기에선 젠킨스 디렉토리 경로)를 설정해 줘야만 합니다. 정상적으로 설정되었다면 도커 컨테이너 상에서 생성된 젠킨스 관련 파일들이 아래 사진처럼 지정한 로컬 디렉토리에도 똑같이 생성된 것을 확인하실 수 있을 겁니다.

호스트 경로로 홈 하위의 디렉토리를 사용하자

한 가지 더 유의할 점은 호스트 경로로 최상위 디렉토리를 이용하면 안 되고 홈 디렉토리 하위 경로를 이용해야 한다는 것입니다. 저는 개발 관련 파일들을 최상위 디렉토리에서 관리하고 있다 보니, 호스트 경로로 최상위 디렉토리 하위에 디렉토리를 생성해서 해당 경로로 volumes 설정을 하였는데 파일들이 생성되지 않더군요. 홈 디렉토리 하위의 경로로 수정하고 나서야 파일들이 제대로 생성되었습니다…

privileged 설정을 해줘도 최상위 디렉토리엔 접근하지 못하는 것 같네요.
이제 docker-compose up -d 커맨드를 통해 컨테이너를 백그라운드에 실행 시킵니다.


정상적으로 접속되는 것을 확인할 수 있습니다.
다음 포스팅에서는 파이프라인을 어떻게 구성하였는지 작성하도록 하겠습니다.


Reference

Jenkins Documentation