镜像
Image的生命周期
常用操作
- 列出本地存储的镜像:
docker images
- 列出完整的image ID:
docker images --no-trunk
- 只列出image ID:
docker images -q
- 删除“悬挂”的Image:
docker images --filter "dangling=true" -q | xargs docker rmi
- 镜像分析Dive:
dive {image id}
- 下载镜像:
docker pull busybox
- 保存和导入镜像:
docker save -o busybox.tar busybox
docker load -i busybox.tar
docker load一般只用于导入由docker save导出的镜像,导入后的镜像跟原镜像完全一样,包括拥有相同的镜像ID和分层等内容.
Docker image的组织结构
Docker image包含着数据及必要的元数据。数据由一层层的image layer组成,元数据则是一些JSON文件,用来描述数据(image layer)之间的关系以及容器的一些配置信息
查看镜像的历史:
docker history victor0217/numberone
IMAGE CREATED CREATED BY SIZE COMMENT
51b41265c9d9 25 hours ago /bin/sh -c #(nop) ENTRYPOINT ["java" "-jar"… 0B
0724e6967b63 25 hours ago /bin/sh -c #(nop) COPY multi:9f7f512ecc1933e… 63.4MB
cdf26cc71b50 7 days ago /bin/sh -c set -eux; dpkgArch="$(dpkg --pr… 205MB
<missing> 7 days ago /bin/sh -c #(nop) ENV JAVA_URL_VERSION=8u24… 0B
<missing> 7 days ago /bin/sh -c #(nop) ENV JAVA_BASE_URL=https:/… 0B
<missing> 7 days ago /bin/sh -c #(nop) ENV JAVA_VERSION=8u242 0B
<missing> 7 days ago /bin/sh -c { echo '#/bin/sh'; echo 'echo "$J… 27B
<missing> 7 days ago /bin/sh -c #(nop) ENV PATH=/usr/local/openj… 0B
<missing> 7 days ago /bin/sh -c #(nop) ENV JAVA_HOME=/usr/local/… 0B
<missing> 7 days ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B
<missing> 7 days ago /bin/sh -c set -eux; apt-get update; apt-g… 11.1MB
<missing> 8 days ago /bin/sh -c apt-get update && apt-get install… 145MB
<missing> 8 days ago /bin/sh -c set -ex; if ! command -v gpg > /… 17.5MB
<missing> 8 days ago /bin/sh -c apt-get update && apt-get install… 16.5MB
<missing> 8 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 8 days ago /bin/sh -c #(nop) ADD file:e05e45c33042db4ec… 114MB
镜像被本地存储在:/var/lib/docker
查看镜像的元数据信息: docker inspect busybox
仓库
Hub和Repository以及Image的关系如下图:
仓库的名字通常由两部分组成,中间以斜线分开。斜线之前是用户名,斜线之后是镜像名。如tom/ubuntu表示属于用户tom的Ubuntu镜像。这也是社区上区分公有仓库和私有仓库的方法.
- 登录仓库:
docker login -u <user name> -p <password>
- 上传镜像:
docker push localhost:5000/official/ubuntu:14.04
上面的示例中是向本地私有仓库上传镜像。如果不写服务器地址,则默认上传到官方DockerHub(https://hub.docker.com)
- 下载镜像:
docker pull ubuntu:14.04
- 查询镜像:
docker search localhost:5000/ubuntu
可以通过Docker Private Registry搭建一个本地的镜像仓库:
docker run -d --hostname localhost --name registry-v2 \
-v /opt/data/distribution:/var/lib/registry/docker/registry/v2 \
-p 5000:5000 registry:2.0
网络
CNM概念模型
Libnetwork提出了新的容器网络模型(Container Network Model,简称CNM),定义了标准的API用于为容器配置网络,其底层可以适配各种网络驱动
- 沙盒。沙盒是一个隔离的网络运行环境,保存了容器网络栈的配置,包括了对网络接口、路由表和DNS配置的管理。在Linux平台上,沙盒是用Linux Network Namespace实现的,在其他平台上可能是不同的概念,如FreeBSD Jail。一个沙盒可以包括来自多个网络的多个Endpoint(端点)
- Endpoint。Endpoint将沙盒加入一个网络,Endpoint的实现可以是一对veth pair或者OVS内部端口,当前的Libnetwork使用的是veth pair。一个Endpoint只能隶属于一个沙盒及一个网络。通过给沙盒增加多个Endpoint可以将一个沙盒加入多个网络。
- 网络。网络包括一组能互相通信的Endpoint。网络的实现可以是Linux bridge、vlan等。
Libnetwork已经实现了五种驱动(driver):
- bridge:Docker默认的容器网络驱动。Container通过一对veth pair连接到docker0网桥上,由Docker为容器动态分配IP及配置路由、防火墙规则等。
- host:容器与主机共享同一Network Namespace,共享同一套网络协议栈、路由表及iptables规则等。容器与主机看到的是相同的网络视图。
- null:容器内网络配置为空,需要用户手动为容器配置网络接口及路由等。
- remote:Docker网络插件的实现。Remote driver使得Libnetwork可以通过HTTPRESTful API对接第三方的网络方案,类似SocketPlane的SDN方案只要实现了约定的HTTP URL处理函数及底层的网络接口配置方法,就可以替换Docker原生的网络实现。
- overlay:Docker原生的跨主机多子网网络方案。主要通过使用Linux bridge和vxlan隧道实现,底层通过类似于etcd或consul的KV存储系统实现多机的信息同步。
常用命令
- 创建网络:
docker network create --driver bridge numberone-net
- 启动时加入网络:
docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7.29 --network=numberone-net
- 动态改成容器网络
docker network disconnect bridge mysql
docker network connect numberone-net mysql
5种容器网络模式
- none:不为容器配置任何网络功能
docker run --net=none -it ubuntu ip addr show
- container:与另一个运行中的容器共享Network Namespace,共享相同的网络视图
docker run -h dockernet --dns 8.8.4.4 -itd ubuntu bash
docker run --net=container:{target container id} -it ubuntu bash
- host:与主机共享Root Network Namespace,容器有完整的权限可以操纵主机的协议栈、路由表和防火墙等,所以被认为是不安全的
docker run -it --net=host ubuntu bash
- bridge:Docker设计的NAT网络模型
Docker daemon启动时会在主机创建一个Linux网桥(默认为docker0,可通过-b参数手动指定)。容器启动时,Docker会创建一对veth pair(虚拟网络接口)设备,veth设备的特点是成对存在,从一端进入的数据会同时出现在另一端。Docker会将一端挂载到docker0网桥上,另一端放入容器的Network Namespace内,从而实现容器与主机通信的目的
在桥接模式下,Docker容器与Internet的通信,以及不同容器之间的通信,都是通过iptables规则控制的
总之,Docker网络的初始化动作包括:创建docker0网桥、为docker0网桥新建子网及路由、创建相应的iptables规则等
-
overlay:Docker原生的跨主机多子网模型
overlay网络模型比较复杂,底层需要类似consul或etcd的KV存储系统进行消息同步,核心是通过Linux网桥与vxlan隧道实现跨主机划分子网
如图5-3所示,每创建一个网络,Docker会在主机上创建一个单独的沙盒,沙盒的实现实质上是一个Network Namespace。在沙盒中,Docker会创建名为br0的网桥,并在网桥上增加一个vxlan接口,每个网络占用一个vxlan ID,当前Docker创建vxlan隧道的ID范围为256~1000,因而最多可以创建745个网络。当添加一个容器到某一个网络上时,Docker会创建一对veth网卡设备,一端连接到此网络相关沙盒内的br0网桥上,另一端放入容器的沙盒内,并设置br0的IP地址作为容器内路由默认的网关地址,从而实现容器加入网络的目的
以图5-3为例,容器1和容器4同属一个网络,容器1需要通过256号vxlan隧道访问另一台主机的容器4。Docker通过vxlan和Linux网桥实现了跨主机的虚拟子网功能。
容器卷
Docker容器里产生的数据,如果不通过docker commit生成新的镜像,使数据作为镜像的一部分保存下来,就会在容器删除后丢失。
为了能够持久化保存和共享容器的数据,Docker提出了卷(volume)的概念。简单来讲,卷就是目录或文件,由Docker daemon挂载到容器中,因此不属于联合文件系统,卷中的数据在容器被删除后仍然可以访问
数据卷
- 增加新数据卷
docker run -d -v /tmp/data --name busyboxtest busybox
其中,-v参数会在容器的/tmp/data目录下创建一个新的数据卷
用户可以通过docker inspect命令查看数据卷在主机中的位置:docker inspect busyboxtest
-
将主机目录挂载为数据卷
-v参数除了可以用于创建数据卷外,还可以用来将Docker daemon所在主机上的文件或文件夹挂载到容器中
docker run -d -v /host/data:/data --name busyboxtest busybox
上述命令可以将Docker daemon所在主机的/host/data目录挂载到容器的/data路径下
-v参数的主机目录必须使用绝对路径,如果指定路径不存在,Docker会自动创建该目录。
以只读的方式挂载一个数据卷:docker run -it -v /host/data:/data:ro --name busyboxtest busybox
数据卷容器
如果用户需要在容器之间共享一些需要永久存储的数据,或者想要使用一个临时容器中的相关数据,可以创建一个数据卷容器,然后使用该容器进行数据共享。
docker create -v /dbdata --name dbdata training/postgress /bin/true
docker run -d --volumes-from dbdata --name db1 training/postgress
docker run -d --volumes-from dbdata --name db2 training/postgress
docker run -d --name db3 --volumes-from db1 training/postgress
数据卷的备份,转储和迁移
docker run --rm --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
上述命令创建了一个容器,该容器挂载了dbdata数据卷,并将主机的当前目录挂载到了容器的/backup目录中;然后在容器中使用tar命令将dbdata数据卷中的内容打包存放到/backup目录的backup.tar文件中。待容器执行结束后,备份文件就会出现在主机的当前目录。之后可以将该备份文件恢复到当前容器或新创建的容器中,完成数据的备份和迁移工作
Docker API
目前Docker提供如下三类RESTful API:
- Docker Remote API:诸如docker run等操作最终均是通过调用Docker Remote API向Docker daemon发起请求的。
- Docker Registry API:与镜像存储有关的操作可通过Docker Registry API来完成。
- Docker Hub API:用户管理等操作可通过Docker Hub API来完成。
RestAPI应用实例
如果没有进行特殊配置,Docker会监听本机的一个unix socket,默认为unix:///var/run/docker.sock
本地访问:
echo -e "GET /images/json HTTP/1.0\r\n" | nc -U /var/run/docker.sock
远程访问
curl -XGET localhost:5678/images/ubuntu/history
使用python格式化json输出
curl -XGET localhost:5678/images/ubuntu/history | python -mjson.tool
启动Docker暴露HTTP访问接口
docker -d -H unix:///var/run/docker.sock -H tcp://0.0.0.0.:5678
安全性
Cgroup
Cgroup用于限制容器对CPU、内存等关键资源的使用,防止某个容器由于过度使用资源,导致host或者其他容器无法正常运作
限制CPU
Docker能够指定一个容器的CPU权重,这是一个相对权重,与实际的处理速度无关。事实上,没有办法限制一个容器只可以获得1GHZ的CPU。每个容器默认的CPU权重是1024,简单地说,假设只有两个容器,并且这两个容器竞争CPU资源,那么CPU资源将在这两个容器之间平均分配。如果其中一个容器启动时设置的CPU权重是512,那它相对于另一个容器只能得到一半的CPU资源,因此这两个容器可以得到的CPU资源分别是33.3%和66.6%。但如果另外一个容器是空闲的,第一个容器则会被允许使用100%的CPU。也就是说,CPU资源不是预先硬性分配好的,而是跟各个容器在运行时对CPU资源的需求有关
选项 | 描述 |
---|---|
--cpuset-cpus="" | 允许使用的 CPU 集,值可以为 0-3,0,1 |
-c,--cpu-shares=0 | CPU 共享权值(相对权重) |
--cpu-period=0 | 限制 CPU CFS 的周期,范围从 100ms~1s,即[1000, 1000000] |
--cpu-quota=0 | 限制 CPU CFS 配额,必须不小于1ms,即 >= 1000 |
--cpuset-mems="" | 允许在上执行的内存节点(MEMs),只对 NUMA 系统有效 |
- 可以为容器设置CPU权重为100:
docker run --rm -it -c 100 ubuntu bash
- 容器在每个0.5秒里最多只能运行0.25秒:
docker run --rm -it --cpu--period=500000 --cpu-quota=250000 ubuntu /bin/bash
- 把容器的进程限定在特定的CPU上运行,例如将容器限定在0号和1号CPU上运行:
docker run --rm -it --cpuset--cpus=0,1 ubuntu /bin/bash
限制内存
选项 | 描述 |
---|---|
-m,--memory | 内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为 4M |
--memory-swap | 内存+交换分区大小总限制。格式同上。必须必-m设置的大 |
--memory-reservation | 内存的软性限制。格式同上 |
--oom-kill-disable | 是否阻止 OOM killer 杀死容器,默认没设置 |
--oom-score-adj | 容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0 |
--memory-swappiness | 用于设置容器的虚拟内存控制行为。值为 0~100 之间的整数 |
--kernel-memory | 核心内存限制。格式同上,最小为 4M |
- 将容器可使用的内存限制在200MB
docker run --rm -it -m 200M ubuntu /bin/bash
这个例子将容器可使用的内存限制在200MB。不过事实上还不是这么简单,我们知道系统在发现内存不足时,会将部分内存置换到swap分区里,因此如果只限制内存使用量,可能会导致swap分区被用光。通过--memory-swap参数可以限制容器对内存和swap分区的使用,如果只是指定-m而不指定--memory-swap,那么总的虚拟内存大小(也即memory加上swap)是-m参数的两倍
Dockerfile
Dockerfile的注释都是以“#”开始的,每一行是一个指令。一般情况下,Dockerfile由4部分组成:
- 基础镜像信息
- 维护者信息
- 镜像操作指令
- 容器启动指令
# This Dockerfile uses the ubuntu image
# Version 2 - EDITION 1
# Author: tester
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
From ubuntu
#Maintainer: tester tester@email.com
MAINTAINER tester tester@hotmail.com
#Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
#Commands when creating a new container
CMD /usr/sbin/nginx
Dockerfile指令
- FROM指令格式为
FROM<image>或FROM<image>:<tag>。
Dockerfile的第一条必须是FROM指令,用来指定要制作的镜像继承自哪个镜像。
需要说明的是,可以在Dockerfile中写多个FROM指令来构建复杂的镜像。 - MAINTAINER
指令格式为MAINTAINER<name>
用来指定维护者信息。 - RUN指令格式为
RUN<command>或RUN["executable","param1","param2"...]
该指令是用来执行shell命令的,当解析Dockerfile时,遇到RUN指令,Docker会将该指令翻译为“/bin/sh–c“xxx””,其中xxx为RUN后边的Shell命令。 - EXPOSE指令格式为
EXPOSE<port>[<port>...]。
该指令用来将容器中的端口号暴露出来,也可以通过“docker run–p”命令实现和服务器端口的映射。 - CMD指令该指令有三种格式:
CMD["executable","param1","param2"]使用exec执行,推荐方式;
CMD command param1 param2在/bin/sh中执行,提供给需要交互的应用;CMD["param1","param2"]提供给ENTRYPOINT的默认参数。
指定启动容器时执行的命令,每个Dockerfile只能有一条CMD指令。
如果指定了多条CMD指令,只有最后一条会被执行。值得说明的是,如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令。 - ENTRYPOINT指令该指令有两种格式:
ENTRYPOINT["executable","param1","param2"]
ENTRYPOINT command param1 param2(Shell中执行)。
每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个有效。 - VOLUME指令格式为VOLUME["/data"]。
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库或需要永久保存的数据。如果和host共享目录,Dockerfile中必须先创建一个挂载点,然后在启动容器的时候通过“docker run–vCONTAINERPATH”来挂载,其中CONTAINERPATH就是创建的挂载点。 - ENV指令格式为
ENV<key><value>。
指定一个环境变量,会被后续RUN指令使用,并在容器运行时保持。 - ADD指令格式为
ADD<src><dest>。
该指令将复制指定的<src>到容器中的<dest>。
其中<src>可以是Dockerfile所在目录的一个相对路径;也可以是一个URL;还可以是一个tar文件(自动解压为目录)。 - COPY指令格式为COPY<src><dest>。复制本地主机的<src>(为Dockerfile所在目录的相对路径)到容器中的<dest>。当使用本地目录为源目录时,推荐使用COPY。
Docker集群管理
Compose
Compose是用来定义和运行一个或多个容器应用的工具。使用Compose可以简化容器镜像的建立及容器的运行。
Compose是使用YML文件来定义多容器应用的,它还会用docker-compose up命令把完整的应用运行起来.
从本质上来讲,Compose把YML文件解析成docker命令的参数,然后调用相应的docker命令行接口,从而把应用以容器化的方式管理起来。它通过解析容器间的依赖关系来顺序地启动容器。而容器间的依赖关系则可以通过在docker-compose.yml文件中使用“links”标记来指定.
实例
web:
build: ./web
ports:
- "5000":"5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
- 每个定义的服务都至少要包含build或image两个命令中的一个,其他的命令都是可选的.
- build命令指定了包含Dockerfile的目录,可以是绝对目录也可以是相对目录,相对目录指的是相对于docker-compose.yml文件所在位置的目录
- docker-compose.yml文件中的“ports”标记对应docker run命令中的“-p”选项
- “volumes”标记对应docker run命令中的“-v”选项
- “links”标记对应docker run命令中的“--links”选项
- 通过运行docker-compose build和docker-compose up命令,上述docker-compose.yml文件中定义的Web和Redis服务都会成功运行起来