>> 이전 글 참조1: vs2017에서 ASP.NET Core Docker 컨테이너 배포 (1/2)

>> 이전 글 참조2: vs2017에서 ASP.NET Core Docker 컨테이너 배포 (2/2)

 

이번에는 최신 .NET Core SDK 3.1 환경에서 ASP.NET Core 앱을 docker-compose를 통해 배포해 봤다.

프로젝트는 이전에 썼던 tutorialapp를 똑같이 썼고(Visual Studio 2019에서 3.1 버전으로 생성 후 빌드),
MariaDB를 추가로 Docker에 올려서 동일한 브릿지 네트워크로 구성해서 바로 사용 가능한 환경으로 만들어 봤다.

[docker-compose.yml(1/2)]

version: '3.1'

volumes:
  v_db: {}

networks:
  v_network:
    driver: bridge

services:
  db:
    container_name: mariadb
    image: mariadb:latest
    restart: always
    ports:
      - 3306:3306
    volumes:
      # mariadb data folder
      - v_db:/var/lib/mysql
      # mariadb config folder
      - ./db/config:/etc/mysql/conf.d
    networks:
      - v_network
    environment:
      # mariadb root password (new)
      MYSQL_ROOT_PASSWORD: qwer1234!
      TZ: Asia/Seoul

...
  1. v_db라는 가상 볼륨을 생성해서 MariaDB 데이터 저장소로 사용했다.
    이렇게 하면 동일한 저장소를 여러 Docker 환경에서 계속 재사용이 가능하다.
  2. 기본 브릿지 네트워크 대신 v_network라는 이름으로 생성한 네트워크를 사용했다.
    이런 방법으로 필요한 경우 external, internal로 네트워크를 나눌 수도 있다.
  3. MariaDB의 기본 환경설정 파일인 /etc/mysql/conf.d/my.cnf 파일을 다운로드 받아서
    로컬 호스트의 ./db/config/my.cnf 파일로 저장하고 그 파일이 사용되도록 했다.

[my.cnf]

# my.cnf
# MariaDB-specific config file.
# Read by /etc/mysql/my.cnf

[client]
# Default is Latin1, if you need UTF-8 set this (also in server section)
#default-character-set = utf8
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

[mysqld]
#
# * Character sets
#
# Default is Latin1, if you need UTF-8 set all this (also in client section)
#
#character-set-server  = utf8
#collation-server      = utf8_general_ci
#character_set_server   = utf8
#collation_server       = utf8_general_ci
# Import all .cnf files from configuration directory
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

!includedir /etc/mysql/mariadb.conf.d/

lower_case_table_names = 1
  1. utf-8 인코딩 대신에 utf-8 mb4 인코딩을 사용했다. 최신 인터넷에서 유행하는 이모지(emoji)를 저장하고 표현하려면 mb4 인코딩을 써야 한다. 회사 업무용으로는 그닥 필요가 없겠지만.
  2. 마지막 lower_case_table_names는 테이블 대소문자 구분을 하지 않고 일괄 소문자로 표시할 것인가의 여부로, Windows용 MariaDB에는 기본적으로 설정되어 있는데(대소문자 구분하지 않음), Docker용이나 Linux 용 MariaDB에는 이 값이 설정되어 있지 않아 대소문자를 가린다. 혹시 모르니 설정해 준다.

가상 볼륨을 따로 저장해서 호스트로 복사한다거나 반대의 작업을 하려고 할 때는 아래와 같이 하면 된다.
docker-hub에 미리 만들어져 있는 hello-world라는 샘플 템플릿 이미지를 사용하면 작고 가벼운 임시 컨테이너 이미지를 만들 수 있다.

D:\tutorialapp\Docker> docker volume ls
DRIVER VOLUME NAME
local docker_v_db

D:\tutorialapp\Docker> docker container create --name dummy -v docker_v_db:/root hello-world
D:\tutorialapp\Docker> docker cp dummy:/root/. ./db/init_data/.
D:\tutorialapp\Docker> docker rm dummy

로컬 호스트로 복사한 다음 지워버리면 끝. 파일 복사 작업을 할 때 컨테이너를 시작할 필요는 없다.

또는 가상 볼륨이 없는 경우 아예 미리 별도로 만들어서 파일을 복사한 다음 연결해서 사용할 수도 있다.

D:\tutorialapp\Docker> docker volume ls
D:\tutorialapp\Docker> docker volume create docker_v_db
D:\tutorialapp\Docker> docker container create --name dummy -v docker_v_db:/root hello-world
D:\tutorialapp\Docker> docker cp ./db/init_data/. dummy:/root/.
D:\tutorialapp\Docker> docker rm dummy

>> 참고: docker-compose.yml 내부에서 사용되는 볼륨이나 네트워크 이름 앞에는 기본적으로 현재 작업 중인 상위 폴더명이 붙게 된다. 위 예제와 같이 v_db 볼륨의 실제 이름은 docker_v_db가 되는 식.

 

 

여기까지가 MariaDB 관련 docker 컨테이너를 준비한 부분이고,
다음은 ASP.NET Core 앱 컨테이너.

[Dockerfile.compose]

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-bionic
WORKDIR /app
EXPOSE 80
EXPOSE 443
COPY publish/. ./
COPY appsettings.Docker.json ./appsettings.json
ENTRYPOINT ["dotnet", "PnP.TutorialWeb.Core.dll"]
  1. ASP.NET Core 3.1 이미지(bionic)를 기반으로 작업했다.
    용량을 줄인 buster나 buster-slim 이미지를 쓰면 TLS 등이 정상 동작하지 않아 MSSQL 접속 등 안되는 게 많다.

    System.Data.SqlClient.SqlException (0x80131904): A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: SSL Provider, error: 31)
  2. ASP.NET Core 앱을 빌드하고 게시한 publish 폴더를 현재 경로로 복사한 다음 컨테이너 이미지 내 /app 경로에 복사해서 올렸다. 상위 폴더를 지정할 수 없으므로 이 작업은 ASP.NET Core 앱 변경 시 빌드-게시-복사 작업을 매번 다시 해야 한다.
  3. ASP.NET Core 설정파일인 appsettings.json 파일은 로컬 호스트에 따로 준비해 둔 파일로 복사/교체했다.

[docker-compose.yml(2/2)]

...

  tutorialapp:
    build:
      context: .
      dockerfile: ./Dockerfile.compose
    ports:
      - 5000:80
      - 5001:443
    volumes:
      - ./logs/:/app/logs/
    networks:
      - v_network

  1. Dockerfile.compose로 /app 경로에 ASP.NET Core 컨테이너를 설치 후 빌드하고
  2. 혹시 몰라 로그 파일은 /app/logs에 기록하는데, 로컬 호스트에서 쉽게 확인 가능하도록 볼륨을 연결했다. 이렇게 하면 현재 경로 하위의 logs 폴더에 로그 파일이 생기고, 컨테이너가 삭제되더라도 해당 로그는 유지된다.
  3. 네트워크는 위 MariaDB에서 사용한 동일 네트워크, v_network를 함께 쓰도록 했다.

위와 같이 Dockerfile.compose 파일을 별도로 만들어서 매번 ASP.NET Core용 docker 컨테이너를 새로 빌드하거나 이미 빌드/게시된 결과물을 이용해서 컨테이너를 만들 수도 있지만, 개발/테스트 환경처럼 최종 운영서버에 배포하는 경우가 아닌 이상 매번 그런 작업을 하기에는 시간도 오래 걸리고 불편하다. 그래서 게시된 로컬 호스트 폴더를 그대로 volumes로 연결만 해서 바로 사용하는 방법으로 바꿔 봤다.

D:\tutorialapp> aspnet publish -c Release -o bin\Release\netcoreapp3.1\publish 
  ㄴbin\Debug 
  ㄴbin\Release 
  ㄴbin\Release\netcoreapp3.1 
  ㄴbin\Release\netcoreapp3.1\publish 

D:\tutorialapp\Docker> mklink /j publish ..\bin\Release\netcoreapp3.1\publish 
D:\tutorialapp\Docker> dir pub* 
 D 드라이브의 볼륨: DATA 
 볼륨 일련 번호: FEAA-4CE1 

 D:\tutorialapp\Docker 디렉터리 

2020-04-22  오후 01:01    <JUNCTION>     publish [D:\tutorialapp\bin\Release\netcoreapp3.1\publish] 
               0개 파일                   0 바이트 
               1개 디렉터리  772,761,542,656 바이트 남음

Docker는 현재 폴더 상위의 다른 폴더를 이용할 수 없지만, 심볼릭 링크(symbolic link)나 디렉터리 교차점(junction) 기법을 사용하면 다른 경로에 있는 파일이나 폴더를 굳이 복사하지 않고도 쉽게 가져다 쓸 수 있다. 위 화면은 상위 폴더에 배포된 publish 폴더를 현재 경로로 복사해 오지 않고 디렉터리 교차점으로 연결한 것. 이렇게 하면 앱 변경 시 매번 게시된 폴더를 다시 복사해 올 필요가 없다, 자동으로 연결되니까. (단, Dockerfile 내에서는 로컬 경로에 심볼릭 링크나 디렉터리 교차점이 있으면 경로 인식이 제대로 되지 않으므로 사용하면 안된다. docker-compose에서만 사용 가능하다.)

이를 활용하면 Dockerfile 없이도 비교적 쉽게 ASP.NET Core 앱 컨테이너를 만들 수 있다.

[docker-compose.yml(2/2)]

...

  tutorialapp:
    container_name: tutorialapp
    image: mcr.microsoft.com/dotnet/core/aspnet:3.1-bionic
    ports:
      - 5000:80
      - 5001:443
    volumes:
      - ./publish/:/app/
      - ./appsettings.Docker.json:/app/appsettings.json
      - ./logs/:/app/logs/
    networks:
      - v_network
    working_dir: /app
    entrypoint:
      - dotnet
      - TutorialWeb.Core.dll
  1. 기본 이미지를 mcr.microsoft.com 레지스트리 허브에 있는 이미지로 직접 지정했다.
  2. 로컬 게시 폴더(디렉터리 교차점)를 컨테이너 내부의 /app 경로와 연결했다. 이렇게 하면 매번 새로 컨테이너를 빌드하지 않고 로컬에서 앱 빌드 후 게시만 하면 컨테이너에 곧장 반영되므로 개발 환경일 때 매우 유용하다.
  3. ASP.NET Core 설정파일인 appsettings.json 파일은 로컬 호스트에 있는 파일을 사용하도록 했다. 역시 이렇게 하면 컨테이너에 들어가서 수정하거나 매번 복사해 줄 필요가 없다.
  4. 로그 폴더 역시 로컬 호스트로 연결된 곳에 생성되도록 했다.
  5. 현재 작업 경로 설정은 working_dir로 한다. Dockerfile의 WORKDIR과 동일한 개념. ASP.NET Core 앱은 작업 경로가 다르면 정상 실행이 되지 않는다. 꼭 필요한 설정.
  6. Dockerfile이 없으므로 entrypoint나 command는 당연히 따라 붙어야 한다.

 

위 두 개를 합친 전체 docker-compose.yml은 아래와 같다.

[docker-compose.yml]

version: '3.1'

volumes:
  v_db: {}

networks:
  v_network:
    driver: bridge

services:
  db:
    container_name: mariadb
    image: mariadb:latest
    restart: always
    ports:
      - 3306:3306
    volumes:
      # mariadb data folder
      - v_db:/var/lib/mysql
      # mariadb config folder
      - ./db/config:/etc/mysql/conf.d
    networks:
      - v_network
    environment:
      # mariadb root password (new)
      MYSQL_ROOT_PASSWORD: qwer1234!
      TZ: Asia/Seoul

  tutorialapp:
    container_name: tutorialapp
    image: mcr.microsoft.com/dotnet/core/aspnet:3.1-bionic
    ports:
      - 5000:80
      - 5001:443
    volumes:
      - ./publish/:/app/
      - ./appsettings.Docker.json:/app/appsettings.json
      - ./logs/:/app/logs/
    networks:
      - v_network
    working_dir: /app
    entrypoint:
      - dotnet
      - TutorialWeb.Core.dll

 

끝.

 

 



Posted by 떼르미
,


자바스크립트를 허용해주세요!
Please Enable JavaScript![ Enable JavaScript ]