Docker实践(6) – 容器内部连接(不需要在主机上映射端口)

上一个技术点展示了如何通过暴露端口来开放你的容器到主机网络。不过当你只是想容器内部之间连接时,就不再想把端口暴露给主机网络或外部网络了。
在本文将介绍如何通过Docker link参数来实现这个目的,来确保外部无法访问你的内部服务。

问题

你想要允许容器内部之间的连接。

方法

通过docker的链接功能来允许容器相互通信。

讨论

继续我们wordpress的设置,我们打算从wordpress容器分离出mysql数据库,然后不通过端口映射方式来使它们能够相互通信。如图:
虚拟化技术
按照如下命令来运行容器,第一个和第二个命令相隔一分钟执行:

  1. $ docker run –name wp-mysql -e MYSQL_ROOT_PASSWORD=yoursecretpassword -d mysql
  2. $ docker run –name wordpress –link wp-mysql:mysql -p 10003:80 -d  wordpress

在第一个命令中,给容器设置了wp-mysql名称以便之后引用。并且提供一个环境变量(-e MYSQL_ROOT_PASSWORD=yoursecretpassword)来使mysql容器能够完成初始化。
在第二个命令中,以wordpress命名容器,并通过–link wp-mysql:mysql来连接wp-mysql与wordpress容器。注意链接不会等待链接容器启动完成,所以上一步的操作需要等待一分钟以便让mysql容器先完成启动。不过最好的方法是通过docker logs wp-mysql来查看mysql容器是否已经准备接受连接了。
在这个示例中最重要的是–link参数,这个参数使wordpress容器自动维护/etc/hosts文件来使wp-mysql mysql等名称指向mysql容器IP。

启动次序事项:容器必须按正确的次序来启动以便存在的容器映射能够正确的替换。目前为止链接的动态解析不是docker的一项功能。

为了能够让容器能够被连接,比如mysql容器,就需要在创建mysql镜像时暴露端口,如暴露3306端口,这个可以在Dockerfile使EXPOSE完成。
至于为什么为这样做,请参考http://dockone.io/article/455

Docker实践(5) – 通过端口连接容器

Docker容器从一开始就设计用来运行服务。在大多数情况下,会是一种HTTP服务或其它。其中很大一部分是通过浏览器访问的Web服务。
这会导致一个问题。如果你有多个运行在它们内部环境的80端口上的Docker容器,它们不能都通过你机器上的80端口访问。下一次技术点将介绍如何通过从你的容器暴露和映射一个端口来处理这个常见的情景。

问题

你想通过你机器上的端口使你的容器能够得到访问。

方法

使用docker的-p参数来映射容器的端口到你的主机。

讨论

在这个示例中我们准备用tutum-wordpress镜像。假设你要在主机上运行两个容器来创建不同的博客。因为很多人想要这样做,有人已经准备好一个镜像让其他人能够获取和启动。你可以使用docker pull命令来得到外部位置的镜像。默认下,镜像会从docker hub去下载。

  1. $ docker pull tutum/wordpress

运行第一个博客,执行如下命令:

  1. $ docker run -d -p 10001:80 –name blog1 tutum/wordpress

docker run命令以守护进程(-d),参数-p运行容器。它标识要映射主机的10001端口到容器的80端口,并设置了一个容器名称用来识别它(–nameblog1tutum/wordpress)。
你可以用同样的方法创建第二个博客:

  1. $ docker run -d -p 10002:80 –name blog2 tutum/wordpress

如果你现在运行这个命令,

  1. $ docker ps -a | grep blog

你会看到两个博客窗口列出,有它们的端口映射,类似如下:
9afb95ad3617 tutum/wordpress:latest “/run.sh”
➥ 9 seconds ago Up 9 seconds
3306/tcp, 0.0.0.0:10001->80/tcp blog1 31ddc8a7a2fd tutum/wordpress:latest
➥ “/run.sh” 17 seconds ago Up 16 seconds 3306/tcp, 0.0.0.0:10002->80/tcp blog2
你可以在浏览器中通过http://localhost:10001和http://localhost:10002访问这两个博客容器。
完成后要删除这些容器(假设你不想保留):

  1. $ docker rm -f blog1 blog2

如果需要,你现在应该能够通过自己管理端口映射来运行多个相同的镜像来提供不同的服务了。

Docker实践(4) – 使用socat查看Docker API流量详情

docker命令可能会偶尔地无法正常工作。大多数情况下是命令行参数没有输入正确,不过也有偶尔一些是严重的配置问题,如Docker二进制文件过期了。为了诊断此问题,查看你正在连接的docker daemon的数据流可能会有所帮助。

问题

你想用docker命令来调度一个问题。

方法

使用流量探测器来检查API调用并输出调用详情。

讨论

在此技术中你将在你的请求与服务器socket之间安放一个Unix域套接字代理来查看传过来了什么东西。现在需要root或sudo权限来做这件事。
要创建这个代理,需要用到socat命令。

socat是一个允许你在几乎任何类型的两个数据通道之间中继数据。

  1. $ sudo socat -v UNIX-LISTEN:/tmp/dockerapi.sock
  2.           UNIX-CONNECT:/var/run/docker.sock &

虚拟化技术
在这个命令中-v参数输出数据流的可读格式。UNIX-LISTEN让socat监听一个域套接字,UNIX-CONNECT让socat连接到Docker的域套接字。’&’使命令后台运行。
你的请求到docker的路由可以看下面。
所有的流量都将被socat看到并记录到你的终端。
虚拟化技术
一个简单的docker命令输出类似如下:
虚拟化技术
socat不仅仅是用来调试docker,也可以用来调试任何的网络服务。

Docker实践(3) – 移动docker到一个不同的分区

Docker把与你的容器和镜像相关的数据保存到一个目录下。由于可能会存储潜在的大量不同的镜像,这个目录会很快变得很大!
如果你的主机有不同的分区,你可能会更快遇到空间受限的问题。在这种情况下,你可能需要把Docker的数据目录移动到其它分区。

问题

你想要移动docker的数据

方法

停止docker daemon,使用-g参数指定新目录启动。

讨论

首先你需要停止你的docker daemon。
假设你想把docker数据放在/home/dockerusr/mydocker目录下,我们先把原来的目录迁移过来:

  1. cp -a /var/lib/docker/* home/dockerusr/mydocker/

然后启动docker:

  1. docker daemon -g /home/dockeruser/mydocker

这时候你通过执行docker images或docker ps -a就可以看到原来的镜像或容器。确认好之后就可以删除原来的数据了。
当然记得把此更改添加到配置文件。

Docker实践(2) – 以守护进程运行容器

当你逐渐了解docker后,你会开始考虑docker的其它用例,第一个首先是运行服务一样运行docker。

问题

你想作为一个服务在后台运行docker.

方法

docker run命令使用-d参数,和使用与容器管理相关的参数来定义服务特性。

讨论

Docker容器 – 像大多数进程 – 默认在前台运行。在后台运行docker容器最常见的方式是使用&控制操作符。即使这个可行,不过当你的日志输出到终端会话时可能会遇到麻烦,或者你使用nohup命令,它会当前目录创建一个日志输出文件,不过增加了一个日志管理的问题。而docker提供有在后台运行的功能,如:

  1. $ docker run -d -i -p 1234:1234 –name daemon ubuntu nc -l 1234

当docker run命令使用-d参数时,docker会以守护进程运行。-i参数使你能够与Telnet会话交互。-p参数使容器的1234端口绑定到主机。–name参数设置容器一个名称方便以后引用。最后,运行了一个监听在1234端口的echo服务器。
如果你现在连接并使用telnet发送消息,你使用docker logs命令会看到容器已经接收到消息,如图:
虚拟化技术
你看到以守护进程运行容器足够简单,但操作上仍有一些问题需要回答:
* 服务失败时会怎样?
* 服务终止时会怎样?
* 如果服务持续故障会怎样?
幸运地是docker为每个问题提供了参数!

虽然restart参数经常与-d参数一起使用,但技术上来说这不是必要的。

docker run –restart命令允许容器终止时应用一系列的规则(也称重启策略)。
重启策略可选值:
no – 容器退出时不重启
always – 容器退出时始终重启
on-failure[:max-retry] – 只在发生错误时重启
no策略很简单:当容器退出时,不重启容器。这个是默认值。
always也同样简单,不过值得简单介绍下:

  1. $ docker run -d –restart=always ubuntu echo done

此命令以守护进程运行容器,并在进程终止时始终重启容器(–restart=always)。执行了一个快速完成的echo命令,然后退出容器。
如果你执行以上命令然后执行docker ps命令,会看到类似的输出:
虚拟化技术
注意STATUS列告诉我们容器少于一秒前退出,正在重启。这是因为echo done命令立即退出了,docker就必须持续地重启容器。
最后on-failure策略只在容器以非0(非正常失败)状态码退出时重启:

  1. $ docker run -d –restart=on-failure:10 ubuntu /bin/false

此命令以守护进程运行(-d)和对尝试重启设置一个限制值(–restart=on-failure:10),超过此次数时不再重启。它运行一个简单的命令(/bin/false),会快速完成且肯定会失败。
如果你执行以上命令,等一分钟后执行docker ps -a,你会看到类似的输出:
虚拟化技术

Docker实践(1) – 设置守护进程对外开放

虽然默认下你的Docker daemon只允许在本机访问,但有时候也有充足理由让其他人访问。你可能有什么问题想让其他人远程调试,或者你可能想允许DevOps工作流来启动主机上的一个进程。

INSECURE!虽然这是一个强大且有用的技术,但是这认为是不安全的。一个开放的Docker daemon可能被其他人利用来获取系统权限。

问题

你想对其他人开放Docker服务器的访问权限

方法

以开放的TCP地址启动Docker daemon

讨论

以下图是这种技术的工作原理.
虚拟化技术
在你开放Docker daemon之前,首先得关闭它。如何关闭它取决于你的系统,如果你不清楚,先执行如下命令:

  1. $ sudo service docker stop

如果你得到如下信息:
The service command supports only basic LSB actions (start, stop, restart,
try-restart, reload, force-reload, status). For other actions, please try
to use systemctl.
表示你的系统启动管理是基于systemctl的,尝试如下命令:

  1. $ systemctl stop docker

如果这个可行,那么下面的命令将没有任务输出:

  1. ps -ef | grep -E ‘docker (-d|daemon)b’ | grep -v grep

一旦Docker daemon已经停止,你就可以使用以下的命令手动重启来打开对外用户的访问:

  1. docker daemon -H tcp://0.0.0.0:2375

此命令以守护进程启动docker,使用-H参数为定义主机服务器,使用TCP协议,监听所有IP(使用0.0.0.0),开放docker服务器标准端口2375。如果Docker提示daemon是一个无效子命令,尝试使用旧的-d参数。
之后你可以在外面使用如下命令来访问docker:

  1. $ docker -H tcp://<your host’s ip>:2375

注意你在本机也使用用此方法来连接docker,因为docker已经不再监听默认的地址。
如果你想让此更改永久生效(即使重启后),虽然更改docker的配置文件:
系统Ubuntu / Debian / Gentoo配置文件为:/etc/default/docker
OpenSuse / CentOS / Red Hat配置文件为:/etc/sysconfg/docker
在配置文件中找到DOCKER_OPTS,设置为以上的启动参数,如DOCKER_OPTS=”daemon -H tcp://0.0.0.0:2375″
如果你的系统使用的是systemd配置文件,那么在/usr/lib/systemd/system/service/docker中找到ExecStart行,设置为ExecStart=/usr/bin/docker daemon -H tcp://0.0.0.0:2375

开放docker daemon后,建议使用防火墙来限制对指定IP开放访问,而且不使用0.0.0.0,非常不安全。

virtualbox host-only网络设置

  • 1、打开virtualbox,设置虚拟机的网络连接方式为host-only。
  • 2、在宿主机器的网卡上,如本地连接,右键属性,切换到共享,勾选允许共享网络,这样虚拟机才能可能连接互联网,这时VirtualBox Host-Only Network虚拟网卡ip会设置为192.168.137.1。
  • 3、开启虚拟机,设置虚拟机的网络的ip为192.168.137.x,子掩码255.255.255.0,网关192.168.137.1。
  • 经过以上三步的设置,宿主机可以连接虚拟机,虚拟机也可以上网了。

    CentOS 6安装配置KVM

    上一篇文章介绍过CentOS 6安装Xen,有点麻烦,因为自CentOS 6开始,虚拟化平台已经从xen转向KVM,所以在CentOS 6安装KVM还是非常的简单的。

    1、安装KVM

    1. yum groupinstall Virtualization ‘Virtualization Client’

    2、安装api支持

    1. yum install libvirt
    2. service libvirtd start

    3、验证是否已经载入KVM模块

    1. $ lsmod | grep kvm
    2. kvm
    3. kvm_intel

    4、使用virt-manager安装guest

    virt-manager是一个api gui工具,可以很方便的管理虚拟机。下面简单介绍使用virt-manager安装虚拟机系统。
    1)打开virt-manager,Add-connection,hypervisor选择QEMU/KVM,点击connect。
    2)在连接localhost(QEMU)右键-NEW,输入guest名称,还有选择安装方式,我这里选择iso文件安装,点击forword。
    3)选择iso文件路径,点击forword。
    4)定义分配内存及cpu个数。
    5)定义磁盘映像大小。
    6)下一步就是常规的系统安装了。

    5、使用virt-install安装guest

    当你的CentOS没有桌面环境时,可以使用virt-install命令安装,如:
    1、输入虚拟机名称
    2、分配多少内存
    3、定义虚拟机磁盘映像的位置
    4、此步可以直接输入iso的位置或是url
    5、进行系统常规安装

    CentOS 6安装配置Xen

    centos 6安装xen并不像centos 5那样轻松,因为在centos 6中,官方源已经去除了xen的rpm包,只能使用第三方源或自行编译,这里推荐使用第三方源,编译安装要解决的问题比较多。还有一个包libvirt,这个是管理xen的api,官方的这个包已经不支持xen,并且是0.9版的需要更高版本的iptables支持,所以libvirt需要编译安装。

    下面是在CentOS 6 64位系统进行安装配置Xen。

    1、安装Xen及内核

    1. rpm -Uvh http://www.crc.id.au/repo/x86_64/kernel-xen-release-6-3.noarch.rpm //导入第三方源安装Xen
    2. yum install bridge-utils //安装网桥设置工具
    3. yum install kernel-xen xen //安装xen及内核

    2、配置grub引导xen内核

    xen内核安装后,会自动插入引导xen内核代码到/boot/grub/grub.conf文件,但还需要进行相应的修改。

    修改后引导xen内核的代码如下:

    1. title CentOS (2.6.32.56-1.el6xen.x86_64)
    2. root (hd0,7)
    3. kernel /xen.gz dom0_mem=1024M loglvl=all guest_loglvl=all
    4. module /vmlinuz-2.6.32.56-1.el6xen.x86_64 ro root=/dev/mapper/VolGroup-LogVol00 rd_LVM_LV=VolGroup/LogVol00 nomodeset
    5. module /initramfs-2.6.32.56-1.el6xen.x86_64.img

    这段代码仅够参考,不一定适用于你的配置。

    3、关闭selinux

    这是必须关闭的,要不会因为selinux的安全机制导致xen无法正常工作。

    编辑selinux配置文件

    1. vi /etc/sysconfig/selinux

    修改为如下:

    1. # This file controls the state of SELinux on the system.
    2. # SELINUX= can take one of these three values:
    3. # enforcing – SELinux security policy is enforced.
    4. # permissive – SELinux prints warnings instead of enforcing.
    5. # disabled – No SELinux policy is loaded.
    6. SELINUX=disabled
    7. # SELINUXTYPE= can take one of these two values:
    8. # targeted – Targeted processes are protected,
    9. # mls – Multi Level Security protection.
    10. SELINUXTYPE=targeted

    还有一点配置也很重要,就是禁止netmanager接管网络,因为这个工具不支持网桥管理。
    在/etc/sysconfig/network-script/ifcfg-eth0中加入:

    1. NM_CONTROLLED=no

    4、重启引导进入xen内核

    引导后执行xm info测试:

    1. [root@localhost ~]# xm info
    2. host                   : localhost.localdomain
    3. release                : 2.6.32.56-1.el6xen.x86_64
    4. version                : #1 SMP Mon Feb 20 13:03:03 EST 2012
    5. machine                : x86_64
    6. nr_cpus                : 4
    7. nr_nodes               : 1
    8. cores_per_socket       : 2
    9. threads_per_core       : 2
    10. cpu_mhz                : 2394
    11. hw_caps                : bfebfbff:28100800:00000000:00003f40:009ae3bd:00000000:00000001:00000000
    12. virt_caps              : hvm
    13. total_memory           : 1972
    14. free_memory            : 412
    15. free_cpus              : 0
    16. xen_major              : 4
    17. xen_minor              : 1
    18. xen_extra              : .2
    19. xen_caps               : xen-3.0-x86_64 xen-3.0-x86_32p hvm-3.0-x86_32 hvm-3.0-x86_32p hvm-3.0-x86_64
    20. xen_scheduler          : credit
    21. xen_pagesize           : 4096
    22. platform_params        : virt_start=0xffff800000000000
    23. xen_changeset          : unavailable
    24. xen_commandline        : dom0_mem=1024M loglvl=all guest_loglvl=all
    25. cc_compiler            : gcc version 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC)
    26. cc_compile_by          : mockbuild
    27. cc_compile_domain      : crc.id.au
    28. cc_compile_date        : Mon Feb 20 12:52:37 EST 2012
    29. xend_config_format     : 4

    5、安装api管理工具

    1. yum install virt-install virt-viewer

    6、编译安装libvirt

    因为rpm安装的libvirt不支持xen连接,所以我们使用编译安装。还有版本也不能选择高的,不然可能会因为iptables的版本低而不能使用libvirt。

    1. yum install gcc xen-devel libxml2-devel gnutls-devel device-mapper-devel libnl-devel make
    2. cd /tmp
    3. wget http://libvirt.org/sources/libvirt-0.8.1.tar.gz
    4. tar xzf libvirt-0.8.1.tar.gz
    5. cd libvirt-0.8.1
    6. ./configure –with-xen –with-xen-inotify –with-libvirtd
    7. make && make install

    然后打开/usr/local/etc/libvirt/libvirtd.conf文件,删除unix_sock_dir = “/var/run/libvirt”前面的注释,并创建/var/run/libvirt目录。
    之后启动libvirtd,并设置开机启动

    1. libvirtd -d
    2. echo "/usr/local/sbin/libvirtd -d" >>/etc/rc.d/rc.local

    7、安装xen guest

    我们这里使用virt-install工具进行guest的安装,当然你也可以使用xm命令安装,不过相对麻烦点。

    可以执行virt-install –help学习这工具的使用方法。
    下面是安装guest的示例

    1. virt-install -n CentOSVM1 -r 512 -f /xen/CentOSVM1.img  -l http://www/  –network=network:default -s 10 –nographics –vcpus=2

    或者使用交互式安装

    1. virt-install –prompt

    -n CentOSVM1 设置虚拟服务器名称
    -r 512 设置内存大小
    -f /xen/CentOSVM1.img  虚拟磁盘文件的保存路径,如果有重名可以使用–force参数强制重建。
    -l http://www/ 安装文件的访问方式,支持nfs http ftp smb等多种方式。如果你对你的带宽比较自信,可以使用http://mirrors.163.com/centos/5.5/os/i386/网易的镜像站
    –network=network:default 网络连接方式,我选择的是route中的default
    -s 10磁盘文件的大小,单位是G
     –nographics 不使用图形界面,可以不加此参数,然后加–vnc看看图形效果
     –vcpus=2 虚拟CPU的个数
     在执行virt-install命令安装系统之前,先确定你的安装源,支持nfs http ftp smb等多种方式,宽带大的话,可以使用http直接连接远程安装源,比如mirror.163.com。
     我这里adsl上网的,本地已经下载有iso文件了,所以在本地架设个http服务器进行安装。
    1、挂载iso文件到/iso目录,这个目录必须存在。

    1. mount -o loop CentOS-6.2-x86_64-minimal.iso /iso

    2、安装httpd服务器,我这里安装apache

    1. yum install httpd
    2. service httpd start

    3、创建软链接或直接复制文件到apache根目录/var/www/html,如果你的centos 6.2是完整版,只需要做个软链接,如果下载的是精简版,也就是minimal,需要复制全部文件到根目录,因为.treeinfo这个文件需要相应的修改。

    centos 6.2完整版:

    1. ln -s /iso /var/www/html

    centos 6.2精简版:

    1. cp -R /iso  /var/www/html

    修改.treeinfo文件,如:

    1. [general]
    2. family = CentOS
    3. timestamp = 1323560005.81
    4. variant =
    5. totaldiscs = 1
    6. version = 6.2
    7. discnum = 1
    8. packagedir =
    9. arch = x86_64
    10.  
    11. [images-x86_64]
    12. kernel = isolinux/vmlinuz
    13. initrd = isolinux/initrd.img
    14.  
    15. [images-xen]
    16. kernel = isolinux/vmlinuz
    17. initrd = isolinux/initrd.img
    18.  
    19. [stage2]
    20. mainimage = images/install.img

    4、在安装之前,需要暂时关闭iptables,否则安装过程中会无法取得相应文件。

    1. service iptables stop

    5、开始使用virt-install安装

    1. virt-install -n centos6 -r 512 -f /xen/CentOSVM1.img -l http://192.168.1.100/iso –network=network:default -s 4 –vcpus=2

    之后会自动调用virt-viewer工具显示安装界面。
    6、启动guest centos6
    安装完成后会要求重启,这时guest关闭之后不会自动启动,需要使用xm start命令启动:

    1. xm start centos6

    7、开启iptables

    之前为了连接安装源,暂时关闭了iptables,现在需要启动iptables,否则guest无法连接外网。

    1. service iptables start

    8、使用virt-viewer管理guest

    1. virt-viewer centos6

    执行这条命令即可连接虚拟机centos6进行管理了,当然也可以直接使用ssh连接更简单。

    另外,我安装过virt-manager来安装guest,但到创建域时就出现KeyError错误,不知道如何解决,有懂的告诉一声。