У меня есть два контейнера Docker на узле Debian 8.3. Один - это официальный образ postgres, другой - базовое приложение Phoenix / elixir. Я связываю оба с файлом стека докеров. Но феникс не может подключиться к postgres, если я не опубликую порт. Это позволяет мне думать, что что-то с внутренней сетью докеров не так, и, поскольку узел представляет собой новую установку Debian, это может быть iptables. Это также исключает неправильный пароль или имя хоста.
Что я делаю не так и как правильно настроить правила iptables, чтобы два контейнера могли взаимодействовать?
Сообщение об ошибке в приложении Phoenix:
app-1 | 2016-03-15T16:15:18.019402549Z ** (Mix) The database for App.Repo couldn't be created, reason given: psql: could not connect to server: Connection refused
app-1 | 2016-03-15T16:15:18.019456447Z Is the server running on host "postgres" (10.7.0.1) and accepting
app-1 | 2016-03-15T16:15:18.019468609Z TCP/IP connections on port 5432?
Вывод файла журнала в контейнер postgres
postgres-1 | 2016-03-15T16:46:32.457844697Z LOG: MultiXact member wraparound protections are now enabled
postgres-1 | 2016-03-15T16:46:32.464806051Z LOG: database system is ready to accept connections
postgres-1 | 2016-03-15T16:46:32.465087076Z LOG: autovacuum launcher started
Мой файл стека Docker
app:
image: myrepo/app
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgrespassword
PORT: 4000
links:
- postgres
ports:
- 80:4000
postgres:
image: postgres:9.5
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgrespassword
volumes:
- /var/dbdata:/var/lib/postgresql/data
Конфигурация базы данных в приложении Phoenix (prod.secret.exs)
config :data_bucket, DataBucket.Repo,
adapter: Ecto.Adapters.Postgres,
username: System.get_env("POSTGRES_USER"),
password: System.get_env("POSTGRES_PASSWORD"),
database: "app_prod",
hostname: "postgres",
pool_size: 20
Результат $ sudo iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere
REJECT all -- anywhere loopback/8 reject-with icmp-port-unreachable
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere tcp dpt:http
ACCEPT tcp -- anywhere anywhere tcp dpt:https
ACCEPT tcp -- anywhere anywhere tcp dpt:2375
ACCEPT tcp -- anywhere anywhere tcp dpt:6783
ACCEPT udp -- anywhere anywhere udp dpt:6783
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
ACCEPT icmp -- anywhere anywhere icmp echo-request
LOG all -- anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix "iptables denied: "
REJECT all -- anywhere anywhere reject-with icmp-port-unreachable
DROP tcp -- anywhere 172.17.0.1 tcp dpt:6783
DROP udp -- anywhere 172.17.0.1 udp dpt:6783
DROP udp -- anywhere 172.17.0.1 udp dpt:6784
ACCEPT udp -- anywhere anywhere udp dpt:domain
ACCEPT tcp -- anywhere anywhere tcp dpt:domain
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DROP all -- anywhere anywhere
DROP all -- anywhere anywhere
DOCKER all -- anywhere anywhere
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT all -- anywhere anywhere
ACCEPT all -- anywhere anywhere
REJECT all -- anywhere anywhere reject-with icmp-port-unreachable
ACCEPT all -- anywhere anywhere
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere
Chain DOCKER (1 references)
target prot opt source destination
Результат $ sudo docker version
Client:
Version: 1.9.1-cs2
API version: 1.21
Go version: go1.4.3
Git commit: 4ade326
Built: Mon Nov 30 21:56:07 UTC 2015
OS/Arch: linux/amd64
Server:
Version: 1.9.1-cs2
API version: 1.21
Go version: go1.4.3
Git commit: 4ade326
Built: Mon Nov 30 21:56:07 UTC 2015
OS/Arch: linux/amd64
Результат sudo docker ps
:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f113435b781b myrepo/app:latest "elixir --erl '-smp d" 35 seconds ago Up 34 seconds 0.0.0.0:80->4000/tcp app-1.App.15ffa2c2
6e2879fd9f2c postgres:9.5 "/docker-entrypoint.s" 37 minutes ago Up 5 minutes 5432/tcp postgres-1.App.cbe400ac
Для последнего вывода: приложение Phoenix, конечно, запускается только в том случае, если я не выполняю миграции. Обычно это в конце моего Dockerfile:
CMD ["elixir", "--erl", "-smp disable", "/usr/local/bin/mix", "do", "compile", ",", "ecto.create", ",", "ecto.migrate", ",", "phoenix.server"]
Что я изменил на следующее, чтобы получить этот вывод
CMD ["elixir", "--erl", "-smp disable", "/usr/local/bin/mix", "do", "compile", ",", "phoenix.server"]
Я нашел решение: проблема заключалась в настройках iptables. У меня было правило отказаться от пересылки, если не указано иное:
-A FORWARD -j REJECT
Решением было избавиться от этого правила.
Редактировать:
Как отметил @Zoredache, просто удаление правила - это, очевидно, плохая идея, и я идиот. Правильный способ - поместить его в самый конец цепочки правил после правил, созданных докером. Итак, если у вас такая же проблема, это работает для меня:
Избавьтесь от правила
$ sudo iptables -D FORWARD -j REJECT
Добавьте его снова, чтобы переместить в конец набора
$ sudo iptables -A FORWARD -j REJECT
Убедитесь, что они расположены в правильном порядке. Так что правило отклонить все остальное должно быть в самом конце.
$ sudo iptables -v -L FORWARD
В Debian вы можете убедиться, что правила по-прежнему применяются при перезапуске сервера.:
Когда вы будете довольны, сохраните новые правила в главном файле iptables:
$ iptables-save > /etc/iptables.up.rules
Чтобы убедиться, что правила iptables запускаются при перезагрузке, мы создадим новый файл:
$ editor /etc/network/if-pre-up.d/iptables
Добавьте к нему эти строки:
#!/bin/sh
/sbin/iptables-restore < /etc/iptables.up.rules
Файл должен быть исполняемым, поэтому измените разрешения:
$ sudo chmod +x /etc/network/if-pre-up.d/iptables