앞서 첫날에는 MSA, Docker에 관해서 알아보았다. 오늘은 그 중에서 프로젝트 안에 있는 Dockerfile을 공부했다.
프로젝트 예시는 앞으로 "eShop on Dapr"라는 것을 사용할 예정이다.
명령어를 알아보기 이전에, Dockerfile을 바탕으로 Docker image가 만들어 진다는 사실을 알았다.
그 방법을 먼저 간단히 살펴보자.
docker build -f /path/to/a/Dockerfile -t testImage:1.0
뒤의 내용들은 옵션이며 내용은 아래와 같다.
어느 이미지에서 시작할 건지를 정해주는 명령어.
표현식은 2가지 방법이 존재한다.
- Shell form (default /bin/sh -c on LINUX, cmd /S /C on Windows)
RUN apt-get update -yq \ && apt-get install curl gnupg -yq \ && curl -sL https://deb.nodesource.com/setup_10.x | bash \ && apt-get install nodejs -yq
- exec form
RUN ["/bin/bash", "-c", "echo hello"]
CMD ["dotnet", "WebSPA.dll"]
ENTRYPOINT ["dotnet", "WebSPA.dll"]
둘의 차이점은 아래와 같다.
- CMD = 추가적인 명령어에 따라서 명령어를 수정해서 실행한다.
docker run --name <container-name> <image-name> > This is a test docker run --name <container-name> <image-name> echo "Hello" > Hello
- ENTRYPOINT = 추가 명령어 존재와 상관없이 무조건 실행
docker run --name <container-name> <image-name> > Hello world docker run --name <container-name> <image-name> ME > Hello ME
명령 옵션들은 아래와 같다.
옵션 | 설명 | 기본값 |
---|---|---|
--interval=Duration | 헬스 체크 간격 | 30s |
--timeout=Duration | 타임 아웃 | 30s |
--retries=N | 타임아웃횟수 | 3 |
상태값은 아래와 같다.
EXIT Code | 설명 |
---|---|
0: success | 컨테이너 정상 |
1: unhealthy | 컨테이너 작동 불량 |
2: starting | 위의 exit code를 사용하지 않음 |
container의 상태는 아래와 같이 확인가능하다.
docker container inspect [container name]
위의 옵션들을 적용한 예시이다.
HEALTHCHECK --interval=10s --timeout=3s CMD curl -f http://127.0.0.1/ || exit 1
( 10초마다 위 주소를 체크 하는데, 3초 이상이 걸리거나, 3번의 재시도가 실패하면 unhealthy 상태로 변경해라 )
둘의 차이는 아래와 같다.
COPY <src>... <destination>
COPY ["<src>",... "<destination>"]
ADD <src>... <destination>
ADD ["<src>",... "<destination>"]
- COPY = container 안으로 정확히 복사만 가능하다.
- ADD = container 안으로 복사를 하면서 tar 파일처럼 압축된 파일을 해제하거나 하는 기능이 들어있다.
보통 COPY가 투명하게 과정이 보이기 때문에 default로 사용한다.
( COPY는 오직 container 안의 local file들을 단순히 COPY하는 것 밖에 못한다. Docker client로 부터 적혀진 디렉토리에 파일을 추가한다. )
( 반면 ADD는 remote URL을 지원하거나, local-only tar 파일을 추출하는 것 같은 직관적이지 않은 부가기능을 제공한다. )
둘의 차이는 아래와 같다.
- ENV = Dockerfile & container 안에서 변수로 사용가능
- ARG = Dockerfile에서만 변수로 사용가능
LABEL title="webserver"
LABEL version="2.0"
( 보통 평균적으로 많이 쓰는 port를 사용, Apache: 80, MongoDB: 27017 )
하지만 이 명령 자체가 port를 실행하여 listening 상태로 올려주지는 않기 때문에 실제로 port를 열기 위해서는 container run -p 로 컨테이너를 실행할때 옵션으로 실행시켜 줘야 함
dockerfile을 작성하는 사람과 container를 실행할 사람 사이에서 공개port를 알려주기 위한 구문
FROM centos:7
VOLUME ["/var/log/", "/data/"]
/var/log 의 데이터와, /data/ 의 데이터는 host OS의 /var/lib/docker/volumes/ 에 저장된다.
eShop on Dapr의 Dockerfile 중에서 2가지를 위에서 공부한 내용을 바탕으로 읽어보자.
// src/Services/Basket/Basekt.API/Dockerfile
ARG NET_IMAGE=5.0-buster-slim // NET_IMAGE라는 dockerfile에서만 쓸 수 있는 변수 설정
FROM mcr.microsoft.com/dotnet/aspnet:${NET_IMAGE} AS base // 시작할 image를 정하고 이름을 base로 설정
WORKDIR /app // app에서 시작하고
EXPOSE 80 // 80 port로 진행
FROM mcr.microsoft.com/dotnet/sdk:${NET_IMAGE} AS build // image 를 build라고 명명
WORKDIR /src // 디렉토리 변경
// <src>를 <destination>에 COPY 함 (1번만 진행)
COPY ["src/ApiGateways/Aggregators/Web.Shopping.HttpAggregator/Web.Shopping.HttpAggregator.csproj", "src/ApiGateways/Aggregators/Web.Shopping.HttpAggregator/"]
COPY ["src/BuildingBlocks/EventBus/EventBus/EventBus.csproj", "src/BuildingBlocks/EventBus/EventBus/"]
COPY ["src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj", "src/BuildingBlocks/EventBus/IntegrationEventLogEF/"]
COPY ["src/BuildingBlocks/WebHost/WebHost.Customization/WebHost.Customization.csproj", "src/BuildingBlocks/WebHost/WebHost.Customization/"]
COPY ["src/Services/Basket/Basket.API/Basket.API.csproj", "src/Services/Basket/Basket.API/"]
COPY ["src/Services/Catalog/Catalog.API/Catalog.API.csproj", "src/Services/Catalog/Catalog.API/"]
COPY ["src/Services/Identity/Identity.API/Identity.API.csproj", "src/Services/Identity/Identity.API/"]
COPY ["src/Services/Ordering/Ordering.API/Ordering.API.csproj", "src/Services/Ordering/Ordering.API/"]
COPY ["src/Services/Payment/Payment.API/Payment.API.csproj", "src/Services/Payment/Payment.API/"]
COPY ["src/Web/WebSPA/WebSPA.csproj", "src/Web/WebSPA/"]
COPY ["src/Web/WebStatus/WebStatus.csproj", "src/Web/WebStatus/"]
COPY ["docker-compose.dcproj", "./"]
COPY ["NuGet.config", "./"]
COPY ["eShopOnDapr.sln", "./"]
RUN dotnet restore "eShopOnDapr.sln" // 이 명령어를 실행함 (eShopOnDapr.sln에 있는 종속성 및 모든 도구 복원)
COPY . . // 현재까지 진행된 모든 자료를 현재 디렉토리에 복사
WORKDIR "/src/src/Services/Basket/Basket.API" // 작업경로 변경
FROM build AS publish // 위에 정의한 build를 publish로 재정의
RUN dotnet publish --no-restore "Basket.API.csproj" -c Release -o /app/publish // 뒤의 명령어를 실행함 (복원을 실행하지 않고, Basket.API.csproj 를 Release 구성으로 /app/publish 에 출력)
FROM base AS final // 위에서 정의한 base image를 final로 재정의
WORKDIR /app // 작업경로 변경
COPY --from=publish /app/publish . // publish로 부터 /app/publish 의 내용을 현재 경로에 복사
ENTRYPOINT ["dotnet", "Basket.API.dll"] // dotnet Basket.API.dll 실행
// src/Web/WebSPA/Dockerfile
ARG NODE_IMAGE=15.12.0-alpine3.12 // Node_IMAGE란 dockerfile 변수
ARG NET_IMAGE=5.0-buster-slim // NET_IMAGE란 dockerfile 변수
FROM mcr.microsoft.com/dotnet/aspnet:${NET_IMAGE} AS base // 해당 이미지를 불러오고 base라고 명명
WORKDIR /app // 디렉토리 /app으로 변경
EXPOSE 80 // 80 port를 사용함을 명시
RUN apt-get update -yq \
&& apt-get install curl gnupg -yq \
&& curl -sL https://deb.nodesource.com/setup_10.x | bash \
&& apt-get install nodejs -yq // node js를 설치함
FROM node:${NODE_IMAGE} as node-build // node base image를 불러옴
WORKDIR /web // 디렉토리 변경
COPY src/Web/WebSPA/package.json .
COPY src/Web/WebSPA/package-lock.json .
COPY src/Web/WebSPA . // 각각의 파일들을 현재의 위치에 복사
RUN npm i npm@6.14.11 -g && npm update && npm install && npm run build:prod // npm을 글로벌로 설치하고 npm 사용된 library 들을 설치하고 build 함
FROM mcr.microsoft.com/dotnet/sdk:${NET_IMAGE} AS build // base image를 불러오고 이름을 build라고 함
RUN apt-get update -yq \
&& apt-get install curl gnupg -yq \
&& curl -sL https://deb.nodesource.com/setup_10.x | bash \
&& apt-get install nodejs -yq // node js를 설치한다.
WORKDIR /src // 디렉토리 변경
COPY ["src/ApiGateways/Aggregators/Web.Shopping.HttpAggregator/Web.Shopping.HttpAggregator.csproj", "src/ApiGateways/Aggregators/Web.Shopping.HttpAggregator/"]
COPY ["src/BuildingBlocks/EventBus/EventBus/EventBus.csproj", "src/BuildingBlocks/EventBus/EventBus/"]
COPY ["src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj", "src/BuildingBlocks/EventBus/IntegrationEventLogEF/"]
COPY ["src/BuildingBlocks/WebHost/WebHost.Customization/WebHost.Customization.csproj", "src/BuildingBlocks/WebHost/WebHost.Customization/"]
COPY ["src/Services/Basket/Basket.API/Basket.API.csproj", "src/Services/Basket/Basket.API/"]
COPY ["src/Services/Catalog/Catalog.API/Catalog.API.csproj", "src/Services/Catalog/Catalog.API/"]
COPY ["src/Services/Identity/Identity.API/Identity.API.csproj", "src/Services/Identity/Identity.API/"]
COPY ["src/Services/Ordering/Ordering.API/Ordering.API.csproj", "src/Services/Ordering/Ordering.API/"]
COPY ["src/Services/Payment/Payment.API/Payment.API.csproj", "src/Services/Payment/Payment.API/"]
COPY ["src/Web/WebSPA/WebSPA.csproj", "src/Web/WebSPA/"]
COPY ["src/Web/WebStatus/WebStatus.csproj", "src/Web/WebStatus/"]
COPY ["docker-compose.dcproj", "./"]
COPY ["NuGet.config", "./"]
COPY ["eShopOnDapr.sln", "./"] // 해당 디렉토리에 앞의 내용들을 복사
RUN dotnet restore "eShopOnDapr.sln" // 명령어 실행 (eShopOnDapr.sln에 있는 종속성 및 모든 도구 복원)
COPY . . // 모든 내용을 현재 디렉토리에 복사
COPY --from=node-build /web/wwwroot /src/src/Web/WebSPA/wwwroot/ // 위 node-build image layer로 부터 /web/wwwroot 를 /src/src/Web/WebSPA/wwwroot/로 복사
WORKDIR /src/src/Web/WebSPA // 디렉토리 변경
RUN dotnet publish --no-restore -c Release -o /app // 뒤의 명령어를 실행함 (복원을 실행하지 않고, Basket.API.csproj 를 Release 구성으로 /app 에 출력)
FROM build AS publish // build image layer의 이름을 publish로 변경
FROM base AS final // base image layer의 이름을 final로 변경
WORKDIR /app // 디렉토리 변경
COPY --from=publish /app . // publish image layer로 부터 /app 의 내용물을 현재 위치에 복사
ENTRYPOINT ["dotnet", "WebSPA.dll"] // dotnet WebSPA.dll 실행