Ожидаем Postgres При Запуске Приложений с Docker-Compose
Работая с Docker-Compose, многие сталкивались со следующей проблемой: контейнер с базой запустился, но сам Postgres еще не успел стартовать. При этом, контейнер с нашим приложением уже запустился и пытается подключиться к БД.
В результате, приложение падает с ошибкой подключения к Postgres.
Рассмотрим следующий пример. У нас есть простое веб-приложение, написаное на Go, которое использует Postgres в качестве БД. Чтобы лучше понимать контекст, перед дальнейшим прочтением рекомендую ознакомится с исходниками.
Так выглядит Dockerfile для приложения:
FROM golang:1.14-busterRUN go version
ENV GOPATH=/COPY ./ ./ # build go app
RUN go mod download
RUN go build -o todo-app ./cmd/main.goCMD ["./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-appCreating todo-app_db_1 ... done
Recreating todo-app_todo-app_1 ... done
Attaching to todo-app_todo-app_1todo-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"}