docker知识点大全

https://vuepress.mirror.docker-practice.com/

一、虚拟机VS容器

  

  一)虚拟机的缺点及容器为何产生

  • 资源占用多
  • 冗余步骤多,繁琐
  • 启动慢

  因为虚拟机存在上面的缺点,于是Linux发展出了另一种虚拟化技术:Linux容器(Linux container,缩写为LXC)。

  Linux容器不是模拟一个完整的操作系统 ,而是对进程进行隔离。

  有了容器,就可以将软件运行所需要的所有资料打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需要的库资源和设置。

  二)docker和虚拟机之间的不同之处

  1、传统虚拟机

  传统虚拟机技术是虚拟出一整套硬件后,在其上运行一个完整的操作系统,在该系统上再运行所需要的应用进程

1、主要是因为每个虚拟机需要运行自己的一组系统进程,这就产生了除组件进程外消耗以外的额外计算资源损耗。

2、不同虚拟机它拥有了完全不同的操作系统

3、在虚拟机之下是宿主机的操作系统与一个管理程序,它将物理硬件资源分成较小部分的虚拟硬件资源,从而被每个虚拟机里的操作系统使用。
注意:存在两种类型的管理程序:第一种类型的管理程序不会使用宿主机OS,而第二种类型会

 

  2、容器

  容器内的应用进程直接运行在宿主机的内核,容器内没有自己的内核,而且也没有进行硬件虚拟 。因此容器要比传统虚拟机更为轻便。    

  每个容器之间互相隔离,每个容器有自己的文件系统,容器之间进程不会互相影响,能区分计算资源,耦合度低。

  Docker 安装时会自动在 host 上创建三个网络:bridge、host和none

    • none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。容器创建时,可以通过 --network=none 指定使用 none 网络。
    • 连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。可以通过 --network=host 指定使用 host 网络。
      • docker host的网络最大的好处就是性能,若容器对网络传输效率有较高要求,则可以选择host网络
    • Docker 安装时会创建一个 命名为 docker0 的 linux bridge。如果不指定--network,创建的容器默认都会挂到 docker0 上
      • 使用brctl show显示出来
容器更加轻量级,它允许在相同的硬件上运行更多数量的组件。

一个容器仅仅是运行在宿主机上被隔离的单个进程,仅消耗应用容器消耗的资源,不会消耗其他进程的开销

多个容器则会完全执行运行在宿主机上的同一个内核的系统调用,此内核是唯一一个在宿主机操作系统上执行x86指令的内核。CPU也不需要再做任何对虚拟机能做那样的虚拟化。
     1)启动时间

  docker启动是秒级的,传统虚拟机启动是分钟级的;另外docker是一个精简版、高度浓缩的小型Linux系统。 

  • docker通过类似Git设计理念的操作来方便用户获取、分发和更新应用镜像,存储复用,增量更新
  • docker通过dockerfile支持灵活的自动化创建和部署机制,提高工作效率,使流程标准化
    2)运行在不同环境的应用

  

   3、docker容器技术与传统虚拟机技术比较

    

 
特性 容器 虚拟机
启动速度 快,秒级 慢,分钟级
性能 接近原生 较弱
内存代价 很小 较多
占用磁盘空间 小,一般为MB 非常大,一般为GB
运行密度 单机支持上千个容器 一般几十个
运行状态 直接运行在宿主机的内核上,不同容器共享同一个Linux内核 运行于Hypervisor上
隔离性 完全隔离 完全隔离
迁移性 优秀 一般
资源利用率

   隔离的内容:文件系统、网络、PID、etc、usr、UTS(UTS命名空间是Linux内核Namespace(命名空间)的一个子系统,主要用来完成对容器HOSTNAME和domain的隔离,同时保存内核名称、版本、以及底层体系结构类型等信息)

  虚拟机的好处:它提供的是完全的隔离的环境,因为每个虚拟机运行在它自己的内核上;

  容器:它都是调用一个内核,这存在安全隐患

  建议:

  1. 若你的硬件资源有限,那当你有少量进程需要隔离时,虚拟机就可以成为一个选项
  2. 为了在同一台机器上运行大量被隔离的进程,容器因它的低消耗而成为一个更好的选择

  4、为什么docker比虚拟机快?

  虚拟机运行它自己的一组系统服务,而容器则不会,因为他们都运行在同一个操作系统上。(运行一个容器不会像虚拟机那样开机,它的进程可以很快启动)

  1. docker有着比虚拟机更少的抽象层。由于docker不需要hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的是实际物理机的硬件资源。因此在cpu、内存利用率上docker将会在效率上有明显的而优势;
  2. docker利用的是宿主机的内核,而不需要guest os。因此当建立一个容器时,docker不需要和虚拟机一样重新加载一个操作系统的内核。从而避免加载操作系统内核返回比较费时的资源过程,当新建一个虚拟机时,虚拟机软件需要加载guest os,返回新建过程是分钟级的。而docker由于直接利用宿主机的操作系统,则省略了加载过程,因此新建一个docker容器只需要几秒。

  三)容器实现隔离机制

tree -L 1
.
|-- anaconda-post.log
|-- bin -> usr/bin
|-- boot
|-- dev
|-- etc
|-- home
|-- lib -> usr/lib
|-- lib64 -> usr/lib64
|-- media
|-- mnt
|-- opt
|-- proc
|-- root
|-- run
|-- sbin -> usr/sbin
|-- srv
|-- sys
|-- tmp
|-- usr
`-- var
目录

  1、限制

  有两个机制可用:
第一个是 隔离namespace命名空间, 它使每个进程只看到它自己的系统视图(文件、进程、网络接口、主机名等);
第二个是 限制cgroups控制组 (control groups),它限制了进程能使用的资源量 (CPU、 内存、 网络带宽等)。 

  默认情况下,每个Linux系统最初仅有一个命名空间。所有系统资源(诸如文件系统、用户ID、网络接口等)属于这一个命名空间。但是你能创建额外的命名空间,以及在他们之间组织资源。对于一个进程,可以在一个命名空间中运行它。进程将只能看到同一个命名空间下的资源。当然,会存在多种类型的命令空间。所以一个进程不单单属于某一个命名空间,而属于每个类型的一个命名空间。

    1)Linux内核里面实现了7种不同类型的命名空间
名称        宏定义(系统调用参数)      隔离内容
Cgroup      CLONE_NEWCGROUP              Cgroup root directory (since Linux 4.6)
IPC         CLONE_NEWIPC                 信号量、消息队列和共享内存System V IPC, POSIX message queues (since Linux 2.6.19)
Network     CLONE_NEWNET                 网络设备、网络栈、端口等Network devices, stacks, ports, etc. (since Linux 2.6.24)
Mount       CLONE_NEWNS                  挂载点(文件系统)Mount points (since Linux 2.4.19)
PID         CLONE_NEWPID                 进程编号Process IDs (since Linux 2.6.24)
User        CLONE_NEWUSER                用户和用户组User and group IDs (started in Linux 2.6.23 and completed in Linux 3.8)
UTS         CLONE_NEWUTS                 主机名与域名Hostname and NIS domain name (since Linux 2.6.19)
      不同类型的命名空间的作用
  1. mount(mnt):每个进程都存在于一个mount Namespace里面,mount Namespace为进程提供了一个文件层次视图。如果不设定这个flag,子进程和父进程将共享一个mount Namespace,其后子进程调用mount或umount将会影响到所有该Namespace内的进程。如果子进程在一个独立的mount Namespace里面,就可以调用mount或umount建立一份新的文件层次视图。
  2. process ID(pid):linux通过命名空间管理进程号,同一个进程,在不同的命名空间进程号不同!进程命名空间是一个父子结构,子空间对于父空间可见。
  3. network(net):Network Namespace为进程提供了一个完全独立的网络协议栈的视图。包括网络设备接口,IPv4和IPv6协议栈,IP路由表,防火墙规则,sockets等等。一个Network Namespace提供了一份独立的网络环境,就跟一个独立的系统一样
  4. inter-process communication(ipc):用于隔离进程间通讯所需的资源( System V IPC, POSIX message queues),PID命名空间和IPC命名空间可以组合起来用,同一个IPC名字空间内的进程可以彼此看见,允许进行交互,不同空间进程无法交互
  5. UTS:用于隔离主机名
  6. user ID(user):用于隔离用户

  每种命名空间被用来隔离一组特定的资源。例如:UTS命名空间决定了运行在命名空间里的进程能看见哪些主机名和域名。通过分派两个不同的UTS命名空间给一对进程,能给他们看见不同的本地主机名。换句话说,这两个进程就好像正在两个不同的机器上运行一样(只是就主机名而言是这样的)

  同样,一个进程属于什么network命名空间决定了运行在进程里的应用程序能看见什么网络接口。每个网络接口属于一个命名空间,但是可以从一个命名空间转移到另一个。每个容器都使用它自己的网络命名空间,因此每个容器仅能看见他自己的一组网络接口。

  2、资源限制

  对容器进行限制。防止容器无限制滥用甚至耗尽宿主机上的资源。而 Linux Cgroups 就是 Linux 内核中用来为进程设置资源限制的一个重要功能。

  Linux Cgroups 的全称是 Linux Control Group。它最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。

  在 Linux 中,Cgroups 给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的 /sys/fs/cgroup 路径下。在 Ubuntu 16.04 机器里,我可以用 mount 指令把它们展示出来,这条命令是

mount -t cgroup 
cpuset on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cpu on /sys/fs/cgroup/cpu type cgroup (rw,nosuid,nodev,noexec,relatime,cpu)cpuacct on /sys/fs/cgroup/cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct)
blkio on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
memory on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
...

  可以看到,在 /sys/fs/cgroup 下面有很多诸如 cpuset、cpu、 memory 这样的子目录,也叫子系统。这些都是我这台机器当前可以被 Cgroups 进行限制的资源种类。而在子系统对应的资源种类下,你就可以看到该类资源具体可以被限制的方法。比如,对 CPU 子系统来说,我们就可以看到如下几个配置文件,这个指令是:

ls /sys/fs/cgroup/cpu
cgroup.clone_children 
cpu.cfs_period_us 
cpu.rt_period_us 
cpu.shares notify_on_releasecgroup.procs 
cpu.cfs_quota_us 
cpu.rt_runtime_us 
cpu.stat 
tasks

  以上输出的关键文件 cfs_periodcfs_quota, 可以用来限制进程在长度为 cfs_period 的一段时间内,只能被分配到总量为 cfs_quota 的 CPU 时间。

  而这样的配置文件又如何使用呢?
  你需要在对应的子系统下面创建一个目录,比如,我们现在进入 /sys/fs/cgroup/cpu 目录下:

root@ubuntu:/sys/fs/cgroup/cpu$ mkdir container
root@ubuntu:/sys/fs/cgroup/cpu$ ls container/
cgroup.clone_children 
cpu.cfs_period_us 
cpu.rt_period_us cpu.shares 
notify_on_releasecgroup.procs 
cpu.cfs_quota_us 
cpu.rt_runtime_us 
cpu.stat tasks

  这个目录就称为一个“控制组”。你会发现,操作系统会在你新创建的 container 目录下,自动生成该子系统对应的资源限制文件。

  现在,我们在后台执行这样一条脚本

$ while : ; do : ; done &
[1] 226

  显然,它执行了一个死循环,可以把计算机的 CPU 吃到 100%,根据它的输出,我们可以看到这个脚本在后台运行的进程号(PID)是 226。

  这样,我们可以用 top 指令来确认一下 CPU 有没有被打满:

$ top
%Cpu0 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

  在输出里可以看到,CPU 的使用率已经 100% 了(%Cpu0 :100.0 us)。

  而此时,我们可以通过查看 container 目录下的文件,看到 container 控制组里的 CPU quota 还没有任何限制(即:-1),CPU period 则是默认的 100 ms(100000 us):

 
$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us 
-1
$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_period_us 
100000

  接下来,我们可以通过修改这些文件的内容来设置限制。

  比如,向 container 组里的 cfs_quota 文件写入 20 ms(20000 us):

$ echo 20000 > /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us

  结合前面的介绍,你应该能明白这个操作的含义,它意味着在每 100 ms 的时间里,被该控制组限制的进程只能使用 20 ms 的 CPU 时间,也就是说这个进程只能使用到 20% 的 CPU 带宽。

接下来,我们把被限制的进程的 PID 写入 container 组里的 tasks 文件,上面的设置就会对该进程生效了:

$ echo 226 > /sys/fs/cgroup/cpu/container/tasks 

  我们可以用 top 指令查看一下:

$ top
%Cpu0 : 20.3 us, 0.0 sy, 0.0 ni, 79.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

  可以看到,计算机的 CPU 使用率立刻降到了 20%(%Cpu0 : 20.3 us)。

  除 CPU 子系统外,Cgroups 的每一项子系统都有其独有的资源限制能力,比如:

  • blkio:为​​​块​​​设​​​备​​​设​​​定​​​I/O 限​​​制,一般用于磁盘等设备
  • cpuset:为进程分配单独的 CPU 核和对应的内存节点
  • memory:为进程设定内存使用的限制

  Linux Cgroups 的设计还是比较易用的,简单粗暴地理解呢,它就是一个子系统目录加上一组资源限制文件的组合。 对于 Docker 等 Linux 容器项目来说,它们只需要在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录),然后在启动容器进程之后,把这个进程的 PID 填写到对应控制组的 tasks 文件中就可以了。

  而至于在这些控制组下面的资源文件里填上什么值,就靠用户执行 docker run 时的参数指定了,比如这样一条命令:
$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash

  在启动这个容器后,我们可以通过查看 Cgroups 文件系统下,CPU 子系统中,“docker”这个控制组里的资源限制文件的内容来确认:

$ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_period_us 
100000
$ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_quota_us 
20000

  这就意味着这个 Docker 容器,只能使用到 20% 的 CPU 带

  四)容器的创建

  1、容器创建需要

   

  2、容器资源限制

 

#内存限制
docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M

1、-m 或 --memory:设置内存的使用限额,例如 100M, 2G。

2、--memory-swap:设置 内存+swap 的使用限额。

3、默认情况下,上面两组参数为 -1,即对容器内存和 swap 的使用没有限制

4、--vm 1:启动 1 个内存工作线程

5、--vm-bytes 280M:每个线程分配 280M 内存。

progrium/stress为镜像名称,如果--memory-swap不写,默认是memory的两倍

# CPU限制
Docker 可以通过 -c 或 --cpu-shares 设置容器使用 CPU 的权重。如果不指定,默认值为 1024。

与内存限额不同,通过 -c 设置的 cpu share 并不是 CPU 资源的绝对数量,而是一个相对的权重值。某个容器最终能分配到的 CPU 资源取决于它的 cpu share 占所有容器 cpu share 总和的比例


# block IO权重

Block IO 是另一种可以限制容器使用的资源。Block IO 指的是磁盘的读写,docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽

默认情况下,所有容器能平等地读写磁盘,可以通过设置 --blkio-weight 参数来改变容器 block IO 的优先级。
--blkio-weight 与 --cpu-shares 类似,设置的是相对权重值,默认为 500。在下面的例子中,container_A 读写磁盘的带宽是 container_B 的两倍。

# 限制 bps 和 iops    

bps 是 byte per second,每秒读写的数据量。

iops 是 io per second,每秒 IO 的次数。

可通过以下参数控制容器的 bps 和 iops:

--device-read-bps,限制读某个设备的 bps。

--device-write-bps,限制写某个设备的 bps。

--device-read-iops,限制读某个设备的 iops。

--device-write-iops,限制写某个设备的 iops。   

                                                                                                                                                                                                                                                                                                                                                                                                         

二、docker与虚拟化

  一)虚拟化概述

  虚拟化技术是通用的概念,在不同的领域有不同的理解。在计算领域,一般是指计算虚拟化,或通常说的服务器虚拟化。

  虚拟化是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构件的不可分割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。

  虚拟化的核心是对资源的抽象,目标往往是为了在同一个主机上同时运行多个系统或应用。从而提高资源的利用率,并带来降低成本、方便管理和容错容灾等好处。

  二)虚拟化分类

  https://www.cnblogs.com/sammyliu/articles/4389765.html

  docker以及其他容器技术,都属于操作系统虚拟化这个范畴。操作系统虚拟化最大的特点是不需要额外的supervisor支持。

  传统方式是在硬件层面实现虚拟化,需要有额外的虚拟机管理应用和虚拟机操作系统层。docker容器是在操作系统层面实现虚拟化,直接复用本地主机的操作系统,因此更加轻量级。

三、docker的关键知识点

  一)docker的版本说明

  docker版本的CE才是开源版,EE是企业版。

  从2017年第一季度开始,docker版本号遵循YY.MM-xx格式,类似于Ubuntu等项目。例如 2018年6月第一次发布的社区版本为18.06.0-ce

  二)docker容器本质

  一个正在运行的 Docker 容器,其实就是一个启用了多个 Linux Namespace 的应用进程,而这个进程能够使用的资源量,则受 Cgroups 配置的限制。这也是容器技术中一个非常重要的概念,即:容器是一个“单进程”模型。

  • 容器其实就是Linux下一个特殊的进程
  • docker容器通过namespace实现进程隔离,通过cgroup实现资源限制
  • docker镜像(rootfs)是一个操作系统的所有文件和目录而不包括内核,docker镜像是共享宿主机的内核的
  • docker镜像是以主副方式挂载,所有的增删改都只会作用在容器层,但是相同的文件会覆盖掉下一层,这种方式也被称为“copy-on-wtite”

  三)docker的三要素

  1、镜像(image)

  一个 只读 的模板。 镜像可以用来创建docker容器,一个镜像可以创建很多容器 。

  镜像的特点: 镜像都是只读的。当容器启动时,一个新的可写层被加载到镜像的顶部。这一层被称为容器层,容器层之下的层都是镜像层。

  2、容器(container)

  独立运行的一个或者一组应用。容器是利用镜像创建的运行实例(相对于Python中的类和对象的概念)。

  它可以被启动、开始、停止、删除。每个容器都是互相隔离的,保证安全的平台。

  可以把容器看做是一个简易版的Linux环境 (包括root用户名权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。

  容器的定义和镜像几乎是一模一样的,也是一堆层的统一视角, 唯一区别的是容器的最上面那一层是可读写的。

  3、仓库(repository)

  仓库(repository)和仓库注册服务器(registry)是有区别的。仓库注册服务器上往往放着很多个仓库,每个仓库中又包含了很多个镜像,每个镜像有不同的标签(tag)。

  仓库分为公开仓库(public)和私有仓库(private)两种形式。

  四)UnionFS(联合文件系统)

  UnionFS(联合文件系统):union文件系统(unionFS)是一种分层、轻量级并且高性能的文件系统,它支持 对文件系统的修改作为一次提交来一层层的叠加 ,同时可以将不同的目录挂载到同一个虚拟文件系统下。

  union文件系统是docker镜像的基础。镜像可以通过分层来进行继承。基于基础镜像(没有父镜像),可以制作各种的应用镜像。 

  特性:一次同时加载多个文件系统,但从外面看来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录 

  五)docker加载原理

  docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统称为联合文件系统(unionFS)。 

  bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,linux刚启动时会加载bootfs文件系统, 在docker镜像的最底层就是bootfs 。这一层与我们典型的linux/unix系统是一样的,包含boot加载器和内核。当boot加载完成之后,整个内核就都在内存中了。此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。 

  rootfs(root file system),在bootfs之上,包含的就是典型的linux系统中的/dev/,/proc,/bin,/etc/等标准目录和文件。rootfs就是各种不同操作系统发行版,比如ubuntu,centos等。

  六)docker的五种存储方式

  详细内容见:https://www.cnblogs.com/fengjian2016/p/6638205.html

  七)docker的四种网络模式

  1. host模式,使用--net=host指定。
  2. container模式,使用--net=container:NAME_or_ID指定。
  3. none模式,使用--net=none指定。
  4. bridge模式,使用--net=bridge指定,默认设置。

  1、host模式

  使用--net=host指定,容器和宿主机共享network namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

  使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好


    

  2、container模式

  使用--net=container:NAME_or_ID指定,容器和另外一个容器共享Network namespace。 kubernetes中的pod就是多个容器共享一个Network namespace。

  新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信

  

  3、none模式

  使用--net=none指定,容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等。也就是这个容器没有网卡、IP、路由等。需要我们自己为容器添加网卡、配置IP。

  这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过--network=none来指定。优势:这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。

   

  4、bridge模式

  使用--net=bridge指定,默认设置。

  当docker启动后,会在主机上创建一个名为docker0的虚拟网桥,此主机上的docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。

  从docker0子网中分批额一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一堆虚拟网卡veth pair设备,docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxx这样的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。

  bridge模式是docker的默认网络模式,不写--net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看

     

  八)支持的三种数据持挂载方式

  docker提供了三种不同的方式将数据从宿主机挂载到容器中,volume、bind mount、tmps mount

  • bind mount:可以存储在宿主机系统的任意位置
    • 例:docker run -v /opt/html:/usr/share/nginx/html -p 81:80 -d --name nginx_bind nginx:latest
    • 缺点:被挂载的宿主机目录(或文件)不受保护,任何容器都可以取随意修改
    • 注意:若使用bind mounts挂载宿主机目录到一个容器中的非空目录,那么此容器中的非空目录中的文件会被隐藏,容器只能访问到的文件均来自宿主机目录
  • volume:docker管理宿主机文件系统的一部分(/var/lib/docker/volumes)

 

docker volume create nginx-volume
docker volume ls
docker run --mount type=volume,source=nginx-volume,destination=/usr/share/nginx/html,readonly -p 82:80 -d --name nginx_volume nginx:latest

 

  若强制删除容器后,数据卷不会被删除,还是会保存在宿主机的docker/volumes目录下

  • tmpfs mounts:挂载存储在宿主机系统的内存中,不会写入宿主机的文件系统
    • docker run --mount type=tmpfs,destination=/usr/share/nginx/html -p 83:80 -d --name nginx_tmpfs nginx:latest

  

  对比docker三种挂载方式

docker三种挂载方式对比
  bind(-v) volume tmpfs
volume位置 可指定任意位子 $(docker_data_dir)/docker/volumes/... 宿主机内存中
对已有 mount point影响 隐藏并替换为volume 原有数据复制到volume  
是否支持单个文件 支持 不支持,只能是目录  
权限控制 可设置为只读,默认为读写权限 可设置为只读,默认为读写权限  
移植性 移植性弱,与host path绑定 移植性强,无须指定host目录  
是否支持持久化 支持 支持 不支持
应用场景

主机和容器共享配置文件(docker默认情况下,通过这种方式为容器提供DNS解析,将/etc/resolv.conf挂载到容器中)

多个运行容器间共享数据

备份、恢复或将数据从一个docker迁移到另一个docker主机

既不想将数据存在宿主机中,也不想存于容器中(出于安全考虑,或当演员需要些大量非持久性的状态数据时为了保护容器的性能)

 

  Bind 模式无法很好地解决多跨宿主机共享存储的问题、Bind 模式的管理问题等。

  提出 Volume 的最核心的目的提升Docker对不同存储介质的支撑能力,这同时也可以减轻Docker本身的工作量。存储不仅有挂载在宿主机上的物理存储,还有网络存储,Docker 抽象出了存储启动(Sotroage Driver)来去解决对网络存储的读写问题

  九)docker的应用场景

  • web应用的自动化打包发布
  • 自动化测试和持续集成、发布
  • 在服务型环境中部署调整数据库或其他的后台应用
  • 从头编译或扩展现有的OpenShift或cloud Foundry平台来搭建自己的PaaS环境

四、安装指定版本docker

1、查看系统要求
Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看CentOS的内核版本。
uname -a
2、删除旧版本 yum remove docker docker-common docker-selinux docker-engine
3、安装需要的软件包 yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的 sudo yum install -y yum-utils device-mapper-persistent-data lvm2
4、设置Docker yum源 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
5、查看所有仓库中所有docker版本 可以查看所有仓库中所有docker版本,并选择特定的版本安装。 yum list docker-ce --showduplicates | sort -r
6、安装docker sudo yum install docker-ce 由于repo中默认只开启stable仓库,故这里安装的是最新稳18.03.0.ce-1.el7.centos。 如果要安装特定版本: sudo yum install docker-ce-18.06.1.ce
7、启动 设置为开机启动 systemctl enable docker 启动 systemctl start docker 查看启动状态 systemctl status docker 查看版本 docker version

 

五、docker相关命令

  docker官方文档:https://docs.docker.com/engine/reference/run/

  

  一)docker帮助命令

docker info #查看docker相关的信息
docker version #查看安装docker的版本信息
docker help #docker命令的帮助信息

  二)docker 镜像相关的命令

docker images #列出本地的所有镜像
docker search imagesname/imageid #搜索镜像 注意,即使你配置了阿里云加速,docker search命令查找的网站默认还是 https://hub.docker.com。阿里云加速只是下载时才会被用到。 -s starts超过多少的镜像
docker pull imagesname/imageid #下载镜像 -s starts超过多少的镜像
docker rmi imagesname/imageid #删除镜像,默认基于这个镜像还有正在运行的镜像是删除不了的,需要加上-f参数强制删除
删除多个:docker rmi -f 镜像1:tag 镜像2:tag,不写tag,删除的就是latest 
删除全部:docker rmi -f $(docker images -qa)
docker save 命令可以将镜像保存为归档文件:docker save alpine | gzip > alpine-latest.tar.gz
docker load  命令可以将保存的镜像加载到本地docker里面:docker load -i alpine-latest.tar.gz
docker tag #给镜像打标签 docker tag mysql:5.7.22 docker-registry:5000/mysql:5.7.22

  三)docker 容器相关的命令

  1、以交互性启动容器:docker run -it

docker run -it
 --name='容器新名字':为容器指定一个别名;
 -d:后台运行容器,并返回容器ID,也就是启动守护式容器;
 -i:以交互式模式运行容器,通常与-t同时使用;
 -t:tty,为容器重新分配一个伪终端,通常与-i同时使用;
 -P:随机端口映射;
 -p:指定端口映射,有以下四种格式:
 -v /宿主机绝对路径目录:/容器内目录 镜像名  

  镜像添加存储卷

1、查看数据卷是否挂载成功
docker inspect 8ad4df9ec2fd     

2、验证容器和宿主机之间数据共享 
    在/myDataVolume和/dataVolumeContainer上各创建文件,都能互相看到。

3、容器停止退出后,主机修改后数据是否同步 
    答案:还同步 

4、带权限的数据卷 
    命令:docker run -it -v /宿主机绝对路径目录:/容器内目录:ro  镜像名 
    说明:-v是volume卷的意思 
    宿主机绝对目录和容器内目录都不用提前建立
    ro:是read only的意思 

  2、查看容器运行情况:docker ps

  参数

-a:列出当前所有正在运行的容器+历史上运行过的;
-l:last,显示最近创建的容器;
-n:显示最近n个创建的容器;
-q:静默模式,只显示容器编号
--no-trunc:不截断输出
-d 以守护式方式启动的容器

  3、查看容器日志

命令格式:docker logs -f -t --tail 容器id 
参数说明: 
    -t是加入时间戳 
    -f是跟随最新的日志打印 
    --tail是Linux命令,表示显示最后多少条 

docker logs -t --help

Usage: docker logs [OPTIONS] CONTAINER

Fetch the logs of a container

Options:
--details Show extra details provided to logs
-f, --follow Follow log output
--since string Show logs since timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)
--tail string Number of lines to show from the end of the logs (default "all")
-t, --timestamps Show timestamps
--until string Show logs before a timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)

  4、查看容器内的进程

命令格式:docker top 容器id 

  5、查看容器内的细节

  以json形式返回

命令格式:docker inspect 容器id 

  6、进入正在运行的容器并以命令行交互 

  以下两种方式

1 docker exec -it 容器id bash shell 
2 docker attach 容器id

  docker exec与docker attach的区别

  • attach:直接进入容器启动命令的终端,不会启动新的进程;
    • 当多个窗口同时用attach命令连到同一个容器的时候,所有窗口都会同步显示。当某个窗口因命令阻塞时,其他窗口也无法执行操作了。
  • exec:是在容器中打开新的终端,并且可以启动新的进程。
    • 通过指定-it参数来保持标准输入打开,并且分配一个伪终端。通过exec命令对容器进行操作是最推荐的方式。

  7、从容器内拷贝文件到宿主机上 

命令格式:docker cp 容器id或容器名:容器内路径 目的宿主机路径

  应用场景:当我们以前台交互式方式运行容器后,如果在容器里面执行exit操作,容器就会停止,此时容器中运行的数据也就没有了。这时我们就需要把运行中的容器数据拷贝到宿主机上来保留。 

  8、从宿主机上拷贝文件到容器内

命令格式:docker cp 宿主机路径  容器id或容器名:容器内路径

  9、容器生命周期管理

docker stop imagename/imageid #停止容器
docker start imagename/imageid  #启动容器
docker restart imagename/imageid #重启容器
docker kill  imagename/imageid  #强制停止容器
docker rm imagename/imageid #删除容器 -f强制删除正在容器

  10、批量操作容器和镜像

  减少工作量,一劳永逸的方法就是将痛苦提前,先思考如何批量删除,以后就安装文本,直接操作。

  可以知道删除 docker container 和 docker image 的关键都是找到准确的 ID。如果能批量提供 ID 号给删除命令,就能实现批量删除了。

  批量获取容器和镜像的ID

批量获取容器的ID
docker ps -a -q
或
docker container ls -aq

批量获取镜像的ID
docker image ls -aq
7c03076402d9
7ae4e0670a3f

  批量删除本机所有容器

docker rm -rf $(docker ps -aq)
docker ps -a -q |xargs docker rm
docker  rm  $(docker  container  ls -aq)

   批量删除本机所有镜像

docker rmi $(docker image ls -aq)
docker rmi $(docker images -q)

  如果只想批量删除一部分容器或者镜像

将要删除的容器名写到一个文件中,然后根据提供的名字,再结合 grepawk 等命令,就可以轻松地获取准确的 ID 号了。只要获取了 ID 号,就可以用上面的方法将它们删除。

  按条件删除镜像

  没有打标签

docker rmi `docker images -q | awk '/^<none>/ { print $3 }'`
  镜像名包含关键字

docker rmi --force `docker images | grep doss-api | awk '{print $3}'`    //其中doss-api为关键字

  11、将容器打成新的镜像

docker commit -m "change somth" -a "somebody info" container_id(docker ps -a获取id) 新镜像名字

  12、进入node容器可实时查看当前运行容器负载状态

docker stats ef6dceff2557| grep -viE "disk|oss|nas|csi|POD|kube|node-exporter"

 

  四)docker save与docker export的区别

  1、docker save

  docker save是用来将一个或多个image打包保存的工具。

docker save -o images.tar postgres:9.6 mongo:3.4

  docker save可以指定image,可以指定container,docker save将保存的是容器背后的image。

  将打包的镜像导入,若本地存在同名的镜像会覆盖

docker load -i images.tar

  应用场景:如果你的应用是使用docker-compose.yml编排的多个镜像组合,但你要部署的客户服务器并不能连外网。这时,你可以使用docker save将用到的镜像打个包,然后拷贝到客户服务器上使用docker load载入。

  2、docker export

  docker export是用来将container的文件系统进行打包的(只能指定container

docker export -o postgres-export.tar postgres

  将打包的container载入进来使用docker import,可以指定tag

docker import postgres-export.tar postgres:latest

  注意:

  1. docker import将container导入后会成为一个image,而不是恢复为一个container。
  2. 如果本地镜像库中已经存在同名的镜像,则原有镜像的名称将会被剥夺,赋给新的镜像。原有镜像将成为孤魂野鬼,只能通过IMAGE ID进行操作。

  docker export的应用场景主要用来制作基础镜像:比如你从一个ubuntu镜像启动一个容器,然后安装一些软件和进行一些设置后,使用docker export保存为一个基础镜像。然后,把这个镜像分发给其他人使用

  3、docker save和docker export的区别

  1. docker save保存的是镜像(image),docker export保存的是容器(container);
  2. docker load用来载入镜像包,docker import用来载入容器包,但两者都会恢复为镜像;
  3. docker load不能对载入的镜像重命名,而docker import可以为镜像指定新名称。

  五)docker 的prune清理资源

  1、docker system df 查看资源信息,RECLAIMABLE是可回收比例

# docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              291                 41                  101.3GB             93.2GB (92%)
Containers          194                 65                  5.274GB             5.257GB (99%)
Local Volumes       1                   1                   0B                  0B
Build Cache         0                   0                   0B                  0B

  查看帮助信息

docker system df -h
Flag shorthand -h has been deprecated, please use --help

Usage:  docker system df [OPTIONS]

Show docker disk usage

Options:
      --format string   Pretty-print images using a Go template
  -v, --verbose         Show detailed information on space usage  ##查看详情

  2、docker system prune

  查看帮助信息

# docker system prune -h
Flag shorthand -h has been deprecated, please use --help

Usage:  docker system prune [OPTIONS]

Remove unused data

Options:
  -a, --all             Remove all unused images not just dangling ones
      --filter filter   Provide filter values (e.g. 'label=<key>=<value>')
  -f, --force           Do not prompt for confirmation
      --volumes         Prune volumes
  清理磁盘,删除关闭的容器、无用的数据卷和网络,以及旧的镜像(无tag的镜像) -f免交互

  在 Docker 17.06.1 及更高版本中必须为 docker system prune 命令明确指定 --volumes 标志才会修剪卷。

]# docker system prune
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all dangling images
  - all dangling build cache

Are you sure you want to continue? [y/N] 
  清理得更加彻底,可以将没有容器使用Docker镜像都删掉
# docker system prune -a
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all images without at least one container associated to them
  - all build cache

Are you sure you want to continue? [y/N]

 

  3、清理镜像

  清理none镜像 Prune命令
  none镜像的产生和困惑

我们偶尔会看到 none镜像(虚悬镜像),那是因为

  1. 构建镜像过程中因为脚本错误导致很多镜像构建终止,产生很多none标签的版本
  2. 手动构建镜像的时候没有进行提交,遗留来的垃圾镜像
  3. 这些镜像占据较大的存储空间,需要删除
  清理none镜像(虚悬镜像)
命令: docker image prune
默认情况下,docker image prune 命令只会清理 虚无镜像(没被标记且没被其它任何镜像引用的镜像
  清理无容器使用的镜像
命令: docker image prune -a

  默认情况下,系统会提示是否继续。要绕过提示,请使用 -f 或 --force 标志。

  4、清理容器

  停止容器后不会自动删除这个容器,除非在启动容器的时候指定了 –rm 标志。使用 docker ps -a 命令查看 Docker 主机上包含停止的容器在内的所有容器。你可能会对存在这么多容器感到惊讶,尤其是在开发环境。停止状态的容器的可写层仍然占用磁盘空间。要清理掉这

docker images prune -h
Flag shorthand -h has been deprecated, please use --help

Usage:  docker images [OPTIONS] [REPOSITORY[:TAG]]

List images

Options:
  -a, --all             Show all images (default hides intermediate images)
      --digests         Show digests
  -f, --filter filter   Filter output based on conditions provided
      --format string   Pretty-print images using a Go template
      --no-trunc        Don't truncate output
  -q, --quiet           Only show numeric IDs

  默认情况下,所有停止状态的容器会被删除。可以使用 --filter 标志来限制范围。例如,下面的命令只会删除 24 小时之前创建的停止状态的容器:  

  5、清理卷

# docker volume prune -h
Flag shorthand -h has been deprecated, please use --help

Usage:  docker volume prune [OPTIONS]

Remove all unused local volumes

Options:
      --filter filter   Provide filter values (e.g. 'label=<label>')
  -f, --force           Do not prompt for confirmation

 

  6、清理网络

  Docker 网络不会占用太多磁盘空间,但是它们会创建 iptables 规则,桥接网络设备和路由表条目。要清理这些东西,可以使用 docker network prune 来清理没有被容器未使用的网络。

# docker network prune -h
Flag shorthand -h has been deprecated, please use --help

Usage:  docker network prune [OPTIONS]

Remove all unused networks

Options:
      --filter filter   Provide filter values (e.g. 'until=<timestamp>')
  -f, --force           Do not prompt for confirmation

 

六、dockerfile保留字指令

  http://blog.itpub.net/28916011/viewspace-2213324/

  一)自定义镜像时,建议思考一下几点

  • 精简镜像用途:尽量让每个镜像的用途比较集中、单一,避免构造大而复杂、多功能的镜像
  • 选用合适的基础镜像:过大的基础镜像会造成生成臃肿的镜像,一般推荐较为小巧的debian镜像
  • 提供足够清晰的命令注释和维护者信息:dockerfile也是一种代码,需要考虑方便后面扩展和他人使用
  • 正确的使用版本号:使用版本号信息,如1.0,2.0,而非latest,将避免内容不一致可能引发的惨案
  • 减少镜像层数:若希望所生成的镜像层数尽量少,则要尽量合并指令,例如多个RUN指令可以合并成一条
  • 及时删除临时文件和缓存文件:特别是在执行apt-get指令后,/var/cache/apt下面会缓存一些安装包
  • 提高生成速度:如合理使用缓存,减少内容目录下的文件,或使用.dockerignore文件指定等
  • 调整合理的指令顺序:在开启缓存的情况下,内容不变的指令尽量放在前面,这样可以复用
  • 减少外部源的干扰:若确认需要从外部引入数据,需要指定持久的地址,并带有版本信息,让他人可以重复而不出错

  二)操作系统基础镜像

  BusyBox、Alphine、Debian/Ubuntu、CentOS等

  选择操作系统基础镜像需要注意:

  • 官方镜像体积都比较小,之带有一些基本的组件。精简的系统有利于安全、稳定和高效运行,也适合进行定制
  • 个别第三方镜像(如tutum,已被docker收购),质量也非常高。这些镜像通常针对于应用进行配置。
  • 处于安全考虑,几乎所有官方制作的镜像几乎没有安装SSH服务,无法使用用户名和密码直接登录

  注意点:

  1. Dockfile中可以有多个CMD指令,但只有最后一个生效。
  2. ADD与COPY的区别:本地目录为源目录
    1. ADD src可以为URL;若是tar文件,会自动解压到dest路径下
    2. COPY只是复制,推荐使用COPY
  3. HEALTHCHECK健康检查
  4. 使用.dockerignore文件来让docker忽略匹配模块路径下的目录和文件(每一行添加一条匹配模式)

七、容器在生产环境实践

  注意:

  1. 若docker出现不可控的风险,是否考虑到了备选的解决方案
  2. 是否需要对docker容器进行资源限制,以及如何限制(如CPU、内存、网络、磁盘等)
  3. docker对容器的安全管理做的不够完善,在应用到生产环境之前可以使用第三方工具来加强容器的安全管理。如使用apparmor对容器的能力进行限制、使用更加严格的iptable规则、禁止root登录、限制普通用户权限以及做好系统日志的记录
  4. 公司内部私有仓库的管理、镜像的管理问题是否解决。目前官方提供的私有仓库管理工具功能并不十分完善。harbor了解一下

  一)问题汇总

  1、解决镜像无法删除的问题multiple repositories

Error response from daemon: conflict: unable to delete ea5f89e79b1e (must be forced) - image is referenced in multiple repositories

意思是说,存在镜像多个引用,即同一个image id有多个repo。需要注意,自己是否真的确定要删除!

可以通过docker rmi   $REPO/$NAME:$VERSION方式删除。

  二)扩展知识点

  1、如何查看docker容器的启动命令

1.安装runlike
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py    # 运行安装脚本
pip install runlike 

2.执行命令 runlike -p 容器ID

  2、docker以root身份登录容器

# 前言
在部署服务的过程中,会遇到进到的容器内部,不是以root的身份,如果我们需要进行在容器内部执行命令,就会出现权限的问题,比如:如下显示

> bash-4.2$ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime          
- cp: cannot create regular file ‘/etc/localtime’: Permission denied

#### 解决办法
以root身份进入容器内部,命令如下:
> docker exec --privileged -u root -it 容器名字/容器id /bin/bash{sh}(bash{sh})

  3、 查看构建docker镜像的完整命令(dockerfile每个步骤内容)

# 查看构建镜像的完整命令
docker history centos:7 --no-trunc

 

posted @ 2018-11-27 19:37  思维无界限  阅读(369)  评论(0编辑  收藏  举报