以下内容引用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 就创建了在主机和所有容器之间一个虚拟共享网络。如图
下面以自定义的容器方式,一步步配置网络, 达到以下目标:
首先创建一个容器,但不使用默认网络配置,使用--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没有被禁
测试环境: ubuntu14.04
64位
wget -qO- https://get.docker.com/ | sh
默认只有root用户能进行操作,不过docker组也具有权限,因此若需要当前用户不需要root 也能访问docker,需要把用户加入到docker组:
sudo usermod -a -G docker username
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镜像
docker pull gitlab/gitlab-ce
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
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-客户端授权密码
查看。
这个必须配置,否则默认以容器的主机名作为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"
docker restart gitlab
翻译自: Data-only container madness
数据容器就是本身只创建一个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
太简单了,创建任何容器,然后使用-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
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用户).
因此我们应该使用和共享的容器相同的镜像做数据容器镜像?是的!
那我们使用这么大的镜像不会浪费空间么?
其中一个原因,在上一节已经解释。遗留的一个问题是使用这么大的镜像(因为一般的镜像都会比较大)会不会浪费空间呢?
首先我们需要知道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
查找.