8 분 소요

0. 플로우

이 글을 참고했다.

원래는 github 올림Jenkins에서 github에 올라와있는 springboot 프로젝트를 가져와서 빌드메인 서버에 .jar 형태로 파일을 전송실행 을 하고 싶다면 다음과 같은 방법을 써야하지만,
나는 springboot 서버를 docker 컨테이너에서 실행할 것이므로 아래과 같은 플로우로 가려고 한다.

github 올림 
→ Jenkins에서 github에 올라와있는 springboot 프로젝트를 가져와서 빌드
→ jenkins 컨테이너에서 자동 빌드 및 docker-compose 명령어로 배포


목표:

  1. 우분투 인스턴스에서 Dockerfiledocker-compose.yml를 통해 젠킨스 구동하기
  2. github webhook을 이용해 Jenkins와 github 연동하기
  3. Spring Boot 프로젝트에서 Dockerfiledocker-compose.yml을 이용해 mysql 컨테이너와 웹 컨테이너 구동하기
  4. git push를 통해 젠킨스에서 Spring Boot 애플리케이션 자동 빌드 & 배포 시스템 구축


1. 우분투 인스턴스 생성 및 swap file 할당

swap file 할당은 이 글을 참고했다.

인스턴스 볼륨은 넉넉하게 30G로 생성하자!! (프리티어는 최대 30G까지 무료이다.)
8G로 했다가 컨테이너 생성할 때 공간이 부족해서 에러 해결하는데 애먹었다..

2. Ubuntu에 Docker Engine 설치

공식 사이트를 참고했다.

Repository 설정

# apt-get 업데이트
$ sudo apt-get update

$ sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release

$ sudo mkdir -p /etc/apt/keyrings

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Docker Engine 설치 및 실행

$ sudo apt-get update

# 설치
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 도커 실행
$ sudo service docker start

# 상태 확인
$ sudo service docker status

권한 설정

linux에서 root 권한이 아닌 상태로 docker를 실행하면 권한 문제가 발생할 수 있다.

[linux@localhost ]$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/json: dial unix /var/run/docker.sock: connect: permi
ssion denied

이런 경우 매번 sudo로 커맨드를 실행해도 되지만, 번거롭기 때문에 docker group에 해당 유저를 추가해주면 해결할 수 있다.

# 보통은 docker group이 생겼을테지만, 만약 없으면 생성해준다.
$ sudo groupadd docker

# docker group에 해당 유저를 추가
$ sudo usermod -aG docker $USER

# 로그아웃 후 다시 로그인하거나 다음 명령어를 실행시켜야 적용이 된다.
$ newgrp docker

3. 도커 컴포즈 설치

$ sudo apt  install docker-compose
$ docker-compose --version

💥 만약 위 방법으로 도커 컴포즈를 설치하고 버전을 확인했을 때 build unknown이 나온다면?

이 글 참고

스크린샷 2023-06-05 오후 6 38 36

sudo apt remove docker-compose 커맨드로 이전에 설치한 docker-compose를 제거하고 아래 명령을 실행하자.

$ sudo curl -L "https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/bin/docker-compose
# 이 명령어는 외부에서 그대로 파일을 가져와서 현재의 시스템에 올려 놓는 것이다.
# 결과적으로 "/usr/bin/" 경로에 "docker-compose" 라는 이름의 파일로 다운된다.
# 참고) https://github.com/docker/compose/releases 에서 최신 버전 확인이 가능하다.
# 최신 버전을 설치하고 싶다면 위 명령어에 보이는 1.28.5 라는 버전 숫자를 바꿔주면 된다!

$ sudo chmod +x /usr/bin/docker-compose # chmod 를 통해서 실행이 가능하게 세팅

$ docker-compose --version

스크린샷 2023-06-05 오후 6 42 40

4. 젠킨스 설치 (with.Docker Compose)

이 글이 글을 참고했다.

1) 폴더 만들기

우분투 인스턴스에 접속해서 진행한다.
docker 폴더 생성 후 그 안에 jenkins 폴더를 만들고, 그 안에 home 폴더까지 만들어주자.

docker
⎿ jenkins
 ⎿ home

이제 home 디렉터리에 권한을 부여해야 한다.

$ chmod -R 777 home

🚨 주의
권한을 부여하지 않으면 아래 에러가 발생한다…

touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?

2) Dockerfile 생성

이 글을 참고했다.

jenkins 폴더 안에 Dockerfile을 생성할 것이다.
이제 폴더 구조는 아래처럼 된다.

docker
⎿ jenkins
 ⎿ home
 ⎿ Dockerfile

나중에 젠킨스 컨테이너 안에서 docker-compose up -d 커맨드를 실행할 것이기 때문에 docker 와 docker-compose를 설치해준다.
(젠킨스 컨테이너에 접속해서 직접 설치해도 되지만 그러면 컨테이너를 지우고 새로 만들때마다 매번 설치해주어야 해서 굉장히 번거롭다..)

FROM jenkins/jenkins:lts
USER root

# docker 설치
RUN apt-get update && \
 apt-get -y install apt-transport-https \
 ca-certificates \
 curl \
 gnupg2 \
 zip \
 unzip \
 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

 # docker-compose 설치
 RUN curl -L "https://github.com/docker/compose/releases/download/1.28.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \
    chmod +x /usr/local/bin/docker-compose && \
    ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

3) docker-compose.yml 생성

jenkins 폴더 안에 docker-compose.yml을 생성할 것이다.
이제 폴더 구조는 아래처럼 된다.

docker
⎿ jenkins
 ⎿ home
 ⎿ Dockerfile
 ⎿ docker-compose.yml

docker-compose.yml 내용은 아래처럼 작성한다.

version: '3'
services:
  jenkins:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: 'jenkins'
    image: 'jenkins/jenkins:lts'
    restart: always
    user: root
    #privileged: true
    ports:
      - 9090:8080
      - 50000:50000
    volumes:
      - /home/ubuntu/docker/jenkins/home:/var/jenkins_home
      - /var/run:/var/run:ro              # docker 실행 폴더 공유
      - /var/run/docker.sock:/var/run/docker.sock # docker 소켓 파일 마운트
    environment:
      TZ: "Asiz/Seoul"

만약 Jenkins의 포트를 9090이 아닌 다른 포트를 쓰고 싶다면 “앞”의 포트를 9090이 아닌 다른 포트로 바꾼다.
뒤의 포트까지 바꾸면 Jenkins에 접속할 수 없다. 왜냐하면 Jenkins 내부에서 포트를 8080과 50000만 열어두었기 때문이다.
즉, 9090:8080은 접속할 수 있지만 9090:9090은 접속할 수 없다.

3) 젠킨스 컨테이너 실행

우분투 인스턴스의 jenkins 폴더 안에서 진행한다.

$ docker-compose up -d # permission denied가 발생하면 sudo를 붙이자!

# 만야 에러가 발생한다면 로그를 확인해보자
$ docker logs jenkins # 맨 마지막은 컨테이너 이름!

스크린샷 2023-02-15 오후 2 42 53

최종적으로 폴더 구조는 아래처럼 된다!
스크린샷 2023-02-16 오후 3 39 59

5. 젠킨스 설정

1) 젠킨스 접속 및 설정

이 글을 참고했다.

젠킨스 컨테이너를 실행한 후, 인스턴스의 public ip:9090로 접속하면 다음과 같은 페이지가 나온다.

패스워드 입력

스크린샷 2023-02-15 오후 4 10 01
위 사진에 보이는 경로로 이동해 비밀번호를 확인해야하니 경로를 복사하자!
🌟 지금은 도커를 이용했기 때문에 도커 컨테이너에 접속해서 확인해야 한다!

$ docker exec -it jenkins /bin/bash # jenkins 자리는 젠킨스 컨테이너 이름!
# 혹은 docker exec -u 0 -it jenkins bash

# 젠킨스 컨테이너 접속 후 아래 커맨드 실행
$ cat ***/initialAdminPassword # 위에서 복사한 경로를 입력한다.

스크린샷 2023-02-15 오후 4 15 50
위의 빨간색으로 표시한 부분에 출력되는 값을 Administrator password 에 입력한다.
비밀번호를 제대로 입력했다면 어드민 설정페이지가 나온다.

어드민 설정페이지에서 설정한 아이디와 패스워드는 앞으로 사용해야하기 때문에 잊으면 안된다!

Trouble Shooting

처음에는 도커를 사용하지 않았을 때처럼 우분투에서 바로 cat ***/initialAdminPassword 커맨드를 실행하니 No such file or directory 에러가 떴다…
스크린샷 2023-02-15 오후 4 15 20
도커를 이용해 젠킨스를 구동했다면 젠킨스 컨테이너에 접속한 뒤에 해당 명령어를 실행해야 한다!

플러그인 설치 화면

패스워드를 입력 후 아래와 같이 플러그인 설치화면이 나오는데 특별히 설치할 플러그인이 필요없다면 install suggested plugins를 선택한다.
스크린샷 2022-10-04 오후 11 48 09

그럼 아래처럼 Getting Started 페이지가 나올 것이다. 잠시 기다리자.
스크린샷 2022-10-04 오후 11 48 52

Admin 계정 생성

기다리면 이렇게 Admin 사용자를 생성하는 페이지가 나오는데, Admin 사용자를 생성하고 다음 화면을 누르면 Jenkins 화면이 보이는 것을 확인할 수 있다.

Instance Configuration

마지막으로 젠킨스의 TCP 포트를 앞으로도 계속 같은 포트를 쓸 것인지 물어본다.

이제 설정이 끝났다. Start using Jenkins를 누르자.
스크린샷 2022-10-04 오후 8 51 42
그럼 아래와 같은 대시보드가 나온다.
스크린샷 2022-10-04 오후 8 52 56

2) 젠킨스를 통한 자동 빌드 & 자동 배포 🌟

이 글을 참고했다.

Item 생성 및 기본 설정

젠킨스 대시보드에서 진행한다.

  1. 새로운 아이템 선택
  2. Freestyle project 선택 및 이름 작성
  3. 소스 코드 관리 -> Git 선택 -> Repository URL & Branch Specifier 작성
  4. 빌드 유발 -> GitHub hook trigger for GITScm polling 선택

이 상태로 우선 저장한다.
3번에서 Credentials 는 나중에 생성한 뒤에 추가할 것이다.

ssh 키 발급

이 글을 참고했다.

$ docker exec -it [jenkins 컨테이너 이름] ssh-keygen
# 연속으로 엔터 세 번 입력하기
# Created directory 확인 !! 🌟

# 생성 된 Public SSH 키 검색
$ docker exec -it [jenkins 컨테이너 이름] cat [확인한 Created directory]/id_rsa.pub
# 예. docker exec -it jenkins cat /root/.ssh/id_rsa.pub

# 생성 된 Private SSH 키 검색
docker exec -it [jenkins 컨테이너 이름] cat /[확인한 Created directory]/id_rsa
# 예. docker exec -it jenkins cat /root/.ssh/id_rsa

스크린샷 2023-02-16 오후 4 11 20

비밀키와 공개키가 잘 생성되었는지 확인해보자
스크린샷 2023-02-16 오후 4 12 44

깃 deploy key 등록

이제 GitHub로 가서 Deploy Key를 등록해주자!
백엔드 레포지토리로 가서 Settings > Deploy keys > Add deploy key 를 클릭한다.
스크린샷 2023-02-15 오후 5 04 49
위 명령으로 공개키를 확인한 뒤, 복붙해주고 Add key 버튼을 누른다.
스크린샷 2023-02-15 오후 5 06 45

jenkins credentials 추가

다시 젠킨스 대시보드로 가서 Jenkins 관리 > Manage Credentials로 이동해준다.
스크린샷 2023-02-15 오후 5 08 47
Stores scoped to Jenkins에 있는 global을 눌러 이동한다.

스크린샷 2023-02-15 오후 5 11 29
Add Credenials를 눌러 키를 추가해준다.

Kind를 SSH Username with private Key로 바꾸고 privateKeyEnter directly를 활성화한뒤 Add를 통해 이번에는 젠킨스의 비밀키를 추가해준다.
스크린샷 2023-02-15 오후 5 12 57
위 명령으로 공개키를 확인한 뒤, 복붙해주고 아래처럼 붙여넣는다.
스크린샷 2023-02-15 오후 5 14 06 스크린샷 2023-02-15 오후 5 14 16
Username은 원하는 이름으로 설정해주면 된다. 나는 deploy key로 설정했다.
이제 Create를 눌러준다.

Item에 생성한 credential 추가

이제 아까 만든 Item에 방금 생성한 credential(deploy key)을 추가하자.

대시보드에서 아까 만든 Item 이름을 클릭한다.
스크린샷 2023-02-15 오후 5 19 48

왼쪽 사이드바에서 구성을 클릭한다.
스크린샷 2023-02-15 오후 5 19 59

소스코드관리 > Credentials에 아까 만들어준 ssh키(deploy key)를 설정해준다.
스크린샷 2023-02-15 오후 5 20 26

빌드 유발에는 깃허브 webhook 설정을 위해 GitHub hook trigger for GITScm polling을 체크해준다.

Build 설정

이제 Build 설정을 하자.
Build Steps 항목에서 Execute shell을 선택해주고 Github에 푸쉬가 되었을때 어떻게 작동할지를 적어준다.
나는 아래처럼 적었다.

echo "jar 파일 생성"
./gradlew build jar -x test

echo "도커 이미지 빌드"
docker-compose build --no-cache

echo "도커 컨테이너 생성 및 실행"
docker-compose up --force-recreate -d

# echo "도커 이미지 빌드 & 컨테이너 새로 만들기 & 컨테이너 백그라운드 실행"
# docker-compose up --build --force-recreate -d

sleep 5s
docker ps -a

이대로 돌리게 되면 sudo를 사용할 수 없다는 에러가 뜬다. root 계정이 아니면 sudo를 사용할 수가 없기 때문이다.
이를 해결하기 위해 젠킨스 계정이 sudo를 사용할 수 있도록 설정 해주어야 한다.

(나는 위에 Execute shell에서 sudo를 사용하지는 않았지만 혹시 몰라서 아래 sudoers 설정까지 해주었다..!)

sudoers 설정

우분투 인스턴스에 접속한 상태로 진행한다.

$ sudo su
$ vi /etc/sudoers
# jenkins ALL=(ALL) NOPASSWD: ALL 추가

jenkins ALL=(ALL) NOPASSWD: ALL는 jenkins에게 sudo 접근권한을 주는 명령어이다.
스크린샷 2023-02-15 오후 5 35 42

스크린샷 2023-02-15 오후 5 35 25

깃 web hook 설정

레포지토리의 세팅에 들어가 Webhooks를 선택한다.
스크린샷 2023-02-15 오후 5 41 45

Add webhook을 누른 뒤, 아래처럼 payload URL에 해당 젠킨스 주소를 적어주고 Content typeapplication/json으로 설정해주면 된다.
스크린샷 2023-02-15 오후 5 42 45

이때, 꼭 보안그룹의 인바운드 규칙에 젠킨스 포트를 0.0.0.0으로 퍼블릭으로 열어두어야 깃허브가 보낸 Webhook 요청을 받을 수 있다.
이제 푸쉬요청을 보내면, 젠킨스가 알아서 깃에서 푸쉬된 내용을 가져와 파일을 빌드하고 이를 도커 컨테이너로 빌드하여 실행하는 것을 확인할 수 있을 것이다.

6. Spring Boot 프로젝트 구성

로컬에 있는 Spring Boot 프로젝트의 최상위에 Dockerfiledocker-compose.yml을 생성한다.

Dockerfile

부모 이미지는 이 글에서 3) Use Eclipse Temurin instead of JDK, if possible을 참고해 eclipse-temurin:11-jdk로 했다.
eclipse-temurin 이미지의 다른 태그는 도커허브에서 확인할 수 있다.

FROM eclipse-temurin:11-jdk
#VOLUME /tmp
ARG JAR_FILE=./build/libs/jenkins-test-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
EXPOSE 8080

docker-compose.yml

version: '3'
services:
  database:
    container_name: my_mysql
    image: mysql
    environment:
      - MYSQL_ROOT_PASSWORD=1234
      - MYSQL_DATABASE=testdb
      - MYSQL_ROOT_HOST=%
    command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci']
    ports:
      - '3306:3306'
    volumes:
      - ./docker/data:/var/lib/mysql
    restart: always
    networks:
      - test_network

  application:
    container_name: my_app
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - '8080:8080'
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://database:3306/testdb?serverTimezone=UTC&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true
      - SPRING_DATASOURCE_USERNAME=root
      - SPRING_DATASOURCE_PASSWORD=1234
    restart: always
    depends_on:
      - database
    networks:
      - test_network

networks:
  test_network:
    driver: bridge


이때 db 접속 정보를 입력해주었기 때문에 application.yml에는 spring.datasource 정보를 작성할 필요가 없다!

7. env 파일이 있다면? -> Jenkins 빌드 시 매개변수 추가

스크린샷 2023-06-12 오후 11 08 11

이 빌드는 매개변수가 있습니다 -> String Parameter로 매개변수를 추가해주자!

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${MYSQL_IP}:${MYSQL_PORT}/${MYSQL_NAME}?${MYSQL_OPT}
    username: ${MYSQL_USERNAME}
    password: ${MYSQL_PASSWORD}
...

마무리

이제 Spring Boot 코드를 깃헙에 푸시하면,
젠킨스가 webhook을 통해 가져와서 자동으로 build 하고, Execute shell에 적은 docker-compose up -d 커맨드를 통해 배포까지 진행한다.

아래는 젠킨스에서 빌드 및 배포에 성공한 콘솔 출력이다!
스크린샷 2023-02-16 오후 9 03 36

Trouble Shooting

혹시나 빌드 시에 아래와 같은 에러가 발생한다면…
스크린샷 2023-02-16 오전 11 06 14

Dockerfile에서 COPY 부분에서 문제가 발생한 것 같다…

이 글을 보고 .gitignore의 문제임을 알았다.

Dockerfile에서 build/libs/jenkins-test-0.0.1-SNAPSHOT.jarapp.jar 로 COPY 해야하는데 build 디렉토리가 .gitignore에 등록되어있었다.
그래서 build/libs.gitignore에서 제외했다.

변경 전 gitignore

...

build/

...

변경 후 gitignore

...

build/classes
build/generated
build/resources
build/tmp
build/bootJarMainClassName

...

이후 빌드해보니 에러가 해결되었다!!!

+) 추가
나중에 검색해보니 build/ 파일은 용량이 커서 git에 올리지 않는게 좋은 것 같다.
COPY 명령어로 보내는게 맞을 것 같은데… 아직 하지 못하므로 나중에 시도해보자..



💛 개인 공부 기록용 블로그입니다. 👻

맨 위로 이동하기

태그:

카테고리:

업데이트: