Создание миграции при запуске приложения в Docker
Как создать создавать миграции, и схему бд при запуске контейнера в Docker ?
Написал такой файл docker-compose, все запускается хорошо, но вот при попытке обращения к базе, и поиска там сущности, таблиц там нет. Как их создать при запуске контейнера (Если их нет) ?
Не бейте камнями, только первый день докер осваиваю
version: '3.4'
networks:
webapi:
driver: bridge
services:
webapi:
container_name: webapi-dev
image: ${DOCKER_REGISTRY-}webapi
build:
context: .
dockerfile: webapi/Dockerfile
ports:
- "8080:8080"
networks:
- "webapi"
environment:
- ASPNETCORE_ENVIRONMENT=Development
depends_on:
- postgres_db
- redis
- clamav
postgres_db:
container_name: postgres
image: postgres:latest
ports:
- "5432:5432"
networks:
- "webapi"
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123
POSTGRES_DB: filecryptweb
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
container_name: redis
image: redis:latest
ports:
- "6379:6379"
networks:
- "webapi"
clamav:
container_name: clamav
image: mkodockx/docker-clamav:alpine
environment:
CLAMD_CONF_MaxFileSize: 250M
CLAMD_CONF_MaxScanSize: 250M
CLAMD_CONF_StreamMaxLength: 250M
restart: always
ports:
- "3310:3310"
networks:
- "webapi"
volumes:
postgres-data:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["webapi/webapi.csproj", "webapi/"]
RUN dotnet restore "./webapi/webapi.csproj"
COPY . .
WORKDIR "/src/webapi"
RUN dotnet build "./webapi.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./webapi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "webapi.dll"]
2024-03-16 04:10:54 webapi-dev | [21:10:54 INF] { username = , id = , role = } { path = /api/auth/login, method = POST }
2024-03-16 04:10:54 webapi-dev | [21:10:54 INF] Executing endpoint 'webapi.Controllers.Account.AuthSessionController.Login (webapi)'
2024-03-16 04:10:54 webapi-dev | [21:10:54 INF] Route matched with {action = "Login", controller = "AuthSession"}. Executing controller action with signature System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.IActionResult] Login(webapi.DTO.AuthDTO) on controller webapi.Controllers.Account.AuthSessionController (webapi).
2024-03-16 04:10:55 postgres | 2024-03-15 21:10:55.845 UTC [33] ERROR: relation "users" does not exist at character 91
2024-03-16 04:10:55 postgres | 2024-03-15 21:10:55.845 UTC [33] STATEMENT: SELECT u.id, u.email, u.is_2fa_enabled, u.is_blocked, u.password, u.role, u.username
2024-03-16 04:10:55 postgres | FROM users AS u
2024-03-16 04:10:55 postgres | WHERE u.email = $1
2024-03-16 04:10:55 postgres | LIMIT 1
2024-03-16 04:10:55 webapi-dev | [21:10:55 ERR] Failed executing DbCommand (99ms) [Parameters=[@__ToLowerInvariant_0='?'], CommandType='Text', CommandTimeout='30']
2024-03-16 04:10:55 webapi-dev | SELECT u.id, u.email, u.is_2fa_enabled, u.is_blocked, u.password, u.role, u.username
2024-03-16 04:10:55 webapi-dev | FROM users AS u
2024-03-16 04:10:55 webapi-dev | WHERE u.email = @__ToLowerInvariant_0
2024-03-16 04:10:55 webapi-dev | LIMIT 1
2024-03-16 04:10:55 webapi-dev | [21:10:55 ERR] An exception occurred while iterating over the results of a query for context type 'webapi.DB.FileCryptDbContext'.
2024-03-16 04:10:55 webapi-dev | Npgsql.PostgresException (0x80004005): 42P01: relation "users" does not exist
2024-03-16 04:10:55 webapi-dev |
2024-03-16 04:10:55 webapi-dev | POSITION: 91
2024-03-16 04:10:55 webapi-dev | at Npgsql.Internal.NpgsqlConnector.ReadMessageLong(Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
2024-03-16 04:10:55 webapi-dev | at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
2024-03-16 04:10:55 webapi-dev | Exception data:
2024-03-16 04:10:55 webapi-dev | Severity: ERROR
2024-03-16 04:10:55 webapi-dev | SqlState: 42P01
2024-03-16 04:10:55 webapi-dev | MessageText: relation "users" does not exist
2024-03-16 04:10:55 webapi-dev | Position: 91
2024-03-16 04:10:55 webapi-dev | File: parse_relation.c
2024-03-16 04:10:55 webapi-dev | Line: 1449
2024-03-16 04:10:55 webapi-dev | Routine: parserOpenTable
2024-03-16 04:10:55 webapi-dev | Npgsql.PostgresException (0x80004005): 42P01: relation "users" does not exist
2024-03-16 04:10:55 webapi-dev |
2024-03-16 04:10:55 webapi-dev | POSITION: 91
2024-03-16 04:10:55 webapi-dev | at Npgsql.Internal.NpgsqlConnector.ReadMessageLong(Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
2024-03-16 04:10:55 webapi-dev | at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
2024-03-16 04:10:55 webapi-dev | Exception data:
2024-03-16 04:10:55 webapi-dev | Severity: ERROR
2024-03-16 04:10:55 webapi-dev | SqlState: 42P01
2024-03-16 04:10:55 webapi-dev | MessageText: relation "users" does not exist
2024-03-16 04:10:55 webapi-dev | Position: 91
2024-03-16 04:10:55 webapi-dev | File: parse_relation.c
2024-03-16 04:10:55 webapi-dev | Line: 1449
2024-03-16 04:10:55 webapi-dev | Routine: parserOpenTable
2024-03-16 04:10:55 webapi-dev | [21:10:55 FTL] Npgsql.PostgresException (0x80004005): 42P01: relation "users" does not exist
2024-03-16 04:10:55 webapi-dev |
2024-03-16 04:10:55 webapi-dev | POSITION: 91
2024-03-16 04:10:55 webapi-dev | at Npgsql.Internal.NpgsqlConnector.ReadMessageLong(Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
2024-03-16 04:10:55 webapi-dev | at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
2024-03-16 04:10:55 webapi-dev | at webapi.DB.Repository`1.GetByFilter(Func`2 queryModifier, CancellationToken cancellationToken) in C:\Users\Stewi\Source\Repos\air2921\FileCryptWeb\webapi\DB\Repository.cs:line 77
2024-03-16 04:10:55 webapi-dev | Exception data:
2024-03-16 04:10:55 webapi-dev | Severity: ERROR
2024-03-16 04:10:55 webapi-dev | SqlState: 42P01
2024-03-16 04:10:55 webapi-dev | MessageText: relation "users" does not exist
2024-03-16 04:10:55 webapi-dev | Position: 91
2024-03-16 04:10:55 webapi-dev | File: parse_relation.c
2024-03-16 04:10:55 webapi-dev | Line: 1449
2024-03-16 04:10:55 webapi-dev | Routine: parserOpenTable
2024-03-16 04:10:56 webapi-dev | [21:10:56 INF] Executing ObjectResult, writing value of type '<>f__AnonymousType0`1[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'.
2024-03-16 04:10:56 webapi-dev | [21:10:56 INF] Executed action webapi.Controllers.Account.AuthSessionController.Login (webapi) in 1115.6806ms
2024-03-16 04:10:56 webapi-dev | [21:10:56 INF] Executed endpoint 'webapi.Controllers.Account.AuthSessionController.Login (webapi)'
2024-03-16 04:10:56 webapi-dev | [21:10:56 INF] Status Code: 500
2024-03-16 04:10:56 webapi-dev | [21:10:56 INF] Request finished HTTP/2 POST https://localhost:8081/api/auth/login - 500 null application/json; charset=utf-8 1280.3549ms
Ответы (2 шт):
Дело в том, что те volumes которые вы указываете в docker-compose не видны в момент сборки (Dockerfile) чтобы запустить миграцию, я думаю имеет смысл делать это в рантайме используя скрипт запущенный из параметра command: docker-compose
т.е. пишем такой примерно start.sh
#!/bin/sh
# если я правильно понимаю - это скрипт миграции
BUILD_CONFIGURATION=Release dotnet publish "./webapi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
dotnet webapi.dll
удаляем соответвующие строки в Dockerfile
в docker-compose примерно так:
version: '3.4'
networks:
webapi:
driver: bridge
services:
webapi:
container_name: webapi-dev
image: ${DOCKER_REGISTRY-}webapi
build:
context: .
dockerfile: webapi/Dockerfile
ports:
- "8080:8080"
networks:
- "webapi"
environment:
- ASPNETCORE_ENVIRONMENT=Development
depends_on:
- postgres_db
- redis
- clamav
command: "/bin/sh ./start.sh"
postgres_db:
container_name: postgres
image: postgres:latest
ports:
- "5432:5432"
networks:
- "webapi"
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123
POSTGRES_DB: filecryptweb
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
container_name: redis
image: redis:latest
ports:
- "6379:6379"
networks:
- "webapi"
clamav:
container_name: clamav
image: mkodockx/docker-clamav:alpine
environment:
CLAMD_CONF_MaxFileSize: 250M
CLAMD_CONF_MaxScanSize: 250M
CLAMD_CONF_StreamMaxLength: 250M
restart: always
ports:
- "3310:3310"
networks:
- "webapi"
volumes:
postgres-data:
идея делать миграцию в билде не осмысленна, т.к. база то у вас в volume, а это уже часть рантайма, а не билда, поэтому
ещё раз повторю - Docker file это для сборки "железных образов" которые вы собираетесь в идее положить в репозиторий docker и одинаково раскатывать на все инстансы, а docker-compose - конфигурация оркестратора runtime этих контейнеров, "личинка kubrnetics" еcли можно так выразиться, т.е. то что в идее крутится на серваке.
так же вам может понадобиться запускать миграцию только один раз, в этом случае просто выберите как вы узнаете о том что миграцию надо запустить (например версия поменялась) положите этот признак при сборке в контейнер (например в виде файла db_version), проверьте в скрипте запуска что версия совпадает (см чуть позже с чем) и если нет, запустите миграцию и положите в volume (добавьте в compose) содержимое файла версии (вот с этим сравнивать)
Все решилось одной строкой кода.
В моем случае, у меня тело конструктора моего класса DbContext было пустым:
public FileCryptDbContext(DbContextOptions<FileCryptDbContext> options) : base(options)
{
}
Но после добавления проверки на существование базы данных для моего констекста в теле конструктора, таблицы создались:
public FileCryptDbContext(DbContextOptions<FileCryptDbContext> options) : base(options)
{
Database.EnsureCreated();
}