Docker基础

镜像 Image的生命周期 Docker基础
文章图片
Docker Image lifecycle.PNG 常用操作

  • 列出本地存储的镜像: 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 IMAGECREATEDCREATED BYSIZECOMMENT 51b41265c9d925 hours ago/bin/sh -c #(nop)ENTRYPOINT ["java" "-jar"…0B 0724e6967b6325 hours ago/bin/sh -c #(nop) COPY multi:9f7f512ecc1933e…63.4MB cdf26cc71b507 days ago/bin/sh -c set -eux; dpkgArch="$(dpkg --pr…205MB 7 days ago/bin/sh -c #(nop)ENV JAVA_URL_VERSION=8u24…0B 7 days ago/bin/sh -c #(nop)ENV JAVA_BASE_URL=https:/…0B 7 days ago/bin/sh -c #(nop)ENV JAVA_VERSION=8u2420B 7 days ago/bin/sh -c { echo '#/bin/sh'; echo 'echo "$J…27B 7 days ago/bin/sh -c #(nop)ENV PATH=/usr/local/openj…0B 7 days ago/bin/sh -c #(nop)ENV JAVA_HOME=/usr/local/…0B 7 days ago/bin/sh -c #(nop)ENV LANG=C.UTF-80B 7 days ago/bin/sh -c set -eux; apt-get update; apt-g…11.1MB 8 days ago/bin/sh -c apt-get update && apt-get install…145MB 8 days ago/bin/sh -c set -ex; if ! command -v gpg > /…17.5MB 8 days ago/bin/sh -c apt-get update && apt-get install…16.5MB 8 days ago/bin/sh -c #(nop)CMD ["bash"]0B 8 days ago/bin/sh -c #(nop) ADD file:e05e45c33042db4ec…114MB

镜像被本地存储在:/var/lib/docker
查看镜像的元数据信息: docker inspect busybox
仓库 Hub和Repository以及Image的关系如下图:

Docker基础
文章图片
仓库组织图
仓库的名字通常由两部分组成,中间以斜线分开。斜线之前是用户名,斜线之后是镜像名。如tom/ubuntu表示属于用户tom的Ubuntu镜像。这也是社区上区分公有仓库和私有仓库的方法.
  • 登录仓库:docker login -u -p
  • 上传镜像: 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概念模型

Docker基础
文章图片
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基础
文章图片
Bridge
在桥接模式下,Docker容器与Internet的通信,以及不同容器之间的通信,都是通过iptables规则控制的
总之,Docker网络的初始化动作包括:创建docker0网桥、为docker0网桥新建子网及路由、创建相应的iptables规则等
  • overlay:Docker原生的跨主机多子网模型
    overlay网络模型比较复杂,底层需要类似consul或etcd的KV存储系统进行消息同步,核心是通过Linux网桥与vxlan隧道实现跨主机划分子网

    Docker基础
    文章图片
    Capture.PNG
如图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部分组成:
  1. 基础镜像信息
  2. 维护者信息
  3. 镜像操作指令
  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或FROM
    Dockerfile的第一条必须是FROM指令,用来指定要制作的镜像继承自哪个镜像。
    需要说明的是,可以在Dockerfile中写多个FROM指令来构建复杂的镜像。
  • MAINTAINER
    指令格式为MAINTAINER
    用来指定维护者信息。
  • RUN指令格式为
    RUN或RUN["executable","param1","param2"...]
    该指令是用来执行shell命令的,当解析Dockerfile时,遇到RUN指令,Docker会将该指令翻译为“/bin/sh–c“xxx””,其中xxx为RUN后边的Shell命令。
  • EXPOSE指令格式为
    EXPOSE[...]。
    该指令用来将容器中的端口号暴露出来,也可以通过“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
    指定一个环境变量,会被后续RUN指令使用,并在容器运行时保持。
  • ADD指令格式为
    ADD
    该指令将复制指定的到容器中的
    其中可以是Dockerfile所在目录的一个相对路径;也可以是一个URL;还可以是一个tar文件(自动解压为目录)。
  • COPY指令格式为COPY。复制本地主机的(为Dockerfile所在目录的相对路径)到容器中的。当使用本地目录为源目录时,推荐使用COPY。
Docker集群管理 Compose Compose是用来定义和运行一个或多个容器应用的工具。使用Compose可以简化容器镜像的建立及容器的运行。
Compose是使用YML文件来定义多容器应用的,它还会用docker-compose up命令把完整的应用运行起来.
从本质上来讲,Compose把YML文件解析成docker命令的参数,然后调用相应的docker命令行接口,从而把应用以容器化的方式管理起来。它通过解析容器间的依赖关系来顺序地启动容器。而容器间的依赖关系则可以通过在docker-compose.yml文件中使用“links”标记来指定.
【Docker基础】实例
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服务都会成功运行起来
Machine

    推荐阅读