我在 Debian 8.3 节点上有两个 Docker 容器。一个是官方的 postgres 镜像,一个是基本的 phoenix / elixir 应用程序。我用 docker 堆栈文件链接了这两个容器。但是,除非我发布端口,否则 phoenix 无法连接到 postgres。这让我认为内部 docker 网络出了问题,而且由于该节点是全新安装的 Debian,所以可能是 iptables 出了问题。这也排除了密码或主机名错误的可能性。
我做错了什么?设置 iptables 规则以允许两个容器进行通信的正确方法是什么?
凤凰应用程序中的错误信息:
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 App 中的数据库配置(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 App 当然只有在我不运行迁移时才会启动。通常这在我的 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"]
答案1
我找到了解决方案:问题出在 iptables 设置上。我在其中设置了一条规则,除非另有定义,否则将放弃所有转发:
-A FORWARD -j REJECT
解决方案就是废除这条规则。
编辑:
正如 @Zoredache 指出的那样,直接删除规则显然是个坏主意,我是个白痴。正确的方法是将其放在规则链的最末端,在 docker 创建的规则之后。所以如果你有同样的问题,这对我来说是有效的:
摆脱规则
$ 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