Docker - SuperProjectGit/super-repository GitHub Wiki

1.什么是Docker?

Docker是基于Go语言实现的云开源项目。Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或者数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。 Docker引擎的基础是Linux自带的容器(Linux Containers,LXC)技术。IBM对于容器技术的准确描述如下: 容器有效的将单个操作系统管理的资源划分到孤立的组中,以便更好的在孤立的组之间平衡有冲突的资源使用需求。与虚拟化相比,这样既不需要指令级模拟,也不需要即时编译。容器可以在核心CPU本地运行指令,而不需要任何专门的解释机制。此外,也避免了准虚拟化(paravirtualization)和系统调用替换中的复杂性。 我们可以将容器理解为一种沙盒。每个容器内运行一个应用,不同的容器相互隔离,容器之间可以建立通信机制。容器的创建和停止都十分快速(秒级),容器自身对资源的需求十分有限,远比虚拟机本身占用的资源少。

2. Docker给DevOps带来的好处

更快速的交付和部署:开发人员可以使用镜像快速的构建标准开发环境;开发完成后,测试和运维人员可以使用开发人员提供的docker镜像快速部署应用,可以避免开发和测试运维人员之间的环境差异导致的部署问题。 更高校的资源利用:Docker容器的运行不需要额外的虚拟化管理程序支持,它是内核级的虚拟化,在占用更少资源的情况实现更高的性能。 更方便的迁移和扩展:Docker容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、服务器等。这种兼容使得用户可以在不同的平台之间很方便的完成应用迁移。 更简单的更新管理:使用Dockerfile,只需要小小的配置修改,就可以替代以往大量的更新工作,并且所有修改都以增量方式进行分发和更新。

3. 虚拟化与Docker

虚拟化的核心是对资源进行抽象,目标往往是为了在同一个机器上运行多个系统或应用,从而提高系统资源的利用率。虚拟化分为很多类型,比如常见的硬件辅助虚拟化(VMware workstation、 KVM等)。Docker所代表的容器虚拟化技术属于操作系统级虚拟化:内核通过创建多个虚拟的操作系统实例(内核和库)来隔离不同的进程。# 传统虚拟化和容器技术结构比较:传统虚拟化技术是在硬件层面实现虚拟化,增加了系统调用链路的环节,有性能损耗;容器虚拟化技术以共享Kernel的方式实现,几乎没有性能损耗。

4. Docker怎么实现的?

Docker是Client/Server的架构,Docker客户端与Docker daemon进行交互,daemon负责构建、运行和发布Docker容器。客户端可以和服务端运行在同一个系统中,也可以连接远程的daemon。Docker的客户端的daemon通过RESTful API进行socket通信。 如上图,Docker守护进程(daemon)在主机上运行,用户不能直接和守护进程打交道,但是可以通过Docker客户端与其进行交互;Client是Docker的初始用户界面,它接收用户的命令并反馈,并且与Docker的守护进行交互。 Docker基于Linux容器技术(LXC),Namespace,Cgroup,UnionFS(联合文件系统)等技术实现:

namespace(命名空间):

命名空间是 Linux 内核一个强大的特性。每个容器都有自己单独的命名空间,运行在其中的应用都像是在独立的操作系统中运行一样。命名空间保证了容器之间彼此互不影响。docker实际上一个进程容器,它通过namespace实现了进程和进程所使用的资源的隔离。使不同的进程之间彼此不可见。

Docker用到的一些命名空间有: pid命名空间:用于隔离进程,容器都有自己独立的进程表和1号进程; net命名空间:用于管理网络,容器有自己独立的networkinfo; ipc命名空间:用于访问IPC资源(IPC:InterProcess Communication); mnt命名空间:用于管理挂载点,每个容器都有自己唯一的目录挂载; uts命名空间:用于隔离内核和版本标识(UTS:UnixTimeProcess System),每个容器都有独立的hostname和domain。

cgroup(控制组):

是 Linux 内核的一个特性,主要用来对共享资源进行隔离、限制、审计等。只有能控制分配到容器的资源,才能避免当多个容器同时运行时的对系统资源的竞争。控制组技术最早是由 Google 的程序员 2006 年起提出,Linux 内核自 2.6.24 开始支持。控制组可以提供对容器的内存、CPU、磁盘 IO 等资源的限制和审计管理。

UnionFS(联合文件系统):

Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对 文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。Docker 中使用的 AUFS(AnotherUnionFS)就是一种 Union FS。 AUFS 支持为每一个成员目录(类似 Git 的分支)设定只读(readonly)、读写(readwrite)和写出(whiteout-able)权限, 同时 AUFS 里有一个类似分层的概念, 对只读权限的分支可以逻辑上进行增量地修改(不影响只读部分的)。Docker 目前支持的 Union 文件系统种类包括 AUFS, btrfs, vfs 和 DeviceMapper。

5. Docker核心概念

5.1 image

Docker镜像类似于虚拟机镜像,是一个只读模板,并且包含了文件系统。一个镜像可以只包含一个操作系统环境(比如SUSE镜像),也可以安装了用户程序及其运行环境(比如eBackup镜像)。镜像其实就是一个文件,任何用户程序都可以成为镜像的一部分。 镜像=操作系统+软件运行环境+用户程序

如上图,一个layer就是一个image,多个image又可以打包成一个image。Image类似一个单链表系统,每个image包含一个指向parent image的指针,没有parent image的image是baseimage(image的指针靠sqlite数据库来保存)。 最上面的一层(不属于image)是可写的,上面的内容依赖于下面的内容,如果要修改下面的内容,先将下面的内容复制到上面再进行修改。 Image是创建container的基础。 关于image的一些命令: docker pull //从网络上下载镜像 docker images //查看本地主机已经存在的镜像

5.2 Container

容器是从镜像创建的运行实例,可以将其启动、开始。停止、删除,而这些容器都是相互隔离(独立进程),互不可见的。

5.3 Repository

我们对Docker有了认识和了解,docker还有非常重要的一块,那就是docker镜像。提到docker镜像必不可少的一个文件是Dockerfile docker可以通过build命令自动从Dockerfile文件中读取并执行命令,最终生成docker镜像。使用Dockerfile非常简单,且重要!

Dockerfile命令详解

写文章时docker版本号为:1.8.2 目前Dockerfile中共包含14个命令,文章内容详细介绍每个一个命令的含义用法

Dockerfile

格式:INSTRUCTION arguments

instruction 不区分大小写,推荐用大写,以免和其他参数冲突

FROM

格式:

  • FROM \<image\>
  • FROM \<image\>:\<tag\>
  • FROM \<image\>@\<digest\>

Dockerfile中的第一条指令必须是FROM,FROM指令指定了构建镜像的base镜像

MAINTAINER

格式:MAINTAINER \<name\> \<Email\>

编写维护Dockerfile的作者信息

ENV

格式:

  • ENV \<key\>\<value\>
  • ENV \<key\>=\<value\>

为容器声明环境变量,环境变量在子镜像中也可以使用。可以直接使用环境变量$variable_name 运行容器指定环境变量:docker run --env <key>=<value>

RUN

格式:

  • RUN \<command\> (类似/bin/sh -cshell格式)

  • RUN ["executable", "param1", "param2"] (exec格式)

第一种 使用shell格式时,命令通过/bin/sh -c执行;

第二种 使用exec格式时,命令直接执行,容器不调用shell,并且exec格式中的参数会看作是JSON 数组被Docker解析,所以要用(")双引号,不能用单引号(')。

举例: RUN [ "echo", "$HOME" ]$HOME变量不会被替换,如果你想运行shell程序,使用:RUN [ "sh", "-c", "echo", "$HOME" ]

COPY

格式:COPY <src><dest>

拷贝本地<src>目录下的文件到容器中<dest>目录。

ADD

格式:

  • ADD <src><dest>
  • ADD ["",... ""]

ADD hom* /mydir/ 从<src>目录下拷贝文件,目录或网络文件到容器的<dest>目录。和COPY非常相似,但比COPY功能多,拷贝的文件可以是一个网络文件,并且ADD有解压文件得功能。

CMD

格式:

  • CMD \<commadn\> param1 param2(shell格式)
  • CMD ["executable", "param1", "param2"] (exex格式)
  • CMD ["param1", "param2"](为ENTRYPOINT命令提供参数)

虽然有三种格式,但在Dockerfile中如果有多个CMD命令,只有最后一个生效。

CMD命令主要是提供容器运行时得默认值。默认值,可以是一条指令,也可以是参数(ENTRYPOINT),CMD命令参数是一个动态值,信息会保存到镜像的JSON文件中。

举例:

ENTRYPOINT ["executable"]
CMD ["param1", "param2"]

启动容器执行:["executable", "param1", "param2"] 注意: 如果在命令行后面运行docker run时指定了命令参数,则会把Dockerfile中定义的CMD命令参数覆盖

ENTRYPOINT

格式:

  • ENTRYPOINT \<command\> (shell格式)
  • ENTRYPOINT ["executable", "param1", "param2"](exec格式)

ENTRYPOINTCMD命令类似,也是提供容器运行时得默认值,但它们有不同之处。同样一个Dockerfile中有多个ENTRYPOINT命令,只有最后一个生效。

  • ENTRYPOINT命令使用<commmand>格式,ENTRYPOINT会忽略任何CMD指令和docker run 传来的参数,直接运行在/bin/sh -c中;也就是说ENTRYPOINT执行的进程会是/bin/sh -c子进程。所以进程的PID不会是1,而且也不能接受Unix信号。(执行docker stop $container_id的时候,进程接收不到SIGTERM信号)

  • 使用exec格式,docker run传入的命令参数会覆盖CMD命令,并附加到ENTRYPOINT命令的参数中。推荐使用exec方式

ONBUILD

格式:ONBUILD [INSTRUCTION]

ONBUILD指令的功能是添加一个将来执行触发器指令到镜像中。当Dockerfile中FROM的镜像中包含ONBUILD指令,在构建此镜像的时候会触发ONBUILD指令。但如果当前Dockerfile中存在ONBUID指令,不会执行就。ONBUILD指令在生成应用镜像时用处非常大。

ONBUILD如何工作

1.构建过程中,ONBUILD指令会添加到触发器指令镜像元数据中。触发器指令不会在当前构建过程中生效

2.构建完后,触发器指令会被保存到镜像的详情中,主键是OnBuild,可以使用docker inspect命令查看到

3.之后此镜像可能是构建其他镜像的父镜像,在构建过程中,FROM指令会查找ONBUILD触发器指令,并按照之前定义的顺序执行;如果触发器指令执行失败,则构建新镜像失败并退出;如果触发器指令执行成功,则继续往下执行。

4.构建成功后ONBUILD指令清除,固不会被孙子辈镜像继承。

VOLUME

格式:VOLUME ["/data"]

为docker主机和容器做目录映射,volume目录信息会保存到镜像的JSON文件中,在运行docker run命令时指定$HOST_DIR

LABEL

格式:LABEL <key>=<value> <key>=<value> <key>=<value> ...

LABEL指令,添加一个元数据到镜像中。一个镜像中可以有多个标签,建议写一个(因为没多个LABEL指令镜像会多一个layerLABEL是一个键值对<key><value>,在一个标签值,包括空间使用引号和反斜杠作为您在命令行解析。 举例: LABEL multi.label1="value1" multi.label2="value2" other="value3"

EXPOSE

格式:EXPOSE <port> [<port>...]

在Dockerfile中定义端口,默认是不往外暴露,在运行docker run -p or -P暴露

USER

格式:USER daemon

设定一个用户或者用户ID,在执行RUN``CMD``ENTRYPOINT等指令时指定以那个用户得身份去执行

WORKDIR

格式:WORKDIR /path/to/workdir

当执行RUN``CMD``ENTRYPOINT``ADD``CMD等命令,设置工作目录

.dockerignore

如果在Dockerfile文件目录下有.dockerignore文件,docker在构建镜像的时候会把在.dockerignore文件中定义的文件排除出去

    */temp*
    */*/temp*
    temp?
    *.md
    !LICENSE.md

Docker安装 (基于17.06.2-ce,Centos7)

  1. 系统要求 正在维护的Centos7以上版本, centos-extras被启用(默认是启用状态)启用centos-extras
  2. 卸载旧版本docker

旧版本被称为docker或者docker-engine,如果之前装有旧版本,需要卸载旧版本,相关依赖一同卸载。

$ sudo yum remove docker \ docker-common \ docker-selinux \ docker-engine

/var/lib/docker/路径下的内容将被保留,现在Docker-CE包统称docker-ce 3. 安装Docker-CE

  • 使用repository安装,这种方式免除了安装和升级的任务,官方推荐的安装方式
  • 下载RPM包手动安装和升级,适用于无外网的隔离环境
  • 使用自动化的便捷脚本安装,适用于测试和开发环境

使用repository安装

如果是第一次在一台新的机器上安装,则需要安装repository,之后就可以通过repository进行安装和升级

  • 安装repository

    1. 安装依赖包,yum-utils包为yum-config-manager包提供了可用性,devicemapper的存储驱动则需要device-mapper-persistent-data和lvm2

    $ sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2

    1. 安装 stable repository,即使要使用edge或者test repository,也需要stable repository的支持(docker-ce有两个更新通道 stablet提供可靠的版本,每个季度会更新一次,edge提供新版本,每个月更新一次)

    $ sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo

    1. (可选) 开启edge或者test repository,默认是未启用的

    $ sudo yum-config-manager --enable docker-ce-edge

    $ sudo yum-config-manager --enable docker-ce-test

    禁用edge或者test repository可以使用如下命令:

    $ sudo yum-config-manager --disable docker-ce-edge $ sudo yum-config-manager --disable docker-ce-test

  • 安装DOCKER CE

    1. 安装最新版本的docker ce

    $ sudo yum install docker-ce

    Note:如果启用了多个docker repository,则在使用yum install或者yum update没有指定版本时,总是会安装最高的版本,可能最高的版本并不是很稳定。

    1. 在生产环境,应该使用指定版本的docker,而不是使用最新的版本,查看可用版本

    $ yum list docker-ce --showduplicates | sort -r

    docker-ce.x86_64 17.09.ce-1.el7.centos docker-ce-stable

    安装指定版本的docker ce

    sudo yum install <FULLY-QUALIFIED-PACKAGE-NAME>

    example:上面列表中的全称版本 docker-ce-17.06.1.ce

    1. 启动docker

    $ sudo systemctl start docker

    1. 运行hello-world镜像检查docker是否正确安装

    $ sudo docker run hello-world

    这个命令会下载测试镜像,并且运行一个容器,当容器运行成功,会打印信息然后退出。

⚠️ **GitHub.com Fallback** ⚠️