Best way to use Docker for Entity Framework core update with Postgres

1

Background

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.

Problem

The problem occurs when I try to update the schema right from the dotnet API container. Here are my dockerfiles :

First attempt with only dockerfile

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.

Note

If I run the update from the Package Manager or my Powershell, with local EF, it works fine.

Second attempt with compose

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

Dbeaver showing docker up

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.

Questions

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 ?

Thank you in advance for your time.

postgresql
docker
.net-core
entity-framework-core
npgsql
asked on Stack Overflow May 31, 2020 by LePingu • edited Jun 1, 2020 by LePingu

1 Answer

2

Please update your connection string.

Change port number back to 5432

New connection string:

"DefaultConnection": "Server=usersdb;Database=docker;Port=5432;User id=docker;Password=docker"

Justification:

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:

  • Your host/localcomputer
  • 2nd API-DEV

API Dev will continue using default port 5432 for Postgres. Hope this helps.

Further thoughts

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.

answered on Stack Overflow Jun 1, 2020 by Ziaullah Khan • edited Jun 1, 2020 by tgogos

User contributions licensed under CC BY-SA 3.0