0. 造冰箱
工欲善其事必先利其器 , 在把大象装进冰箱 , 我们得先有个冰箱
相信大家都是用的 Windows 或 Mac 进行开发的 , 直接使用官方提供工具即可
Mac
Windows
1. 基本概念
我们先从一个日常中会使用到的命令开始
首先我们在任意一个目录下新建一个文件 。
$ echo "Hello, Docker~" > index.html
启动容器
$ docker run --rm -p 8080:80 -v $(PWD):/usr/share/nginx/html nginx:latest
打开浏览器访问 localhost:8080
, 我们可以访问到我们刚才新建的文件 。 返回终端 , 我们可以看到已经输出了一些日志 。
至此 , 我们已经完成了最简单的 nginx 服务的启动 。
我们来拆解一下这个命令
$ docker run
--rm
-p 8080:80
-v $(PWD):/usr/share/nginx/html
nginx:latest
-
--rm
没啥好讲的 , 如其意思 , 在容器停止后删除该容器 , 由于容器轻量级的设计 , 我们一般随用随开 , 随停随删 。 原因上一篇有讲 , Docker 启动一个新容器的速度是极快的 , 我们每次都从新跑一个新容器并不会造成太大的时间成本 , 也能保证每次的容器都是全新的一致的 。
-
-p 8080:80
这里涉及 Docker 中网络的概念 。 在这里是将宿主机
8080
端口映射到容器中80
端口 。 因此 , 我们可以通过宿主机的 8080 端口 访问到容器 80 端口 。 -
-v $(PWD):/usr/share/nginx/html
这里涉及 Docker 中挂载的概念 。 PWD 命令是将当前目录输出 。 在这里就是将当前目录挂载到镜像中的
/usr/share/nginx/html
目录 。 该目录是 nginx 镜像指定的静态资源目录 。因此 , 我们可以通过 nginx 容器提供的服务访问到当前目录下的文件 , 如我们刚才新建的index.html
。 -
nginx:latest
所使用的镜像名称 。
在上面使用的命令中 , 我们就已经接触到了 Docker 四大件 : 镜像 、 容器 、 数据卷 、 网络 。
接下来一一简介。
镜像
镜像是容器的基础 , 描述了运行环境文件系统的内容 。
命名规范
- username: 用于识别上传镜像的用户身份 。 Docker 官方维护项目中没有 username。
- repository:用于识别项目名 。
- tag:表示镜像的版本 , 方便进行版本或内容的区分 。 latest 为缺省tag 。
组成
镜像为一个只读的文件包,其中包含了虚拟环境运行最原始文件系统的内容。 Docker 中的镜像与虚拟机中的镜像是不一样的。由于 Docker 使用了 UnionFS , Docker 的进行是一种增量式的结构。
基于该结构 , 每次对镜像内容的修改 , Docker都会将这些修改形成一个新的镜像层 , 而一个镜像就是由其下层所有镜像层所组成 。 这种结构允许了不同镜像之间共享镜像层 。 由此 , 我们可以在一台机器中存放许多镜像 。
网络
在 Docker 中,实现了强大的网络功能。我们可以对单个容器进行网络配置,还可以在容器间建立虚拟网络。与其他网络环境隔离。我们甚至能够在不同的物理服务器间实现,让处在两台物理服务器上的两个 Docker 所提供的容器,加入到同一个虚拟网络中,形成完全屏蔽硬件的效果。
我们使用 Docker 时最常用的是其提供的端口映射功能 。 可将容器中 , 容器中指定端口映射到宿主机中 。
-p <ip>:<host-port>:<container-port>
$ docker run --rm -p 8080:80 -v $(PWD):/usr/share/nginx/html nginx:latest
如最顶部简单所示 , 将容器中端口映射到宿主机以提供服务 。 详细网络部分见后续技术详解部分 。
数据卷
由于容器的特性 , 在容器中操作的文件无法被持久化 。 且容器与宿主机环境隔离 , 无法直接操作容器外的文件 。 同理 , 宿主机也无法操作容器中的文件 。
在我们需要保持一些数据的时候 , 我们通常会单独挂载一个文件系统来存放数据 。 由于 Docker 采用了 UnionFS 文件系统 , 可以将不同物理位置的目录合并 Mount 到同一目录中 , 因此轻松的实现了容器的挂载功能 , 在 UnionFS 的加持下 , 除了能够从宿主操作系统中挂载目录外 , 还能够建立独立的数据卷持久存放数据且在容器间共享 。
一些云计算商也会提供花样众多的定制存储驱动 , 如阿里云可将 OSS 挂载到容器中 。
如上图所示 , Docker 中提供了以下三种挂载方式 。
-
BindMount
直接将外部路径挂载到容器中 。 容器内外读写互相可见
-
Volume
与 BindMount 相同 , 区别在于挂载目录交由 Docker 管理
-
TmpfsMount
挂载内存中的一部分到容器中 。 存储不是持久的 , 内容随着容器停止而消失
实际使用中 , 我们常用的为 BindMount 、 Volume 。
BindMount
通过 BindMount 的方式 , 我们可以直接将外部路径挂载到容器中 。
在创建运行容器时 , 加入 -v
或 --volume
选项 。
-v <host-path>:<container-path>
--volume <host-path>:<container-path>
# host-path 为宿主机路径
# container-path 为容器路径
如下所示 , 我们就将宿主机 /opt/nginx/html
目录挂载到容器 /usr/share/nginx/html
目录中了
$ docker run -d --name frontend -v /opt/nginx/html:/usr/share/nginx/html nginx
此时在容器中对该目录的操作都会反应在宿主机中
如果需要控制容器对文件只读 , 我们可以在后面加上 :ro
可以只读方式挂载
-v <host-path>:<container-path>:ro
--volume <host-path>:<container-path>:ro
如下所示
$ docker run -d --name frontend -v /opt/nginx/html:/usr/share/nginx/html:ro nginx
需要注意的是 , 在挂载时我们必须使用绝对路径
我们也可以不挂载目录 , 只挂载单个文件到容器中 , 如下所示
$ docker run -d --name frontend -v /opt/nginx/html/index.html:/usr/share/nginx/html/index.html:ro nginx
场景
部署时 , 共享静态 HTML 文件 、 一些必要的配置文件 。
借助 Docker 开发时 , 我们会将源码放置在外部 , 通过挂载的方式供容器使用 , 这样我们只需要在容器外修改代码即可
Volume
该挂载方式与 BindMount 相同 , 区别在于挂载目录交由 Docker 管理了 。
在创建运行容器时 , 加入 -v
或 --volume
选项 。
-v [<volume-name>:]<container-path>
--volume [<volume-name>:]<container-path>
# volume-name 为数据卷别名
# container-path 为容器路径
不带别名创建
$ docker run -d --name frontend -v /usr/share/nginx/html nginx
带别名创建
$ docker run -d --name frontend -v html:/usr/share/nginx/html nginx
需要注意的是 , 该别名在 Docker 中是唯一的 , 如果之前已经存在 , 则会直接使用该数据卷 , 不存在则创建
之所以此前 BindMount 方式不允许使用相对路径 , 就是因为会出现与数据卷使用方式冲突 。
场景
多容器共享数据卷
对容器内容进行管理时
可以对存储介质的使用隐藏更多细节 , 如阿里云 OSS 挂载
###TmpfsMount
挂载内存中的一部分到容器中 。
在创建运行容器时 , 加入 --tmpfs
选项 。
--tmpfs <container-path>
# container-path 为容器路径
如下所示 , 我们就将内存中的一部分挂载到容器 /usr/share/nginx/html
目录中了
$ docker run -d --name frontend --tmpfs /usr/share/nginx/html nginx
由于是在内容中挂载 , 所以我们不需要指定具体位置 , 只传递挂载在容器内部的路径即可 。
需要注意的是 , 由于容器及内存的特性 , 该挂载的临时文件路径并不能持久化存储 。
场景
读写速度要求高 、 数据变化量大 、 不需要持久保存的数据
--mount
选项
Docker 还提供了一个较丰富且明确的挂载选项 , --mount
选项
--mount 'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>,"volume-opt=o=addr=<nfs-address>,vers=4,soft,timeo=180,bg,tcp,rw"'
--mount
相对复杂 , 一般来说 -v及
--volume``` 选项已经足够满足我们的需求 。
容器
在容器技术中,容器就是用来隔离虚拟环境的基础设施,而在 Docker 里,它也被引申为隔离出来的虚拟环境。
主进程
在 Docker 中 , 容器会存在一个 PID 为 1 的主进程 。 容器的启动可以理解为该进程的启动 。
当我们控制容器结束时 , Docker 会向主进程发送结束信号 , 通知程序退出后关闭容器 。
当容器中主进程主动关闭时 , 容器也会随之停止 。
生命周期
2. 基础操作
了解完一些基本概念后 , 我们来了解一下基本操作 。
-
docker images
查看镜像列表$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE node latest a9c1445cbd52 6 months ago 904MB
-
docker pull
拉取镜像$ docker pull nginx Using default tag: latest latest: Pulling from library/nginx b8f262c62ec6: Pull complete e9218e8f93b1: Pull complete 7acba7289aa3: Pull complete Digest: sha256:aeded0f2a861747f43a01cf1018cf9efe2bdd02afd57d2b11fcc7fcadc16ccd1 Status: Downloaded newer image for nginx:latest
-
docker search
搜索镜像$ docker search nginx NAME DESCRIPTION STARS OFFICIAL AUTOMATED nginx Official build of Nginx. 12055 [OK] jwilder/nginx-proxy Automated Nginx reverse proxy for docker… 1672 [OK]
-
docker inspect
查看镜像详情$ docker inspect nginx
-
docker rmi
移除镜像$ docker rmi nginx Untagged: nginx:latest Untagged: nginx@sha256:aeded0f2a861747f43a01cf1018cf9efe2bdd02afd57d2b11fcc7fcadc16ccd1 Deleted: sha256:f949e7d76d63befffc8eec2cbf8a6f509780f96fb3bacbdc24068d594a77f043 Deleted: sha256:301c5d89cad94a6a99703841b021cf7df4326d2f14715c52b4b27893b13e02c0 Deleted: sha256:c3172409dcf95530cce7aad6a4c16a476fec9c43ac38426e9487b43efd246357 Deleted: sha256:2db44bce66cde56fca25aeeb7d09dc924b748e3adfe58c9cc3eb2bd2f68a1b68 # 支持删除多个, 空格分割 $ docker rmi nginx mysql
-
docker create
创建容器$ docker create --name nginx8080 nginx d57619691f65a593eb4610d134f7fb4780907733d91947322bbc022c2320d98e
-
docker start
启动容器$ docker start nginx8080 nginx8080
-
docker run
创建并启动容器$ docker run --name nginx8080 nginx
默认情况下通过
docker run
会采用前台运行的方式 , 可以加入-d
或--detach
选项使其在启动后后台运行 。$ docker run --name nginx8080 -d nginx 09bb5968bccd2f1c955cafef9abd275a721296228150d72c52d69b4d68e4b8f1
-
docker ps
罗列容器$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 09bb5968bccd nginx "nginx -g '…" 15 seconds ago Up 14 seconds 80/tcp nginx8080
默认情况下通过
docker ps
只会显示运行中的容器 , 可以加入-a
或--all
选项使其列出所有状态的容器 。$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 09bb5968bccd nginx "nginx -g…" 4 minutes ago Up 4 minutes 80/tcp nginx8080 0200ce4aa200 fe82f8db1d8b "yarn run…" 6 months ago Exited (1) 6 months ago goofy_dubinsky
-
docker stop
暂停容器$ docker stop nginx8080 nginx8080
-
docker rm
删除容器$ docker rm nginx8080 nginx8080
默认情况下通过
docker rm
无法删除运行中的容器 。 可以加入-f
或--force
选项将其强制停止并删除 。 -
docker exec
执行命令$ docker exec nginx8080 cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.2 58a33449f761
一般我们会通过直接执行容器中的控制台来不间断输入命令
$ docker exec -it nginx8080 bash root@58a33449f761:/# ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var $ docker exec -it nginx8080 sh # ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
-i
(--interactive
) 表示保持输入流 。-t
(--tty
) 表示启用一个伪终端 。 使用他形成与 bash 的交互 。 -
docker attach
连接到容器$ docker attach nginx8080
该命令将当前控制台连接到容器的主程序上 。
通过上面的介绍 , 我们会对 Docker 有一些基本的了解 , 我们不用全部记住 。 这篇文章只是将容器中的基本操作 、 技术及其部分原理概括 。 在后续实战文章中 , 我们会一一介绍回顾 。
下一文 , 我们来了解一下容器网络部分内容 , 回见 。
君子生非异也,善假于物也。
《荀子·劝学》