지난 포스팅에 이어 백엔드용 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은 라인의 갯수를 의미한다.

 

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

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

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

 

+ Recent posts