I am building an API to hold user info in a Postgres DB, using dotnet core and EF core.
I do not think it has any impact on the question following but for a background explanation, I am also using IdentityServer4, and Npgsql package for Postgres access.
In local, everything works absolutely fine, I can update my schema as I want, and it all reflects correclty in the database. Now I would like to make this containers out of these to help new team members set up quickly.
The problem occurs when I try to update the schema right from the dotnet API container. Here are my dockerfiles :
API Dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine AS build-env
WORKDIR /app
# Explicitly ask for path to be added
ENV PATH="${PATH}:/root/.dotnet/tools"
RUN dotnet tool install --global dotnet-ef --version 3.1.3
# Copy csproj and restore as distinct layers
COPY *.csproj /app
RUN dotnet restore api.csproj
COPY . /app
RUN dotnet publish api.csproj -c Release -o out
# Run updates
RUN dotnet ef database update -c HelloApiDbContext
RUN dotnet ef database update -c ConfigurationDbContext
# Build runtime image
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "api.dll"]
Postgres Dockerfile :
FROM postgres:12-alpine AS build-env
WORKDIR /app
ENV POSTGRES_USER docker
ENV POSTGRES_PASSWORD docker
ENV POSTGRES_DB docker
# Expose the PostgreSQL port
EXPOSE 5432
# Add VOLUMEs to allow backup of config, logs and databases
VOLUME ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"]
# Set the default command to run when starting the container
CMD ["postgres"]
Also my default configuration connection string looks like this:
"DefaultConnection": "Server=localhost;Database=docker;Port=32778;User id=docker;Password=docker"
But it always results with :
Build started...
Build succeeded.
Npgsql.NpgsqlException (0x80004005): Exception while connecting
---> System.Net.Internals.SocketExceptionFactory+ExtendedSocketException (99): Address not available [::1]:32778
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Connect(EndPoint remoteEP)
at Npgsql.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
at Npgsql.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
at Npgsql.NpgsqlConnector.RawOpen(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlConnection.<>c__DisplayClass32_0.<<Open>g__OpenLong|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
I have tried several things.
If I run the update from the Package Manager or my Powershell, with local EF, it works fine.
I understood that the issue was coming from internal DNS from Docker container not being resolved. I used other methods to link the two with the most promising one being a shell script following this post like so :
#!/bin/bash
set -e
run_cmd="dotnet run --server.urls http://*:80"
until dotnet ef database update -c HelloApiDbContext; do
>&2 echo "SQL Server is starting up"
sleep 1
done
>&2 echo "SQL Server is up - executing command"
exec $run_cmd
And a docker-compose file :
version: '3.4'
networks:
api-dev:
driver: "bridge"
services:
logic-api:
image: project/api:latest
depends_on:
- "usersdb"
build:
context: ./api/
dockerfile: dockerfile
ports:
- "5000:80"
networks:
- api-dev
usersdb:
image: usersdb:latest
build:
context: ./users-db/
dockerfile: dockerfile
ports:
- "32778:5432"
volumes:
- db_volume:/var/lib/postgresql/data
environment:
POSGRES_USER: "docker"
POSTGRES_PASSWORD : "docker"
POSGRES_DB: "docker"
networks:
- api-dev
volumes:
db_volume:
And changed the entrypoint of the API dockerfile to make sure the Postgres db was up with DBeaver
before launching the update command like so :
EXPOSE 80/tcp
RUN chmod +x ./entrypoint.sh
CMD /bin/bash ./entrypoint.sh
I also changed the default connection to be
"DefaultConnection": "Server=usersdb;Database=docker;Port=32778;User id=docker;Password=docker"
Al attempts have failed an I keep running into this connection issue.
I have read in several posts that it is easier to manage SQL Migrations in prod with SQL scripts instead of EF auto migrations. Should I abandon the idea to update from one container to another?
I would like to use EF completely though if possible. Is there a way to make this connection work in a clean manner?
Please update your connection string.
Change port number back to 5432
"DefaultConnection": "Server=usersdb;Database=docker;Port=5432;User id=docker;Password=docker"
API-DEV is a private network.
When working with docker trick is to understand what network are you into.
There are two networks in your example:
API Dev will continue using default port 5432 for Postgres.
I've read/heard on multiple places that docker for databases is not a good choice. Based on my limited knowledge I'd suggest that you use a native install of postgress
Docker-compose has a few limitations too. Kubernetes and YAML files solve creating infrastructure way better that docker-compose. Consider those as well.