[ Docker ] - 컨테이너 이미지 만드는 5가지 방법

김정욱·2021년 10월 23일
0
post-thumbnail
  • 이 내용은 컨테이너 인프라 환경 구축을 위한 쿠버네티스/도커 책을 ref 합니다

[ 개요 ]

  • 이 포스팅에서는 SpringBoot 애플리케이션을 컨테이너 이미지로 만들고 실행하는 과정까지 정리
  • 직접 만든 애플리케이션을 이미지(Docker Image)로 만들기 위한 5가지 방법이 존재
    • 기본 방법
    • 컨테이너 용량 줄인 방법
    • 컨테이너 내부에서 빌드하는 방법
    • 멀티 스테이지 빌드(Multi-Stage Build)
    • (추가) docker 계층화 방법

[ 기본 방법 ]

순서

  • 동작 순서
    1. 호스트에서 SpringBoot 빌드를 위한 Java 설치 (OpenJDK)
    2. Maven(빌드 툴)을 이용해서 실행 가능한 JAR 생성
    3. Dockerfile 실행
      • openjdk 설치
      • JAR 실행
    4. 만든 Docker Image 실행

1. 호스트에 Java 설치

/* host 환경이 리눅스라고 가정 */
$ yum install java-1.8.0-openjdk-devel -y

2. 실행 가능한 JAR 생성

/* 메이븐 실행을 위한 환경 설정을 자동화 하는 "메이븐 래퍼"의 실행권한 추가 */
$ chmod 700 mvnw
/* 빌드를 진행할 디렉터리를 비우고, JAR를 생성하는 명령 */
$ ./mvnw clean package

3. Dockerfile 실행

  • Dockerfile 실행
    • docker build의 -t 옵션 : <저장소 이름>/<이미지 이름>:<태그> 형태로 태그를 지정할 수 있는 옵션
    • 추가적으로, 같은 이미지로 만들 경우 하나의 공간사용하기 때문에 용량이 모두 같다
    • 그리고 Docker는 캐시(Cache) 시스템이 있어서 변경사항이 아닌 부분Cache되어 빠르게 빌드
$ docker build -t basic-img:1.0 .
  • (추가) Dockerfile 내용 확인
/* openjdk 설치 : 컨테이너 내부에서 SpringBoot App을 실행하려면 Java를 설치해야 함 */
FROM openjdk:8
/* LABEL을 통해 이미지에 부가적인 설명 추가 */
LABEL description="Echo IP Java Application"
/* 컨테이너를 구동할 때 호스트와 연결해야 하는 포트를 기록
   -> 정보만 알려줄 뿐, 실제 docker run 할때 -p 로 포트를 지정해줘야 함 */
EXPOSE 60431
/* 실행 가능한 JAR파일을 이미지의 경로인 /opt아래에 app-in-image.jar로 복사 */
COPY ./target/app-in-host.jar /opt/app-in-image.jar
/* /opt 디렉터리로 이동 -> cd명령어와 동일 */
WORKDIR /opt
/* 최종적으로, [] 대괄호 안에 있는 명령어 실행 */
ENTRYPOINT [ "java", "-jar", "app-in-image.jar" ]

4. Docker Image 실행

$ docker run -d -p 60431:80 --name basic-run --restart always basic-img
  • docker run 옵션
    • -d : 데몬으로 실행. 즉, 백그라운드로 실행
    • -p : 호스트의 60431 포트를 컨테이너의 80번 포트로 포트포워딩
    • --name : 컨테이너의 이름 지정
    • --restart : 컨테이너의 문제로 인해 재시작 되는 방법 지정

[ 컨테이너 용량 줄인 방법 ]

순서

  • 동작 순서
    1. 호스트에서 SpringBoot 빌드를 위한 Java 설치 (OpenJDK)
    2. Maven(빌드 툴)을 이용해서 실행 가능한 JAR 생성
    3. Dockerfile 실행
      • gcr.io/distroless/java:8 설치 - Java 실행을 위한 가벼운 이미지
      • JAR 실행
    4. 만든 Docker Image 실행

기본 방법과 차이점

  • Dockerfile 내용의 일부분 빼고는 기본 방법과 동일
  • DockerfileFROM에서 openjdk8 대신 경량화 Java 이미지 가져오기
/* gcr.io/distroless/java:8 설치 : Java 실행을 위한 가벼운 이미지 */
FROM gcr.io/distroless/java:8
LABEL description="Echo IP Java Application"
EXPOSE 60431
COPY ./target/app-in-host.jar /opt/app-in-image.jar
WORKDIR /opt
ENTRYPOINT [ "java", "-jar", "app-in-image.jar" ]

[ 컨테이너 내부에서 빌드하는 방법 ]

순서

  • 핵심
    • 호스트에서 java를 설치 & 실행 가능한 JAR를 만드는 과정
      => 컨테이너 내부에서 실행
  • 동작 순서
    1. Dockerfile 실행
      • openjdk8 설치
      • git에서 springboot 소스 가져오기
      • 실행 가능한 JAR 생성
      • JAR 실행
    2. 만든 Docker Image 실행

1. Dockerfile 실행

  • (추가) Dockerfile 내용
FROM openjdk:8
LABEL description="Echo IP Java Application"
EXPOSE 60433
/* 컨테이너 내부에서 git clone으로 springboot 소스 가져오기 */
RUN git clone https://github.com/iac-source/inbuilder.git
WORKDIR inbuilder
/* 메이븐을 통해 프로젝트 build -> 실행 가능한 JAR 생성 */
RUN chmod 700 mvnw
RUN ./mvnw clean package
/* 생성된 JAR를 생성될 이미지의 경로로 복사 */
RUN mv target/app-in-host.jar /opt/app-in-image.jar
WORKDIR /opt
ENTRYPOINT [ "java", "-jar", "app-in-image.jar" ]
  • 결과
    • 컨테이너 내부에서 프로젝트 빌드 실행
      -> 앞선 방법보다 매우 편해졌다. 하지만, 용량이 크다
      -> 왜냐하면, 프로젝트 빌드 과정중에 나온 중간 파일들 모두 최종 이미지에 포함되기 때문!
      => 멀티 스테이지 빌드 방법으로 이 단점을 개선 가능!
  • 이미지 크기 증가
    • 138MB -> 633MB

2. Docker Image 실행

$ docker run -d -p 60431:80 --name basic-run --restart always basic-img

[ 멀티 스테이지 빌드 ]

순서

  • 동작 순서
    1. Dockerfile 실행
      • [image 1]
        • openjdk8 설치
        • git에서 springboot 소스 가져오기
        • 실행 가능한 JAR 생성
      • [image 2]
      • gcr.io/distroless/java:8 설치 - Java 실행을 위한 가벼운 이미지
      • 실행 가능한 JAR 파일을 최종 /opt 아래에 app-in-image.jar로 저장
      • JAR 실행
    2. 만든 Docker Image 실행
  • 핵심
    • 실행 가능한 JAR로 만드는 프로젝트 빌드 과정별도의 이미지에서 수행
    • 생성된 실행 가능한 JAR 파일실제 컨테이너 이미지에 포함
      (기존 프로젝트 빌더용 이미지삭제해 주어야 한다)

1. Dockerfile 실행

  • (추가) Dockerfile 내용
// 1단계 : Java 소스를 빌드해 JAR로 만듦
FROM openjdk:8 AS int-build // openjdk 이미지에 int-build라는 별칭을 붙임
LABEL description="Java Application builder"
RUN git clone https://github.com/iac-source/inbuilder.git
WORKDIR inbuilder
RUN chmod 700 mvnw
RUN ./mvnw clean package

// 2단계 : 빌드된 JAR을 경량화 이미지에 복사
FROM gcr.io/distroless/java:8 // 경량화 이미지
LABEL description="Echo IP Java Application"
EXPOSE 60434
// --from으로 앞에 별칭을 붙인 openjdk 이미지를 선택
// 실행 가능한 JAR파일을 가져온다
COPY --from=int-build inbuilder/target/app-in-host.jar /opt/app-in-image.jar
WORKDIR /opt
ENTRYPOINT [ "java", "-jar", "app-in-image.jar" ]
  • 결과
    • 프로젝트 빌드용 이미지에서 만든 JAR파일을 실제 사용할 경량화 이미지에 복사
      -> 결국 최종 이미지는 크기가 작다
  • 이미지 크기 최적화
    • 633MB -> 138MB
  • 추가
    • 프로젝트 빌드 목적으로 사용한 Docker Image은 댕글링 이미지로 생성된다
      (댕글링 이미지 : 이름이 없는 이미지, 이름이 <none> 으로 표시)
    • 댕글링 이미지 삭제
$ docker rmi $(docker images -f dangling=true -q)

2. Docker Image 실행

$ docker run -d -p 60431:80 --name basic-run --restart always basic-img

[ Docker 계층화 ]

  • 아마, 대부분 앞에 multi-state build 형식을 많이 사용할 것이다
  • Docker의 계층화 & 캐시 특징으로 변경된 계층만 빌드하는 효율적인 방법이 있다
    • 빌드 툴(gradlw, maven) 을 통한 계층형 JAR 생성
    • Docker Cache를 통해서 변경된 부분만 build
profile
Developer & PhotoGrapher

0개의 댓글