1.镜像(image):Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)
统一文件系统(Union File System)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角。
用到了一个重要的资源管理技术,叫写时复制。写时复制(copy-on-write),也叫隐式共享,是一种对可修改资源实现高效复制的资源管理技术。对于一个重复资源,若不修改,则无需立刻创建一个新的资源,该资源可以被共享使用。当发生修改的时候,才会创建新资源。
存储驱动:AUFS、overlay和overlay2(默认)
2.容器(container):Container是在原先的Image之上新加的一层,称作Container layer,这一层是可读可写的(Image是只读的)。
在面向对象的编程语言中,有类跟对象的概念。类是抽象的,对象是类的具体实现。Image跟Container可以类比面向对象中的类跟对象,Image就相当于抽象的类,Container就相当于具体实例化的对象。
Image跟Container的职责区别:Image负责APP的存储和分发,Container负责运行APP。
3.仓库(Repository):Docker 仓库是集中存放镜像文件的场所。镜像构建完成后,可以很容易的在当前宿主上运行。
但是,如果需要在其他服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。
我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 Latest 作为默认标签。
仓库又可以分为两种形式:Public(公有仓库)、Private(私有仓库)
Linux 内核2.4.19中开始陆续引用了namespace概念。每个容器都有自己的命名空间,运行在其中的应用都是在独立操作系统中运行一样。命名空间保证了容器之间彼此互不影响
但是光有运行环境隔离还不够,因为这些进程还是可以不受限制地使用系统资源,比如网络、磁盘、CPU以及内存等。为了防止它占用了太多的资源而影响到其它进程,Docker使用Linux cgroups来限制容器中的进程允许使用的系统资源 Linux Cgroup可以让系统中所运行任务(进程)的用户定义组分配资源—比如CPU时间、系统内存、网络带宽
四大功能:
①资源限制:可以对任务使用的资源总额进行限制
②优先级分配:通过分配的cpu时间片数量以及磁盘IO带宽大小,实际上相当于控制了任务运行优先级
③资源统计:可以统计系统的资源使用量,如cpu时长,内存用量等
④任务控制:cgroup可以对任务执行挂起、恢复等操作
docker client 是docker架构中用户用来和docker daemon建立通信的客户端,用户使用的可执行文件为docker,通过docker命令行工具可以发起众多管理container的请求。
docker daemon 是docker架构中一个常驻在后台的系统进程,功能是:接收处理docker client发送的请求。
该守护进程在后台启动一个server,server负载接受docker client发送的请求;接受请求后,server通过路由与分发调度,找到相应的handler来执行请求。
Engine是Docker架构中的运行引擎,同时也Docker运行的核心模块。它扮演Docker container存储仓库的角色,并且通过执行job的方式来操纵管理这些容器。
一个Job可以认为是Docker架构中Engine内部最基本的工作执行单元。Docker可以做的每一项工作,都可以抽象为一个job。例如:在容器内部运行一个进程,这是一个job;创建一个新的容器,这是一个job,从Internet上下载一个文档,这是一个job;包括之前在Docker Server部分说过的,创建Server服务于HTTP的API,这也是一个job,等等。
Graph在Docker架构中扮演已下载容器镜像的保管者,以及已下载容器镜像之间关系的记录者。一方面,Graph存储着本地具有版本信息的文件系统镜像,另一方面也通过GraphDB记录着所有文件系统镜像彼此之间的关系。
主要用于完成容器镜像的管理,包括存储与获取。即当用户需要下载指定的容器镜像时,graphdriver将容器镜像存储在本地的指定目录;同时当用户需要使用指定的容器镜像来创建容器的rootfs时,graphdriver从本地镜像存储目录中获取指定的容器镜像。
在graphdriver的初始化过程之前,有各种文件系统会在其内部注册,它们分别是aufs、btrfs、vfs和devmapper、overlay和overlay2(默认)。而Docker在初始化之时,通过获取系统环境变量”DOCKER_DRIVER”来提取所使用driver的指定类型。而之后所有的graph操作,都使用该driver来执行。
用途是完成Docker容器网络环境的配置,其中包括Docker启动时为Docker环境创建网桥;Docker容器创建时为其创建专属虚拟网卡设备;以及为Docker容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等。
作为Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。在execdriver的实现过程中,原先可以使用LXC驱动调用LXC的接口,来操纵容器的配置以及生命周期,而现在execdriver默认使用native驱动,不依赖于LXC。
libcontainer是Docker架构中一个使用Go语言设计实现的库,设计初衷是希望该库可以不依靠任何依赖,直接访问内核中与容器相关的API。
正是由于libcontainer的存在,Docker可以直接调用libcontainer,而最终操纵容器的namespace、cgroups、apparmor、网络设备以及防火墙规则等。这一系列操作的完成都不需要依赖LXC或者其他包。
Docker容器是Docker架构中服务交付的最终体现形式。
Docker按照用户的需求与指令,订制相应的Docker容器:
①用户通过指定容器镜像,使得Docker容器可以自定义rootfs等文件系统;
②用户通过指定计算资源的配额,使得Docker容器使用指定的计算资源;
③用户通过配置网络及其安全策略,使得Docker容器拥有独立且安全的网络环境;
④用户通过指定运行的命令,使得Docker容器执行指定的工作。
例如,我们需要拉取一个 Docker 镜像,我们可以用如下命令:
docker pull image_name
image_name
为镜像的名称,而如果我们想从 Docker Hub
上去下载某个镜像,我们可以使用以下命令:
docker pull centos:latest
centos:lastest
是镜像的名称,Docker Daemon
发现本地没有我们需要的镜像,会自动去 Docker Hub
上去下载镜像,下载完成后,该镜像被默认保存到 /var/lib/docker
目录下。
接着我们如果想查看主机下存在多少镜像,我们可以用如下命令:
docker images
我们要想知道当前有哪些容器在运行,我们可以用如下命令:
docker ps -a
-a
是查看当前所有的容器,包括未运行的。我们该如何去对一个容器进行启动,重启和停止呢?
我们可以用如下命令:
docker start container_name/container_id
docker restart container_name/container_id
docker stop container_name/container_id
这个时候我们如果想进入到这个容器中,我们可以使用 attach
命令:
docker attach container_name/container_id
那如果我们想运行这个容器中的镜像的话,并且调用镜像里面的 bash
,我们可以使用如下命令:
docker run -t -i container_name/container_id /bin/bash
那如果这个时候,我们想删除指定镜像的话,由于 Image
被某个 Container
引用(拿来运行),如果不将这个引用的 Container
销毁(删除),那 Image
肯定是不能被删除。
我们首先得先去停止这个容器:
docker ps
docker stop container_name/container_id
然后我们用如下命令去删除这个容器:
docker rm container_name/container_id
然后这个时候我们再去删除这个镜像:
docker rmi image_name
挂载本地目录:
docker可以支持把一个宿主机上的目录挂载到镜像里。
docker run -it -v /home/dock/Downloads:/usr/Downloads ubuntu64 /bin/bash
通过-v
参数,冒号前为宿主机目录,必须为绝对路径,冒号后为镜像内挂载的路径。
默认挂载的路径权限为读写。如果指定为只读可以用:ro
,也就是
docker run -it -v /home/dock/Downloads:/usr/Downloads:ro ubuntu64 /bin/bash
docker端口映射docker容器在启动的时候,如果不指定端口映射参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。
端口映射通过-P和-p参数来实现
一、-P将容器内部开放的网络端口随机映射到宿主机的一个端口上;
二、-p指定要映射的端口,一个指定端口上只可以绑定一个容器;支持的格式如下:
①IP:HOSTPORT:CONTAINERPORT
:指定ip、指定宿主机port、指定容器port
适用于映射到指定地址的指定端口
eg:将容器的5000端口映射到指定地址127.0.0.1的5000端口上:
docker run -it -d -p 127.0.0.1:5000:5000 docker.io/centos:latest /bin/bash
②IP::CONTAINERPORT
:指定ip、未指定宿主机port(随机)、指定容器port
适用于映射到指定地址的任意端口
eg:将容器的4000端口映射到127.0.0.1的任意端口上:
docker run -it -d -p 127.0.0.1::4000 docker.io/centos:latest /bin/bash
注:会将容器的ip127.0.0.1和4000端口,随机映射到宿主机的一个端口上。
③HOSTPORT:CONTAINERPORT
:未指定ip、指定宿主机port、指定容器port
适用于将容器指定端口指定映射到宿主机的一个端口上(映射所有接口地址)
eg:将容器的80端口映射到宿主机的8000端口上:
docker run -itd -p 8000:80 docker.io/centos:latest /bin/bash
注:上边的操作默认会绑定本地所有接口上的所有地址。映射访问示例
④查看映射端口配置
docker port CONTAINER_ID
Dockerfile 是自动构建 Docker 镜像的配置文件,用户可以使用 Dockerfile 快速创建自定义的镜像
Dockerfile 是由一行行命令语句组成,并且支持已 # 开头的注释行。
一般来说,我们可以将 Dockerfile 分为四个部分:
①基础镜像(父镜像)信息指令 FROM。
②维护者信息指令 MAINTAINER。
③镜像操作指令 RUN 、EVN 、ADD 和 WORKDIR 等。
④容器启动指令 CMD 、ENTRYPOINT 和 USER 等。
下面是一段简单的 Dockerfile 的例子:
FROM python:2.7
MAINTAINER Angel_Kitty
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
EXPOSE 5000
ENTRYPOINT ["python"]
CMD ["app.py"]
我们可以分析一下上面这个过程:
从 Docker Hub 上 Pull 下 Python 2.7 的基础镜像。
显示维护者的信息。
Copy 当前目录到容器中的 /App 目录下
指定工作路径为 /App。
安装依赖包。
暴露 5000 端口。
启动 App。
1.FROM
是用于指定基础的 images ,一般格式为
FROM or FORM :
所有的 Dockerfile 都应该以 FROM 开头,FROM 命令指明 Dockerfile 所创建的镜像文件以什么镜像为基础,FROM 以后的所有指令都会在 FROM 的基础上进行创建镜像。
2.MAINTAINER
是用于指定镜像创建者和联系方式,一般格式为
MAINTAINER
3.COPY
是用于复制本地主机的 (为 Dockerfile 所在目录的相对路径)到容器中的 。
当使用本地目录为源目录时,推荐使用 COPY 。一般格式为
COPY
4.WORKDIR
用于配合 RUN,CMD,ENTRYPOINT 命令设置当前工作路径。
可以设置多次,如果是相对路径,则相对前一个 WORKDIR 命令。默认路径为/。一般格式为
WORKDIR /path/to/work/dir
5.RUN
用于容器内部执行命令。每个 RUN 命令相当于在原有的镜像基础上添加了一个改动层,原有的镜像不会有变化。一般格式为
RUN
6.EXPOSE
用来指定对外开放的端口。一般格式为
EXPOSE [...]
7.ENTRYPOINT
可以让你的容器表现得像一个可执行程序一样。一个 Dockerfile 中只能有一个 ENTRYPOINT,如果有多个,则最后一个生效。
ENTRYPOINT 命令也有两种格式:
①推荐使用的 Exec 形式
ENTRYPOINT ["executable", "param1", "param2"]
②Shell 形式
ENTRYPOINT command param1 param2
8.CMD
命令用于启动容器时默认执行的命令,CMD 命令可以包含可执行文件,也可以不包含可执行文件。
不包含可执行文件的情况下就要用 ENTRYPOINT 指定一个,然后 CMD 命令的参数就会作为 ENTRYPOINT 的参数。
CMD 命令有三种格式:
①推荐使用的 exec 形式
CMD ["executable","param1","param2"]
②无可执行程序形式
CMD ["param1","param2"]
③Shell 形式
CMD command param1 param2
一个 Dockerfile 中只能有一个 CMD,如果有多个,则最后一个生效。而 CMD 的 Shell 形式默认调用 /bin/sh -c 执行命令。
CMD 命令会被 Docker 命令行传入的参数覆盖:docker run busybox /bin/echo Hello Docker 会把 CMD 里的命令覆盖。
9.ADD
命令的格式和 COPY
命令相同,也是:
ADD
除了不能用在 multistage
的场景下,ADD
命令可以完成 COPY
命令的所有功能,并且还可以完成两类超酷的功能:
①解压压缩文件并把它们添加到镜像中
②从 url 拷贝文件到镜像中
如果要构建 Dockerfile,可以在 Dockerfile 文件所在目录执行:docker build -t angelkitty/nginx_web:v1
我们解释一下:
-t
是为新镜像设置仓库和名称
angelkitty
为仓库名
nginx_web
为镜像名
v1
为标签(不添加为默认 latest )
我们构建完成之后,使用docker images
命令查看所有镜像
Docker 17.05版本以后,新增了Dockerfile多阶段构建。所谓多阶段构建,实际上是允许一个Dockerfile 中出现多个 FROM 指令。
多个 FROM 指令并不是为了生成多根的层关系,最后生成的镜像,仍以最后一条 FROM 为准,之前的 FROM 会被抛弃,那么之前的FROM 又有什么意义呢?
每一条 FROM 指令都是一个构建阶段,多条 FROM 就是多阶段构建,虽然最后生成的镜像只能是最后一个阶段的结果,但是,能够将前置阶段中的文件拷贝到后边的阶段中。
最大的使用场景是将编译环境和运行环境分离,比如,之前我们需要构建一个Go语言程序,那么就需要用到go命令等编译环境。基础镜像 golang:1.10.3 是非常庞大的,因为其中包含了所有的Go语言编译工具和库,而运行时候我们仅仅需要编译后的 server 程序就行了,不需要编译时的编译工具,最后生成的大体积镜像就是一种浪费。
在 Docker 17.05版本以后,就有了新的解决方案,直接一个Dockerfile就可以解决:
# 编译阶段
FROM golang:1.10.3
COPY server.go /build/
WORKDIR /build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server
# 运行阶段
FROM scratch
# 从编译阶段的中拷贝编译结果到当前镜像中
COPY --from=0 /build/server /
ENTRYPOINT ["/server"]
这个 Dockerfile
的玄妙之处就在于 COPY
指令的 --from=0
参数,从前边的阶段中拷贝文件到当前阶段中,多个FROM语句时,0代表第一个阶段。除了使用数字,我们还可以给阶段命名,比如:
# 编译阶段 命名为 builder
FROM golang:1.10.3 as builder
# ... 省略
# 运行阶段
FROM scratch
# 从编译阶段的中拷贝编译结果到当前镜像中
COPY --from=builder /build/server /
更为强大的是,COPY --from
不但可以从前置阶段中拷贝,还可以直接从一个已经存在的镜像中拷贝。比如,
FROM ubuntu:16.04
COPY --from=quay.io/coreos/etcd:v3.3.9 /usr/local/bin/etcd /usr/local/bin/
我们直接将etcd
镜像中的程序拷贝到了我们的镜像中,这样,在生成我们的程序镜像时,就不需要源码编译etcd
了,直接将官方编译好的程序文件拿过来就行了。