使用 OpenVPN 互联多地机房及Dokcer跨主机/机房通讯

服务器网络结构及 Docker 网络配置

下面是整体网络结构:

这个是 Docker 在各服务器的网络配置

配置要求

将两地机房及办公室网络互联, 即 IDCA, IDCB, OFFICE 互相可访问

详细配置

将 IDCA 的网关(10.10.1.159) 作为 OpenVPN Server, IDCB的网关(10.11.1.171) 及 OFFICE 内的机器用作 OpenVPN Client

10.10.1.159 (IDCA 网关)

OpenVPN Server 配置

首先需要在这台机器上安装 OpenVPN Server, 并为 IDCB 及 OFFICE 生成相应的 SSL 证书, 以备后续使用

证书制作方法可见这里 http://www.lsproc.com/post/easyrsa-generate-ssl-cert/

证书 Common Name 要使用不同的名称

VPN 相关配置如下

port 1194
proto udp
dev tun
ca keys/ca.crt
cert keys/server.crt
key keys/server.key
dh keys/dh.pem
server 10.7.0.0 255.255.0.0

push "route 10.10.1.0 255.255.255.0"
push "route 10.11.0.0 255.255.0.0"
push "route 10.21.0.0 255.255.0.0"
push "route 10.31.0.0 255.255.0.0"
push "route 10.0.9.0 255.255.255.0"

client-config-dir ccd
route 10.11.0.0 255.255.0.0
route 10.31.0.0 255.255.0.0
route 10.0.9.0 255.255.255.0

client-to-client
duplicate-cn
keepalive 10 120
comp-lzo
persist-key
persist-tun
verb 4

其中 10.7.0.0/16 段的 IP 用于 OpenVPN 的 tun

另外需要在 ccd 目录下放置与 OpenVPM Client 证书文件(上面生成好的) Common Name 同名的文件, 内容如下

ccd/client-idcb:

iroute 10.11.0.0 255.255.0.0
iroute 10.31.0.0 255.255.0.0

ccd/client-office:

iroute 10.0.9.0 255.255.255.0

路由设置

vim /etc/sysconfig/network-scripts/eth0.route

ADDRESS0=10.21.2.0
NETMASK0=255.255.255.0
GATEWAY0=10.10.1.161

ADDRESS1=10.21.3.0
NETMASK1=255.255.255.0
GATEWAY1=10.10.1.163

手动执行命令示例如下:

route add -net 10.21.2.0/24 gw 10.10.1.161 eth0

此处 eth0 为内网网卡

10.11.1.171 (IDCB 网关)

OpenVPN Client 配置

现在此机器上安装 OpenVPN Client, 并使用之前生成的证书(client-idcb)连接 OpenVPN Server

相关配置如下:

client
dev tun
proto udp
remote x.x.x.x 1194
persist-key
persist-tun
ca /path/to/ca.crt
cert /path/to/client-idcb.crt
key /path/to/client-idcb.key
comp-lzo
verb 4
auth-user-pass /path/to/openvpn-secret

openvpn-secret 中可放置 VPN 的帐号密码, 实现自动登录, 格式如下

username
password

路由配置

vim /etc/sysconfig/network-scripts/eth0.route

ADDRESS0=10.31.2.0
NETMASK0=255.255.255.0
GATEWAY0=10.11.1.173

10.0.9.10 (OFFICE)

OpenVPN Client 配置

办公室的 OpenVPN Client 配置到 10.0.9.10 这台机器, 用于从服务器访问办公室内网使用

配置基本与 10.11.1.171 相同, 只需改成对应的证书就可以

client
dev tun
proto udp
remote x.x.x.x 1194
persist-key
persist-tun
ca /path/to/ca.crt
cert /path/to/client-office.crt
key /path/to/client-office.key
comp-lzo
verb 4
auth-user-pass /path/to/openvpn-secret

办公室内其他机器需要访问服务器内网的话需单独运行 OpenVPN Client, 服务端无需额外的路由配置

如果想要使办公室的其他电脑直接访问服务器内网可以将 10.0.9.10 当作网关

IDCA 内其他机器

路由配置

ADDRESS0=10.0.0.0
NETMASK0=255.0.0.0
GATEWAY0=10.10.1.159

IDCB 内其他机器

路由配置

ADDRESS0=10.0.0.0
NETMASK0=255.0.0.0
GATEWAY0=10.11.1.171

Docker 网络配置

为了使 Docker 能够访问外网以及与其他机器互访还需要做相应的配置

首先, 需要开启 ip_forward

/etc/sysctl.conf

net.ipv4.ip_forward = 1

sysctl -p 使配置生效

其次, 需要配置 iptables nat 规则, 示例如下:

iptables -t nat -A POSTROUTING -s 10.21.2.0/24 ! -o docker0 -j MASQUERADE

这里的 10.21.2.0 就是当前机器 docker0 网桥的 IP

Docker daemon iptables 开启(默认)时会自动创建这条规则

其他

OpenVPN 问题排查可查看日志文件 openvpn-status.log, 里面包含了已连接客户端及路由表信息

Docker 常见问题 (FAQ)

最后更新时间: 2015-06-08

内核需求

  • rhel/centos 要求内核在 2.6.32-431 (系统版本6.5) 及以上
  • debian/ubuntu 要求内核在 3.8 以上

修改镜像/容器文件的存储位置

方法一 修改 docker daemon 的启动参数

-g, --graph=""
  Path to use as the root of the Docker runtime. Default is /var/lib/docker.

docker -d --graph=/opt/docker

docker daemon 的启动参数修改方法

rhel/centos 下, 默认启动参数在 /etc/sysconfig/docker, 如:

6.x:

other_args="--graph=/opt/docker "

7.x: (update: 2015-01-21)

OPTIONS="--graph=/opt/docker "

debian/ubuntu 下, 默认启动参数在 /etc/default/docker, 如:

DOCKER_OPTS="--graph=/opt/docker "

方法二 指定挂载目录

mount -o bind /var/lib/docker /opt/docker

永久修改在需要在 /etc/fatab 添加:

/opt/docker      /var/lib/docker         none    bind            0 0

方法三 软连接

ln -s /var/lib/docker /opt/docker

容器刚运行就退出了(Exited)

很多人发现刚刚执行了一个 docker run 的命令, 再用 docker ps -a 的时候, 刚刚那个容器就已经 Exited 了, 比如

docker run -d <IMAGE> service sshd start

上述命令一执行完就会发现容器就退出了, 那是因为 service sshd start 不是一个前台进程, 这个进程执行完以后就会退出, 所以这里必须使用前台方式(nodaemon)运行进程, 上述例子中要使用

docker run -d <IMAGE> /usr/sbin/sshd -D

其他应用程序也要使用对应的 nodaemon 的参数, 没有的话就要加一个不会退出的命令, 如:

docker run -d <IMAGE> <CMD> && tail -f xxx.log

selinux (Permission denied)

当开启 selinux 经常会遇到 Permission denied 错误, 如:

docker run -i -v /data:/data ubuntu bash

然后运行 ls /data 时就会报如下错误

ls: cannot open directory .: Permission denied

解决方案有两个:

一是关闭宿主机的 selinux

二是在宿主机上添加 selinux 规则, 以上面为例:

chcon -Rt svirt_sandbox_file_t /data

其他情形下 如果是因为 selinux 引起 Permission denied 也要添加相应的规则或者直接关闭 selinux

容器固定 IP

pipework

  • OS: rhel/centos 6.x

在 rhel/centos 6.x 下使用 pipework 时会报如下错误:

Object "netns" is unknown, try "ip help".

原因是 rhel/centos 6.x 的 iproute 包默认并不支持 ip netns 命令, 所以这里需要安装新的 iproute 包

这里使用 RDO 的源:

yum install -y https://repos.fedorapeople.org/repos/openstack/openstack-icehouse/rdo-release-icehouse-4.noarch.rpm
yum install -y iproute

注意: 在这里最新的 RDO 源(openstack-juno)已经不支持 rhel/centos 6.x 了, 如有更新可以到这个目录下查看
如果已经安装了 openstack-juno 的需先卸载

进入容器

因为容器本身其实就是把进程/资源隔离了, 严格意义上讲不存在所谓的进入容器, 通常这里的所说的进入容器指的是在容器的 namespace 内执行 shell

小于 1.3 版本

小于 1.3 版本的可以使用 nsenter:

https://github.com/jpetazzo/nsenter

1.3 版本以上

如果 docker 版本已经在 1.3 以上了, 那么可以用 docker exec 这个命令:

docker exec -it <CONTAINER ID> bash

注意

ssh

不建议使用 ssh 进入容器, 关于为什么不建议使用, 请参考如下文章:

docker attach

为什么执行 docker attach 卡住了?

首先要明确的是 docker attach 不是一个用来进入容器的命令, 或者说他不是用来在容器内运行一个 bash(shell) 的命令, 它是用来连接到容器中运行中的进程, 也就是容器的 CMD, 容器内 PID=1 的那个进程, 如果这个进程没有 stdout/stderr 那么你将看不到任何输出, 如果它没有接收 stdin 你也无法发送指令给它. 这也就是为什么你运行一个 bash 的容器, 就可以 attach 进去执行命令, 而你运行一个 mysql server 的容器就无法操作的原因

如何退出 attach 的容器

这里要使用 CTRL-P CTRL-Q 来退出容器, 如果使用 CTRL+C 那么会导致容器结束(Exited), 因为它会发送 SIGKILL 给容器的进程, 然后这个容器就 Exited 了, 当然这里可以使用 docker attach --sig-proxy=false 防止发送 SIGKILL 给进程

https://docs.docker.com/reference/commandline/cli/#attach
You can detach from the container again (and leave it running) with CTRL-p CTRL-q (for a quiet exit), or CTRL-c which will send a SIGKILL to the container, or CTRL-\ to get a stacktrace of the Docker client when it quits. When you detach from the container's process the exit code will be returned to the client.

Docker 私有库自签名 SSL 报错 (Invalid registry endpoint ... unknown CA certificate)

docker 升级到 1.3 以后使用 docker pull/push 等命令时必须要求 registry 使用 SSL, 否则就会报如下错误

Error: Invalid registry endpoint https://registry.xx.com/v1/: Get https://registry.xx.com/v1/_ping: EOF. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry registry.xx.com` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/registry.xx.com/ca.crt

这里是因为你使用了自签名的证书导致系统不信任, 如报错信息所描述, 解决方法有两个

一是添加 --insecure-registry registry.xx.com 参数到 docker daemon

二是把私有 docker registry 的 CA 证书放到指定的位置(/etc/docker/certs.d/registry.xx.com/ca.crt)

这里的 CA 证书是指你签发 registry.xx.com 这个域名的 SSL 证书所用的 CA
证书的制作方法可参见 http://www.lsproc.com/post/easyrsa-generate-ssl-cert/

docker daemon 重启后某些容器无法启动 (device or resource busy)

当重启 docker daemon 有时候会导致某些容器无法启动, 手动启动容器会报如下错误

Error response from daemon: Cannot start container 5e9bde9b409b:
Error getting container 5e9bde9b409b001bcc685c0b478e925a53a03bab8d8ef3210bf24aa39410e30d
from driver devicemapper:
Error mounting '/dev/mapper/docker-253:0-267081-5e9bde9b409b001bcc685c0b478e925a53a03bab8d8ef3210bf24aa39410e30d'
on
'/var/lib/docker/devicemapper/mnt/5e9bde9b409b001bcc685c0b478e925a53a03bab8d8ef3210bf24aa39410e30d':
device or resource busy

目前来看这应该是一个 bug: docker/docker#5684, docker/docker#4036

解决办法有两个:

一是发现报错后, 在启动容器之前手动 umount:

umount /var/lib/docker/devicemapper/mnt/5e9bde9b409b001bcc685c0b478e925a53a03bab8d8ef3210bf24aa39410e30d

二是 docker daemon stop 时先停止容器运行, 修改 /etc/init.d/docker 如下

stop() {
    [ -x $exec ] || exit 5

    echo -n $"Stopping $prog: "
    if [[ -n $($exec ps -q) ]]; then
        $exec stop $($exec ps -q) > /dev/null
    fi
    killproc -p $pidfile -d 300 $prog
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}

不过这样因为会停止所有容器, 所以重启速度会变慢

另外重启后所有容器默认都不会启动, 如果让容器在 daemon 启动时自动开启, 那么需要在 docker run 时添加参数 --restart always, 如:

docker run -d --restart always <IMAGE> <CMD>

docker 1.6.x 关闭 iptables 时不能 link 容器

2015-06-08 更新

使用 docker 1.6, daemon 参数关闭了 iptables 选项时(--iptables=false), link 容器时会报错:

Cannot start container 35437aa2c224d580d7555a2f900b95d6d5ab282bb91c34e96ae0cda106557ff0: (exit status 1)

解决方法一是 daemon 打开 iptables(--iptables=true), 或者手动创建 DOCKER clain:

iptables -N DOCKER

bug 详情在此: https://github.com/docker/docker/issues/12701

docker-compose 使用2进制方式安装后每过一段时间就不可用

2015-10-11 更新

使用 docker-compose 的过程中发现每过一段时间2进制文件就不可用, 报如下错误

Cannot open self /usr/bin/docker-compose or archive /usr/bin/docker-compose.pkg

解决方法为(rhel/centos):

echo "-b /usr/bin/docker-compose" > /etc/prelink.conf.d/docker-compose.conf

debian/ubuntu 也找到 prelink 相应的配置文件地址增加上述配置即可

bug 详情在此: https://github.com/docker/compose/issues/1135

延伸阅读

2015-01-08 1041 docker faq