Jenkins로 CD 구축하기 with Docker(Hub, Compose)

meong·2024년 1월 17일
1
post-thumbnail

CD란?

CD는 지속적인 서비스 제공(Continuous Delivery) 또는 지속적인 배포(Continuous Deployment)를 의미한다. 간단하게 자동 배포라고 이해하면 된다.

CD 구축 목적?

프로젝트 빌드, 도커 빌드, 배포 등 배포의 전 과정을 직접하지 않고
GitHub가 업데이트 될 때마다 자동으로 수행하도록 하기 위해 구축했습니다.

자동 배포를 통해 개발자는 개발에만 집중을 할 수 있게 됩니다 !

전체적인 흐름

  1. GitHub Repository 에 Merge 발생
  2. Jenkins가 Webhook 전달 받아 git pull
  3. Gradle Build -> .jar 생성
  4. Docker Image로 Build -> Docker Hub Push
  5. Publish Over SSH 로 docker-compose.yaml 및 compose 명령 Send
  6. 운영 서버에서 Docker Container 실행

🚀 공통 설정

도커 설치

배포 & 운영 서버에 도커를 설치합니다

# Ubuntu의 패키지 목록을 업데이트
sudo apt update 

# 도커 설치
sudo apt install -y docker.io
  • 운영 서버 추가 수행
# 도커 컴포즈 설치 
sudo apt install docker-compose

Docker Sudo 권한 주기

편의를 위해 sudo 명령 생략 작업 수행

# docker에 sudo 권한 주기
sudo usermod -aG docker ubuntu

# 서버 로그아웃
logout

# 재 접속 후 확인
id -nG

🐳 Docker 설정

Dockerfile 작성

스프링 jar 파일을 Docker Image로 빌드할 때 빌드 설정을 지정하는 Dockerfile이 필요하다

파일명: Dockerfile
위치: 프로젝트 root 디렉토리

# 프로젝트 Java 버전에 맞는 openjdk 이미지 설정
FROM openjdk:17-jdk-slim

# db Dependencies 설정
RUN apt-get update && apt-get install -y mariadb-client

# gradle 빌드를 하면 build/libs 하위에 *.jar 생성됨. 해당 '*.jar'를 'app.jar'로 cpoy
ARG JAR_FILE_PATH=build/libs/*.jar 
COPY ${JAR_FILE_PATH} app.jar

# .yaml local/prod 프로필 분리 구조일때 실행할 프로필 지정 prod(=운영)
ENV USE_PROFILE prod

# 이미지 빌드 명령
ENTRYPOINT ["java", "-Dspring.profiles.active=${USE_PROFILE}", "-jar", "app.jar"]

Docker Compose 설정

'스프링 프로젝트 컨테이너'와 'redis 컨테이너'를 함께 실행시키고 연결하기 위해
Docker Compose를 이용할 것이기 때문에 docker-compose.yaml 도 작성해둔다

파일명: docker-compose.yaml
위치: 프로젝트 root 디렉토리

기존 docker run 명령어를 하나의 파일로 관리한다고 이해하면 쉽다
'수행할 서비스 이름', 'image', 'container_name', 'ports' 는 상황에 맞게 지정하면 된다.

services:  
  redis: 						# 수행할 서비스 이름 
    image: redis:latest 			# run 할 image
    container_name: test_redis		# container 이름
    ports:							# container port 바인딩
      - 6379:6379
    volumes:						# redis volume 설정 
      - ./redis/data:/data
      - ./redis/conf/redis.conf:/usr/local/conf/redis.conf
    restart: always					# 컨테이너가 종료됐을 때 재시작 여부 
    command: redis-server /usr/local/conf/redis.conf # 실행할 명령 

  spring_application:		
    image: test_spring
    container_name: application
    ports:
      - 8080:8080		# 서버 port
      - 443:443	 		# https 인증 port 
    depends_on:			# 의존관계 설정 <- redis 서비스를 먼저 시작하도록 지정
      - redis

Docker Hub 설정

Docker image 빌드 후 Docker Hub에 Push & Pull 하기 위해
회원가입 및 Private Repository를 생성한다.

Namespace에는 자신의 계정이름이 자동 입력되어 있고
Repository Name을 설정한 후 Private 로 선택하면 된다.

나중에 아래와 같이 접근하게 된다.

docker push Namespace/Repository:tagname

👴🏻 Jenkins 서버 설정

메모리 swap 설정

EC2 프리티어는 메모리가 작아 젠킨스 빌드 발동 시 서버가 다운된다.
따라서 메모리 swap 설정을 통해 늘려준다.
보통 기존의 2배로 설정. EC2 프리티어 메모리 사이즈: 1G

# 2GB 크기의 스왑 파일을 생성
sudo fallocate -l 2G /swapfile

# /swapfile의 권한을 600으로 설정
sudo chmod 600 /swapfile

# /swapfile을 스왑 파일로 설정
sudo mkswap /swapfile

# /swapfile을 스왑 파일로 활성화
sudo swapon /swapfile

Jenkins 설치

jenkins 컨테이너 내부에서 docker 이미지를 빌드하려면 docker를 사용하여 빌드를 해야한다.
기본적으로 컨테이너 내부에는 docker 가 없기 때문에 이를 해결하는 다양한 방법이 존재한다.

하나는 jenkins 컨테이너 내에 다른 docker 데몬을 실행하는 것 인데 이 방법은 docker에서 권장하지 않는다.

채택한 방법으로는, host의 docker 소켓을 컨테이너와 공유하는것이다.
이렇게 하면 컨테이너가 host의 docker 데몬을 사용하여 이미지 빌드를 수행을 할 수 있다.

1. jenkins 이미지 실행

# host port:docker container port 연결 
# docker 소켓 연결
# container name
# docker image

docker run -p 8080:8080 \	
  -v /var/run/docker.sock:/var/run/docker.sock \  
  --name jenkins \	
  jenkins/jenkins:lts

2. docker apt repository 구성 및 docker ce 바이너리 설치

# 해당 jenkins 컨테이너의 shell에 접속
docker exec -it -u root jenkins bash

# docker apt repository 구성 및 docker ce 바이너리 설치
apt-get update && \
apt-get -y install apt-transport-https \
     ca-certificates \
     curl \
     gnupg2 \
     software-properties-common && \
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
   $(lsb_release -cs) \
   stable" && \
apt-get update && \
apt-get -y install docker-ce

3. jenkins에서 host docker 접근 권한 부여

# "docker"라는 이름의 그룹을 생성
groupadd -f docker

# "jenkins" 사용자를 "docker" 그룹에 추가
usermod -aG docker jenkins

# /var/run/docker.sock 파일의 소유권을 root 사용자와 docker 그룹으로 변경
chown root:docker /var/run/docker.sock

Jenkins 접속 및 System 설정

1. 로그인 및 기본 setting

[ http://서버 ip 주소:port ] 로 접속하면 Jenkins 초기 페이지가 뜬다.
아래 명령어로 초기 비밀번호를 확인 하여 입력해준다.

docker exec -it jenkins bash -c "cat /var/jenkins_home/secrets/initialAdminPassword"

다음 화면에서 [Install Suggested plugins]를 선택하고 plugins 설치가 끝나면 계속 화면을 따라 계정을 생성해준다

마지막으로 Jenkins URL 설정이 나오는데 위에서 접속했던 [ http://서버 ip 주소:port ] 로 설정해주면 된다.

2. plugins 추가 설치

[Dashboard]-[Jenkins 관리]-[plugins]-[Available plugins]로 가서 파이프라인 구축에 필요한 플러그인들을 설치해준다.

설치할 plugins

  • gradle # Gradle build scripts 플러그인
  • github integration # github의 webhook 사용 플러그인
  • post build task # 빌드 로그를 판단하여 script 혹은 shell을 실행할 수 있게 하는 플러그인
  • publish over ssh # 다른 EC2에 접속하여 작업을 가능하게 해주는 플러그인

3. Credentials 관리

GitHub Personal access tokens 발급받기

[Settings]-[Developer Settings]-[Personal access tokens]-[Tokens (classic)]-[new] 선택 후
아래 이미지와 동일하게 설정해준다. 발급받은 토큰은 !반드시! 따로 저장해두어야 한다.
발급 시에만 확인이 가능하고 토큰을 잃어버리면 새로 발급 받아야 한다.

Jenkins에 Credential 추가하기

총 3개의 Credentials 등록이 필요하다.

[Dashboard]-[Jenkins 관리]-[Credentials]에 들어와서 global 도메인의 [Add credentials] 선택

  1. GitHub Web Hook 설정을 위한 credential
    • kind: Secret text 선택
    • Scpoe: Global 선택
    • Secret: GitHub Personal access tokens 입력
    • ID: 식별 가능한 이름 입력
    • Description: 설명 입력

  1. GitHub git pull 설정을 위한 credential
    • kind: Username with password 선택
    • Scpoe: Global 선택
    • username: GitHub 계정 아이디
    • password: GitHub Personal access tokens 입력
    • ID: 식별 가능한 이름 입력
    • Description: 설명 입력

  1. Docker Hub 설정을 위한 credential
    위의 2번과 동일하게 Username with password 유형
    • kind: Username with password 선택
    • Scpoe: Global 선택
    • username: Docker Hub 계정 아이디
    • password: Docker Hub 계정 비밀번호
    • ID: 식별 가능한 이름 입력
    • Description: 설명 입력

4. GitHub Web Hook 연결

[DashBoard]-[Jenkins 관리]-[System] 에 들어와서 쭉 내리다 보면 GitHub 파트가 나온다.

Name: 식별 가능한 이름
API URL: 자동 입력된 그래도 두기. 수정 x
Credentials: 위에서 등록한 1번 Credentials

그리고 GitHub Repository 에서도 설정을 해줘야 하는데
[Repository]-[Settings]-[Webhooks]-[Add webhook]로 가서

Payload URL: Jenkins URL
Content type: application/json
Just the push event ✅
Active ✅

5. SSH Servers 설정

[DashBoard]-[Jenkins 관리]-[System] 맨 아래로 내려오면 SSH Servers 탭이 있다.
여기에 배포시킬 운영 EC2를 연결해줘야 한다.

Name: 식별 가능한 이름
Hostname: 운영 서버 ip
Username: EC2 OS 차이가 있다. 우분투의 경우 ubuntu
Remote Directory : 파일이 업로드될 디렉토리. 기본은 루트 디렉토리

그 다음 고급을 눌러 [Use password authentication, or user a different key]를 클릭하고 Key를 작성한다. Key는 운영 EC2의 Pem키를 넣어주면 된다.

# Key 확인 방법 {pemname}에 pem 이름 
cat ~/.ssh/pemname.pem

6. Gradle 설정

[DashBoard]-[Jenkins 관리]-[Tools] 로 들어가서 프로젝트 Gradle 버전과 동일하게 설정해준다.

⭐️ Item(job) 설정

이제 기본적인 setting 은 끝났다. 본격적으로 Jenkins가 할 일을 정해주자

DashBoard 로 나와서 좌측 [새로운 Item] -> 이름을 작성 한 후 유형은 FreeStyle Project 로 생성
(Item 이름으로 디렉토리가 생성되기 때문에 '공백'은 피하는 것이 편함)

1. GitHub Repository 연결

아이템 생성 후 아이템 구성 페이지로 이동하면 바로 GitHub 설정을 할 수 있다.

GitHub project ✅ 후, url에 Repository 주소 입력

조금 내리다보면 나오는 [소스 코드 관리]에서 Git ✅

Repository URL: .git으로 끝나는 clone HTTPS 주소
Credentials: 위에서 등록한 2번 Credentials

⚠️ 서브 모듈이 있는 경우

Secret key, yaml 등의 파일을 Submodule 로 관리하고 있는 경우
그 Repository의 파일들도 가져와야 하기 때문에 추가 설정이 필요하다.

[소스 코드 관리] 탭 하단에 [add] 드롭 박스를 열어 [Advanced sub-modules behaviours]를 추가해준다.

Recursively update submodules, Use credentials from default remote of parent repository ✅ 해주고
마찬가지로 서브모듈의 .git으로 끝나는 clone HTTPS 주소를 입력해준다.

마지막으로 조금 내려서 [빌드 유발]에서 GitHub hook trigger for GITScm polling ✅

2. 빌드 환경 변수 추가 (for Docker Hub)

도커 허브에 로그인 할 때 쓰일 환경 변수를 추가해준다.

3. Build Steps 설정

[Add Build step]으로 [Invoke Gradle script], [Execute shell] 순서로 추가해준다

Invoke Gradle script

아까 위에서 설정한 Gradle 을 선택 해준 후, Task에 clean build 작성
이 구간에서 jar 파일로 빌드가 된다.

Execute shell

이제 빌드한 jar 파일을 Docker image 로 빌드하고 Docker Hub에 Push 하는 Shell script를 작성해줄 것이다.

이제부터 Docker image 이름은 Namespace/Repository:tagname 라고 생각하면 된다.
ex) 도커 허브 계정 이름이 meong이고 repository 이름이 myjenkinsrepo 라면
-> meong/myjenkinsrepo:1.0

# docker 이미지로 빌드
docker build -t meong/myjenkinsrepo:1.0 .

# 도커 허브 로그인
echo $PASSWORD | docker login -u $USERNAME --password-stdin

# 도커 허브 repository에 push
docker push meong/myjenkinsrepo:1.0

# 이미지 제거 
docker rmi meong/myjenkinsrepo:1.0

제대로 push 가 되었다면 Docker Hub에 이렇게 뜨게 된다

4. Send build artifacts over SSH

맨 마지막 [빌드 후 조치] 탭에서 [추가]-[Send build artifacts over SSH] 선택

Name: 초기 설정했던 SSH Server(운영 서버) 선택
Source files: SSH Server로 보낼 파일
Remove prefix: 파일 경로에서 지울 경로
Remote directory: SSH Server에 파일을 저장할 위치

docker-compose.yaml 파일은 서브모듈에 있고 git pull 받은 후
운영 서버에 전달하여 운영 서버에서 compose 명령을 수행하는 구조이다.

Exec command 에는
도커 허브에 로그인 - pull - run 과정의 스크립트를 작성해준다

#도커 허브 로그인
echo $PASSWORD | docker login -u $USERNAME --password-stdin

# 도커허브 이미지 pull
docker pull meong/myjenkinsrepo:1.0

# 도커 compose run
docker-compose -f ./scripts/docker-compose.yaml up -d

(*) 고급 탭을 열어 Verbose output in console을 체크해주면
빌드 후 조치 과정에서 발생하는 output 도 콘솔에 찍힌다.

(추가) 배포 결과를 디스코드 알림으로 받고 싶다면?

이용을 하다보니 배포가 제대로 되고 있는지 매번 Jenkins에 접속하는 것도 일이라는 생각이 들었다.
너무 편리하게도 디스코드, 슬랙, 이메일, github 등 다양한 채널로 빌드 결과를 받을 수 있다.

기존에 GitHub hook도 디스코드로 받고 잇었기 때문에 디스코드를 연결하기로 했다.

  1. 디스코드 웹후크 URL 발급
    디스코드에서 [서버 설정]-[앱]-[연동]-[웹후크]로 가서 새 웹후크 추가

    이름과 알림을 받을 채널을 설정 한 후 [웹후크 URL 복사]

  2. Plugins 로 가서 디스코드 플러그인 설치

  3. Item의 빌드후 조치에서 Discord Notifier 추가
    Webhook URL에 디스코드 웹후크 URL 입력

  4. 알림 설정
    [고급]을 눌러서 Title 을 지정하고 받고 싶은 내용 선택

  5. 결과

profile
새싹 개발자

0개의 댓글