Svilen
Svilen

Reputation: 2648

Connection to server in Docker container on localhost very slow

I wanted to try Docker as a tool for local development — I used docker-compose to spin up 3 containers: one for the database (postgres), front-end (running webpack and node sass in watch mode) and backend (Elixir/Phoenix).

Everything works great, except that when I hit localhost:4000 the page load is really slow — 10-15 seconds just to get the login page, which is static.

I checked the logs from docker-compose and the server response times are fast as usual, which to me it means that something is slowing down the connection to the container.

When I ran the server directly in the Terminal again, everything is pretty fast, e.g. takes 1s to load the same page.

I'm new to Docker so I suspect I might be missing something configuration-wise. Any ideas are appreciated. Thank you!

Configuration

docker-compose.yml

version: "3.6"

services:
  postgres:
    container_name: postgres
    image: postgres:11.0-alpine
    ports:
      - 5432:5432
    volumes:
      - postgres:/var/lib/postgresql/data

  front-end:
    container_name: front-end
    env_file:
      - "docker/dev/.env"
    build:
      context: "."
      dockerfile: "docker/dev/Dockerfile.front-end"
    volumes:
      - .:/app
      - node_modules:/app/node_modules
      - static:/app/priv/static
    command: npm run dev

  backend:
    container_name: backend
    build:
      context: "."
      dockerfile: "docker/dev/Dockerfile.backend"
    env_file:
      - "docker/dev/.env"
    depends_on:
      - postgres
      - front-end
    ports:
      - 4000:4000
    stdin_open: true
    tty: true
    volumes:
      - .:/app
      - elixir-deps:/app/deps
      - static:/app/priv/static
    command: iex -S mix phx.server

volumes:
  postgres:
  elixir-deps:
  node_modules:
  static:
    driver_opts:
      type: "tmpfs"
      device: "tmpfs"

Dockerfile.front-end

FROM node:8.10-alpine

WORKDIR /app

COPY package.json ./
COPY package-lock.json ./

RUN npm install

COPY . .

Dockerfile.backend

FROM elixir:1.8-alpine

RUN apk update && apk add build-base inotify-tools postgresql-dev

WORKDIR /app

COPY mix.exs ./
COPY mix.lock ./

RUN mix local.hex --force && mix local.rebar --force \
    && mix deps.get && mix deps.compile

COPY . .

EXPOSE 4000

Versions & other info:

MacOS Mojave 10.14.3
Docker Desktop 2.0.0.3 (Engine 18.09.2, Compose: 1.23.2)

MacBook Pro 13" (Early 2015, 16GB RAM/3.1GHz Core i7)

Upvotes: 5

Views: 11671

Answers (1)

Svilen
Svilen

Reputation: 2648

Update 5 June 2020:

It is significantly better to use docker-sync for local development.

First make sure you down existing services to remove old volumes.

Then create a docker-sync.yml like so:

version: "2"
options:
  # Renamed compose file to `docker-compose.dev.yml`
  compose-dev-file-path: 'docker-compose.dev.yml'

syncs:
  # This name should be unique and should not clash with
  # an existing container or service name. You can use it
  # as a volume in docker compose yml.
  project:
    src: '.'
    sync_excludes: ['node_modules', 'deps', '_build']

Now you can use the project volume instead of - .:/app for front-end and backend services, e.g.

    volumes:
      - project:/app:nocopy
      - elixir-deps:/app/deps
      - elixir-build:/app/_build
      - static:/app/priv/static:ro

Run docker-sync start to start it in the background, followed by running docker-compose up --build --detach as usual.

Original answer:

As one commenter pointed out (@DavidMaze), there are currently known performance issues with Docker for Mac. How much are they linked to my use case I cannot tell, but after reading the Performance tuning guide in the official docs, I managed to make some progress in performance:

docker-compose.yml

version: "3.6"

services:
  postgres:
    container_name: postgres
    image: postgres:11.0-alpine
    ports:
      - 5432:5432
    volumes:
      - postgres:/var/lib/postgresql/data

  front-end:
    container_name: front-end
    env_file:
      - "docker/dev/.env"
    build:
      context: "."
      dockerfile: "docker/dev/Dockerfile.front-end"
    volumes:
      - .:/app:delegated
      - node_modules:/app/node_modules
      - static:/app/priv/static
    command: npm run dev

  backend:
    container_name: backend
    build:
      context: "."
      dockerfile: "docker/dev/Dockerfile.backend"
    env_file:
      - "docker/dev/.env"
    depends_on:
      - postgres
      - front-end
    ports:
      - 4000:4000
    stdin_open: true
    tty: true
    volumes:
      - .:/app:delegated
      - elixir-deps:/app/deps
      - elixir-build:/app/_build
      - static:/app/priv/static:ro
    command: iex -S mix phx.server

volumes:
  postgres:
  elixir-deps:
  elixir-build:
  node_modules:
  static:
    driver_opts:
      type: "tmpfs"
      device: "tmpfs"

Note the use of :delegated when declaring the .:/app:delegated volumes.

Although this is an improvement, it is still much slower for day-to-day development than running things natively, so I'm welcoming other answers to my question. For the time being, I think that's probably the best solution.

Upvotes: 5

Related Questions