Docker를 이용해 프로젝트 컨테이너화 하기 - 배포를 위한 Dockerfile 작성하기

2026. 2. 8. 17:26도커

이전 글에서 개발 환경을 Docker Compose를 이용해서 구축했습니다.

배포 시에 이 docker-compose.yml을 그대로 EC2 서버에 올리면 동작은 하겠지만 여러 가지 문제가 있을 수 있습니다.

어떤 문제가 생길지 예상해보고 하나씩 해결해 보겠습니다.


현재 방식으로 배포했을 때 문제점

배포 과정을 단순히 보면 아래의 과정이 됩니다.

# EC2 접속
# 프로젝트 클론
git clone https://github.com/project/project.git
# 컨테이너 실행
docker compose up -d

 

실제로 동작은 하겠지만 docker-compose.yml을 보면 문제가 보입니다.

services:
  ...

  backend:
    image: gradle:9-jdk17
    container_name: backend-container
    working_dir: /app
    ports:
      - "8081:8081"
    volumes:
      - /backend:/app
  command: sh -c "(sleep 10; gradle build --continuous -x test) & gradle bootRun --no-daemon"
    environment:
      - SPRING_PROFILES_ACTIVE=DEV_ENV
    depends_on:
      mysql-service:
        condition: service_healthy
      redis-service:
        condition: service_healthy
    networks:
      - app-network

  ...

 

우선 백엔드 서비스만 보더라도

  • 약 700MB의 gradle 이미지를 그대로 다운로드 받음
  • 소스코드가 서버에 그대로 노출되어 보안이 좋지 않음
  • 개발환경(DEV_ENV)으로 배포
  • --continuous 모드가 들어있어 계속 EC2의 CPU를 사용함.

이러한 문제점이 있습니다.

여기서 설정 관련 문제는 docker-compose.yml을 분리해서 해결하고,

보안 및 리소스 문제는 Dockerfile을 이용해 해결해 보겠습니다.

 


 

docker-compose.yml 분리하기

현재는 docker-compose.yml이 하나만 있습니다.

로컬에서 개발할 때는 문제가 없겠지만 배포할 때는 문제가 생깁니다.

 

1. 스프링 환경설정 문제

environment:
	- SPRING_PROFILES_ACTIVE=PROD_ENV # 배포용으로 변경

 

배포를 위해서는 스프링 환경을 배포용으로 변경해야 하는데,

배포를 위해 이 값을 바꾸고 'git push'하면 다른 PC에서 pull 했을 때 로컬 개발 환경이 깨지게 됩니다.

 

2. auto reload를 위한 --continuous 문제

서버에서는 코드를 수정할 일이 크게 없습니다.

하지만 --continuous 모드로 인해서 불필요하게 CPU를 계속 사용하게 됩니다.

 

이러한 문제들로 인해서 개발용, 배포용으로 파일을 분리해 줄 필요가 있습니다.

 

이렇게 기존의 docker-compose.yml을 2개로 분리해 주고

docker-compose.dev.yml의 내용은 기존대로 유지하고, docker-compose.prod.yml의 내용은 아래와 같이 바꿔줍니다.

...
    command: gradle bootRun --no-daemon  # continuous 모드 제거
    environment:
      - SPRING_PROFILES_ACTIVE=PROD_ENV  # 배포 환경   
...

 


 

Dockerfile로 이미지 만들기

현재 방식의 동작과정

EC2 서버:
1. git clone 또는 git pull로 소스코드 다운로드
2. gradle 이미지 다운로드
3. 소스코드를 /app에 마운트
4. 빌드 후 실행

 

위 방식으로 인해 아래와 같은 결과가 나옵니다.

  • EC2 서버에 전체 소스코드가 올라가므로 보안 취약
  • gradle 이미지 다운로드 필요로 인한 리소스 낭비

배포 시에는 JAR파일을 실행만 하면 되기 때문에 JRE + JAR만 있으면 됩니다.

 

 

Dockerfile 방식

로컬 PC:
1. 소스코드 빌드해서 JAR 파일 생성
2. JAR파일만 포함한 이미지를 Dockerfile로 빌드
3. 완성된 이미지 Docker Hub에 업로드

EC2 서버:
1. 완성된 이미지 다운로드
2. 컨테이너 실행

 

Dockerfile 방식으로 변경하면 이미지만 다운로드해서 바로 컨테이너를 실행하면 되므로

  • 소스코드가 노출되지 않음
  • 이미지 크기 감소
  • 빌드를 로컬에서 하므로 불필요한 빌드 도구를 포함하지 않음

Dockerfile을 이용한 이미지 빌드 및 배포

1. Dockerfile 작성

FROM eclipse-temurin:17-jre
WORKDIR /app
COPY build/libs/app.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
  • FROM: 베이스 이미지 지정
  • WORKDIR: 컨테이너 안에서 작업할 디렉토리 설정
  • COPY: build/libs/app.jar를 복사
  • ENTRYPOINT: 컨테이너 실행 시 실행할 명령어.

 

2. 이미지 빌드

docker build -t myapp:latest .
  • -t myapp:lastest : 이미지 이름과 태그 지정
  • . : 현재 디렉토리 (backend/)에서 Dockerfile을 찾는다고 지정

 

3. DockerHub 업로드

# 1. Docker Hub 로그인
docker login

# 2. 이미지에 태그 달기
docker tag myapp:latest [계정]/myapp:0.1

# 3. push
docker push [계정]/myapp:0.1

 

 

4. docker-compose.prod.yml 변경

생성한 이미지를 DockerHub에서 받아서 실행해야 하므로 docker-compose.prod.yml도 변경되어야 합니다.

# 변경 전
backend:
  image: gradle:9-jdk17
  container_name: backend-container
  working_dir: /app
  ports:
    - "8081:8081"
  volumes:
    - ./backend:/app
  command: sh -c "(sleep 10; gradle build --continuous -x test) & gradle bootRun --no-daemon"  # ENTRYPOINT로 대체되므로 제거
  environment:
    - SPRING_PROFILES_ACTIVE=PROD_ENV
  depends_on:
    mysql-service:
      condition: service_healthy
    redis-service:
      condition: service_healthy
  networks:
    - app-network
# 변경 후
backend:
  image: [계정]/myapp:0.1
  container_name: backend-container
  ports:
    - "8081:8081"
  environment:
    - SPRING_PROFILES_ACTIVE=PROD_ENV
  depends_on:
    mysql-service:
      condition: service_healthy
    redis-service:
      condition: service_healthy
  networks:
    - app-network
  • image: gradle이미지에서 직접 만든 이미지로 변경
  • working_dir: Dockerfile의 WORKDIR이 대신함
  • volume: JAR가 이미지 안에 포함되어 있으므로 소스코드 마운트 불필요
  • command: Dockerfile의 ENTRYPOINT가 대신함

 


간단한 예제

1. 간단한 프로젝트 생성

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello World";
    }
}

 

간단한 프로젝트를 만들고 Dockerfile을 추가한 후 빌드해서 JAR 파일을 만들어줍니다.

 

 

2. Docker 빌드해서 이미지를 만들어줍니다.

docker build -t myapp:latest .

 

위 명령어를 입력하면

 

이와 같이 이미지가 생성된 걸 확인할 수 있습니다.

 

 

3. Docker Hub 업로드

이 상태에서 이미지를 push 하면 아래와 같은 에러가 발생합니다.

계정 이름이 없기 때문에 Docker Hub 공식 이미지 저장소 [docker.io/library/]에 올리려고 시도를 했지만,

해당 저장소에 push 권한이 없기 때문에 발생하는 에러입니다.

 

따라서 아래와 같은 명령어를 입력해서 이미지에 태그를 붙여줍니다.

docker tag myapp:latest [username]/myapp:0.1

 

이는 기존 도커 이미지에 별칭을 생성하는 것으로 이미지 원본을 복사하는 것이 아니라 동일한 이미지를 가리키는 이름만 하나 더 만드는 방식입니다.

 

그 후 다시 push하면 성공적으로 올라갑니다.