感谢支持
我们一直在努力

Docker基础与高级

最近在学习 Docker 看到国外一个牛人和自己的实践顾写下此篇文章,以用来为故而知新!

1.  Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app)。几乎没有性能开销,可以很容易地在机器和数据中心中运行。最重要的是,他们不依赖于任何语言、框架或包括系统。

2.

CentOS 6/7系列安装Docker http://www.linuxidc.com/Linux/2014-07/104768.htm

Docker的搭建Gitlab CI 全过程详解 http://www.linuxidc.com/Linux/2013-12/93537.htm

Docker安装应用(CentOS 6.5_x64) http://www.linuxidc.com/Linux/2014-07/104595.htm

在 Docker 中使用 MySQL http://www.linuxidc.com/Linux/2014-01/95354.htm

在Ubuntu Trusty 14.04 (LTS) (64-bit)安装Docker http://www.linuxidc.com/Linux/2014-10/108184.htm

Docker安装应用(CentOS 6.5_x64) http://www.linuxidc.com/Linux/2014-07/104595.htm

Ubuntu 14.04安装Docker  http://www.linuxidc.com/linux/2014-08/105656.htm

阿里云CentOS 6.5 模板上安装 Docker http://www.linuxidc.com/Linux/2014-11/109107.htm

1.Docker安装

2.devicemapper

3.玩转net namespace

•3.1. ENV(环境变量)

port map直接使用docker默认分配的IP对外提供服务(测试中)

•5.1. 使用参数以及将docker0的ip配为机房内网网段

Docker COMMAND

•6.1. docker参数

•6.2. run

搭建私有Registry注册中心

•7.1. 下载软件

•7.2. 启动服务

•7.3. 使用

•7.4. 套一层透明代理(不推荐,有bug)

•7.5. Web UI

docker with HTTPS

•8.2.1. 创建CA(证书颁发中心)

•8.2.2. 创建服务端公钥和私钥

•8.2.3. 创建客户端公钥和私钥

•8.2.4. 移除服务端私钥、客户端私钥密码

•8.2.5. 使用

•8.2.6. 管理

•8.1. 原理

•8.2. 使用

Docker Web-UI(shipyard)

•9.5.1. 页面上的Images(http://192.168.1.1:8000/images/)进行镜像删除要注意

•9.5.2. server端管理

•9.5.3. 不建议生产使用,可作为学习借鉴

•9.4.1. 登录页面

•9.4.2. 接受agent注册

•9.3.1. 下载镜像

•9.3.2. 启动容器(自动注册到server)

•9.2.1. 下载镜像

•9.2.2. 启动容器(自动完成部署)

•9.2.3. 验证

•9.1. 工作原理

•9.2. server配置

•9.3. agent配置

•9.4. 页面配置

•9.5. 注意

镜像制作

•10.1. 远程编译Dockerfile

11.内置bridge(nat)和自建网络桥接使用区别Docker Event事件监听

•12.1. 方法1:使用remote api

•12.2. 方法2:使用unix socket

•12.3. 方法3: 使用docker events命令

神器

•13.1. nsenter(无需sshd、无需attach也可以登录容器)

FAQ

•14.5.1. HTTP code 403 while uploading metadata: invalid character ‘<‘ looking for beginning of value

•14.5.2. dial tcp 127.0.0.1:5000: connection refused

•14.1. sshd服务起不来

•14.2. ulimit无法更改open-file、max processes

•14.3. 改变/var/lib/docker路径

•14.4. 将指定镜像标识为latest

•14.5. docker push报错

•14.6. CMD 和 ENTRYPOINT的区别

tag: cloud, virtual, docker, lxc

 

1. Docker安装

按照官方说明:红帽6、centos均通过epel源,执行yum install docker-io进行docker安装,启动服务是service docker start

经测试,红帽6、centos也可以通过在官网上下载编译好的二进制文件到本地也可以使用,但需要提前手动执行service cgconfig start来挂载cgroup,然后./docker-latest -d来启动服务。
下载地址:https://get.docker.io/builds/Linux/x86_64/docker-latest

https://get.docker.io/builds/Linux/x86_64/docker-1.0.1

 

但是官方提示需要内核大于3.8版本,否则可能会有问题。el>3.8

 

2. devicemapper

扩容存储池大小、扩容容器文件系统大小
https://www.dockboard.org/resizing-docker-containers-with-the-device-mapper-plugin/

实验成功,但是一旦容器关闭再启动,就会报错,还得根据文档再做一次dmsetup load; dmsetup resume才能成功启动容器(但是如果不先启动容器,就无法使用dmsetup命令来resume),因此能否用于生产环境有待继续探索

 

3. 玩转net namespace

首先要支持ip netns指令。而红帽6及epel均不支持,解决方法:
yum install -y http://rdo.fedorapeople.org/rdo-release.rpm
yum update -y iproute

ip netns
直接执行这个命令(或ip netns list)读取的是/var/run/netns下的文件名,因此若不存在/var/run/netns,需要mkdir -p /var/run/netns

配置像LXC一样的网络
I. 宿主配置
  1. 宿主上升级iproute包,以便支持ip netns指令:
    yum install -y http://rdo.fedorapeople.org/rdo-release.rpm
    yum update -y iproute

  2. 在宿主上配置好桥接:
    一. 方法1(不推荐): 敲命令配置桥接(很容易导致网络中断,需要ILO连上操作)
      1) 创建桥接网卡br1并激活:brctl addbr br1; ip link set br1 up
      2) 配置br1的mac地址,和宿主准备桥接的网卡mac相同,通常为内网网卡eth1:ip link set br1 address xx:xx:xx:xx:xx:xx
      3) 给br1配置一个ip地址,或者将eth1的ip地址配置在br1上,2种方法任选其一都可行:
      前者:
      ifconfig br1 192.168.2.1 netmask 255.255.255.0
      后者:
      ifconfig eth1 0.0.0.0; ifconfig br1 192.168.2.2 netmask 255.255.255.0
      4) 配置宿主网关,从br1出
      ip ro del default
      ip ro add default via 192.168.2.254 dev br1
      5) 将eth1桥接至br1:
      brctl addif br1 eth1
    二. 方法2(推荐):写网卡配置文件
      ifcfg-br1:
  DEVICE=”br1″
  TYPE=”Bridge”
  NM_CONTROLLED=”no”
  ONBOOT=”yes”
  BOOTPROTO=”static”
  IPADDR=192.168.2.2
  NETMASK=255.255.255.0

      ifcfg-eth1:
  DEVICE=”eth1″
  BRIDGE=”br1″
  BOOTPROTO=”none”
  NM_CONTROLLED=”no”
  ONBOOT=”yes”
  TYPE=”Ethernet”

      注意:要在/etc/sysconfig/network-scripts/ifup-eth里if [ “${TYPE}” = “Bridge” ]; then -> fi段落最后(fi前)加个ip link set br1 address $(get_hwaddr eth1),防止桥接网卡mac地址随机生成导致网络暂时中断

    service network restart  # 重启网络生效

II. 容器配置:
  1. 启动docker容器:
      docker run -t -i -d –name=”net_test” –net=none centos:latest /bin/bash
      记录下输出(即CONTAINER ID),然后通过docker inspect -f ‘{{.State.Pid}}’ CONTAINER ID获得该容器的pid(也即容器首进程在宿主上的pid),假设为1000
  2. 为容器创建网卡命名空间,建立点对点连接(容器命名空间网卡和宿主上生成的网卡点对点)
      mkdir -p /var/run/netns  #创建网络命名空间目录,ip netns会读取该目录下的文件名
      ln -s /proc/1000/ns/net /var/run/netns/1000  #将网络命名空间文件软链接到/var/run/netns,以便ip netns能够读取
      ip link add vethA type veth peer name vethB  #在宿主上创建2张直连网卡(vethA与vethB直连),将vethA作为容器里的网卡,vethB作为宿主上能看到的网卡
      ip link set vethB up   # 激活网卡vethB
      ip link set vethA netns 1000  # 将刚才创建的网卡归位网络命名空间
      配置vethA网卡参数:
        ip netns exec 1000 ip link set vethA name eth1
        ip netns exec 1000 ip addr add 192.168.2.3/24 dev eth1
        ip netns exec 1000 ip link set eth1 up
        ip netns exec 1000 ip route add default via 192.168.2.254 dev eth1
      brctl addif br1 vethB   # 将eth1桥接至br1
  3. 测试:
      docker attach登录容器,查看是否能ping通网关及其他子网或公网

 

3.1. ENV(环境变量)

Dockerfile支持ENV参数,表示启动容器时候设置变量。

 

只在CMD启动的进程export设置变量,而不是将变量赋值命令写入/etc/profile等脚本里,因此通过ssh方式登录容器获得的shell是没有这个变量的

 

4. port map

docker支持端口映射,通过iptables DNAT将宿主上的端口转发至容器ip对应端口。

 

虽然配置了端口映射后,在宿主上通过netstat -lntpu可以看到docker进程会监听这个端口,但是还没发现其作用,因为流量直接从iptables就进入容器里。

docker run -p、docker run -P、docker port作用:
docker run -P 就是将image定好的port给做个端口映射(若没指定-p,则外部端口随机)
docker run -p “8080:80” 启动容器时候做端口映射:宿���的0.0.0.0:8080 -> 容器80
docker run -P -p “8080:80” 假如image已经有一个port 22的配置,那么就会做2个端口映射:宿主0.0.0.0:xxxxx -> 容器22、宿主0.0.0.0:8080 -> 容器80
docker port 是查看容器已经做了端口映射的端口被映射到了哪个端口上,其实直接用docker ps就能看到,使用docker port可能是为了方便二次开发

 

5. 直接使用docker默认分配的IP对外提供服务(测试中)

 

5.1. 使用参数以及将docker0的ip配为机房内网网段

将宿主eth1桥接到docker0上,将docker0的ip更改为原来eth1的ip(机房内网网段)

 

存在一个问题:docker run时候分配的ip是从1开始,到254。因此存在和网关或者其他机器ip冲突的可能,无法避免。因此docker分配ip不会做ping检查是否存活

docker run分配出去的ip,docker kill并且docker rm都不会收回并重新使用,而是重启docker daemon才会将ip收回

–iptables=false
使用这个参数后,就不会再往iptables里生成nat、forward等信息了。

这样启动的容器,登录容器能看到网关是宿主docker0的ip,这样网络是通的,是可以访问外网,但路是这么走的:
1. 容器里的数据包将数据经过point-to-point网卡传送到宿主的对应veth网卡上
2. 宿主veth网卡接收到数据包后发现网关是254,于是通过icmp数据包告知网关是254,然后容器发送数据包时自动将网关更改为254,可以从ping的输出看到:
[ 17:37:23-root@21e77bf38fc0:~ ]#ping www.baidu.com
PING www.a.shifen.com (115.239.210.27) 56(84) bytes of data.
64 bytes from 115.239.210.27: icmp_seq=1 ttl=55 time=13.9 ms
From 192.168.3.1: icmp_seq=2 Redirect Host(New nexthop: 192.168.3.254)
64 bytes from 115.239.210.27: icmp_seq=2 ttl=55 time=13.6 ms
From 192.168.3.1: icmp_seq=3 Redirect Host(New nexthop: 192.168.3.254)
64 bytes from 115.239.210.27: icmp_seq=3 ttl=55 time=13.6 ms
From 192.168.3.1: icmp_seq=4 Redirect Host(New nexthop: 192.168.3.254)
64 bytes from 115.239.210.27: icmp_seq=4 ttl=55 time=14.0 ms
From 192.168.3.1: icmp_seq=5 Redirect Host(New nexthop: 192.168.3.254)
64 bytes from 115.239.210.27: icmp_seq=5 ttl=55 time=14.7 ms
From 192.168.3.1: icmp_seq=6 Redirect Host(New nexthop: 192.168.3.254)
64 bytes from 115.239.210.27: icmp_seq=6 ttl=55 time=13.8 ms
64 bytes from 115.239.210.27: icmp_seq=7 ttl=55 time=13.7 ms
From192.168.3.1: icmp_seq=8 Redirect Host(New nexthop: 192.168.3.254)
64 bytes from 115.239.210.27: icmp_seq=8 ttl=55 time=13.8 ms
64 bytes from 115.239.210.27: icmp_seq=9 ttl=55 time=13.6 ms
64 bytes from 115.239.210.27: icmp_seq=10 ttl=55 time=13.5 ms
From 192.168.3.1: icmp_seq=11 Redirect Host(New nexthop: 192.168.3.254)
64 bytes from 115.239.210.27: icmp_seq=11 ttl=55 time=13.8 ms
64 bytes from 115.239.210.27: icmp_seq=12 ttl=55 time=14.1 ms
64 bytes from 115.239.210.27: icmp_seq=13 ttl=55 time=13.8 ms
64 bytes from 115.239.210.27: icmp_seq=14 ttl=55 time=13.6 ms
64 bytes from 115.239.210.27: icmp_seq=15 ttl=55 time=13.7 ms
64 bytes from 115.239.210.27: icmp_seq=16 ttl=55 time=13.8 ms
From 192.168.3.1: icmp_seq=17 Redirect Host(New nexthop: 192.168.3.254)
64 bytes from 115.239.210.27: icmp_seq=17 ttl=55 time=13.6 ms
64 bytes from 115.239.210.27: icmp_seq=18 ttl=55 time=13.8 ms
64 bytes from 115.239.210.27: icmp_seq=19 ttl=55 time=13.7 ms
64 bytes from 115.239.210.27: icmp_seq=20 ttl=55 time=14.1 ms
64 bytes from 115.239.210.27: icmp_seq=21 ttl=55 time=13.7 ms
64 bytes from 115.239.210.27: icmp_seq=22 ttl=55 time=13.8 ms
64 bytes from 115.239.210.27: icmp_seq=23 ttl=55 time=13.7 ms
64 bytes from 115.239.210.27: icmp_seq=24 ttl=55 time=13.9 ms
64 bytes from 115.239.210.27: icmp_seq=25 ttl=55 time=14.2 ms
64 bytes from 115.239.210.27: icmp_seq=26 ttl=55 time=13.7 ms
64 bytes from 115.239.210.27: icmp_seq=27 ttl=55 time=13.7 ms
64 bytes from 115.239.210.27: icmp_seq=28 ttl=55 time=13.8 ms
64 bytes from 115.239.210.27: icmp_seq=29 ttl=55 time=13.9 ms
64 bytes from 115.239.210.27: icmp_seq=30 ttl=55 time=14.1 ms
64 bytes from 115.239.210.27: icmp_seq=31 ttl=55 time=13.9 ms
64 bytes from 115.239.210.27: icmp_seq=32 ttl=55 time=13.9 ms
64 bytes from 115.239.210.27: icmp_seq=33 ttl=55 time=14.0 ms
64 bytes from 115.239.210.27: icmp_seq=34 ttl=55 time=13.7 ms
64 bytes from 115.239.210.27: icmp_seq=35 ttl=55 time=14.0 ms
64 bytes from 115.239.210.27: icmp_seq=36 ttl=55 time=14.4 ms
64 bytes from 115.239.210.27: icmp_seq=37 ttl=55 time=13.9 ms

 

docker服务启动时候会把内核参数ip.forward给打开(数据包转发)

 

6. Docker COMMAND

 

6.1. docker参数

–api-enable-cors
开启cors,以便浏览器能够通过ajax调用。但是若开启了tls,使用cors就变得困难了,目前网络上还未找到解决方案

 

6.2. run

–link:2个容器互通
其实就做3件事:
1. 若有端口映射,则在iptables的FORWARD链里将端口ACCEPT
2. /etc/hosts:做link的容器的/etc/hosts能看到被link的容器的hosts条目
3. 环境变量:做link的容器可以看到被link的容器的环境变量(仅为–env变量),如:ALIAS_ENV_变量名、ALIAS_NAME=xxx

–volume: 目录共享
支持2种模式:
1. 从宿主挂载:-v /tmp:/tmp/foo 表示将宿主的/tmp目录挂载至容器的/tmp/foo目录,可读可写,和mount –bind的效果类似
2. 容器之间共享:
  启动第一个容器时带参数-v /tmp/foo表示在宿主上创建/var/lib/docker/vfs/dir/xxxxx(id,但不是容器id),然后挂进容器的/tmp/foo目录;
  启动第二个容器时带参数–volumes-from=b5f8320cf019(*第一个容器id)表示和第一个容器共享挂载,因此第二个容器启动后也能从df -h看到/tmp/foo目录被挂载。从inspect也可以很容易看出来(2个容器的inspect以下内容相同):
    “Volumes”: {
        “/opt”: “/var/lib/docker/vfs/dir/a7b1b03773d9391718b8524e7ac001bb877eb6d0596fa2a4328435d8c49f2415”
    },
    “VolumesRW”: {
        “/opt”: true
    }

更多详情见请继续阅读下一页的精彩内容: http://www.linuxidc.com/Linux/2014-11/109612p2.htm

7. 搭建私有Registry注册中心

 

7.1. 下载软件

安装pip:
yum install Python-devel libevent-devel python-pip gcc xz-devel

安装registry:
pip install docker-registry
pip install docker-registry[BUGSNAG]
pip install -i http://pypi.douban.com/simple/ backports.lzma
pip install –upgrade -i http://pypi.douban.com/simple/ backports.lzma

 

7.2. 启动服务

使用默认配置文件启动服务:
cd /usr/lib/python2.6/site-packages/config
cp config_sample.yml config.yml
mkdir /tmp/registry                # config_sample.yml的默认配置
gunicorn –access-logfile – –debug -k gevent -b 0.0.0.0:5000 -w 1 docker_registry.wsgi:application

加入开机启动
可以将以下内容写入/etc/rc.local,或者放在一个脚本里,/etc/rc.local调用这个脚本
pkill gunicorn
sleep 1
rm -f /data/docker-registry.db
sleep 1
/usr/bin/gunicorn –access-logfile /opt/logs/docker-registry/access.log –error-logfile /opt/logs/docker-registry/error.log –daemon –debug -k gevent -b 0.0.0.0:5000 -w 8 docker_registry.wsgi:application
sleep 3
pkill gunicorn
sleep 1
/usr/bin/gunicorn –access-logfile /opt/logs/docker-registry/access.log –error-logfile /opt/logs/docker-registry/error.log –daemon –debug -k gevent -b 0.0.0.0:5000 -w 8 docker_registry.wsgi:application

启动不成功FAQ
1. 日志目录没有创建
2. .db文件已存在,启动前删掉就行
排错可通过error.log分析

 

7.3. 使用

上传镜像
docker tag busybox localhost:5000/busybox
docker push localhost:5000/busybox

上传成功后在/tmp/registry上应该能看到2个目录:images和repositories

下载镜像
docker pull localhost:5000/busybox

若想给下载的镜像取个别名:
docker tag localhost:5000/busybox aliasname 或 docker tag id aliasname  #id为localhost:5000/busybox的image id

删除别名:
docker rmi aliasname
当然也可以选择把原始名字删掉:
docker rmi localhost:5000/busybox

 

若一个镜像有至少2个tag,那么通过docker rmi删除的只是别名,若只有1个名字(没有别名),那么删除的是真正的镜像。

已测试:当镜像还在上传过程中时,其他机器是无法pull下来的。因此不会生成不完整的镜像

搜索镜像
curl –silent “http://192.168.1.1:5000/v1/search?q=”  | json_reformat

 

7.4. 套一层透明代理(不推荐,有bug)

配置nginx,vhost内容如下
upstream docker-registry {
      server 127.0.0.1:5000;
}

server {
      listen 192.168.1.1:80;
      server_name registry.17173ops.com;

      proxy_set_header Host      $host;
      proxy_set_header X-Real-IP  $remote_addr;
     
      client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads

      # required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486)
      chunked_transfer_encoding on;
     
      location / {
            proxy_pass http://docker-registry;
      }
}

注意:只有以下2种情况,套一层透明代理是行的通的:
1. nginx监听80端口,gunicorn监听0.0.0.0
2. nginx监听非80端口,且gunicorn监听在网络可通的ip,如192.168.1.1这样的内网ip,而不能监听在127.0.0.1

测试发现,当端口不为80时,在docker push时候,会首先连接image name中的ip、端口,如registry.17173ops.com:80,然后连接实际的gunicorn端口。因此当gunicorn端口无法访问时,就会报错。
而端口为80时候就不会这样,原因未知,应该是代码逻辑。
<=1.1.2版本都有如上共性,>1.1.2的尚未测试。

 

7.5. Web UI

下载
docker pull atcol/docker-registry-ui

启动
docker run –name “registry_UI” -tid -p 127.0.0.1:5001:8080 -e REG1=http://registry.17173ops.com/v1/ atcol/docker-registry-ui:latest

开机启动:
echo ‘docker start registry_UI’ >> /etc/rc.local

套一层透明代理
配置nginx,vhost内容如下
upstream docker-registry-web {
      server 127.0.0.1:5001;
}

server {
      listen 192.168.1.1:80;
      server_name registry-web.17173ops.com;

      location / {
            proxy_pass http://docker-registry-web;
      }
}

使用
http://registry-web.17173ops.com/

注意:页面上的删除镜像只是删除tag标签,实际id和image不会删除
docker镜像的元数据里记录着FROM哪个镜像,如果真的删除镜像所有数据,正常逻辑应该是A->B B->C -C->D D->E,这样一连串都给删除了,但是其中的某个镜像可能又被其他镜像给依赖了,猜测正是基于这种逻辑,才只删除了tag

 

8. docker with HTTPS

 

8.1. 原理

Docker HTTPS原理:双向验证。官方说明(https://docs.docker.com/articles/https/):
In daemon mode, it will only allow connections from clients authenticated by a certificate signed by that CA.
In client mode, it will only connect to servers with a certificate signed by that CA.
核心:服务端和客户端的数字证书都由同一个CA签发,因此双方在认证通讯时使用和签发时的同一个CA就能互相认证。

 

8.2. 使用

 

8.2.1. 创建CA(证书颁发中心)

测试
echo 01 > ca.srl
openssl genrsa -des3 -out ca-key.pem 2048
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca.pem

 

8.2.2. 创建服务端公钥和私钥

生成私钥:
openssl genrsa -des3 -out server-key.pem 2048

生成公钥(数字签名证书):
1. 生成CSR文件(Certificate Signing Request 证书签名请求):openssl req -subj ‘/CN=docker.17173ops.com’ -new -key server-key.pem -out server.csr
2. 编写openssl.conf文件,内容如下:
————- BEGIN ———————————
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req

[req_distinguished_name]

[ v3_req ]
subjectAltName = @alt_names

[alt_names]
DNS.1 = *.h.173ops.com
DNS.2 = *.docker.17173ops.com
————- END ————————————

生成公钥(数字签名证书):
openssl x509 -req -days 3650 -in server.csr -CA ca.pem -CAkey ca-key.pem -out server-cert.pem -extensions v3_req -extfile openssl.conf

 

8.2.3. 创建客户端公钥和私钥

生成私钥:
openssl genrsa -des3 -out client-key.pem 2048

生成公钥(数字签名证书):
1. 生成CSR文件(Certificate Signing Request 证书签名请求):openssl req -subj ‘/CN=client’ -new -key client-key.pem -out client.csr
2. echo extendedKeyUsage = clientAuth > extfile.cnf
3. 生成公钥(数字签名证书):openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca-key.pem -out client-cert.pem -extfile extfile.cnf

 

8.2.4. 移除服务端私钥、客户端私钥密码

服务端
openssl rsa -in server-key.pem -out server-key.pem

客户端
openssl rsa -in client-key.pem -out client-key.pem

 

8.2.5. 使用

将服务端3个文件拷贝到docker daemon(假设为192.168.1.2)的/root/.docker/下:
1. scp ca.pem server-cert.pem server-key.pem 192.168.1.2:/root/.docker/
2. 登录192.168.1.2
  cd /root/.docker/
  chmod 0600 *
3. 添加启动参数:
  修改/etc/sysconfig/docker中的other_args值,添加以下内容,如:other_args=”–graph /opt/docker –tlsverify –tlscacert=/root/.docker/ca.pem –tlscert=/root/.docker/server-cert.pem –tlskey=/root/.docker/server-key.pem -H unix:///var/run/docker.sock -H tcp://192.168.1.2:2376 -H tcp://127.0.0.1:2376″

将客户端3个文件拷贝至”中控机”,然后就可以通过HTTPS远程对docker daemon进行操作:
docker –tlsverify –tlscacert=/opt/docker_tls/ca.pem –tlscert=/opt/docker_tls/client-cert.pem –tlskey=/opt/docker_tls/client-key.pem -H localhost:2376 images

 

8.2.6. 管理

查看CSR文件:
openssl req -noout -text -in server.csr

查看签名证书(server-cert.pem、client-cert.pem):
openssl x509 -noout -text -in server-cert.pem

 

9. Docker Web-UI(shipyard)

 

9.1. 工作原理

在每台docker宿主机上启动一个容器(shipyard/agent),这个容器通过挂载宿主的/var/run/docker.sock文件来获取该宿主上容器、镜像的信息,同时在容器启动时将agent注册到管理中心(shipyard/deploy)上,实现从Web查看和操作docker容器与镜像

每个docker宿主启动一个agent(shipyard/agent),管理中心启动一个server(shipyard/deploy)

 

9.2. server配置

 

9.2.1. 下载镜像

docker pull shipyard/deploy

 

9.2.2. 启动容器(自动完成部署)

docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy setup
执行该命令时,实际做了如下操作:
在本地宿主上依次下载并启动
1. shipyard/redis
2. shipyard/router
3. shipyard/lb(Load Balance的意思)
4. shipyard/db
5. shipyard/shipyard(Web-UI)

如何做到上述的:docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy setup会将socket文件挂载进容器里,这样在容器里就能对宿主的镜像和容器进行管理,而shipyard/deploy的CMD是一个脚本,这个脚本就是进行上述操作

 

9.2.3. 验证

docker ps查看是否都已启动

 

注意docker logs shipyard_router可能会看到大量报错,并且占用大量CPU,负载也会升高。暂时不知原因,已提交至github.com,等待答复,详见https://github.com/shipyard/docker-shipyard-router/issues/3

 

9.3. agent配置

 

9.3.1. 下载镜像

docker pull shipyard/agent

 

9.3.2. 启动容器(自动注册到server)

docker run -i -t -v /var/run/docker.sock:/docker.sock -e IP=`ip -4 address show br1 | grep ‘inet ‘ | sed ‘s/.*inet \([0-9\.]\+\).*/\1/’` -e URL=http://192.168.1.1:8000 -p 4500:4500 shipyard/agent
如果看不懂,可以这么写:
docker run -i -t -v /var/run/docker.sock:/docker.sock -e IP=192.168.1.2 -e URL=http://192.168.1.1:8000 -p 4500:4500 shipyard/agent
(192.168.1.2是docker宿主ip)

注意:要先启动server,才能启动agent,否则agent注册到server可能会失败。

 

9.4. 页面配置

 

9.4.1. 登录页面

http://192.168.1.1:8000
默认账号:admin
默认密码:shipyard

 

9.4.2. 接受agent注册

http://192.168.1.1:8000/hosts/,在打开网页的右部分点击按钮,选择authorize host

 

9.5. 注意

 

9.5.1. 页面上的Images(http://192.168.1.1:8000/images/)进行镜像删除要注意

自动进行了去重(根据image id),因此从页面上删除镜像时会将相同image id的全部删除

 

9.5.2. server端管理

移除:docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy cleanup

重启:docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy restart

升级:docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy upgrade

 

9.5.3. 不建议生产使用,可作为学习借鉴

原因1:封装太多,用户无法定制修改(20140903)

原因2:一些细节功能方面,例如不同host的容器打印在一张表里,连排序功能都没有(20140903)

 

10. 镜像制作

 

10.1. 远程编译Dockerfile

docker -H tcp://xxx:2376 build –force-rm –no-cache -t foo/rhel6.5:1.0 /path/to/ 那么这个/path/to/指的是本地文件,而非远程编译机上的文件。

/path/to/Dockerfile文件会在命令执行之初就通过远端2376端口将/path/to/*(Dockerfile所在目录下的所有文件)传送到编译机上
Sending build context to Docker daemon xx MB过程就是将文件发送过去

Dockerfile除了支持文件方式外,还支持URL,即/path/to/可以改为http://xxx(尚未测试过)

 

11. 内置bridge(nat)和自建网络桥接使用区别

内置bridge(nat)
优点:
1. 节省IP

缺点:
1. 需要配套服务注册/发现,否则宿主上端口分配困难,容易冲突。
2. 由于每个容器暴露的端口都不一致,造成前端路由层nginx配置(proxy_pass)里无法使用dns的方式。
3. 端口映射要在容器启动时就指定好,后期无法变更。
4. 测试发现nat不支持websocket。

自建桥接网络
优点:
1. 每个容器都有独立ip,对外提供服务,如nginx+php,nginx+resin,都可以使用默认的80端口
2. 由于容器暴露端口都可以使用80端口,因此前端路由层nginx配置(proxy_pass)里可以使用dns的方式。
3. 无需为了后期端口映射添加而烦恼
4. 桥接支持websocket

缺点:
1. 每个容器都需要一个IP(内网ip是不需要钱的)

 

12. Docker Event事件监听

 

12.1. 方法1:使用remote api

还未找出靠谱的阻塞方式

 

12.2. 方法2:使用unix socket

测试发现:docker服务关闭时,/var/run/docker.sock文件并不会自动删掉
使用python连接/var/run/docker.sock文件会一直连着,docker daemon停止后,文件仍在,连接也仍在,因此不适用于事件监听。

 

12.3. 方法3: 使用docker events命令

测试发现:输出内容与api的输出不完全一样,比如docker events输出的时间格式为可读的格式,而api输出的是unix timestamp。

可以通过python的模块os.popen(‘docker events’)来建立监听连接
# -*- coding: utf-8 -*-
import os
handler = os.popen(‘docker -H 127.0.0.1:2376 events’)
while True:
    res = handler.readline()
    if res:
        print res
    else:
        print “Null”
        exit(1)

 

13. 神器

 

13.1. nsenter(无需sshd、无需attach也可以登录容器)

原理:进入namespace(通过/proc/xxxx/ns/)

安装:docker run -v /usr/local/bin:/target registry.17173ops.com:5000/jpetazzo/nsenter:latest
执行成功后会在宿主的/usr/local/bin下生成1个二进制程序nsenter和1个shell脚本docker-enter

docker-enter是对nsenter用法的封装,让使用更加简单

使用:docker-enter 容器id(或容器名)
docker-enter是一个shell脚本,其实是调用nsenter(二进制文件),因此可以直接使用nsenter:
nsenter –target $PID –mount –uts –ipc –net –pid  # 这个$PID指容器里的任意进程在宿主上的真实PID

 

14. FAQ

 

14.1. sshd服务起不来

docker在源码里就关掉了audit_control(linux Capabilities),而/etc/pam.d/sshd里有一行session required pam_loginuid.so,把这行删掉就可以了

 

14.2. ulimit无法更改open-file、max processes

docker容器默认移除sys_resource(Linux能力),因而ulimit -n设置只能改小无法改大,改大会报错:ulimit: open files: cannot modify limit: Operation not permitted。

红帽7下docker run可以使用–privileged选项来不移除Linux能力,但docker默认移除这个Linux能力肯定是有安全方面的考量,因此尽量别用该选项

 

红帽6下要使用–privileged,docker版本不能>=1.0.1,否则会报错;stat /dev/.udev/db/cpuid:cpu0: no such file or directory。

解决方法:
若启动docker使用sysV服务,则在/etc/init.d/functions最开头添加一行:ulimit -u 204800 -HSn 204800

若启动docker使用命令,如docker -d,那么在启动之前先执行ulimit -u 204800 -HSn 204800即可

 

14.3. 改变/var/lib/docker路径

使用–graph参数:docker –graph=/opt/docker -d,会自动生成/opt/docker目录(0700),并在该目录下创建docker相关文件

 

原来的镜像和容器都找不到了,因为路径改了(原来的镜像是在/var/lib/docker/devicemapper/devicemapper/{data,metadata})

 

14.4. 将指定镜像标识为latest

docker tag 镜像id cyent/rhel6.5:latest

 

14.5. docker push报错

 

14.5.1. HTTP code 403 while uploading metadata: invalid character ‘<‘ looking for beginning of value

报错示例:
[ 14:50:44-root@localhost:vhosts ]#docker push registry.17173ops.com:82/crosbymichael/dockerui
The push refers to a repository [registry.17173ops.com:82/crosbymichael/dockerui] (len: 1)
Sending image list
Pushing repository registry.17173ops.com:82/crosbymichael/dockerui (1 tags)
511136ea3c5a: Pushing
2014/09/01 14:50:46 HTTP code 403 while uploading metadata: invalid character ‘<‘ looking for beginning of value

解决方法:在nginx配置里注释掉”proxy_set_header Host $host;”

 

14.5.2. dial tcp 127.0.0.1:5000: connection refused

报错示例:
[ 18:24:35-root@localhost:~ ]#docker push registry.17173ops.com:81/17173/as6.5-ng1.4:1.4
The push refers to a repository [registry.17173ops.com:81/17173/as6.5-ng1.4] (len: 1)
Sending image list
Pushing repository registry.17173ops.com:81/17173/as6.5-ng1.4 (1 tags)
511136ea3c5a: Pushing
2014/09/01 18:27:27 Failed to upload metadata: Put http://127.0.0.1:5000/v1/images/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/json: dial tcp 127.0.0.1:5000: connection refused

解决方法:不要这么用,不要套nginx,gunicorn直接对外使用

 

14.6. CMD 和 ENTRYPOINT的区别

CMD是可以在docker run时候被覆盖的,而ENTRYPOINT无法被覆盖

CMD通常被用于调试,可以选择不同的CMD,可支持带参数

而ENTRYPOINT通常被用于应用发布,也可支持带参数

举例说明:
假设CMD和ENTRYPOINT都是/bin/run.sh,run.sh的内容是echo “Hello,$1”
那么,若为CMD,则docker run -t -i xxx/yyy /bin/ls /root就会列出/root目录下的内容
若为ENTRYPOINT,则docker run -t -i xxx/yyy /bin/ls /root就会打印出Hello,/bin/ls

最重要的区别:ENTRYPOINT是会被继承下去的
比如做镜像A的时候Dockerfile里写了一行ENTRYPOINT /usr/local/bin/run.sh,那么之后做的镜像(FROM:)的Dockerfile里如果不指定ENTRYPOINT,而是指定CMD,那么这个CMD虽然从inspect里看是存在的,但却是无效的。

Docker 的详细介绍:请点这里
Docker 的下载地址:请点这里

本文永久更新链接地址:http://www.linuxidc.com/Linux/2014-11/109612.htm

赞(0) 打赏
转载请注明出处:服务器评测 » Docker基础与高级
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏