docker网络原理

以下内容引用Docker —— 从入门到实践

当 Docker 启动时,会自动在主机上创建一个 docker0 虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。 同时,Docker 随机分配一个本地未占用的私有网段(在 RFC1918 中定义)中的一个地址给 docker0 接口。比如典型的 172.17.42.1,掩码为 255.255.0.0。此后启动的容器内的网口也会自动分配一个同一网段(172.17.0.0/16)的地址。 当创建一个 Docker 容器的时候,同时会创建了一对 veth pair 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 eth0;另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头(例如 vethAQI2QT)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。如图 

docker network

 

下面以自定义的容器方式,一步步配置网络, 达到以下目标:

  • 容器间能够通信
  • 容器能够联外网

首先创建一个容器,但不使用默认网络配置,使用--net=none选项:

docker run -t -i --net=none ubuntu:14.04 bash
docker ps # 获取容器id=d344e6e05a99

获取容器pid:

docker inspect d344e6e05a99 | grep -i "\<pid\""
#  "Pid": 27383,
pid=27383

创建netns,并把容器放入新建的netns中,好像不能使用ip netns命令创建,使用以下方法创建:

sudo ln -s /proc/$pid/ns/net /var/run/netns/$pid

验证是否创建成功:

sudo ip netns show
# 27383
# ns1
# test

可见命名为27383的netns已经成功创建!

接下来创建一个veth对,其中一个设置为容器所在的netns

sudo ip link add name veth_d344 type veth peer name veth_d344_peer
sudo ip link set veth_d344_peer netns $pid

进入$pid netns设置网卡名称和ip:

sudo ip netns exec  27383 bash
sudo ip link set veth_d344_peer name eth0
sudo ifconfig  eth0 10.0.0.2/24 # 设置ip为10.0.0.2
ping 10.0.0.2 # 能ping通
exit

在容器中ping 10.0.0.2也能ping通,说明设置正确

ping 10.0.0.2 # 应该不通
docker exec d344e6e05a99 ping 10.0.0.2 # 成功ping通

创建网桥,并把veth另一端的虚拟网卡加入新创建的网桥中:

sudo brctl addbr br0 # 创建新网桥br0
sudo brctl addif br0 veth_d344 # 把虚拟网卡加入网桥br0中
sudo ifconfig br0 10.0.0.1/24 # 设置网桥ip
sudo ip link set veth_d344 up # 启动虚拟网卡

测试下:

ping 10.0.0.2 # 成功ping通
docker exec d344e6e05a99 ping 10.0.0.1 # 成功ping通

若以上两个都能ping通说明配置成功!

最后,我们需要使得容器能够联外网,需要设置NAT,使用iptables设置:

sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o em1 -j MASQUERADE

设置容器默认路由为网桥ip(注意在容器内使用route add 添加, 会出现SIOCADDRT: Operation not permitted错误), 因此只能使用ip netns exec设置:

sudo ip netns exec 27383 route add default gw 10.0.0.1

测试,此时请确保宿主机能够联外网,进入容器内部:

ping baidu.com # 成功ping通,确保icmp没有被禁

 

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

docker安装和使用

安装docker

测试环境: ubuntu14.04 64位

一命令式安装

wget -qO- https://get.docker.com/ | sh

把用户加入docker组

默认只有root用户能进行操作,不过docker组也具有权限,因此若需要当前用户不需要root 也能访问docker,需要把用户加入到docker组:

sudo usermod -a -G docker username

启动docker服务

sudo service docker start

测试是否安装好

docker info

若输出以下则说明安装成功

Containers: 0
Images: 0
Storage Driver: aufs
Root Dir: /var/lib/docker/aufs
Backing Filesystem: extfs
Dirs: 0
Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.16.0-41-generic
Operating System: Ubuntu 14.04.2 LTS
CPUs: 4
Total Memory: 7.707 GiB
Name: Alibaba-fgp
ID: GA2K:3OV2:GNVU:PETS:DBZR:OYYP:FHWE:3QM5:QZPW:XNCM:IHP6:U2KN

启动第一个实例

docker run -t -i --rm busybox

此时应该会先从docker hub中拉取镜像,然后启动容器实例

其他命令

docker images #查看本地镜像
docker search hadoop #搜索hadoop相关镜像
docker pull krystism/openstackclient # 下载krystism/openstackclient镜像到本地
docker ps # 查看运行实例
docker ps -a # 查看所有实例,包括没有运行的
docker run -t -i ubuntu:14.04 # 运行ubuntu 14.04镜像
docker start id # 启动指定id实例(开机)
docker stop id # 停止指定id实例(关机)
docker rm id # 删除指定id实例(移除)
docker rmi id # 删除指定i镜像

 

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

使用Docker部署Gitlab

1. 下载gitlab镜像

docker pull gitlab/gitlab-ce

2. 运行gitlab实例

GITLAB_HOME=`pwd`/data/gitlab
docker run -d \
    --hostname gitlab \
    --publish 8443:443 --publish 80:80 --publish 2222:22 \
    --name gitlab \
    --restart always \
    --volume $GITLAB_HOME/config:/etc/gitlab \
    --volume $GITLAB_HOME/logs:/var/log/gitlab \
    --volume $GITLAB_HOME/data:/var/opt/gitlab \
    gitlab/gitlab-ce

3. 配置gitlab实例

3.1 配置邮箱:

docker exec -t -i gitlab vim /etc/gitlab/gitlab.rb

下面以网易163邮箱为例配置邮箱:

gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.163.com"
gitlab_rails['smtp_port'] = 25
gitlab_rails['smtp_user_name'] = "xxxx@163.com"
gitlab_rails['smtp_password'] = "xxxxpassword"
gitlab_rails['smtp_domain'] = "163.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = false
gitlab_rails['smtp_openssl_verify_mode'] = "peer"

gitlab_rails['gitlab_email_from'] = "xxxx@163.com"
user["git_user_email"] = "xxxx@163.com"

注意以上的xxxx@163.com代表用户名,即邮箱地址,而xxxxpassword不是邮箱的登陆密码而是网易邮箱的客户端授权密码, 再网易邮箱web页面的设置-POP3/SMTP/IMAP-客户端授权密码查看。

3.2 配置外部访问URL

这个必须配置,否则默认以容器的主机名作为URL,刚开始由于做了端口映射80->8080, 因此设置为

external_url "http://10.103.240.36:8080"

后来发现external_url只能配置ip或者域名,不能有端口,否则不能启动。 于是只能把端口设置为80->80,然后external_url设置为:

external_url "http://10.103.240.36"

4. 重启gitlab

docker restart gitlab

 

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

使用Docker创建数据容器

翻译自: Data-only container madness

1.什么是数据容器?

数据容器就是本身只创建一个volume供其他容器共享,创建完后即退出,不执行任何任务。比如通过以下方式创建一个postgres容器。

docker run --name dbdata postgres echo "Data-only container for postgres"

该容器运行echo "Data-only container for postgres"即退出,然而只要没有删除该容器,该容器/var/lib/postgresql/data的volume(在Dockerfile使用VOLUME指令定义)就会一直存在。

然后我们可以新建若干容器来共享数据,比如:

docker run -d --volumes-from dbdata --name db1 postgres

2.如何创建数据容器?

太简单了,创建任何容器,然后使用-v创建volume即可。但大家一定会想到用最小的镜像吧,比如hello-world,即

docker run -v /data hello-world

但这样是错误的! 为什么呢?

我们首先创建一个简单的镜像:

FROM debian:jessie
RUN useradd mickey
RUN mkdir /foo && touch /foo/bar && chown -R mickey:mickey /foo
USER mickey
CMD ls -lh /foo

构建:

docker build -t mickey_foo -< Dockerfile

运行下:

docker run --rm -v /foo mickey_foo

输出:

total 0
-rw-r--r-- 2 mickey mickey 0 Nov 18 05:58 bar

运行正常,没有任何问题。

下面我们尝试使用busybox来作为数据容器:

docker run -v /foo --name mickey_data busybox true
docker run --rm --volumes-from mickey_data mickey_foo

输出:

total 0
# Empty WTF??
docker run --rm --volumes-from mickey_data mickey_foo ls -lh /
total 68K
drwxr-xr-x   2 root root 4.0K Nov 18 06:02 bin
drwxr-xr-x   2 root root 4.0K Oct  9 18:27 boot
drwxr-xr-x   5 root root  360 Nov 18 06:05 dev
drwxr-xr-x   1 root root 4.0K Nov 18 06:05 etc
drwxr-xr-x   2 root root 4.0K Nov 18 06:02 foo
drwxr-xr-x   2 root root 4.0K Oct  9 18:27 home
drwxr-xr-x   9 root root 4.0K Nov 18 06:02 lib
drwxr-xr-x   2 root root 4.0K Nov 18 06:02 lib64
drwxr-xr-x   2 root root 4.0K Nov  5 21:40 media
drwxr-xr-x   2 root root 4.0K Oct  9 18:27 mnt
drwxr-xr-x   2 root root 4.0K Nov  5 21:40 opt
dr-xr-xr-x 120 root root    0 Nov 18 06:05 proc
drwx------   2 root root 4.0K Nov 18 06:02 root
drwxr-xr-x   3 root root 4.0K Nov 18 06:02 run
drwxr-xr-x   2 root root 4.0K Nov 18 06:02 sbin
drwxr-xr-x   2 root root 4.0K Nov  5 21:40 srv
dr-xr-xr-x  13 root root    0 Nov 18 06:05 sys
drwxrwxrwt   2 root root 4.0K Nov  5 21:46 tmp
drwxr-xr-x  10 root root 4.0K Nov 18 06:02 usr
drwxr-xr-x  11 root root 4.0K Nov 18 06:02 var
Owened by root? WTF???
docker run --rm --volumes-from mickey_data mickey_foo touch /foo/ba
touch: cannot touch '/foo/bar': Permission denied

发生了什么呢?我们的/foo 仍然存在, 但是它是空的并且所有者是root

让我们再试试使用我们刚刚构建的mickey_foo作为数据容器:

~: docker rm -v mickey_data # remove the old one
mickey_data
~: docker run --name mickey_data -v /foo mickey_foo true
~: docker run --rm --volumes-from mickey_data mickey_foo
total 0
-rw-r--r-- 1 mickey mickey 0 Nov 18 05:58 bar
# Yes!
~: docker run --rm --volumes-from mickey_data mickey_foo ls -lh /
total 68K
drwxr-xr-x   2 root   root   4.0K Nov 18 06:02 bin
drwxr-xr-x   2 root   root   4.0K Oct  9 18:27 boot
drwxr-xr-x   5 root   root    360 Nov 18 06:11 dev
drwxr-xr-x   1 root   root   4.0K Nov 18 06:11 etc
drwxr-xr-x   2 mickey mickey 4.0K Nov 18 06:10 foo
drwxr-xr-x   2 root   root   4.0K Oct  9 18:27 home
drwxr-xr-x   9 root   root   4.0K Nov 18 06:02 lib
drwxr-xr-x   2 root   root   4.0K Nov 18 06:02 lib64
drwxr-xr-x   2 root   root   4.0K Nov  5 21:40 media
drwxr-xr-x   2 root   root   4.0K Oct  9 18:27 mnt
drwxr-xr-x   2 root   root   4.0K Nov  5 21:40 opt
dr-xr-xr-x 121 root   root      0 Nov 18 06:11 proc
drwx------   2 root   root   4.0K Nov 18 06:02 root
drwxr-xr-x   3 root   root   4.0K Nov 18 06:02 run
drwxr-xr-x   2 root   root   4.0K Nov 18 06:02 sbin
drwxr-xr-x   2 root   root   4.0K Nov  5 21:40 srv
dr-xr-xr-x  13 root   root      0 Nov 18 06:05 sys
drwxrwxrwt   2 root   root   4.0K Nov  5 21:46 tmp
drwxr-xr-x  10 root   root   4.0K Nov 18 06:02 usr
drwxr-xr-x  11 root   root   4.0K Nov 18 06:02 var
# YES!!
~: docker run --rm --volumes-from mickey_data mickey_foo touch /foo/baz
~: docker run --rm --volumes-from mickey_data mickey_foo ls -lh /foo
total 0
-rw-r--r-- 1 mickey mickey 0 Nov 18 06:11 bar
-rw-r--r-- 1 mickey mickey 0 Nov 18 06:12 baz
# YES!!!

由于我们刚刚使用了相同的镜像作为数据容器镜像,共享的容器能够找到共享数据。为什么使用busybox不可以呢?由于busybox没有/foo这个目录,当我们使用-v创建/foo这个数据卷时,docker会以默认用户自动创建对应的目录(这里是root),而--volumes-from仅仅是重用存在的卷,而不会对卷自动做任何事情。因此当我们尝试去写/foo时由于没有权限(root所有,mickey用户).

因此我们应该使用和共享的容器相同的镜像做数据容器镜像?是的!

那我们使用这么大的镜像不会浪费空间么?

3. 为什么不使用小镜像作为数据容器?

其中一个原因,在上一节已经解释。遗留的一个问题是使用这么大的镜像(因为一般的镜像都会比较大)会不会浪费空间呢?

首先我们需要知道Docker的文件系统是如何工作的。Docker镜像是由多个文件系统(只读层)叠加而成。当我们启动一个容器的时候,Docker会加载只读镜像层并在其上(译者注:镜像栈顶部)添加一个读写层。如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除Docker容器,并通过该镜像重新启动时,之前的更改将会丢失。在Docker中,只读层及在顶部的读写层的组合被称为Union File System(联合文件系统)。

因此当我们创建了一个debian容器实例时(大约150MB),根据以上的原理,我们再创建1000个debian镜像能够重用原来的只读层,需要的空间还是150MB.

容器本身并不会占任何空间,除非你修改了内容。

因此Docker无论创建一个镜像的多少实例,都不会占据更多的空间。

因此实际上,我们为了创建数据容器而使用busybox反而会占用更多的空间,这个空间就是busybox的镜像大小。

实际上我们经常这样使用:

~: docker run --name mydb-data --entrypoint /bin/echo mysql Data-only container for mydb
~: docker run -d --name mydb --volumes-from mydb-data mysql

上面的实例指行/bin/echo mysql Data-only container for mydb,能够更容易知道这是一个数据容器,利于使用grep查找.

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!