Docker로 배포하기
컨테이너에 담아 올립니다.
돌아갈 자리까지 함께 담습니다
5.1에서 환경을 나눴습니다. 그런데 운영 서버에 .NET 런타임이 깔려 있어야 앱이 돕니다. 버전이 다르거나 없으면 뜨지 않습니다.
컨테이너는 앱과 런타임, 그리고 필요한 것들을 한 덩어리로 묶습니다. 그 덩어리를 이미지라고 부르고, 그것을 띄운 것이 컨테이너입니다. 어느 기계에서 띄우든 안쪽은 같습니다.
- "제 기계에서는 되는데" 가 줄어듭니다. 런타임 버전과 설치된 것이 이미지에 박혀 있습니다.
- 올리는 것이 파일 묶음이 아니라 이미지 하나입니다. 되돌릴 때도 앞 이미지를 띄우면 됩니다.
- 같은 이미지를 여러 개 띄우기 쉽습니다.
Dockerfile 없이 만들기
.NET SDK 에 이미지를 만드는 기능이 들어 있습니다. Dockerfile 을 적지 않아도 되고, Docker 가 깔려 있지 않아도 됩니다. 파일로 뽑아 두었다가 나중에 올릴 수 있습니다.
dotnet publish -c Release --os linux --arch x64 \ -t:PublishContainer \ -p:ContainerArchiveOutputPath=./api.tar.gz
이 단원의 명령은 브라우저가 아니라 여러분의 터미널에서 돌립니다.
만들어진 파일을 열어 보면 층이 여럿입니다. 마지막 하나가 우리 앱이고 나머지는 기본 이미지입니다.
0926a8eb…/layer.tar
fec4a809…/layer.tar
0473cde9…/layer.tar
…
28e60b7b….json // 이미지 설정
Docker 가 있다면 곧바로 올릴 수도 있습니다. 레지스트리 주소를 적으면 그쪽으로 갑니다.
dotnet publish -c Release --os linux --arch x64 -t:PublishContainer \ -p:ContainerRegistry=myregistry.azurecr.io \ -p:ContainerRepository=todo-api \ -p:ContainerImageTag=1.0.3
이미지의 거의 전부가 기본 이미지입니다
우리 앱을 그냥 게시하면 얼마나 될까요. 재어 보았습니다.
dotnet publish -c Release --os linux --arch x64 -o ./out ls ./out
앱은 121KB 이고 이미지는 91.5MB 입니다. 나머지는 리눅스와 .NET 런타임입니다. 여기서 두 가지가 따라옵니다.
- 레이어 캐시가 중요합니다. 기본 이미지는 바뀌지 않으므로 한 번 받아 두면 다시 받지 않습니다. 배포할 때마다 오가는 것은 앱 층뿐입니다.
- 기본 이미지를 무엇으로 두느냐가 크기를 정합니다. 우리 코드를 줄여도 표가 나지 않습니다.
더 작게 만들려면
런타임을 앱 안에 넣고(자체 포함) 사용하지 않는 것을 걷어내면(트리밍), .NET 이 들어 있지 않은 더 작은 기본 이미지를 사용할 수 있습니다.
dotnet publish -c Release --os linux --arch x64 -t:PublishContainer \ --self-contained true -p:PublishTrimmed=true \ -p:ContainerBaseImage=mcr.microsoft.com/dotnet/runtime-deps:10.0 \ -p:ContainerArchiveOutputPath=./slim.tar.gz
대신 잃는 것이 있습니다. 트리밍은 실제로 부르는 것만 남기므로, 반사로 형식을 찾는 코드가 실행할 때 깨질 수 있습니다. 오류가 빌드가 아니라 실행에서 나므로 돌려 보고 확인해야 합니다.
서버가 몇 대뿐이라면 91.5MB 로 두는 편이 안전합니다. 이미지를 자주 내려받는 곳이라면 줄일 값이 있습니다.
Dockerfile 로 적을 때
빌드 과정을 직접 정해야 한다면 Dockerfile 을 적습니다. 두 단계로 나누는 것이 관례입니다.
# ── 1단계: 빌드 (SDK 가 있는 이미지) ── FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src # 프로젝트 파일만 먼저 넣고 복원합니다. # 소스만 바뀌면 이 층은 캐시가 그대로 쓰입니다. COPY Api/Api.csproj Api/ RUN dotnet restore Api/Api.csproj COPY . . RUN dotnet publish Api/Api.csproj -c Release -o /app # ── 2단계: 실행 (런타임만 있는 이미지) ── FROM mcr.microsoft.com/dotnet/aspnet:10.0 WORKDIR /app COPY --from=build /app . ENV ASPNETCORE_ENVIRONMENT=Production ENV ASPNETCORE_HTTP_PORTS=8080 EXPOSE 8080 ENTRYPOINT ["dotnet", "Api.dll"]
COPY 를 두 번 나눈 것이 요령입니다. Docker 는
줄 단위로 캐시합니다. 소스만 고쳤을 때
restore 를 다시 하지 않으려면, 프로젝트 파일을 먼저
넣고 복원한 뒤에 나머지를 넣어야 합니다.
.dockerignore 도 함께 둡니다. 없으면 bin·obj 가 통째로 들어가 캐시를 매번 깨뜨립니다.
bin/ obj/ .git/ **/appsettings.Development.json
마지막 줄을 눈여겨보십시오. 4.4에서 본 대로 개발용 설정은 이미지에 넣지 않습니다. 5.1의 환경 변수로 정합니다.
포트와 사용자
컨테이너 기본 포트가 80에서 8080으로 바뀌었습니다. 그리고 이미지가 루트가 아닌 사용자로 돌도록 바뀌었습니다. 1024보다 작은 포트는 루트가 아니면 열 수 없어 함께 바뀐 것입니다.
옛 예제를 그대로 옮겨
EXPOSE 80 만 적으면 연결되지 않습니다. 포트는
ASPNETCORE_HTTP_PORTS 로 정합니다(예전의
ASPNETCORE_URLS 도 그대로 동작합니다).
직접 해보기
1.2에서 만든 프로젝트로 이미지를 만들어 파일로 뽑고, 크기를 재어보세요. Docker 를 깔지 않아도 됩니다.
아래 Dockerfile 은 소스를 한 글자만 고쳐도 복원부터 다시 합니다. 왜 그런지와 어떻게 고칠지 적어보세요.
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src COPY . . RUN dotnet restore RUN dotnet publish -c Release -o /app
이미지를 띄웠고 로그에 Application started 까지 나왔습니다. 그런데 브라우저로 열리지 않습니다. 무엇을 살펴야 할지 세 가지 이상 적어보세요.
- 컨테이너는 앱과 런타임을 한 덩어리로 묶어, 어느 기계에서 띄우든 안쪽이 같게 합니다.
- SDK 에 이미지 만들기가 들어 있어 Dockerfile 도 Docker 도 없이 만들 수 있습니다.
- 앱은 121KB 인데 이미지는 91.5MB 입니다. 거의 전부가 기본 이미지이므로 레이어 캐시가 중요합니다.
- Dockerfile 은 빌드와 실행을 두 단계로 나누고, 프로젝트 파일을 먼저 넣어 복원 캐시를 지킵니다.
- .NET 8 부터 기본 포트가 8080 이고 루트가 아닌 사용자로 돕니다.