마찬가지로 내용이 변경되었을 때만 처음부터 실행하도록 하고, 패키지나 라이브러리들은 이전에 받아둔 결과물을 재사용할 수 있지 않을까?
5. EXPOSE
정의
컨테이너가 런타임 시 사용할 네트워크 포트를 알려준다.
실제로 포트가 외부로 열리는(Publish) 것은 아니고 빌드자와 실행자 사이에서 일종의 메모 혹은 문서 역할을 한다.
docker network에 연결된 컨테이너는 특정 포트를 열거나 게시할 필요 없이 모든 포트를 통해 컨테이너 간 서로 통신할 수 있지만,
호스트 시스템에서 포트 리디렉션을 설정3하려면 실행할 때 docker run -p 80:80/tcp처럼 -p 옵션을 따로 써야한다.
사용 예시
# 형식EXPOSE <port>[/<protocol>...]# myEXPOSE 8080
(참고)
/protocol
TCP 또는 UDP 프로토콜 중 하나를 지정할 수 있다. (default: TCP)
EXPOSE를 만든 이유
docker 컨테이너 내부에서는 수많은 포트가 열려 있을 수 있지만, 외부와 통신할 수 있는 통로는 명시적으로 선언하고 연결한 것만 사용하여 보안성을 강화
필수적으로 사용하진 않아도 되지만 테스트 때 호스트에 자동 연결된다거나(-P) 오케스트레이션 툴(쿠버네티스 등)이나 클라우드 서비스에서 이 정보를 참고해 자동으로 네트워크 설정을 구성할 수 있기 때문에 권장되는 사항임
6. ENTRYPOINT & CMD
두 명령어는 모두 이미지에서 컨테이너 실행 시 어떤 명령이 실행될지 설정한다.
Dockerfile에는 두 명령어 중 하나 이상을 지정해야 한다.
ENTRYPOINT에는 실행 파일을, CMD에는 기본 옵션을 넣는 패턴이 가장 많이 사용된다.
ENTRYPOINT
컨테이너를 ==실행 파일로 사용할 때는 ENTRYPOINT를 사용==해야 한다.
컨테이너가 실행될 때 반드시 실행되어야 하는 기본 실행 파일이다. (ex: java, python, git, nginx)
docker는 ENTRYPOINT 또한 두 가지 형태를 제공하지만 이 명령어는 특별히 실행 형태를 더 권장한다.
Exec form (권장)
프로그램이 컨테이너 내에서 직접 실행되어 PID 1로 실행됨
Linux 신호를 직접 받아 안전하게 실행 가능
ex) docker stop을 눌렀을 때 종료 신호(SIGTERM)를 직접 받아서 안전하게 꺼짐
CMD에 쓴 내용을 인자로 그대로 받아들임
Shell form
/bin/sh -c 실행 후 프로그램이 쉘 내부에서 실행되어 쉘의 자식으로 뜸
쉘이 신호를 가로막기 때문에 직접 받을 수 없음
ex) docker stop 을 명령해도 신호를 못 받고 10초 후 강제 종료(SIGKILL) 되는 문제 발생
CMD에 쓴 기본 인자들이 무시됨
CMD
실행 중인 컨테이너에 대한 기본 옵션 값을 제공하는 데 사용된다. (ex: --version, help)
기본값에는 실행 파일이 포함될 수도 있고, 생략할 수도 있다.
실행 파일을 생략하는 경우에는 ENTRYPOINT 명령어도 함께 지정해야 한다.
Dockerfile에는 CMD 명령어가 하나만 있을 수 있다. (두 개 이상 나열 시 마지막 명령어만 유효)
컨테이너를 다른 인수로 실행하면 CMD는 무시된다.
FROM ubuntuENTRYPOINT ["top", "-b"]CMD ["-c"]
위의 경우 기본적으로 top -b -c 실행
사용자가 실행 시 뒤에 -H를 붙이면 -c는 무시됨 →top -b -H 실행
docker는 CMD 또한 두 가지 형태를 제공하지만 ENTRYPOINT와 결합했을 때 서로 다른 형식을 사용한다면 의도치 않게 쉘 명령어가 파라미터로 전달되는 이상한 결과가 나올 수 있기 때문에 똑같이 실행 형식을 사용하는 것이 표준이다.
이미 CMD가 실행될 상태이더라도 그 후에 실행될 Dockerfile에 ENTRYPOINT 명령어가 새롭게 정의된다면 기존 베이스 이미지에 있던 CMD는 자동으로 삭제(초기화) 된다.
만약 인자가 필요하다면 현재 Dockerfile에서 CMD를 다시 써줘야 한다.
3. 전체 코드
위 명령어 조건에 맞추어 내 프로젝트를 기준으로 기본적인 Dockerfile을 작성했다.
# 1. 빌드 환경 설정 (Java 17 버전 사용)FROM gradle:8.12-jdk17 AS builderWORKDIR /app# 2. 전체 소스 코드 복사COPY . .# 3. 컨테이너 내부에서 직접 자바 파일을 컴파일 & 빌드RUN ./gradlew clean build# 3. 실행할 포트 문서화 (Spring Boot 기본값 8080)EXPOSE 8080# 4. 빌드된 jar 파일을 실행 (보통 'build/libs/프로젝트명-SNAPSHOT.jar' 경로에 생성됨)ENTRYPOINT ["java", "-jar", "build/libs/app.jar"]# CMD 생략 가능
현재 작성한 Dockerfile은 이미지가 빌드되어 실행되도록 하는 가장 기본적인 구성으로 작성되어 있다.
다음 글에서는 이미지의 빌드 효율을 높이거나 용량을 최적화 하는 등 다양한 설정을 변경하여 직접 이미지를 만들며 비교해보겠다.
Footnotes
도커의 각 명령어는 독립적으로 실행되기 때문에 1번 줄에서 디렉토리 이동을 하더라도, WORKDIR을 사용한게 아니라면 다음 줄이 실행될 때는 다시 원래 위치로 돌아간다. ↩