스벨트를 빌드해서 Docker 로 배포하려고 한다.

이때 설정하는 .dockerignore , Dockerfile 에 대해 설정하는 법을 작성해보고자 한다.

1) .dockerignore

node_modules
.svelte-kit

나의 경우 svelte-kit를 포함하고 있어서, svelte-kit를 사용하지 않는 사람은 생략해도 된다.

node_modules는 어차피 docker build를 할때 npm i 명령어로 install 하기 때문에 따로 건드리지 않는다.

2) DockerFile

# 이미지의 기반이 될 이미지 선택
FROM node:20.10.0

# 앱의 소스코드를 포함할 디렉토리 생성
WORKDIR /app

# package.json 파일을 복사한다. 만약 다시 빌드할 때 변경사항이 없을 경우 npm install까지 그냥 넘어간다.
COPY package.json /app


# 이미지를 받으면 npm install을 자동으로 해줌
RUN npm install

# 어떤 파일이 이미지에 들어가야 하는지 
# 첫 번째 .은 이 프로젝트의 모든 폴더 및 파일들 (Dockerfile을 제외한)
# 두 번째 .은 파일을 저장할 컨테이너 내부 경로 (ex /app)
COPY . /app

# 앱 빌드
RUN npm run build


# 포트 설정
EXPOSE 3000

# 프로그램 실행
CMD ["npm","run","preview", "--", "--host"]

DockerFile의 경우 설정법이야 워낙 많지만.. 최소한의 것만 작성한다.

일단 나의 경우 vite를 썼기 때문에 npm run build를 적용하고 preview로 실행하는 데, 이때

포트가 0.0.0.0 으로만 켜지는 것을 방지하기 위해 --, --host를 추가적으로 작성해준다.

 

지난 포스팅에 이어 백엔드용 Spring Boot 를 빌드 및 배포해보자.

  1. DockerFile 작성 (프로젝트 루트경로에 작성한다
FROM openjdk:11
# FROM amazoncorretto:11 ==> amazon corretto 11 사용할 경우
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENV SPRING_PROFILES_ACTIVE=prod
ENTRYPOINT ["java","-jar","/app.jar"]
# ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]
# => 설정파일을 분리해서 사용할 때
# java -jar -Dspring.profiles.active=prod app.jar

2. Build

필자는 Gradle 을 쓰기 때문에 Gradle 로 한다.

./gradlew build -x test

3. 도커 이미지 생성

# gradle linux/amd64 옵션은 맥북 M1을 위한 옵션
$ docker build --build-arg DEPENDENCY=build/dependency -t 도커허브 ID/Repository --platform linux/amd64 .

# maven
$ docker build -t 도커허브 ID/Repository --platform linux/amd64 .

# 확인
$ docker images

4. 도커 업로드

# 로그인
$ docker login

# 업로드
$ docker push 도커허브이름/Repository:버전정보

그 외 리눅스 서버 배포는 Next.js 배포와 동일하니 거길 보고 따라하시면 됩니다.

 

미루고 미루다 드디어 Docker를 활용하여 배포를 한다.

개념은 나중에 정리하고 바로 시작해보자.

1) next.config.js 파일 수정

/** @type {import('next').NextConfig} */
const path = require('path');
const withImages = require('next-images');

module.exports = {
  output: 'standalone',
  distDir: 'build',
  reactStrictMode: true,
  async rewrites() {
    if (process.env.NODE_ENV === "production") {
      return [
        {
          source: process.env.PRODUCTION_JAVA_SERVER_PATH,
          destination: process.env.PRODUCTION_JAVA_SERVER_URL,
        }

      ];
    } else {
      return [
        {
          source: process.env.JAVA_SERVER_PATH,
          destination: process.env.JAVA_SERVER_URL,
        }
      ];
    }
  },
};
 

2) .env.local 변경

NODE_ENV = 'development'

PRODUCTION_JAVA_SERVER_PATH = '/java/:path*'
PRODUCTION_JAVA_SERVER_URL = 'http://사용하는 IP 주소:8083/:path*'

JAVA_SERVER_PATH = '/java/:path*'
JAVA_SERVER_URL = 'http://localhost:8083/:path*'

 

3) Dockerfile 생성

​

# 위에서 도커 허브 node 이미지를 기반으로 로컬로 다운로드 및 캐싱 되었기 때문에 이미지를 가져올 수 있다.
FROM node:18.4.0

# 만약 컨테이너 안의 이미지의 경로가 /app 이런식으로 되어있다면 작업할 div 경로를 설정할 수도 있다.
# 설정해주면 COPY 의 두번째 경로를 ./ 이것으로 했을 때 자동으로 /app 경로가 된다.
WORKDIR /app

# package.json 파일을 복사한다. 만약 다시 빌드할 때 변경사항이 없을 경우 npm install까지 그냥 넘어간다.
COPY package.json /app

# 이미지를 받으면 npm install을 자동으로 해줌
RUN npm install


# 어떤 파일이 이미지에 들어가야 하는지 
# 첫 번째 .은 이 프로젝트의 모든 폴더 및 파일들 (Dockerfile을 제외한)
# 두 번째 .은 파일을 저장할 컨테이너 내부 경로 (ex /app)
COPY . /app

# 배포환경으로 설정
ENV NODE_ENV=production

RUN npm run build

# 도케에게 우리가 서버를 실행할 포트를 말해준다.
EXPOSE 3000

# 이미지가 생성될 때 실행되지 않고 컨테이너가 실행될 때 수행하는 명령어
CMD ["npm","start"]

가급적 개발 서버와 Node 버전을 맞춰주어야 한다.

필자는 NVM 을 활용하여 버전을 맞춰주었다.

4) dockerignore 생성

/.dockerignore

.node_modules
.next

5) 이미지 빌드

$ docker build -t 도커허브이름/web-client:버전정보 . ex) docker build -t ohkwonseok/web-client:1.0.1 .

 

만약 맥북에서 빌드하고 리눅스, amd64 서버에 배포할 예정인 경우

$ docker buildx build --platform=linux/amd64 -t 도커허브이름/web_client:버전정보 .
ex) docker buildx build --platform=linux/amd64 -t ohkwonseok/web-client:1.0.1 .

개발서버 및 배포서버의 운영체제에 따라 알맞게 선택하여 둘 중 하나를 사용하면 된다.

6) Dockerhub Repository 생성

 

회원가입을 안했을 경우 회원가입을 해주고, 이미 회원가입이 되어있다면

우측 상단의 Sign In 을 클릭하여 로그인한다.

*** 이때, username 이 실질적인 docker hub name 이 되므로, 비교적 간단하게 작성바란다.

명령어에 계속 작성할 일이 많다. ***

7) Create repository 클릭하여 생성한다. (별로 어렵지 않으니 나머지 부분은 생략)

8) 이제 빌드한 것을 도커허브에 업로드한다.

$ docker push 도커허브이름/web-client:1.0.1
ex) docker push ohkwonseok/web-client:1.0.1

*** 이제부터 리눅스 서버에 도커를 배포하는 것을 진행한다. ***

1) 도커 설치

$ sudo wget -qO- http://get.docker.com/ | sh

2) 도커 로그인

$ docker login
 

3) 도커 시작

$ sudo systemctl start docker
 

4) 도커허브에서 이미지 파일 받기(Pull)

$ docker pull 도커허브이름/web_client:버전정보
ex) docker pull ohkwonseok/web_client:1.0.1

5) 이미지 id 확인

$ docker images
 

6) 이미지 컨테이너 실행

$ docker run -p 80:3000 -d --rm 도커허브이름/web_client:버전정보

3000 번 포트를 80포트로 전환 실행한다.

끝~~!

 

docker image를 계속 받다보니 용량이 쌓인다.

실행중인 컨테이너가 아닌, 기존에 쌓인 이미지를 삭제하자.

docker images -a
 

삭제할 이미지를 선택해보자. 제일 위의 이미지를 삭제해보자.

docker rmi [Image ID]

 

docker rmi [Image ID] // rmi -> remove image

 

정상적으로 삭제된다.

*** 추가

  1. 몇 안되는 경우지만, tag name이 달라도 이미지 ID가 중복되는 사례를 몇번 식별했다. 이런 경우
docker rmi [Image ID] // rmi -> remove image

 

위 명령어를 사용해도 소용이 없는데, 이런 경우

docker rmi [이미지이름:tag]

로 하면 태그네임을 기준으로 삭제한다.

2. 테스트 하느라 너무 많은 이미지를 docker pull로 받았을 경우, 일일이 제거해주기 귀찮다.

이때 안쓰는 이미지를 삭제하는 방법이다.

docker image prune -a

 

Docker 로 스벨트와 스프링을 배포를 하다가.. 저런 에러가 발생했다.

난 처음에는 Docker 문제가 아닌 서버의 방화벽 문제인가 하다가.. 지인의 권고대로

서버자체에서 해당 Docker로 배포한 스벨트에 접근을 못하고 있는 것 같다는 얘기를 듣고..

시도해보았다.

wget localhost:3000 // 여기서 localhost는 Docker로 배포한 스벨트의 url 임

저렇게해보니 정말 접근이 안되더라..

여기저기 둘러보니 나는 스벨트를 배포할때 vite를 사용해서 npm run preview로 배포하고 있었다.

DockerFile(변경전)

... 생략

# 프로그램 실행
CMD ["npm","run","preview"]

그런데, 저렇게 배포를 하면 localhost도, 127.0.0.1도 아닌 0.0.0.0 으로 배포한다고 한다..(윙..?)

그래서 해결방법은 이것저것 많았지만, 가장 간단하면서도 나에게 쉬운방법은 위 코드를 일부만 변경하면 된다.

DockerFile(변경후)

... 생략

# 프로그램 실행
CMD ["npm","run","preview","--", "--host"]
 

저렇게 하니 127.0.0.1, localhost 도 같이 개방된다.

평상시 나는 서버에 도커 실행을 하려면 어지간해서는 서버가 꺼지지 않기에

재시작 설정을 거의 하지 않았다.

그러나 이번에 고객사 서버의 전원이 불량상태로, 잦은 재시작을 하여 교체전까지는 그대로 사용해야 하는 상황이라 재시작 설정 및 도커 데몬 자동 설정을 해야만 했다.

  1. 도커 데몬 자동 실행

* 리눅스

sudo systemctl enable docker // 시스템 재시작시 자동 사용 설정
 

* 윈도우

General -> Start Docker Desktop when you log in 항목 체크, Apply & restart 버튼 클릭

2. 도커 컨테이너 자동 재시작 명령어

# 새로운 컨테이너 생성 시 재시작 정책 적용 
docker run -p [포트번호:포트번호] --restart unless-stopped -d [도커이미지]
 

개발환경에서는 IDE에서 프로그램을 실행시키면 실시간으로 로그를 확인할 수 있지만

도커와 같은 배포환경에서는 로그를 확인하기 쉽지않다.

아래 명령어는 도커를 실행시키고 발생되는 로그를 실시간으로 확인하는 명령어이다.

docker logs -f --tail 10 [컨테이너명] // 뒤에 숫자 10은 라인의 갯수를 의미한다.

 

뒤의 숫자는 로그의 라인 갯수를 의미한다.

숫자를 빼도 되지만, 배포환경에서는 로그가 실시간으로 다수의 내용이 올라오기 때문에 식별이 어려워

필요한 만큼만 숫자지정을 하는 게 좋다.

 

개발환경에서는 멀쩡히 동작하던 소스코드가 Docker 로 배포하고 테스트해보니 위 제목과 같은 에러가 발생한다.

여러가지 방법이 많지만 제일 간단한 방법을 기재한다.

application.properties에 다음 코드를 추가한다.

spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQLDialect

 

서버 설정 가끔씩 하는 데, 가끔 까먹을 때가 있어 기재해놓는다. 나의 개발환경은

리눅스(CentOS),Nginx, 도커, 스벨트,스프링부트, MariaDB 환경이다.

(도메인 갱신은 Let's encrypt)

  1. ELCAP 방화벽 설정

2. 서버에 firewalld 설치

sudo yum install firewalld // 설치
sudo systemctl start firewalld // 시작
sudo systemctl enable firewalld // 재부팅해도 자동실행하도록 등록

 

3. 특정포트 허용(위의 ELCAP 방화벽 관리의 포트를 허용해준다고 보면 됌)

sudo firewall-cmd --zone=public --add-port=80/tcp --permanent // 80 포트 허용 .. 이하 생략

4. 특정 서비스 허용(http,https 등)

sudo firewall-cmd --zone=public --add-service=https --permanent // https 허용 .. 이하 생략

했을 때 success 라고 나오면 된 것임

5. firewalld 업데이트

sudo firewall-cmd --reload

이제 방화벽을 업데이트하자

6. firewalld 업데이트 후 등록된 리스트 확인

sudo firewall-cmd --list-all

방화벽 설정은 이제 끝났다.

이쯤에서, 소프트웨어 패키지 도구인 epel 을 설치해보자

7. epel-release 설치

sudo yum install epel-release // epel 설치

중간에 질문을 할텐데, y눌러주면 됌

8. MariaDB 버전 설정

cd /etc/yum.repos.d   // 해당경로 이동
touch MariaDB.repo   // MariaDB 설정용 파일 생성
vim MariaDB.repo  // 해당 파일 접근

 

vim으로 접근한 MariaDB.repo에 아래 내용 작성

[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.6/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

 

나의 경우 10.6버전대를 사용, 위와 같이 설정한다.

9. mariadb 설치

sudo yum install mariadb-server

epel 로 설치되는 것을 볼 수 있다.

10. 시스템 등록 및 시작

sudo systemctl start mariadb // MariaDB 서비스 시작
sudo systemctl enable mariadb // 재부팅시에도 동작하게 등록

11. 비밀번호 설정

MariaDB의 경우 초기비밀번호는 공백이기 때문에 개나소나 들어올 수 있다.

바로 비밀번호 설정을 해준다.

sudo mysql_secure_installation // 보안설정

 

위와 같이 root 비밀번호 세팅할거냐 물어보면. y 눌러주고 현재 비밀번호가 공백이기 때문에

그냥 엔터누르면 바꿀 비밀번호 입력하고 확인 비밀번호까지 입력하면 성공처리된다.

그 외 보안옵션이 있지만 취향껏 설정한다.

12. 최종체크

데이터베이스 세팅 및 테이블 세팅은 별개로 한다.(자기 개발환경에 맞춰서)

13. Nginx 설치

설치전 내 블로그를 참조해보고 진행하면 좋다.

https://blog.naver.com/ohks486/222832867066

sudo yum install nginx

14. Nginx 경로 이동

cd /etc/nginx/conf.d

15. default.conf 파일 생성

디렉토리로 접근했는데, default.conf 파일이 없을 경우 만들어준다. 있으면 안 만들어도 됌

touch default.conf

 

16. default.conf 파일 수정(중요)

sudo vi default.conf // vim으로 해도됌

17. 각 도메인별 서버 설정한다.

 

server {


     if ($http_user_agent ~* (AhrefsBot|BLEXBot|DotBot|SemrushBot|Eyeotabot|PetalBot|MJ12bot|brands-bot|bbot|AhrefsBo|MegaIndex|UCBrowser|Mb2345Browser|MicroMessenger|LieBaoFast|Headless|netEstate|newspaper|Adsbot/3.1|WordPress/|ltx71) ) {
    return 403;
  }

                        listen 80 default_server;
                        server_name *.도메인.kr;
                        return 301 https://$host$request_uri;

        }


server {
                listen 443 ssl http2;
                listen [::]:443 ssl http2;
                server_name test.도메인.kr;
                ssl_certificate /etc/letsencrypt/live/test.도메인.kr/fullchain.pem;
                ssl_certificate_key /etc/letsencrypt/live/test.도메인.kr/privkey.pem;
                ssl_session_timeout 1440m;
                ssl_session_cache shared:SSL:50m;
                ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
                location / {
                 proxy_pass http://localhost:8081/;
                         proxy_read_timeout 600s;
                         proxy_send_timeout 600s;
                         proxy_redirect off;
                         proxy_pass_header Server;
                         proxy_set_header Host $http_host;
                         proxy_set_header X-Real-IP $remote_addr;
                         proxy_set_header X-Scheme $scheme;
                         proxy_set_header X-Forwarded_For $proxy_add_x_forwarded_for;
                         proxy_set_header X-Forwarded_Server $host;
                         proxy_set_header X-Forwarded-Host $host;
                         proxy_set_header Upgrade $http_upgrade;
                         proxy_buffer_size 128k;
                         proxy_buffers 4 256k;
                         proxy_busy_buffers_size 256k;
                         proxy_http_version 1.1;
               }

}

server {
                listen 443 ssl http2;
                listen [::]:443 ssl http2;
                server_name test.도메인.kr;
                ssl_certificate /etc/letsencrypt/live/test.도메인.kr/fullchain.pem;
                ssl_certificate_key /etc/letsencrypt/live/test.도메인.kr/privkey.pem;
                ssl_session_timeout 1440m;
                ssl_session_cache shared:SSL:50m;
                ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
                location / {


                 proxy_pass http://localhost:3000/;
                         proxy_read_timeout 600s;
                         proxy_send_timeout 600s;

                         proxy_redirect off;
                         proxy_pass_header Server;
                         proxy_set_header Host $http_host;
                         proxy_set_header X-Real-IP $remote_addr;
                         proxy_set_header X-Scheme $scheme;
                         proxy_set_header X-Forwarded_For $proxy_add_x_forwarded_for;
                         proxy_set_header X-Forwarded_Server $host;
                         proxy_set_header X-Forwarded-Host $host;
                         proxy_set_header Upgrade $http_upgrade;
                         proxy_buffer_size 128k;
                         proxy_buffers 4 256k;
                         proxy_busy_buffers_size 256k;
                         proxy_http_version 1.1;
                 }
        }

18. Let's encrypt 설치

설정은 다했는데.. nginx -t 로 테스트를 해보면 실패할 것이다.

왜냐하면 아직 Let's encrypt로 설정을 안했기 때문이다.

자세한내용은 여기 참고

https://blog.naver.com/ohks486/222833865442

sudo yum install certbot

19. 도메인 등록

sudo certbot certonly --standalone -d 도메인주소

만약 80포트 관련 에러가 나올 경우, nginx stop 후 다시 진행한다.

sudo systemctl stop nginx sudo certbot certonly --standalone -d 도메인주소

위와 같이하면 인증서가 나오는데, nginx 에서 서버설정 시 인증서 경로를 추가해주자

20. Docker 설치

 

sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io​

21. Docker 서비스 시작 및 등록

sudo systemctl start docker
sudo systemctl enable docker

22. Docker login

docker login

docker 로 실행하기 때문에 Node.js, 자바 등은 별도로 설치하지 않는다.

이상 끝~

** 혹시 서비스 접근했는데 502 에러 나올 시 조치방법이다. **

vi /var/log/nginx/error.log 내용

13: Permission denied) while connecting to upstream

나의 경우 https만 안되었다.

setenforce 0 // SEL Linux를 disable 처리한다.
sestatus // SEL Linux 상태확인

sestatus를 하면 위에 sentenforce를 0으로 했기 때문에 아래와 같이 간단하게만 나올 것이다.

 

그럼 이제 이부분을 편집해보자.

RHEL/CentOS 8 일경우

vi /etc/selinux/config

RHEL/CentOS 7 일경우

vi /etc/sysconfig/selinux

아래 SELINUX = enforcing 으로 수정한다.

그다음, 서버를 재시작한다.

reboot

+ Recent posts