Блог

Ожидаем Postgres При Запуске Приложений с Docker-Compose

Работая с Docker-Compose, многие сталкивались со следующей проблемой: контейнер с базой запустился, но сам Postgres еще не успел стартовать. При этом, контейнер с нашим приложением уже запустился и пытается подключиться к БД.

В результате, приложение падает с ошибкой подключения к Postgres.

Рассмотрим следующий пример. У нас есть простое веб-приложение, написаное на Go, которое использует Postgres в качестве БД. Чтобы лучше понимать контекст, перед дальнейшим прочтением рекомендую ознакомится с исходниками.

Так выглядит Dockerfile для приложения:

FROM golang:1.14-buster

RUN go version
ENV GOPATH=/
COPY ./ ./
# build go app
RUN go mod download
RUN go build -o todo-app ./cmd/main.go
CMD ["./todo-app"]

Ничего сложного, здесь мы копируем все файлы нашего приложения и компилируем его в изображении.

А так выглядит docker-compose.yml :

version: '3.8'

services:
  todo-app: 
    build: ./
      command: ./todo-app
    ports:
      - 8000:8000
    depends_on:
      - db 
    environment:
      - DB_PASSWORD=qwerty
  db:
    restart: always
    image: postgres:latest
    volumes:
      - ./.database/postgres/data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=qwerty
    ports:
      - 5436:5432

Тут также ничего сложного.

Описываем 2 контейнера: само приложение и БД. Приложение зависит от базы, поэтому контейнер db будет запущен перед приложением.
Однако при первом запуске приложение не может подключиться к базе и выдает ошибку:

docker-compose up todo-app

Creating todo-app_db_1 ... done
Recreating todo-app_todo-app_1 ... done
Attaching to todo-app_todo-app_1
todo-app_1 | {"level":"fatal","msg":"failed to initialize db: dial tcp 172.23.0.2:5432: connect: connection refused","time":"2020-10-08T06:39:32Z"}
todo-app_todo-app_1 exited with code 1


При повторном запуске приложение запуститься без проблем, потому что контейнер с базой уже стартанул и Postgres запустился. Однако при свежем запуске приложения, эта ошибка будет постоянно воспроизводится.

На официальном сайте Docker есть пример скрипта wait-for-postgres.sh , который помогает решить эту проблему.

Давайте перепишем наши файлы Dockerfile и docker-compose.yml , добавив в корень проекта также данный скрипт.




В Dockerfile теперь будем устанавливать утилиту psql для выполнения скрипта. А в docker-compose.yml в командах контейнера укажем следующий код.

command: ./wait-for-postgres.sh db ./todo-app

Теперь все запускается без проблем, и перед запуском приложения, скрипт пытается достучаться к БД.

todo-app_1 | psql: could not connect to server: Connection refused
todo-app_1 | Is the server running on host "db" (172.23.0.2) and accepting
todo-app_1 | TCP/IP connections on port 5432?
todo-app_1 | Postgres is unavailable - sleeping
todo-app_1 | psql: FATAL: the database system is starting up
todo-app_1 | Postgres is unavailable - sleeping
todo-app_1 | psql: FATAL: the database system is starting up
todo-app_1 | Postgres is unavailable - sleeping
todo-app_1 | psql: FATAL: the database system is starting up
todo-app_1 | Postgres is unavailable - sleeping
todo-app_1 | Postgres is up - executing command
todo-app_1 | {"level":"info","msg":"TodoApp Started","time":"2020-10-08T06:50:08Z"}
Разработка