728x90
반응형
FROM node:17-alpine as staged

WORKDIR /opt/app

COPY ["package.json", "package-lock.json", "./"]
RUN ["npm", "install"]

COPY ["tsconfig.build.json", "tsconfig.json", "./"]
COPY ["src/", "./src/"]
RUN ["npm", "run", "build"]

RUN ["/bin/sh", "-c", "find . ! -name dist ! -name node_modules -maxdepth 1 -mindepth 1 -exec rm -rf {} \\\\;"]

FROM node:17-alpine as completed
WORKDIR /opt/app
COPY --from=staged /opt/app ./
ENTRYPOINT ["node", "dist/src/main"]
EXPOSE 8080/tcp
728x90
반응형
728x90
반응형
FROM gradle:8-jdk17-alpine as builder
WORKDIR /build

# 그래들 파일이 변경되었을 때만 새롭게 의존패키지 다운로드 받게함.
COPY build.gradle settings.gradle /build/
RUN gradle build -x test --parallel --continue > /dev/null 2>&1 || true

# 빌더 이미지에서 애플리케이션 빌드
COPY . /build
RUN gradle build -x test --parallel

# APP
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

# 빌더 이미지에서 jar 파일만 복사
COPY --from=builder /build/build/libs/my-app-*-SNAPSHOT.jar .

EXPOSE 8080

CMD java -jar ./my-app-*-SNAPSHOT.jar

이렇게 설정해주면 도커 이미지의 크기를 많이 줄일 수 있다.

 

참고 

Gradle을 사용할 때 도커 빌드를 빠르게 하는 방법 - Soo Story (findstar.pe.kr)

728x90
반응형
728x90
반응형

1. 인스턴스 서버에 도커 설치

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

 

2. 인스턴스 서버에서 도커 로그인

$ docker login

 

3. 도커 시작

$ sudo systemctl start docker

 

4. 도커허브에서 이미지 pull 

ex) Next.js

$ docker pull 도커허브아이디/web_client:버전정보

 

ex) Spring

$ docker pull 도커허브아이디/java_server:버전정보

 

5. 이미지 확인

$ docker images

 

 

6. Docker run

포트는 프로젝트에 맞게 설정해야함.

$ docker run -p 8083:8083 -d --rm --name java_server ID/Repository

 

$ docker run -p 80:3000 -d --rm --name web_client ID/Repository

 

728x90
반응형
728x90
반응형

1. env 수정

 

ex) /next.config.js

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

module.exports = {
  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,
        }
      ];
    }
  },
};

 

 

/.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*'

 

2. Dockerfile 생성

프로젝트 루트 경로에 만들어준다.

/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 버전을 맞춰주어야 한다. 

 

3. dockerignore 생성

/.dockerignore

.node_modules

.next

 

4. next.config.js 수정

module.exports = {
  output: 'standalone'
}

 

5. 이미지 빌드

$ docker build -t 도커허브아이디/web-client:버전정보 .

web_client 는 임의로 제가 지은 이름입니다.

docker 프로그램에서 확인할 수도 있다.

 

혹시 맥북에서 빌드하고 linux/amd64 서버에 배포할 예정이라면 

docker buildx build --platform=linux/amd64 -t 도커허브아이디/web_client:버전정보 .

 

6. Dockerhub Repository 생성

 

7. 도커허브 업로드

# 업로드
docker push 도커허브아이디/web_client

 

 


Server

 

1. 인스턴스 서버에 도커 설치

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

 

2. 인스턴스 서버에서 도커 로그인

$ docker login

 

3. 도커 시작

$ sudo systemctl start docker

 

4. 도커허브에서 이미지 pull 

$ docker pull 도커허브아이디/web_client:버전정보

 

5. 이미지 id 확인

$ docker images

 

6. 컨테이너 실행

$ docker run -p 80:3000 -d --rm 도커허브아이디/web_client
728x90
반응형
728x90
반응형

1. Dockerfile 작성

프로젝트 루트경로에 작성해준다.

FROM openjdk:11
# FROM amazoncorretto:11 ==> amazon corretto 11 사용할 경우
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
# ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]
# => 설정파일을 분리해서 사용할 때
# java -jar -Dspring.profiles.active=prod app.jar

 

2. Build

# Spring Boot 빌드
./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

 

Spring Boot 2.3.x 버전 이상인 경우 Dockerfile 작성 없이 Plugin으로 이미지 생성이 가능하다.

# yml, properties를 여러개 사용하는 경우 profile을 지정하여 image를 생성한다.
$ ./gradlew bootBuildImage --imageName=ID/Repository

4. Docker 업로드

# 로그인
$ docker login

# 업로드
$ docker push ID/Repository

 

배포 팁

환경변수중에 SPRING_PROFILES_ACTIVE=prod 를 지정하면 자동으로 application-prod.properties 파일을 바라봅니다.

개발할 땐 application.properties 설정을 바라보고 docker로 운영 서버에 배포할 땐 -e SPRING_PROFILES_ACTIVE=prod 만 추가하면 됩니다.

728x90
반응형
728x90
반응형

1. Docker를 설치한다.

https://typo.tistory.com/entry/Docker-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0?category=896128 

 

Docker | 시작하기

1. Docker 란?  Docker란 컨테이너를 생성하고 관리하기 위한 도구이다. 여기서 컨테이너란 표준화된 소프트웨어 유닛을 말한다. 기본적으로 해당 코드를 실행하는데 필요한 종속성과 도구가 포함

typo.tistory.com

 

2. Spring Boot 프로젝트를 만든다.

https://start.spring.io/

 

3. IntelliJ에서 열고 Docker plugin을 설치한다.

 

4. 프로젝트 루트 경로에 Dockerfile을 생성한다.

/Dockerfile

FROM openjdk:11
# FROM amazoncorretto:11 ==> amazon corretto 11 사용할 경우
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
# ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]
# => 설정파일을 분리해서 사용할 때
# java -jar -Dspring.profiles.active=prod app.jar

 

5. 아래 명령어로 빌드를 해준다.

./gradlew build -x test

 

6. Dockerhub Repository 생성

 

7. 이미지 생성

Spring Boot 2.3.x 버전 이상인 경우 Dockerfile 작성 없이 Plugin으로 이미지 생성이 가능하다.

# yml, properties를 여러개 사용하는 경우 profile을 지정하여 image를 생성한다.
# SPRING_PROFILES_ACTIVE=dev
# ./gradlew bootBuildImage -Pprofile=dev --imageName=ID/Repository
$ ./gradlew bootBuildImage --imageName=ID/Repository

 

※ 아닐 경우

# 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

 

8. 업로드

# 로그인
docker login

# 업로드
docker push ID/Repository

 

728x90
반응형
728x90
반응형

1. Docker Compose 란?

  1. 'docker build'와 'docker run' 명령을 대체할 수 있는 도구
  2. Dockerfile을 대체하지 않는다. 함께 작동한다.
  3. 이미지나 컨테이너를 대체하지 않는다.
  4. 다수의 호스트에서 다중 컨테이너를 관리하는데는 적합하지 않다.

 

2. Docker Compose를 쓰는 이유?

기본적으로 터미널에서 도커 명령으로 할 수 있는 것들을 service(컨테이너) 로 관리할 수 있다.

  • 포트 정의
  • 환경변수 정의
  • 볼륨 정의
  • 네트워크 정의

 

3. 사용하기

프로젝트 루트 파일에 docker-compose.yaml 파일을 생성해준다.

 

 ( https://docs.docker.com/compose/compose-file 참조 )

 

/docker-compose.yaml

version: "3.8"
service:
	mongodb:
		# 이미지
		image: 'mongo'
        
		# 볼륨
		volumes:
			- data:/data/db
            
		# 환경변수
		environmnet:
			MONGO_USERNAME: max
            
		# 환경변수 파일일 경우
		env_file:
			- ./env/mongo.env
            
		# 네트워크
		networks:
			- goals-net
	backend:
    	
	frontend:

 

4. Linux에 Docker Compose 설치

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
$ docker-compose --version

 

5. Docker Compose up

아래 명령어를 입력하면 이미지를 빌드하고 컨테이너를 실행해준다.

-d 플래그를 붙여 detached 모드에서 실행할 수도 있다.

$ docker-compose up

 

6. Docker Compose down

아래 명령어를 입력하면 모든 컨테이너가 삭제되고 디폴트 네트워크와 모든 것이 종료된다.

-v 플래그를 추가해 볼륨도 삭제할 수 있다.

$ docker-compose down

 

 

7. Backend file, Frontend file 컴포징

version: "3.8"
service:
	mongodb:
    	...
	backend:
		
		# 이미지 빌드
		build: ./backend
		
		# 도커 파일 직접 명시할 때 ( 파일 이름이 Dockerfile이 아닐 때 )
		# build:
		#	context: ./backend
		#	dockerfile: Dockerfile
		#	args:	
		#		some-arg: 1
		
		# 포트지정
		ports:
			- '80:80'	
		
		# 볼륨지정		
		volumes:		
			- logs:/app/logs	
			- ./backend:/app	
			- /app/node_modules	
		
		# 환경 변수 파일	
		env_file:
			- ./env/backend.env
				
		# 의존성 추가
		depends_on				
			- mongodb
						
		
		
	frontend:
    
		# 이미지 빌드
		build: ./frontend
        
		# 포트지정
		ports:
			- '80:80'	

		# 볼륨지정		
		volumes:		
			- ./frontend/src:/app/src

		# it 플래그
		stdin_open: true
		tty: true
        
		# 의존성 추가
		depends_on
			- mongodb
728x90
반응형
728x90
반응형

1. Network 생성

$ docker network create goals-net

 

2. MongoDB 컨테이너 시작

$ docker run --name mongodb \
 -e MONGO_ROOT_USERNAME=root \ <- 몽고db 아이디
 -e MONGO_ROOT_PASSWORD=secret \ <- 몽고db 비밀번호
 -v data:/data/db \ <- 몽고db 바인드 마운트
 --rm \ <- 컨테이너 중지 시 삭제
 -d \ <- detached 모드
 --network goals-net \ <- 네트워크 활성화
 mongo

 

3. NodeJS 이미지 빌드

$ docker build -t goals-node .

 

4. NodeJS 컨테이너 시작

$ docker run --name goals-backend \
 -e MONGODB_USERNAME=max \ <- 환경설정 추가(db아이디)
 -e MONGODB_PASSWORD=secret \ <- 환경설정추가(db비밀번호)
 -v logs:/app/logs \ <- 컨테이너 로그 바인딩 마운트
 -v /Users/abc/def/gh:/app \ <- 하드 드라이브 내 코드 바인딩 마운트
 -v /app/node_modules \
 --rm \
 -d \
 --network goals-net \ 
 -p 80:80 \ <- 포트 포워딩
 goals-node

 

5. ReactJS 컨테이너 시작

$ docker run --name goals-frontend \
 -v /Users/abc/def/gh/frontend/src:/app/src \ <- src 소스코드 바인딩 마운트
 --rm
 -d
 -p 3000:3000 \
 -it \ <- 개발자 모드로 실행
 goals-react

 

 

728x90
반응형
728x90
반응형

지금까지의 이미지 & 컨테이너에서는 한 종류의 데이터만 다뤄봤지만 이번엔 볼륨이라는 개념과 함께 다른 종류의 데이터들도 다뤄보는 방법을 알아보겠다.

 

1. 아래 파일을 다운로드 한다.

data-volumes-01-starting-setup.zip
0.01MB

 

npm package들을 다운받고 서버를 실행해보자.

$ npm install
$ node server.js

 

localhost 80번 포트로 웹을 열어보면

 

이런 식으로 화면이 구성 된 것을 볼 수 있다. 서버 파일을 잠시 보면

 

app.post('/create', async (req, res) => {
  const title = req.body.title;
  const content = req.body.text;

  const adjTitle = title.toLowerCase();

  const tempFilePath = path.join(__dirname, 'temp', adjTitle + '.txt');
  const finalFilePath = path.join(__dirname, 'feedback', adjTitle + '.txt');

  await fs.writeFile(tempFilePath, content);
  exists(finalFilePath, async (exists) => {
    if (exists) {
      res.redirect('/exists');
    } else {
      await fs.rename(tempFilePath, finalFilePath);
      res.redirect('/');
    }
  });
});

내가보낸 피드백이 파일 형식으로 저장이 되는 것 또한 볼 수 있다.

 

이제부턴 이 앱을 도커화를 해보자. 먼저 Dockerfile을 만들어준다.

 

/Dockerfile

FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 80

CMD ["node","server.js"]

 

터미널에서 만든 Dockerfile로 이미지를 빌드한다.

$ docker build -t feedback-node .

 

빌드된 이미지로 run 명령어를 실행한다.

$ docker run -p 3000:80 -d --name feedback-app --rm feedback-node

 

이제는 localhost 3000번 포트에서 사이트를 볼 수 있다.

여기서 피드백을 작성하고 저장을 하면 localhost:3000/feedback/title.txt ( 피드백 제목 )

경로에서 내용을 확인할 수도 있다. ( 서버파일 참조 )

 

하지만 우리는 이미 켜둔 VSCode 프로젝트 안에서는 feedback 폴더 밑에 파일이 생기지 않은 것을 알 수 있다.

이는 당연하겠지만 이미지는 컨테이너를 만드는데에 쓰임이 끝났고, 파일은 격리된 컨테이너에만 생성됐기 때문이다.

 

2. 볼륨이란?

볼륨은 도커에 내장된 기능이며 위에 경우처럼 컨테이너가 삭제될 경우 데이터를 보존할 수 있도록 도와준다.

볼륨은 컨테이너나 이미지에 있는게 아니라 호스트 컴퓨터에 장착된 하드 드라이브에 존재하여 사용가능하거나 컨테이너로 매핑되는 것을 의미한다.

 

3. 컨테이너에 볼륨 적용하기

Dockerfile 파일 안에 VOLUME 을 추가해줄 수 있다.

/Dockerfile

FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 80

# 저장하려는 볼륨
VOLUME ["/app/feedback"]

CMD ["node","server.js"]

 

이제 다시 이미지를 빌드하고 실행해보자.

$ docker build -t feedback-node:volumes .
$ docker run -d -p 3000:80 --rm --name feedback-app feedback-node:volumes

 

이러고 서버를 실행해보면 

피드백을 작성하고 저장버튼을 누르면 로딩이 되면서 더이상 진행이 안되는 것을 확인할 수 있다. 

로그를 확인해보자.

 

$ docker logs feedback-app

 

/create 라우터를 보면 fs.rename이라는 메소드가 있는데 이 부분을 바꿔준다.

app.post('/create', async (req, res) => {
  const title = req.body.title;
  const content = req.body.text;

  const adjTitle = title.toLowerCase();

  const tempFilePath = path.join(__dirname, 'temp', adjTitle + '.txt');
  const finalFilePath = path.join(__dirname, 'feedback', adjTitle + '.txt');

  await fs.writeFile(tempFilePath, content);
  exists(finalFilePath, async (exists) => {
    if (exists) {
      res.redirect('/exists');
    } else {
      // await fs.rename(tempFilePath, finalFilePath);
      await fs.copyFile(tempFilePath, finalFilePath);
      await fs.unlink(tempFilePath);
      res.redirect('/');
    }
  });
});

 

이제 이미지를 삭제한 뒤 다시 만들고 실행해준다.

$ docker stop feedback-app
$ docker rmi feedback-node:volumes
$ docker rmi feedback-node:volumes
$ docker run -d -p 3000:80 --rm --name feedback-app feedback-node:volumes

 

다시 실행해보면 정상적으로 파일이 읽히는 것을 볼 수 있다.

 

이번엔 컨테이너를 종료하고 파일이 잘 남아있는지 확인해보자.

$ docker stop feedback-app

 

이러고 다시 실행해서 확인해보면 test.txt 가 읽혀지지 않는 것을 볼 수 있다.

 

4. 볼륨의 종류

볼륨의 종류에는 명명된 볼륨, 익명의 볼륨이 있다.

볼륨을 확인하는 명령은 다음과 같다.

$ docker volume ls

 

Dockerfile에서 VOLUME 레이어를 삭제한 뒤 이미지를 재구축하고 다시 실행해보자. 

이 때 run 명령어에 볼륨을 지칭하는 부분을 넣어준다.

$ docker stop feedback-app
$ docker rmi feedback-node:volumes
$ docker rmi feedback-node:volumes
$ docker run -d -p 3000:80 -rm --name feedback-app -v feedback:/app/feedback feedback-node:volumes

 

이러면 컨테이너를 삭제하더라도 볼륨의 데이터가 정상적으로 남아있는 것을 확인할 수 있다.

 

4. 바인드 마운트

우리가 개발한 환경을 이미지화해서 컨테이너를 만들고, 다시 이미지를 수정할 경우 계속 언급해왔듯 변경 사항이 바로 반영이 되지않으므로 이미지를 재구축하고 다시 실행해야 했다. 개발하는 동안에 코드를 수정할 때마다 이 과정을 반복하면 매우 불편하고 시간 소요도 클 것이다. 이것을 바인드 마운트가 도와준다.

 

호스트 컴퓨터의 절대경로로 현재 app이 지정하는 파일 경로를 붙여서 다음과같이 run 을 실행한다. ( 한 줄로 구성된 명령어입니다. )

$ docker run -d -p 3000:80 -rm --name feedback-app -v feedback:/app/feedback
-v "/Users/Desktop/workspace/docker:/app" feedback-node:volumes

 

5. docker volume 명령어들

 

728x90
반응형
728x90
반응형

전 포스트에서는 이미지에 대한 기본 지식과 빌드하는 방법을 살펴보았다.

이번 포스트에서는 이미지와 컨테이너를 관리하는 방법에 대해서 알아보겠다.

 

1. docker start vs docker run

docker run 명령어로 컨테이너를 실행할 때는 터미널이 더이상 명령어를 입력할 수 없는 것을 볼 수 있었다. 

docker start를 사용하면 백그라운드에서 컨테이너가 실행되기 때문에 별개로 터미널에서 다른 작업을 할 수 있다.

 

docker run 명령어로 컨테이너를 실행하면 터미널에서 실시간으로 컨테이너의 console.log 출력 결과를 확인할 수 있다.

이를 attached 모드라고 한다. 또한 컨테이너가 새로 생성되면서 실행까지 된다.

 

1. 만약 이미 실행중인 컨테이너에 연결하고 싶으면 docker  attach 를 사용하면 된다.

2. 컨테이너의 이미 지나간 로그 기록을 보고 싶으면 docker logs 라는 명령어도 존재한다. -f 옵션을 주면 attatch 까지 가능하다.

3. docker start 를 attatch 모드로 실행하고 싶으면 docker start -a 옵션을 주면 된다.

 

 

2. 컨테이너 삭제하기

컨테이너를 중지한 뒤 삭제를 안하고 새로 만들고 하다보면 언젠가 계속 쌓이게 될 것이다. 때때로 이 목록을 정리해야 될 필요가 있다.

docker rm '컨테이너이름' 명령어로 컨테이너를 삭제할 수 있는데 이는 컨테이너가 실행중이면 오류가 발생한다.

때문에 실행중이면 컨테이너를 종료하고 제거를 해야한다.

 

3. 이미지 삭제하기

이미지를 삭제할 때에는 docker rmi '이미지ID' 명령어를 사용한다. 다만 한 가지 유의사항이 있다면 해당 이미지를 가지고 있는 컨테이너가 한 개라도 존재할 경우엔 이미지 삭제가 불가능하다. 때문에 컨테이너를 먼저 삭제한 뒤 이미지를 삭제해야한다.

 

다만 docker image prune 이라는 명령어도 존재하는데, 이는 사용되지 않는 모든 이미지를 제거할 수 있게 해준다.

 

4. 중지된 컨테이너 자동으로 제거하기 

이미지로 docker run 을 실행하여 만들어진 컨테이너를 중지될 때 자동으로 제거하게끔 만드는 명령어가 있다.

docker run -p 3000:80 -d --rm '이미지id'

 

5. 컨테이너와 이미지에 이름 지정 & 태그 지정하기

 컨테이너의 자동 생성된 이름을 사용하기에는 불편함이 있을 수 있다. ( 까먹거나 너무 길 경우에 )

docker run -p 3000:80 -d --rm --name teepo '이미지id'

 

이미지에는 이름과 태그를 설정할 수 있는데,

name : tag 이런식으로 설정된다. ( tag 는 부 주제로 버전 등을 나타낼 수 있다)

docker build -t teepo:latest -> latest는 최신 버전임을 나타냄, 숫자도 가능

 

docker images 명령어로 이미지들을 확인할 수 있다.

 

 

728x90
반응형

+ Recent posts