.NET 언어별 가이드

.NET 언어별 가이드

Docker를 사용해 .NET 어플리케이션을 컨테이너화하는 방법을 배워볼게요.

출처: 문서

본문

.NET 시작하기 가이드는 Docker를 사용해 컨테이너화된 .NET 어플리케이션을 만드는 방법을 알려줘요. 이 가이드에서 배울 내용:

  • .NET 어플리케이션 컨테이너화 및 실행하기
  • 컨테이너를 사용해 .NET 어플리케이션을 개발할 로컬 환경 설정하기
  • 컨테이너를 사용해 .NET 어플리케이션 테스트 실행하기

.NET 시작하기 모듈을 완료하면, 이 가이드에 제공된 예제와 지침에 따라 나만의 .NET 어플리케이션을 컨테이너화할 수 있어야 해요.

.NET 어플리케이션 컨테이너화하기

사전 요구사항

  • 최신 버전의 Docker Desktop 을 설치했어야 해요.
  • git 클라이언트 가 있어야 해요. 이 섹션의 예제는 명령줄 기반 git 클라이언트를 사용하지만, 어떤 클라이언트든 사용할 수 있어요.

개요

이 섹션은 .NET 어플리케이션을 컨테이너화하고 실행하는 과정을 안내해요.

샘플 어플리케이션 가져오기

이 가이드에서는 미리 빌드된 .NET 어플리케이션을 사용할 거예요. 이 어플리케이션은 Docker Blog 기사인 Docker Desktop으로 멀티 컨테이너 .NET 앱 빌드하기 에서 빌드한 어플리케이션과 비슷해요.

터미널을 열고 작업할 디렉터리로 이동한 뒤, 다음 명령을 실행해 리포지토리를 클론해주세요.

$ git clone https://github.com/docker/docker-dotnet-sample

Docker 자산 만들기

이제 어플리케이션이 있으니 컨테이너화하는 데 필요한 Docker 자산을 만들 수 있어요. 공식 .NET 이미지나 Docker Hardened Images(DHI) 중에서 선택할 수 있어요.

팁: Gordon 은 내 어플리케이션에 맞춘 .dockerignore 를 제안해줘요.

Docker Hardened Images(DHI) 는 Docker가 유지 관리하는 최소화되고 안전하며 프로덕션 준비가 된 컨테이너 베이스·어플리케이션 이미지예요. DHI 이미지는 보안 개선을 위해 권장돼요. 취약점을 줄이고 규정 준수를 단순화하도록 설계됐어요.

Docker Hardened Images 사용

.NET용 Docker Hardened Images(DHI)는 Docker Hardened Images 카탈로그 에서 사용할 수 있어요. Docker Hardened Images는 구독 없이도 누구나 무료로 사용할 수 있어요. DHI 레지스트리에 로그인한 후 다른 Docker 이미지처럼 내려받아 사용할 수 있어요. 자세한 내용은 DHI 퀵스타트 가이드 를 참고하세요.

DHI 레지스트리에 로그인해주세요:

$ docker login dhi.io

.NET SDK DHI를 내려받아주세요(사용 가능한 버전은 카탈로그 확인):

$ docker pull dhi.io/dotnet:10-sdk

ASP.NET Core 런타임 DHI를 내려받아주세요(사용 가능한 버전은 카탈로그 확인):

$ docker pull dhi.io/aspnetcore:10

docker-dotnet-sample 디렉터리에 다음 파일들을 만들어주세요.

Dockerfile:

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM dhi.io/dotnet:10-sdk AS build
ARG TARGETARCH
COPY . /source
WORKDIR /source/src
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
  dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app

FROM dhi.io/aspnetcore:10 AS final
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "myWebApp.dll"]

참고: DHI 런타임 이미지는 이미 비루트 사용자( nonroot , UID 65532)로 실행되므로 Dockerfile에서 사용자를 만들거나 USER 를 지정할 필요가 없어요. 이는 공격 표면을 줄이고 구성을 단순화해요.

compose.yaml:

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/
# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
  server:
    build:
      context: .
      target: final
    ports:
      - 8080:8080
# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker compose up`.
# depends_on:
#   db:
#     condition: service_healthy
# db:
#   image: postgres
#   restart: always
#   user: postgres
#   secrets:
#     - db-password
#   volumes:
#     - db-data:/var/lib/postgresql/data
#   environment:
#     - POSTGRES_DB=example
#     - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
#   expose:
#     - 5432
#   healthcheck:
#     test: [ "CMD", "pg_isready" ]
#     interval: 10s
#     timeout: 5s
#     retries: 5
# volumes:
#   db-data:
# secrets:
#   db-password:
#     file: db/password.txt

.dockerignore:

# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose.y*ml
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

공식 .NET 10 이미지 사용

docker-dotnet-sample 디렉터리에 다음 파일들을 만들어주세요.

Dockerfile:

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
ARG TARGETARCH
COPY . /source
WORKDIR /source/src
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
  dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app

FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
WORKDIR /app
COPY --from=build /app .
ARG UID=10001
RUN adduser \
  --disabled-password \
  --gecos "" \
  --home "/nonexistent" \
  --shell "/sbin/nologin" \
  --no-create-home \
  --uid "${UID}" \
  appuser
USER appuser
ENTRYPOINT ["dotnet", "myWebApp.dll"]

compose.yaml:

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/
# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
  server:
    build:
      context: .
      target: final
    ports:
      - 8080:8080
# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker compose up`.
# depends_on:
#   db:
#     condition: service_healthy
# db:
#   image: postgres
#   restart: always
#   user: postgres
#   secrets:
#     - db-password
#   volumes:
#     - db-data:/var/lib/postgresql/data
#   environment:
#     - POSTGRES_DB=example
#     - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
#   expose:
#     - 5432
#   healthcheck:
#     test: [ "CMD", "pg_isready" ]
#     interval: 10s
#     timeout: 5s
#     retries: 5
# volumes:
#   db-data:
# secrets:
#   db-password:
#     file: db/password.txt

.dockerignore:

# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose.y*ml
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

이제 docker-dotnet-sample 디렉터리에 다음 내용이 있어야 해요.

├── docker-dotnet-sample/
│   ├── .git/
│   ├── src/
│   ├── .dockerignore
│   ├── compose.yaml
│   ├── Dockerfile
│   └── README.md

이 파일들에 대해 더 알려면 Dockerfile , .dockerignore , compose.yaml 을 참고하세요.

어플리케이션 실행하기

docker-dotnet-sample 디렉터리 안에서 터미널에 다음 명령을 실행해주세요.

$ docker compose up --build

브라우저를 열고 http://localhost:8080 에서 어플리케이션을 확인해주세요. 간단한 웹 어플리케이션이 보여야 해요.

터미널에서 ctrl + c 를 눌러 어플리케이션을 중지해주세요.

백그라운드에서 어플리케이션 실행

-d 옵션을 추가하면 터미널에서 분리된 상태로 어플리케이션을 실행할 수 있어요. docker-dotnet-sample 디렉터리 안에서 터미널에 다음 명령을 실행해주세요.

$ docker compose up --build -d

브라우저를 열고 http://localhost:8080 에서 어플리케이션을 확인해주세요. 간단한 웹 어플리케이션이 보여야 해요.

터미널에서 다음 명령으로 어플리케이션을 중지해주세요.

$ docker compose down

Compose 명령에 대한 자세한 내용은 Compose CLI 참조 를 참고하세요.

.NET 개발에 컨테이너 사용하기

사전 요구사항

.NET 어플리케이션 컨테이너화하기 를 완료하세요.

개요

이 섹션에서는 컨테이너화된 어플리케이션을 위한 개발 환경을 설정하는 방법을 배워요. 여기에는 다음이 포함돼요:

  • 로컬 데이터베이스 추가 및 데이터 영속화
  • 코드를 편집·저장할 때 Compose가 실행 중인 Compose 서비스를 자동으로 업데이트하도록 구성하기
  • .NET Core SDK 도구와 의존성이 포함된 개발 컨테이너 만들기

어플리케이션 업데이트

이 섹션은 업데이트된 .NET 어플리케이션이 들어 있는 docker-dotnet-sample 리포지토리 의 다른 브랜치를 사용해요. 업데이트된 어플리케이션은 .NET 어플리케이션 컨테이너화하기 에서 클론한 리포지토리의 add-db 브랜치 에 있어요.

업데이트된 코드를 얻으려면 add-db 브랜치를 체크아웃해야 해요. .NET 어플리케이션 컨테이너화하기 에서 내가 만든 변경 사항은 이 섹션을 위해 스태시할 수 있어요. 터미널에서 docker-dotnet-sample 디렉터리에 다음 명령을 실행해주세요.

이전 변경 사항을 스태시해주세요.

$ git stash -u

업데이트된 어플리케이션이 있는 새 브랜치를 체크아웃해주세요.

$ git checkout add-db

add-db 브랜치에서는 .NET 어플리케이션만 업데이트됐어요. Docker 자산은 아직 업데이트되지 않았어요.

이제 docker-dotnet-sample 디렉터리에 다음이 있어야 해요.

├── docker-dotnet-sample/
│   ├── .git/
│   ├── src/
│   │   ├── Data/
│   │   ├── Models/
│   │   ├── Pages/
│   │   ├── Properties/
│   │   ├── wwwroot/
│   │   ├── appsettings.Development.json
│   │   ├── appsettings.json
│   │   ├── myWebApp.csproj
│   │   └── Program.cs
│   ├── tests/
│   │   ├── tests.csproj
│   │   ├── UnitTest1.cs
│   │   └── Usings.cs
│   ├── .dockerignore
│   ├── .gitignore
│   ├── compose.yaml
│   ├── Dockerfile
│   └── README.md

로컬 데이터베이스 추가 및 데이터 영속화

컨테이너로 데이터베이스 같은 로컬 서비스를 설정할 수 있어요. 이 섹션에서는 데이터가 영속화되도록 볼륨과 데이터베이스 서비스를 정의하도록 compose.yaml 파일을 업데이트해요.

IDE 또는 텍스트 편집기로 compose.yaml 파일을 열어주세요. PostgreSQL 데이터베이스와 볼륨에 대한 주석 처리된 지침이 이미 포함되어 있는 것을 알 수 있을 거예요.

IDE 또는 텍스트 편집기로 docker-dotnet-sample/src/appsettings.json 을 열어주세요. 모든 데이터베이스 정보가 있는 연결 문자열을 볼 수 있을 거예요. compose.yaml 은 이미 이 정보를 포함하지만 주석 처리되어 있어요.

compose.yaml 파일에서 데이터베이스 지침의 주석을 해제해주세요.

다음은 업데이트된 compose.yaml 파일이에요.

services:
  server:
    build:
      context: .
      target: final
    ports:
      - 8080:8080
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:18
    restart: always
    user: postgres
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/postgresql
    environment:
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 5432
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

참고: Compose 파일의 지침에 대해 더 알려면 Compose 파일 참조 를 참고하세요.

Compose로 어플리케이션을 실행하기 전에, 이 Compose 파일이 secrets 를 사용하고 데이터베이스의 패스워드를 담는 password.txt 파일을 지정한다는 점을 주목하세요. 이 파일은 소스 리포지토리에 포함되어 있지 않으므로 직접 만들어야 해요.

docker-dotnet-sample 디렉터리에 db 라는 새 디렉터리를 만들고 그 안에 password.txt 라는 파일을 만들어주세요. IDE 또는 텍스트 편집기에서 password.txt 를 열고 다음 패스워드를 추가해주세요. 패스워드는 파일에 추가 줄 없이 한 줄이어야 해요.

example

password.txt 파일을 저장하고 닫아주세요.

이제 docker-dotnet-sample 디렉터리에 다음이 있어야 해요.

├── docker-dotnet-sample/
│   ├── .git/
│   ├── db/
│   │   └── password.txt
│   ├── src/
│   ├── tests/
│   ├── .dockerignore
│   ├── .gitignore
│   ├── compose.yaml
│   ├── Dockerfile
│   └── README.md

다음 명령을 실행해 어플리케이션을 시작해주세요.

$ docker compose up --build

브라우저를 열고 http://localhost:8080 에서 어플리케이션을 확인해주세요. Student name is 라는 텍스트가 있는 간단한 웹 어플리케이션이 보여야 해요.

데이터베이스가 비어 있으므로 어플리케이션은 이름을 표시하지 않아요. 이 어플리케이션의 경우 데이터베이스에 접근해 레코드를 추가해야 해요.

데이터베이스에 레코드 추가

샘플 어플리케이션의 경우 샘플 레코드를 만들려면 데이터베이스에 직접 접근해야 해요. docker exec 명령으로 데이터베이스 컨테이너 안에서 명령을 실행할 수 있어요. 그 명령을 실행하기 전에 데이터베이스 컨테이너의 ID를 얻어야 해요. 새 터미널 창을 열고 다음 명령으로 실행 중인 모든 컨테이너를 나열해주세요.

$ docker container ls

다음 같은 출력이 보여야 해요.

CONTAINER ID   IMAGE                    COMMAND                  CREATED          STATUS                  PORTS                    NAMES
cb36e310aa7e   docker-dotnet-sample-server   "dotnet myWebApp.dll"   About a minute ago   Up About a minute   0.0.0.0:8080->8080/tcp   docker-dotnet-sample-server-1
39fdcf0aff7b   postgres:18              "docker-entrypoint.s…"   About a minute ago   Up About a minute (healthy)   5432/tcp            docker-dotnet-sample-db-1

이전 예제에서 컨테이너 ID는 39fdcf0aff7b 예요. 다음 명령으로 컨테이너의 postgres 데이터베이스에 연결해주세요. 컨테이너 ID를 내 컨테이너 ID로 바꾸세요.

$ docker exec -it 39fdcf0aff7b psql -d example -U postgres

마지막으로 데이터베이스에 레코드를 삽입해주세요.

example=# INSERT INTO "Students" ("ID", "LastName", "FirstMidName", "EnrollmentDate") VALUES (DEFAULT, 'Whale', 'Moby', '2013-03-20');

다음 같은 출력이 보여야 해요.

INSERT 0 1

exit 를 실행해 데이터베이스 연결을 닫고 컨테이너 셸에서 나가주세요.

example=# exit

데이터가 데이터베이스에 영속되는지 확인

브라우저를 열고 http://localhost:8080 에서 어플리케이션을 확인해주세요. Student name is Moby Whale 라는 텍스트가 있는 간단한 웹 어플리케이션이 보여야 해요.

터미널에서 ctrl+c 를 눌러 어플리케이션을 중지해주세요.

터미널에서 docker compose rm 을 실행해 컨테이너를 제거한 다음 docker compose up 을 실행해 어플리케이션을 다시 실행해주세요.

$ docker compose rm
$ docker compose up --build

브라우저에서 http://localhost:8080 을 새로고침하고 컨테이너가 제거되고 다시 실행된 후에도 학생 이름이 영속됐는지 확인해주세요.

터미널에서 ctrl+c 를 눌러 어플리케이션을 중지해주세요.

서비스 자동 업데이트

Compose Watch를 사용하면 코드를 편집·저장할 때 실행 중인 Compose 서비스를 자동으로 업데이트할 수 있어요. Compose Watch에 대한 자세한 내용은 Compose Watch 사용하기 를 참고하세요.

IDE 또는 텍스트 편집기에서 compose.yaml 파일을 열고 Compose Watch 지침을 추가해주세요. 다음은 업데이트된 compose.yaml 파일이에요.

services:
  server:
    build:
      context: .
      target: final
    ports:
      - 8080:8080
    depends_on:
      db:
        condition: service_healthy
    develop:
      watch:
        - action: rebuild
          path: .
  db:
    image: postgres:18
    restart: always
    user: postgres
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/postgresql
    environment:
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 5432
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

Compose Watch로 어플리케이션을 실행하려면 다음 명령을 실행해주세요.

$ docker compose watch

브라우저를 열고 http://localhost:8080 에서 어플리케이션이 실행 중인지 확인해주세요.

이제 내 로컬 머신의 어플리케이션 소스 파일 변경 사항이 즉시 실행 중인 컨테이너에 반영될 거예요.

IDE 또는 텍스트 편집기에서 docker-dotnet-sample/src/Pages/Index.cshtml 을 열고 13행의 학생 이름 텍스트를 Student name is 에서 Student name: 으로 업데이트해주세요.

- <p>Student name is @Model.StudentName</p>
+ <p>Student name: @Model.StudentName</p>

Index.cshtml 에 변경 사항을 저장한 다음 어플리케이션이 재빌드될 때까지 몇 초 기다리세요. 브라우저에서 http://localhost:8080 을 새로고침하고 업데이트된 텍스트가 나타나는지 확인해주세요.

터미널에서 ctrl+c 를 눌러 어플리케이션을 중지해주세요.

개발 컨테이너 만들기

이 시점에서 컨테이너화된 어플리케이션을 실행하면 .NET 런타임 이미지를 사용하고 있어요. 이 작은 이미지는 프로덕션에는 좋지만 개발에 필요한 SDK 도구와 의존성이 없어요. 또한 개발 중에는 dotnet publish 를 실행할 필요가 없을 수 있어요. 멀티 스테이지 빌드를 사용해 같은 Dockerfile에서 개발과 프로덕션 모두를 위한 스테이지를 빌드할 수 있어요. 자세한 내용은 멀티스테이지 빌드 를 참고하세요.

Dockerfile에 새 개발 스테이지를 추가하고 compose.yaml 파일을 업데이트해 로컬 개발에 이 스테이지를 사용하세요.

다음은 업데이트된 Dockerfile이에요.

Docker Hardened Images 사용:

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM dhi.io/dotnet:10-sdk AS build
ARG TARGETARCH
COPY . /source
WORKDIR /source/src
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
  dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app

FROM dhi.io/dotnet:10-sdk AS development
COPY . /source
WORKDIR /source/src
CMD dotnet run --no-launch-profile

FROM dhi.io/aspnetcore:10 AS final
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "myWebApp.dll"]

공식 .NET 10 이미지 사용:

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
ARG TARGETARCH
COPY . /source
WORKDIR /source/src
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
  dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app

FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS development
COPY . /source
WORKDIR /source/src
CMD dotnet run --no-launch-profile

FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
WORKDIR /app
COPY --from=build /app .
ARG UID=10001
RUN adduser \
  --disabled-password \
  --gecos "" \
  --home "/nonexistent" \
  --shell "/sbin/nologin" \
  --no-create-home \
  --uid "${UID}" \
  appuser
USER appuser
ENTRYPOINT ["dotnet", "myWebApp.dll"]

다음은 업데이트된 compose.yaml 파일이에요.

services:
  server:
    build:
      context: .
      target: development
    ports:
      - 8080:8080
    depends_on:
      db:
        condition: service_healthy
    develop:
      watch:
        - action: rebuild
          path: .
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
  db:
    image: postgres:18
    restart: always
    user: postgres
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/postgresql
    environment:
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 5432
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

이제 컨테이너화된 어플리케이션이 SDK 이미지(DHI면 dhi.io/dotnet:10-sdk , 공식 이미지면 mcr.microsoft.com/dotnet/sdk:10.0-alpine )를 사용하게 되며, 여기에는 dotnet test 같은 개발 도구가 포함돼요. 다음 섹션에서 dotnet test 를 실행하는 방법을 배워보세요.

컨테이너에서 .NET 테스트 실행하기

사전 요구사항

.NET 어플리케이션 컨테이너화하기 부터 시작해 이 가이드의 이전 모든 섹션을 완료하세요.

개요

테스팅은 현대 소프트웨어 개발의 필수적인 부분이에요. 테스팅은 개발 팀마다 많은 것을 의미할 수 있어요. 단위 테스트, 통합 테스트, 엔드투엔드 테스트가 있어요. 이 가이드에서는 개발할 때와 빌드할 때 Docker에서 단위 테스트를 실행하는 방법을 살펴볼 거예요.

로컬 개발 시 테스트 실행

샘플 어플리케이션에는 tests 디렉터리 안에 xUnit 테스트가 이미 있어요. 로컬에서 개발할 때 Compose를 사용해 테스트를 실행할 수 있어요.

docker-dotnet-sample 디렉터리에서 다음 명령을 실행해 컨테이너 안의 테스트를 실행해주세요.

$ docker compose run --build --rm server dotnet test /source/tests

다음이 포함된 출력이 보여야 해요.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net10.0/tests.dll (net10.0)

명령에 대해 더 알려면 docker compose run 을 참고하세요.

빌드 시 테스트 실행

빌드할 때 테스트를 실행하려면 Dockerfile을 업데이트해야 해요. 테스트를 실행하는 새 테스트 스테이지를 만들거나, 기존 빌드 스테이지에서 테스트를 실행할 수 있어요. 이 가이드에서는 빌드 스테이지에서 테스트를 실행하도록 Dockerfile을 업데이트해요.

다음은 업데이트된 Dockerfile이에요.

Docker Hardened Images 사용:

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM dhi.io/dotnet:10-sdk AS build
ARG TARGETARCH
COPY . /source
WORKDIR /source/src
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
  dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app
RUN dotnet test /source/tests

FROM dhi.io/dotnet:10-sdk AS development
COPY . /source
WORKDIR /source/src
CMD dotnet run --no-launch-profile

FROM dhi.io/aspnetcore:10 AS final
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "myWebApp.dll"]

공식 .NET 10 이미지 사용:

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
ARG TARGETARCH
COPY . /source
WORKDIR /source/src
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
  dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app
RUN dotnet test /source/tests

FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS development
COPY . /source
WORKDIR /source/src
CMD dotnet run --no-launch-profile

FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
WORKDIR /app
COPY --from=build /app .
ARG UID=10001
RUN adduser \
  --disabled-password \
  --gecos "" \
  --home "/nonexistent" \
  --shell "/sbin/nologin" \
  --no-create-home \
  --uid "${UID}" \
  appuser
USER appuser
ENTRYPOINT ["dotnet", "myWebApp.dll"]

다음 명령을 실행해 build 스테이지를 대상으로 이미지를 빌드하고 테스트 결과를 확인해주세요. 빌드 출력을 보려면 --progress=plain , 테스트가 항상 실행되게 하려면 --no-cache , build 스테이지를 대상으로 하려면 --target build 를 포함하세요.

$ docker build -t dotnet-docker-image-test --progress=plain --no-cache --target build .

다음이 포함된 출력이 보여야 해요.

#11 [build 5/5] RUN dotnet test /source/tests
#11 1.564 Determining projects to restore...
#11 3.421 Restored /source/src/myWebApp.csproj (in 1.02 sec).
#11 19.42 Restored /source/tests/tests.csproj (in 17.05 sec).
#11 27.91 myWebApp -> /source/src/bin/Debug/net10.0/myWebApp.dll
#11 28.47 tests -> /source/tests/bin/Debug/net10.0/tests.dll
#11 28.49 Test run for /source/tests/bin/Debug/net10.0/tests.dll (.NETCoreApp,Version=v10.0)
#11 28.67 Microsoft (R) Test Execution Command Line Tool Version 17.3.3 (x64)
#11 28.67 Copyright (c) Microsoft Corporation. All rights reserved.
#11 28.68
#11 28.97 Starting test execution, please wait...
#11 29.03 A total of 1 test files matched the specified pattern.
#11 32.07
#11 32.08 Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net10.0/tests.dll (net10.0)
#11 DONE 32.2s

요약

이 섹션에서 Compose로 로컬 개발 시 테스트를 실행하는 방법과 이미지를 빌드할 때 테스트를 실행하는 방법을 배웠어요.

관련 정보:

  • Dockerfile 참조
  • Compose 파일 참조
  • Compose CLI 참조

더 알아보기 (Learn more)

  • .NET
  • Docker Hardened Images
  • Docker Compose