Jenkins로 Spring Boot 2 + Vue 3 프로젝트를 리눅스 서버에 배포하기 (1)

시작하기에 앞서...

이 글은 내가 만든 도서 관리 앱을 AWS Lightsail에 Jenkins로 자동 배포하는 과정을 정리한 글임을 먼저 밝힌다.

이 글을 정독하기 전에, 먼저 아래의 글을 읽고 AWS Lightsail 인스턴스를 생성하고 PuTTY 사용법에 대해서 알아보는 것을 권장한다. 만약 본인이 이미 VPS를 통해 인스턴스를 생성하고 PuTTY로 원격 접속하는 방법에 대해 알고 있다면 다음 최소사양을 만족하는 가상 서버를 직접 생성하도록 하자.

AWS Lightsail 인스턴스 생성하고 PuTTY로 접속하기
AWS Lightsail 인스턴스 생성하기 AWS Lightsail의 특징 원래 AWS 하면 가장 먼저 떠올리는 서비스는 EC2일 것이다. EC2 서비스의 특징은 사용한 시간에 따라 과금이 된다는 점이다. 따라서 EC2 인스턴스는 24/7(항상 가동되는) 서비스를 사용하는 데에 부적합하다. (그리고 뭣보다 EC2 인스턴스를 계속 켜두면 당신의 지갑이 위험해질 것이다...) 이 점을 아마존도 알고

서버 최소사양

  • CPU: 1 Core
  • Memory: 512MB
  • Disk: 20GB

참고로 이 사양은, AWS Lightsail 에서 제공하는 인스턴스의 최소 사양이다.


배포 과정

나의 프로젝트 배포 과정은 다음과 같다.

  1. Github에 소스코드를 push 한다.
  2. Jenkins에서 일정 시간마다 Github 리포지토리를 복제하고 빌드 한다.
  3. 빌드 된 결과물을 리눅스 서버에 배포한다.
내 프로젝트 배포의 개략적인 도식도

서버 세팅하기

서버 세팅 과정은 다음과 같다.

  1. 서버 최신화
  2. Docker 설치
  3. Nginx 컨테이너 생성
  4. Postgresql DB 컨테이너 생성
  5. OpenJDK 설치

서버 최신화

우선 서버의 상태를 최신 상태로 만들어야 한다. 아래의 명령어를 입력해 최신 상태로 갱신하자.

$ sudo apt update && sudo apt upgrade -y
apt 리포지토리를 최신화 하고, 리눅스에 이미 설치된 패키지를 업그레이드 한다.

처음 업그레이드 명령어를 사용한다면 아래와 같은 화면이 뜰 수 있는데, 기본으로 선택되어 있는 'keep the local version currently installed'를 선택하자.

기본적으로 골라져 있는 걸 선택하자

Docker 설치

다음은 Docker를 설치하도록 하겠다. 아래의 글을 참조해 Docker를 설치하도록 하자.

Ubuntu 운영체제에 Docker 설치하기
Docker 설치 전 확인 사항 권장 운영체제 * Ubuntu 22.04 LTS * Ubuntu 21.04 * Ubuntu 20.04 LTS * Ubuntu 18.04 LTS * 위의 Ubuntu를 기반으로 두는 파생 리눅스 운영체제 지원하는 CPU 아키텍쳐 * AMD64(x86-64) * ARM64 * ARMHF * S390X 2021/04/30을 기준으로 Ubuntu 16.04 LTS의 공식 지원이 종료됨으로써 더 이상
Rocky 리눅스 운영체제에 Docker 설치하기
Docker 설치 전 확인 사항 권장 운영체제 * Rocky Linux 8.X * Rocky Linux 9.X 이 글은 Rocky 리눅스 뿐만 아니라 CentOS7 이상의 운영체제 에서도 동일하게 적용이 가능하다. Docker 리포지토리 설정 * 업데이트 $ sudo dnf update * 리포지토리 추가 $ sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 최신
혹시라도 본인이 CentOS/Rocky 리눅스를 사용한다면 이쪽 글을 참조하자

Docker 설치가 완료되면 사용자 계정을 docker 그룹에 추가해야 한다. docker 그룹에 추가하지 않더라도 크게 상관은 없지만, docker 명령어를 사용할 때 마다 sudo 명령어를 앞에 붙여줘야 하므로 편의를 위해 그룹에 추가해 주는 것이 좋다. docker 그룹에 계정을 추가하는 방법은 아래의 글을 참고하도록 하자.

리눅스에서 sudo 없이 도커 사용하기
sudo 쓰기 귀찮은데... Windows나 MacOS에서는 Docker(이하 도커)를 설치할 때, 일반 계정 권한으로 설치하게 된다. 그렇기 때문에 도커를 사용할 때, 관리자 권한으로 도커를 실행할 필요가 없다. 그러나 Linux 에서는 root 권한으로 도커를 설치하기 때문에 root 외의 사용자가 docker 명령어를 사용하면 권한 오류가 발생한다. 이 때문에 도커를 실행하고 docker 명령어를
docker 그룹에 현재 로그인한 계정 추가하기

Nginx 컨테이너 생성하기

Docker가 설치 되었으니 이제 Vue 3 프로젝트를 배포할 Nginx 컨테이너를 생성해 보겠다.

$ docker pull nginx
nginx 이미지 다운로드
docker pull nginx 결과
$ mkdir -p ~/data/nginx/html
Vue 3 프로젝트를 배포할 디렉토리 생성
$ docker run --name webserver -v ~/data/nginx/html:/usr/share/nginx/html -d -p 80:80 nginx
nginx 컨테이너 생성 후 실행

Nginx 컨테이너를 실행 후, 해당 인스턴스의 공개 IP를 웹 브라우저 주소창에 입력해보자. 아래와 같이 403 Forbidden HTTP Status가 뜨면서 nginx/1.xx.x 가 표시되면 정상적으로 컨테이너가 생성 된 것이다.

현재 html 디렉토리에는 아무것도 없기 때문에 Forbidden이 뜨는게 맞다

이제 Nginx 설정을 변경하겠다. 다음 명령어를 입력하자.

$ docker exec -it webserver /bin/bash

이 명령어를 입력하고 나면 유저 권한이 root로 변경되는데, 해당 컨테이너의 root 권한으로 접속 된 것이다. 설정을 변경하기 위해서는 에디터가 필요하다. 나는 nano 에디터를 사용하기 때문에 nano 패키지를 설치하려고 한다. 본인이 vi나 vim이 더 편하다면 그쪽을 설치해도 무관하다. nginx 기본 이미지는 debian 리눅스를 기반으로 하기 때문에 apt 패키지 관리자를 사용하면 된다.

# apt update
# apt install nano -y

에디터를 설치 했다면 설정 파일을 열도록 하자.

# nano etc/nginx/conf.d/default.conf

파일을 열면 기본적으로 설정되어 있는 내용이 있을 것이다. 모든 내용을 지운 다음 아래의 내용으로 변경하자. <호스트 IP 혹은 도메인>은 해당하는 서버를 가리키는 주소를 적으면 된다. <요청URI>는 Vue 3 프론트엔드에서 Spring Boot 2 백엔드에 API 요청을 할 때 사용하는 URI를 입력해주면 된다. 나 같은 경우에는 백엔드에 요청하는 API URI가 /api이기 때문에 proxy_pass 뒤에 http://apiserver/api로 적어주었다.

# Spring Boot 2로 만든 API Server 업스트림
upstream apiserver {
    server 172.17.0.1:8080;
    keepalive 100;
}

server {
    listen       80;
    listen  [::]:80;
    server_name <호스트 IP 혹은 도메인>;

    charset utf-8;

    location / {
        root   /usr/share/nginx/html;
        index  index.html;
        try_files $uri $uri/ /index.html;
    }

    location /api {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://apiserver/<요청URI>;
    }
}
💡
172.17.0.1란?
Docker는 호스트의 커널은 공유하지만 네트워크 환경은 공유하지 않는 격리된 환경이다. 그렇기 때문에 127.0.0.1이나 localhost를 호출하면 호스트를 호출하는 게 아니라 컨테이너 자기 자신을 호출하게 된다. Docker는 기본적으로 172.17.0.X IP 대역으로 네트워크를 관리하는데, 이 중 172.17.0.1이 호스트에 부여되는 IP다. 따라서 호스트에서 서비스가 돌아가는 백엔드에 요청을 보내기 위해서는 172.17.0.1을 호출해야 한다.

Postgresql 컨테이너 생성하기

내 Spring Boot 2 프로젝트는 Postgresql 데이터베이스를 사용하기 때문에, Postgresql 컨테이너도 생성할 것이다.

$ docker pull postgres:14
끝에 14를 붙이는 이유는 내가 Postgresql 버전 14를 사용하고 있기 때문이다

Postgresql 14 이미지를 정상적으로 내려 받았다면 다음의 명령줄을 입력해 Postgresql 컨테이너를 생성한다. 여기서 -e 옵션을 이용해 초기 데이터베이스 사용자 아이디와 패스워드를 지정해 줄 수 있다. USER_NAME과 USER_PASSWORD는 각각 Spring Boot 2 프로젝트에서 datasource를 연결할 때 사용하는 아이디/패스워드 값을 입력해주면 된다.

$ docker run -d -p 5432:5432 -e POSTGRES_USER="<USER_NAME>" -e POSTGRES_PASSWORD="<USER_PASSWORD>" --name springboot-postgres --restart="always" postgres:14
USER_NAME은 데이터베이스 유저 명, USER_PASSWORD는 해당 유저의 패스워드다

OpenJDK 설치

Spring Boot 2 프로젝트를 실행하려면 당연히 JRE나 JDK가 필수다. 리눅스 서버에서 지원하는 OpenJDK 버전을 먼저 찾아보도록 하자.

$ apt-cache search openjdk

apt 기본 리포지토리 에서는 많은 버전의 OpenJDK를 지원한다. 이 중에서 나는 openjdk-17-jre를 설치할 것이다.

이 서버에서 개발을 할 게 아니라면, 굳이 JDK 까지는 필요 없다
$ sudo apt install openjdk-17-jre -y
OpenJDK 17 JRE 설치
$ java --version
OpenJDK 설치 확인

정리하며

프로젝트 배포를 위해 Docker, OpenJDK를 설치했다. 그리고 Docker로는 프로젝트에서 사용할 Nginx 서버 컨테이너와 Postgresql DB 서버 컨테이너를 생성했다. 다음 글에서는 Jenkins를 이용해 Nginx 서버 컨테이너에 Vue 3 프로젝트를 배포하고 인스턴스에는 직접 Spring Boot 2 프로젝트를 배포하고 자동 실행하는 방법에 대해 다루도록 하겠다.


참고한 문헌 및 블로그 글

  1. [Nginx] connect() failed (111: Connection refused) while connecting to upstream 오류 해결