Docker学习
Docker 学习
基本概念

- Docker Daemon:运行在 Docker 服务器的后台进程,侦听 Docker API 请求并管理 Docker 对象,例如镜像、容器、网络和挂载卷,守护进程还可以与其他守护进程通信以管理 Docker 服务
- Docker Client: Docker 客户端是许多 Docker 用户与 Docker 交互的主要方式,当使用 docker run 之类的命令时,客户端会将这些命令发送到 dockerd,从而执行它们,该 docker 命令使用 Docker API,Docker 客户端可以与多个守护进程通信
- Docker Host:安装 Docker 服务的主机
- Docker Registries:存储 Docker Image 的地方,Docker Hub 是一个任何人都可以使用的公共仓库,Docker 默认配置为在 Docker Hub 上查找镜像,也可以使用自己的仓库,当使用 docker pull 或 docker run 命令时,所需的镜像将从配置的仓库中查找,当使用 docker push 命令时,镜像会被推送到你配置的仓库中
- Images:镜像是用于创建 Docker 容器的只读模板,通常,一个镜像基于另一个镜像,并带有一些额外的自定义,可以自己创建镜像,也可以使用公共仓库中发布的镜像,构建镜像需要创建一个 Dockerfile,用于定义创建镜像和运行镜像所需的步骤,Dockerfile 中的每条指令都会在镜像中创建一个层,当更改 Dockerfile 并重建镜像时,只会重建那些已更改的层,与其他虚拟化技术相比,这是使镜像如此轻巧、小巧和快速的部分原因
- Containers:容器是镜像的可运行实例,可以使用 Docker API 或 CLI 创建、启动、停止、移动或删除容器,可以将容器连接到一个或多个网络,为其附加存储,也可以根据其当前状态创建新镜像,默认情况下,容器与其他容器及其主机相对隔离,可以控制容器的网络、存储或其他底层子系统与其他容器或主机之间的隔离程度,容器由镜像以及创建或启动它时提供给它的任何配置选项定义,当容器被移除时,未存储在持久存储中的对其状态的任何更改都会消失
Docker 用 Go 语言编写,并利用 Linux 内核的几个特性来提供其功能,Docker 使用一种称为 namespaces 的技术来实现资源隔离,运行容器时,Docker 会为该容器创建一组 namespaces, 这些 namespaces 提供了一层资源隔离,容器的每个方面都在单独的 namespaces 中运行,并且对其的访问仅限于该 namespaces
安装
1 | # 移除旧版本 |
compose
1 | sudo yum update |
镜像加速
1 | mkdir -p /etc/docker |
可视化界面-Portainer
1 | # 服务端部署(访问 9000 端口) |
常用命令

| 命令 | 作用 |
|---|---|
| attach | 绑定到运行中容器的标准输入、输出、以及错误流,绑定的是控制台,退出后可能会导致容器停止 |
| build | 从一个 Dockerfile 文件构建镜像,docker build -t imageName -f DockerfileName . |
| commit | 把容器的改变提交并创建一个新的镜像 |
| cp | 容器和本地文件系统间复制文件或目录 |
| create | 创建新容器,但是不启动,需要手动启动,docker create –name myredis -p 6379(主机的端口):6379(容器的端口) redis 主机端口必须唯一 |
| diff | 检查容器里文件系统结构的更改(A:添加文件或目录 D:文件或者目录删除 C:文件或者目录更改) |
| events | 获取服务器的实时事件 |
| exec | 在运行时的容器内运行命令,docker exec -it -u 0:0 –privileged mynginx4 /bin/bash 0 用户,以特权方式进入容器 |
| export | 导出容器的文件系统为一个 tar 文件,commit 是直接提交成镜像,export 是导出成文件方便传输 |
| import | 导入 tar 的内容创建一个镜像,导入进来的镜像直接启动不了容器,需要指定命令 |
| history | 显示镜像的历史 |
| images | 列出所有镜像,image ls |
| info | 显示系统信息 |
| inspect | 获取 docker 对象的底层信息 |
| kill | 杀死一个或多个容器 |
| save | 把一个或者多个镜像保存为 tar 文件 |
| load | 从 tar 文件加载镜像 |
| login | 登录 Docker registry |
| logout | 退出 Docker registry |
| logs | 获取容器日志,可以看到容器在控制台输出的所有内容 |
| pause | 暂停一个或多个容器 |
| unpause | pause 的反操作 |
| port | 列出容器的端口映射 |
| ps | 列出所有容器 |
| pull | 从 registry 下载一个 image 或者 repository |
| push | 给 registry 推送一个 image 或者 repository |
| rename | 重命名一个容器 |
| restart | 重启一个或者多个容器 |
| rm | 删除一个或多个容器 |
| rmi | 删除一个或多个镜像,docker rmi -f $(docker images -aq) 删除全部镜像 |
| run | 创建并启动容器 |
| search | 去 docker hub 寻找镜像 |
| stats | 显示容器资源的实时使用状态 |
| start | 启动一个或者多个容器 |
| stop | 停止一个或者多个容器 |
| tag | 给源镜像创建一个新的标签,变成新的镜像,docker tag 原镜像:标签 新镜像名:标签 |
| top | 显示正在运行容器的进程 |
| udpate | 更新一个或多个 docker 容器配置 |
| version | 查看 Docker 的版本信息 |
| container | 管理容器 |
| image | 管理镜像 |
| network | 管理网络 |
| volume | 管理卷 |
| prune | 删除游离镜像,docker image prune |
docker run
- -d: 后台运行容器,并返回容器 ID
- -i: 以交互模式运行容器,通常与 -t 同时使用
- -P: 随机端口映射,容器内部端口随机映射到主机的端口
- -p: 指定端口映射,格式为:主机(宿主)端口:容器端口
- -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用
- –name=”nginx-lb”: 为容器指定一个名称
- –dns 8.8.8.8: 指定容器使用的 DNS 服务器,默认和宿主一致
- –dns-search example.com: 指定容器 DNS 搜索域名,默认和宿主一致
- -h “mars”: 指定容器的 hostname
- -e username=”ritchie”: 设置环境变量
- –env-file=[]: 从指定文件读入环境变量
- –cpuset=”0-2” or –cpuset=”0,1,2”: 绑定容器到指定CPU运行
- -m: 设置容器使用内存最大值;
- –net=”bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container 四种类型
- –link=[]: 添加链接到另一个容器
- –expose=[]: 开放一个端口或一组端口
- –restart: 指定重启策略,可以写–restart=awlays 总是故障重启
- –volume: -v 绑定一个卷,一般格式 主机文件或文件夹:虚拟机文件或文件夹
常见部署案例
部署 Nginx
1 | # 注意: 外部的 /nginx/conf 下面的内容必须存在,否则挂载会覆盖 |
部署 MySQL
1 | docker run -p 3306:3306 \ |
部署 Redis
1 | docker run -p 6379:6379 \ |
部署 ElasticSearch
1 | docker run --name=elasticsearch \ |
部署 Tomcat
1 | docker run --name tomcat \ |
重启策略
- no: 默认策略,在容器退出时不重启容器
- on-failure: 在容器非正常退出时(退出状态非0),才会重启容器
- on-failure: 在容器非正常退出时重启容器,最多重启3次
- always: 在容器退出时总是重启容器
- unless-stopped: 在容器退出时总是重启容器,但是不考虑在 Docker 守护进程启动时就已经停止了的容器
export and import 操作容器
1 | docker export 导出的文件被 import 导入以后变成镜像,并不能直接启动容器,需要知道之前的启动命令(docker ps --no-trunc),然后再用下面启动 |
save and load 操作镜像
1 | docker save -o busybox.tar busybox:latest 把 busybox 镜像保存成 tar 文件 |
产生镜像的三种方式
基于已经存在的容器,提取成镜像
使用给定的 tar 包导入成镜像
使用 Dockerfile 生成镜像
容器的状态
Created(新建)、Up(运行中)、Pause(暂停)、Exited(退出)
如何 push 镜像到仓库
使用 tag 命令重命名镜像成你的仓库名称
docker push chenkaixin12121/mynginx:v1
Docker 存储
镜像如何存储
镜像的目录结构
容器也会自己建立层,如果想要改东西,把改的内容复制到容器层即可,docker container inspect
1 | docker image inspect nginx |
LowerDir:底层目录,diff(只是存储不同),包含小型 linux 和装好的软件
MergedDir:合并目录,容器最终的完整工作目录全内容都在合并层,数据卷在容器层产生,所有的增删改都在容器层

UpperDir:上层目录
WorkDir:工作目录(临时层),pid
Images and layers
Docker 镜像由一系列层组成,每层代表着 Dockerfile 中的一条指令(Dockerfile文件里面几句话,镜像就有几层),除最后一层外的每一层都是只读的,如以下 Dockerfile:
1 | FROM ubuntu:15.04 |
该 Dockerfile 包含四个命令,每个命令创建一个层
FROM 语句从 ubuntu:15.04 映像创建一个图层开始
COPY命令从Docker客户端的当前目录添加一些文件
RUN 命令使用 make 命令构建您的应用程序
最后,最后一层指定要在容器中运行的命令
每一层只是与上一层不同的一组,这些层彼此堆叠
创建新容器时,可以在基础层之上添加一个新的可写层,该层通常称为“容器层”,对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此薄可写容器层

1 | [root@localhost ~]# docker history nginx |
Container and layers
容器和镜像之间的主要区别是可写顶层
在容器中添加新数据或修改现有数据的所有写操作都存储在此可写层中
删除容器后,可写层也会被删除 基础图像保持不变 因为每个容器都有其自己的可写容器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对同一基础映像的访问,但具有自己的数据状态

磁盘容量预估
1 | docker ps -s |
镜像如何挑选
1 | busybox:是一个集成了一百多个最常用 Linux 命令和工具的软件,linux 工具里的瑞士军刀 |
Copy On Write
写时复制是一种共享和复制文件的策略,可最大程度地提高效率
如果文件或目录位于映像的较低层中,而另一层(包括可写层)需要对其进行读取访问,则它仅使用现有文件
另一层第一次需要修改文件时(在构建映像或运行容器时),将文件复制到该层并进行修改,这样可以将 I/O 和每个后续层的大小最小化
容器如何挂载

每一个容器里面的内容,支持三种挂载方式:
- docker 自动在外部创建文件夹自动挂载容器内部指定的文件夹内容(Dockerfile VOLUME 指令的作用)
- 自己在外部创建文件夹,手动挂载
- 可以把数据挂载到内存中
–mount 挂载到 linux 宿主机,手动挂载(不用了)
-v 可以自动挂载,到 linux 主机或者 docker 自动管理的这一部分区域
Volumes(卷):存储在主机文件系统的一部分中,该文件系统由 Docker 管理(在 Linux 上是 /var/lib/docker/volumes/),非 Docker 进程不应修改文件系统的这一部分,卷是在 Docker 中持久存储数据的最佳方法
Bind mounts(绑定挂载):可以在任何地方存储在主机系统上,它们甚至可能是重要的系统文件或目录,Docker 主机或 Docker 容器上的非 Docker 进程可以随时对其进行修改
tmpfs mounts(临时挂载):仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统
volume(卷)
匿名卷使用
1 | docker run -dP -v :/etc/nginx nginx |
具名卷使用
1 | docker run -dP -v nginx:/etc/nginx nginx |
如果将空卷装入存在文件或目录的容器中的目录中,则容器中的内容(复制)到该卷中,如果启动一个容器并指定一个尚不存在的卷,则会创建一个空卷
-v 宿主机绝对路径:Docker容器内部绝对路径:叫挂载,这个有空挂载问题
-v 不以 / 开头的路径:Docker容器内部绝对路径:叫绑定(docker 会自动管理,docker 不会把他当前目录,而把它当前卷)
nginx 测试 html 挂载几种不同情况:
不挂载 效果:访问默认欢迎页
-v /root/html:/usr/share/nginx/html 效果:访问 forbidden
-v html:/usr/share/nginx/html:ro 效果:访问默认欢迎页
-v /usr/share/nginx/html 效果:匿名卷(什么都不写也不要加冒号,直接写容器内的目录)
bind mount
如果将绑定安装或非空卷安装到存在某些文件或目录的容器中的目录中,则这些文件或目录会被安装遮盖,就像您将文件保存到 Linux 主机上的 /mnt 中一样,然后 将 USB 驱动器安装到 / mnt 中,在卸载 USB 驱动器之前,/mnt的内容将被 USB 驱动器的内容遮盖,被遮盖的文件不会被删除或更改,但是在安装绑定安装或卷时将无法访问
总结:外部目录覆盖内部容器目录内容,但不是修改,所以谨慎,外部空文件夹挂载方式也会导致容器内部是空文件夹
1 | docker run -dP -v /my/nginx:/etc/nginx:ro nginx |
警惕 bind mount 方式,文件挂载没有在外部准备好内容而导致的容器启动失败问题
1 | # 一行命令启动 nginx,并且配置文件和 html 页面,需要知道卷的位置才能改 |
管理卷
1 | docker volume create xxx:创建卷名 |
docker cp
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-:把容器里面的复制出来
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH:把外部的复制进去
1 | SRC_PATH 指定为一个文件 |
Docker 网络
端口映射
1 | docker create -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name hello-mysql mysql:5.7 |
容器互联
–link name:alias,name 连接容器的名称,alias 连接的别名
场景:我们无需暴露 mysql 的情况下,让 web 应用使用 mysql
1 | docker run -d -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 |
自定义网络
默认网络原理
Docker 使用 Linux 桥接,在宿主机虚拟一个 Docker 容器网桥(docker0),Docker 启动一个容器时会根据 Docker 网桥的网段分配给容器一个 IP 地址,称为 Container-IP,同时 Docker 网桥是每个容器的默认网关
因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的 Container-IP 直接通信
Docker 容器网络就很好的利用了 Linux 虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫 veth pair),Docker 中的网络接口默认都是虚拟的接口,虚拟接口的优势就是转发效率极高(因为 Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多
网络模式
| 网络模式 | 配置 | 说明 |
|---|---|---|
| bridge 模式 | –net=bridge | 默认值,在 Docker 网桥 docker0 上为容器创建新的网络栈 |
| none 模式 | –net=none | 不配置网络,用户可以稍后进入容器,自行配置 |
| container 模式 | –net=container:name/id | 容器和另外一个容器共享 Network namespace,kubernetes 中的 pod 就是多个容器共享一个 Network namespace |
| host 模式 | –net=host | 容器和宿主机共享 Network namespace |
| 用户自定义 | –net=mynet | 用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定义的网络 |