一、概述
Docker is the world’s leading software container platform. Developers use Docker to eliminate “works on my machine” problems when collaborating on code with co-workers. Operators use Docker to run and manage apps side-by-side in isolated containers to get better compute density. Enterprises use Docker to build agile software delivery pipelines to ship new features faster, more securely and with confidence for both Linux and Windows Server apps.
Docker是一个开源的引擎,可以为任何应用创建一个轻量级的、可移植的、自给自足的容器。
应用场景:
- web应用的自动化打包和发布
- 自动化测试和持续集成、发布
- 在服务型环境中部署和调整数据库或其他后台应用
- 编译或扩展现有的OpenShift或Cloud Foundry平台来搭建Paas环境
Docker使用客户端/服务器架构模式,通常客户端和服务器运行在同一台机器上。
- docker服务器是一个守护进程,负责管理所有的容器
- docker客户端是一个远程控制器,负责接收用户指令并与守护进程通信
二、主要部件
1、镜像image
Docker镜像是Docker容器运行时的只读模板,由文件系统叠加而成。
最底层是一个引导文件系统bootfs,当容器启动后会被移到内存中,引导文件系统会被卸载。 第二层是一个root文件系统rootfs,位于引导文件系统之上,可以是一种或多种操作系统。Docker中的rootfs是只读状态,并且利用联合加载技术,在rootfs之上加载更多的文件系统。联合加载指一次同时加载多个文件系统,但在外部只能看到一个文件系统,最终的文件系统会包含所有底层的文件和目录。
Docker将这样的文件系统称为镜像,一个镜像可以放到另一个镜像的顶部,位于下面的镜像称为父镜像,最底层的镜像称为基础镜像。
2、仓库repository
Docker仓库用来保存镜像,而仓库存在于Registry中,默认的Registry是由Docker公司提供的公共Registry服务。
Docker Hub是公有的Docker仓库,提供了大量的镜像集合以供使用。Docker官网提供了所有可用的容器镜像,网址:index.docker.io。除了公有仓库中的镜像,还可以自己创建私有镜像。
3、容器container
Docker容器包含了某个应用运行所需要的环境,每一个容器都是一个独立的平台。容器是通过镜像所创建,可以运行、开始、停止、删除等。
当从一个镜像启动容器时,Docker会在该镜像的最顶层加载一个读写文件系统。在Docker中运行的程序就是在这个读写层中执行。当创建一个新容器时,Docker会构建出一个镜像栈,并在栈的最顶端添加一个读写层。这个读写层加上其下面的镜像层以及一些配置数据,就构成了一个容器。
三、用法
1、安装
可参考官方文档:https://docs.docker.com/engine/installation/
2、常用命令
查看docker命令列表:
docker
查看docker命令的具体用法:
docker <command> --help
查看docker版本:
docker version
查看docker信息:
docker info
检索docker镜像:
docker search <image_name>
下载docker镜像:
docker pull <image_name>
查看所有安装过的镜像:
docker images
构建新的镜像,返回一个新版本镜像的id,提交的只是创建容器的镜像与容器当前状态之间有差异的部分(目前已不推荐此做法):
docker commit id <new_image_name>
- -m 指定新创建的镜像的提交信息
- --author 指定镜像的作者信息
查看镜像的详细信息:
docker inspect <image_name>
将镜像发布到官网:
docker push <image_name>
使用dockerfile文件构建镜像:
docker build -t="仓库名/新镜像名:标签" Dockerfile文件所在的目录
查看镜像的构建历史:
docker history <image_id>
删除镜像:
docker rmi <image_id>
持久化镜像:
docker save <image_id> > /tmp/save_image.tar
使用镜像创建一个新的容器,并在容器中执行命令:
docker run <image_name> <command>
- --name 设置容器名(容器的命名是唯一的)
- -i 保持标准输入开启
- -t 分配一个伪终端
- -d 在后台运行容器(守护式容器)
- -p 指定容器与宿主机的端口映射
- -h/--hostname 设置容器的主机名
- -v/--volume 挂载卷,host:/dir从主机挂载,/dir从容器挂载
Docker首先会检查本地是否存在镜像,如果没有,则会连接官方Docker Hub查看是否存在。找到之后,会下载该镜像并将其保存在本地宿主机中。创建的容器拥有自己的网络、IP地址、用来和宿主机进行通信的桥接网络接口等。容器的主机名就是容器的ID,并且在/etc/hosts文件中为容器的IP地址添加了一条主机配置项。
创建一个新容器,用法同run,但不启动容器:
docker create <image_name> <command>
查看正在运行的容器列表,可以获得容器的id:
docker ps
- -a 查看所有容器
- -l 查看最近一次运行的容器
- -n x 查看最后x个容器
- -q 只显示容器ID
启动/停止/重启/附着/暂停/取消暂停容器:
docker start/stop/restart/attach/pause/unpause <container_id>
查看容器日志:
docker logs <container_id>
- -f 监控日志
- -t 显示时间戳
- --tail 输出日志末尾的指定内容
查看容器内运行的进程:
docker top <container_id>
在容器中启动新进程:
docker exec <container_id> <process_name>
- -d 后台进程
- -i 保持标准输入开启
- -t 分配一个伪终端
通过docker exec后台命令,可以在正在运行的容器中进行维护、监控及管理任务。
查看容器的详细信息:
docker inspect <container_id>
- -f/--format 指定查看结果
删除容器(不能删除运行中的容器):
docker rm <container_id>
删除所有容器:
docker rm `sudo docker ps -a -q`
持久化容器:
docker export <container_id> > /tmp/export_container.tar
在docker容器和宿主机之间复制文件:
- 宿主机->容器:sudo docker cp host_path containerID:container_path
- 容器->宿主机:sudo docker cp containerID:container_path host_path
四、Dockerfile
目前推荐使用Dockerfile和build命令来构建镜像。Dockerfile由一系列的指令和参数组成,指令会按顺序从上到下执行,每条指令都会创建一个新的镜像层并进行提交。
1、执行流程
- Docker从基础镜像运行一个容器
- 执行一条指令,对容器做出修改
- 提交一个新的镜像层
- 执行下一条指令,直到所有指令都执行完毕
2、Dockerfile指令
- FROM <image>
每个Dockerfile的第一条指令都应该是FROM,指定一个已经存在的镜像,后续指令都基于该镜像进行。FROM指令可以在Dockerfile中出现多次。如果本地没有指定的镜像,则从Docker公共库pull镜像。如果没有指定镜像标签,则使用latest标签。
- MAINTAINER <name>
用来说明该镜像的作者以及其他信息。
- CMD
指定容器启动时要运行的命令,存放在一个数组结构中:CMD ["/bin/bash", "-l"]。Docker run命令可以覆盖CMD指令。在Dockerfile中只能指定一条CMD指令,如果指定多条,只有最后一条指令有效。
- RUN
在当前镜像上执行指定命令,并提交为新镜像,后续的RUN都在之前RUN提交后的镜像为基础上执行。RUN产生的缓存在下一次构建时不会失效,可以使用--no-cache选项清除缓存。
- ENTRYPOINT
与CMD指令类似,但提供的命令不会被run命令覆盖,run命令行中指定的任何参数都会被传递给ENTRYPOINT指令。每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个生效。与CMD指令联合使用,既可以实现指定选项的参数,又可以实现不指定参数时的默认行为。
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
- EXPOSE <port>
告诉Docker该容器内的应用程序将会使用容器的哪个端口。
- WORKDIR
当创建一个新容器时,在容器内部设置一个工作目录,CMD或ENTRYPOINT指定的程序在此目录下执行。在执行run命令时,可以通过-w参数设置工作目录,此时会覆盖WORKDIR的设置。
WORKDIR /opt/webapp
- ENV
用来在镜像构建过程中设置环境变量,该环境变量可以在后续的RUN指令中使用,如同在命令前面指定前缀:
ENV RVM_PATH /home/rvm/
RUN gem install unicorn
相当于:
RVM_PATH=/home/rvm/ gem install unicorn
也可以在其他指令中直接使用环境变量:
ENV TARGET_DIR /opt/app
WORKDIR $TARGET_DIR
这些环境变量会被持久保存到所创建的容器中。如果在run命令行中使用-e参数来传递环境变量,则这些变量只会在运行时有效。
- USER
用来指定镜像以什么用户去运行容器,可以指定用户名/UID/GID等,默认为root。也可以在run命令中使用-u参数来覆盖指令指定的值。
USER user:group
- VOLUME
用来向新创建的容器添加卷。卷是可以存在于一个或多个容器内的特定目录,提供共享数据或对数据进行持久化。
VOLUME ["/opt/project"]
- ADD
用来将构建环境下的文件和目录复制到镜像中,需要源文件位置和目的文件位置两个参数。
ADD hello.txt /opt/app/hello.txt
如果目的地址以“/”结尾,则Docker认为源位置指向的是目录,否则,认为源位置指向的是文件。如果将归档文件指定为源文件时,Docker会自动将文件解压。如果通过ADD指令向镜像添加一个文件或目录,将会使后续指令不能继续使用之前的构建缓存。
- COPY
与ADD指令相似,但COPY指令只复制文件,不会去做文件提取和解压的工作,也不支持远程URL。
COPY conf.d/ /etc/apache/
- ONBUILD
为镜像添加一个触发器,当一个镜像被用做其他镜像的基础镜像时,该镜像中的触发器将会被执行。触发器可以是任何构建指令,会在构建过程中插入新的指令。这些指令是紧跟在FROM之后指定的。FROM、MAINTAINER、ONBUILD指令不能用在ONBUILD指令中,防止构建过程中产生递归调用。
3、最佳实践
- 使用.dockerignore文件
为了在构建过程中更加高效,应该使用该文件过滤掉不需要的文件或目录
- 避免安装不需要的软件包
为了降低复杂度、依赖性、文件大小、构建时间,应该避免安装额外的包
- 每个容器只跑一个进程
为了解耦,应该一个容器只单独运行一个应用程序
- 最小化层级
每执行一个指令,都会有一次镜像提交,应该考虑可读性和最小化层级之间的平衡
- 多行参数排序
为了增强可读性,应该通过字母进行排序,并使用换行
- 创建缓存
为了有效地利用缓存,应该保持Dockerfile的一致,并尽量在文件末尾进行修改
五、端口映射
Docker通过端口绑定主机系统的接口,允许非本地客户端访问容器内部运行的服务。
查看容器内部端口所映射的外部端口:
docker port <container_id>
端口映射:
docker run -P --expose <port> <image_name> <command>
- --expose 指定容器对外提供的端口
- -P 自动映射容器对外提供的端口,映射的端口从未被使用的端口池中自动选择
- -p 指定端口映射,格式为ip:hostPort:containerPort
六、网络配置
1、桥接
Docker通过Linux桥接提供容器之间的通信,docker0桥接就是用来实现此目的。当Docker守护进程启动时:
- 如果docker0不存在,则创建docker0桥接
- 搜索与当前路由不冲突的ip段
- 在ip段中选择一个ip
- 分配该ip给docker0桥接
2、网络模式
当Docker创建容器时,可以使用--net选项来指定容器的网络模式,默认为桥接模式。
- host模式
如果使用该模式,容器会和宿主机共用一个网络命名空间,使用宿主机的ip和端口,不会虚拟自己的网卡、配置自己的ip等。
- container模式
该模式指定新创建的容器与其他容器共享一个网络命名空间,不会创建自己的网卡、配置自己的ip等。
- none模式
该模式所创建的容器拥有自己的网络命名空间,但不会为容器进行任何网络配置,需要手动为容器添加网卡、配置ip等。
- bridge模式
该模式会为每一个容器分配网络命名空间、设置ip等,并将容器连接到一个虚拟网桥docker0上。每个容器都会配置同docker0的ip相同网段的专用ip地址,docker0的ip地址被用于所有容器的默认网关。Docker一般会使用172.17.0.0/16网段为容器分配ip,并将172.17.42.1/16分配给docker0网桥。
查看当前docker0的ip:
ifconfig docker0
七、数据管理
1、数据卷
是一个或多个容器指定专门的目录,实现持久化或共享数据,默认是可读写的。
- 可以在容器之间共享和重用
- 数据改变是直接修改的
- 数据改变不会被包括在容器中
- 是持久性的,直到没有容器使用它
为新建容器创建新的数据卷,可以使用多次-v选项挂载多个数据卷:
docker run -v <volumn>
挂载宿主机的目录到新建容器:
docker run -v <hostpath>:<volumn>
2、数据卷容器
如果想在容器间共享持久性数据,或使用在非持久性的容器上,最好的方法是创建一个数据卷容器。不同的容器可以挂载同一个数据卷容器,也可以继承挂载了数据卷容器的容器。
创建数据卷容器:
docker run -d -v <volumn_name>
在容器中挂载数据卷容器,无论数据卷容器是否运行:
docker run --volumes-from <container>
3、数据卷的备份、恢复、迁移
启动新容器并且从其他容器中挂载卷,挂载当前目录到容器的backup目录,完成之后删除容器:
docker run --rm --volumes-from <container> -v $(pwd):/backup <image> <command>
可以恢复给同一个容器或迁移到另一个容器:
docker run -v <volumn>
docker run --rm --volumes-from <container> -v $(pwd):/backup <image> <command>
4、数据卷的删除
数据卷只有以下两种情况才能被删除:
- 删除容器时添加了-v选项
- 运行容器时添加了--rm选项
八、容器链接
Docker允许把多个容器链接在一起进行通信,父容器可以访问子容器的信息。
1、命名
创建容器时,如果不知道容器名字,默认会自动创建一个,推荐给容器命名。在创建容器时,使用--name选项给容器自定义名字,容器名字必须唯一。
2、链接
链接允许容器之间进行安全通信,创建容器时使用--link选项创建链接。
docker run --name <container> --link <container>:<alias>
一个链接允许一个源容器提供信息访问给一个接收容器。使用链接,可以避免对外公开任何端口给外部容器。
Docker通过两种方式提供链接信息给接收容器:
- 环境变量
在接收容器上设置一些环境变量,以获取源容器的相关信息。这些环境变量只设置给容器中的第一个进程,如一些守护进程。
- 更新/etc/hosts文件
在接收容器上添加相关主机条目到/etc/hosts中。该文件在源容器重启之后会自动更新IP地址,而环境变量中的IP地址则不会自动更新。