Docker
docker简介
Docker是一个开源的引擎,可以轻松地为任何应用创建一个轻量级的、可移植的、自给自足的容器。特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为 MB | 一般为 GB |
系统资源 | 0~5% | 5~15% |
性能 | 接近原生 | 弱于原生 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
Docker优势
docker有以下优势:- 更高效地利用系统资源
- 更快速的启动时间
- 一致的运行环境
- 持续交付和部署
- 更轻松的迁移
- 更轻松的维护和扩展
- web应用的自动化打包和发布;
- 自动化测试和持续集成、发布;
- 在服务型环境中部署和调整数据库或其他的后台应用;
- 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境。
基本概念
在Docker中有三个基本概念:- 镜像(Image)
- 容器(Container)
- 仓库(Repository)
镜像
上面介绍过,镜像是Docker的三个基本组件之一;运行容器需要本地有相应的镜像,如果没有会从远程仓库下载;那么来看下如何操作镜像。查找镜像
可以从Docker Hub来搜索镜像查找结果:
docker search ubuntu
- NAME:镜像仓库源的名称
- DESCRIPTION:镜像的描述
- STARS:类似Github里面的 star。
- OFFICIAL:是否docker官方发布
- AUTOMATED:自动构建。
获取镜像
要获取镜像,可以通过<font style="color:rgb(53, 148, 247);">docker pull</font>
命令,它的格式如下:
还是以ubuntu为例:
docker pull <repository>:<tag>
可以看到最后一行docker.io显示这是从官方仓库拉取的。 从下载过程可以看出上面说的分层存储的概念,即镜像是由多层存储构成;下载也是一层层的去下载,而不是单独一个文件;因此如果下载中有某个层已经被其他镜像下载过,则会显示Already exists。下载过程中给出了每一层的ID的前12位,下载结束后给出镜像完整的sha256摘要。 Docker的镜像仓库分为官方仓库和非官方,官方的镜像就是从Docker Hub拉取的;如果想要从第三方的镜像仓库获取,可以在仓库名称前加上仓库的服务地址:
$ docker pull ubuntu:16.04
16.04: Pulling from library/ubuntu
58690f9b18fc: Pull complete
b51569e7c507: Pull complete
da8ef40b9eca: Pull complete
fb15d46c38dc: Pull complete
Digest: sha256:0f71fa8d4d2d4292c3c617fda2b36f6dabe5c8b6e34c3dc5b0d17d4e704bd39c
Status: Downloaded newer image for ubuntu:16.04
docker.io/library/ubuntu:16.04
docker pull <Docker Registry地址:端口号><repository>:<tag>
列出镜像
通过下面的命令,可以列出本地已经下载的镜像:运行命令出现以下列表:
$ docker image ls
$ docker image ls mongo
REPOSITORY TAG IMAGE ID CREATED SIZE
mongo latest dfda7a2cf273 2 months ago 693MB
mongo 4.0 e305b5d51c0a 2 months ago 430MB
删除镜像
可以通过rm命令删除本地镜像:或者简写为rmi命令:
$ docker image rm [选项] <镜像1> [<镜像2> ...]
这里的<镜像>,可以是镜像短ID、镜像长ID、镜像名或者镜像摘要;
$ docker rmi [选项] <镜像1> [<镜像2> ...]
<font style="color:rgb(53, 148, 247);">docker image ls</font>
列出来的已经是短ID了,还可以取前三个字符进行删除;比如想要删除上面的mongo:4.0:
$ docker rmi e30
构建镜像
除了使用官方的镜像,可以构建自己的镜像;一般都在其他的镜像基础上进行构建,比如node、nginx等;构建镜像需要用到Dockerfile,它是一个文本文件,文本内容包含了一条条构建镜像所需的指令和说明。 在一个空白目录新建一个Dockerfile:向Dockerfile写入以下内容:
mkdir mynginx
cd mynginx/
touch Dockerfile
这里的Dockerfile很简单,就两个命令:FROM和RUN,在Dockerfile里面对命令进行详细的介绍;使用
FROM nginx
RUN echo '<h1>Hello, This is My Nginx</h1>' > /usr/share/nginx/html/index.html
<font style="color:rgb(53, 148, 247);">build</font>
命令构建镜像,它的格式为:
因此,在Dockerfile所在的目录执行命令:
docker build [选项] <上下文路径/URL/->
运行命令,可以看到镜像也是按照Dockerfile里面的步骤,分层进行构建的:
$ docker build -t mynginx:v3 .
<font style="color:rgb(53, 148, 247);">.</font>
,它表示了当前目录,如果不写这个目录会报错提示;如果对应上面的格式,它其实就是上下文路径,那这个上下文路径是做什么用的呢?要理解这个路径的作用,首先要来理解Docker的架构。
Docker是一个典型的C/S架构的应用,它可以分为Docker客户端(平时敲的Docker命令)和Docker服务端(Docker守护进程)。Docker客户端通过REST API和服务端进行交互,docker客户端每发送一条指令,底层都会转化成REST API调用的形式发送给服务端,服务端处理客户端发送的请求并给出响应。
因此表面上看好像在本机上执行各种Docker的功能,实际上都是都是在Docker服务端完成的,包括Docker镜像的构建、容器创建、容器运行等工作都是Docker服务端来完成的,Docker客户端只是承担发送指令的角色。
- 执行build命令
- Docker客户端会将构建命令后面指定的上下文路径下的所有文件打包成一个tar包,发送给Docker服务端;
- Docker服务端收到客户端发送的tar包,然后解压,根据Dockerfile里面的指令进行镜像的分层构建;
这里要复制的package.json文件,并不一定在docker build命令执行的目录下,也不一定是在Dockerfile文件同级目录下,而是docker build命令指定的上下文路径目录下的package.json。
COPY ./package.json /app/
容器
介绍了镜像,到了Docker第三个核心概念了:容器。容器是镜像的运行时的实例,可以从一个镜像上启动一个或多个容器。 对容器的管理包括创建、启动、停止、进入、导入导出和删除等,分别来看下每个操作的具体命令以及效果。创建启动容器
新建并启动一个容器用的命令是docker run,它后面有时候会带上有很长很长的选项,不过其基本的语法如下:它可以带上一些常见的选项:
$ docker run [选项] 镜像名称 [命令] [参数...]
<font style="color:rgb(89, 89, 89);">-d</font>
:容器在后台运行<font style="color:rgb(89, 89, 89);">-t</font>
:为容器重新分配一个伪输入终端,通常与-i同时使用<font style="color:rgb(89, 89, 89);">-i</font>
:以交互模式运行容器,通常与-t同时使用<font style="color:rgb(89, 89, 89);">-P</font>
:随机端口映射<font style="color:rgb(89, 89, 89);">-p</font>
:指定端口映射<font style="color:rgb(89, 89, 89);">--name</font>
:为容器指定一个名称<font style="color:rgb(89, 89, 89);">-e</font>
:设置环境变量<font style="color:rgb(89, 89, 89);">--dns</font>
:指定容器使用的DNS服务器<font style="color:rgb(89, 89, 89);">-m</font>
:设置容器使用内存最大值<font style="color:rgb(89, 89, 89);">--net="bridge"</font>
:指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;<font style="color:rgb(89, 89, 89);">--link</font>
:链接另一个容器<font style="color:rgb(89, 89, 89);">-v</font>
:绑定卷<font style="color:rgb(89, 89, 89);">--rm</font>
:退出容器后删除该容器
$ docker run hello-world
<font style="color:rgb(53, 148, 247);">-it</font>
选项(-i和-t的简写),来让Docker分配一个终端给这个容器:
可以在容器内部进行操作了,退出终端可以使用exit命令或者ctrl+d;退出容器后如果查看运行中的容器,发现并没有任何容器信息。 一般都是需要让容器在后台运行,因此加上
$ docker run -it ubuntu:18.04 /bin/bash
root@fdb133227c9a:/# pwd
root@fdb133227c9a:/# ls
root@fdb133227c9a:/# exit
<font style="color:rgb(53, 148, 247);">-d</font>
:
容器不再以命令行的方式呈现了,而是直接丢出一长串的数字字母组合,这是容器的唯一id;再用ps命令查看运行状态的容器,可以看到容器已经在后台默默运行了:
$ docker run -itd ubuntu:18.04 /bin/bash
ad4d11b6d3b6a2a37fc702345a09fa0a5671f5b3943def7963994535e8600f7b
- 检查本地是否存在指定的镜像,不存在就从 registry 下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个 ip 地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
终止容器
可以使用stop命令来终止容器的运行;如果容器中的应用终结或者报错时,容器也会自动终止;可以使用ps命令查看到的容器短id来终止对应的容器:对于终止状态的容器,ps命令已经不能看到它了,可以加上
$ docker stop ad4d11b6d3b6
<font style="color:rgb(53, 148, 247);">-a</font>
选项(表示所有)来查看,它的STATUS已经变成了Exited:
终止状态的容器可以使用
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
ad4d11b6d3b6 ubuntu:16.04 "/bin/bash" 2 hours ago Exited (0) 2 minutes ago
<font style="color:rgb(53, 148, 247);">docker start [容器id]</font>
来让它重新进入启动状态,运行中的容器也可以使用<font style="color:rgb(53, 148, 247);">docker restart [容器id]</font>
让它重新启动。
进入容器
有时候会需要进入容器进行一些操作,比如进入nginx容器进行平滑重启,可以使用<font style="color:rgb(53, 148, 247);">docker attach</font>
或者<font style="color:rgb(53, 148, 247);">docker exec</font>
进入,不过推荐使用<font style="color:rgb(53, 148, 247);">exec</font>
命令。
首先看下如果使用attach命令:
当从终端exit后,整个容器会停止;而使用exec命令不会导致容器停止。 如果只使用
$ docker attach ad4d11b6d3b6
root@ad4d11b6d3b6:/# exit
<font style="color:rgb(53, 148, 247);">-i</font>
参数,由于没有分配伪终端,界面没有熟悉的Linux命令提示符,但是执行命令仍然可以看到运行结果;当使用<font style="color:rgb(53, 148, 247);">-i</font>
和<font style="color:rgb(53, 148, 247);">-t</font>
参数时,才能看到常见的Linux命令提示符。
需要注意的是,进入的容器需要是运行状态,如果不是运行状态,则会报错:
$ docker exec -i ad4d11b6d3b6 bash
ls
bin
boot
dev
etc
home
lib
pwd
/
$ docker exec -it ad4d11b6d3b6 bash
root@ad4d11b6d3b6:/# exit
Error response from daemon: Container ad4d11b6d3b6 is not running
查看容器日志
经常需要对容器运行过程进行一些监测,查看它的运行过程记录的日志情况,以及是否报错等等;使用logs命令获取容器的日志。它还支持以下几个参数:
$ docker logs ad4d11b6d3b6
<font style="color:rgb(89, 89, 89);">-f</font>
:跟踪日志输出<font style="color:rgb(89, 89, 89);">--since</font>
:显示某个开始时间的所有日志<font style="color:rgb(89, 89, 89);">-t</font>
:显示时间戳<font style="color:rgb(89, 89, 89);">--tail</font>
:仅列出最新N条容器日志
# 列出最新的10条日志
$ docker logs --tail=10 ad4d11b6d3b6
分析容器
对于已经创建的容器,可以使用<font style="color:rgb(53, 148, 247);">inspect</font>
来查看容器的底层基础信息,包括容器的id、创建时间、运行状态、启动参数、目录挂载、网路配置等等;另外,该命令也能查看docker镜像的信息,它的格式如下:
docker inspect [选项] <镜像1> [<镜像2> ...]
<font style="color:rgb(43, 43, 43);">inspect</font>
支持以下选项:
<font style="color:rgb(89, 89, 89);">-f</font>
:指定返回值的模板文件。<font style="color:rgb(89, 89, 89);">-s</font>
:显示总的文件大小。<font style="color:rgb(89, 89, 89);">--type</font>
:为指定类型返回JSON。
<font style="color:rgb(53, 148, 247);">-f</font>
参数:
# 获取容器名
docker inspect -f {{.Name}} <容器ID>
# 获取容器目录挂载信息
docker inspect -f {{.NetworkSettings.Mounts}} <容器ID>
# 获取容器网络设置的相关信息
docker inspect -f {{.NetworkSettings}} <容器ID>
# 获取容器的 IP 的相关信息
docker inspect -f {{.NetworkSettings.IPAddress}} <容器ID>
删除容器
如果一个容器不想再使用了,可以使用rm命令来删除:如果要删除一个运行中的容器,可以添加-f参数:
$ docker rm ad4d11b6d3b6
$ docker rm -f ad4d11b6d3b6
数据管理
上面介绍到容器是保持无状态化的,就是随用随删,并不会保留数据记录;在使用docker的时候,经常会用到一些需要保留数据的容器,比如mysql、mongodb,往往需要对容器中的数据进行持久化;或者在多个容器之间进行数据共享,这就涉及到了容器的数据管理,主要有两种方式:- 数据卷(Data Volumes)
- 挂载主机目录 (Bind mounts)
数据卷
数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:- 数据卷可以在容器之间共享和重用
- 对数据卷的修改会立马生效
- 对数据卷的更新,不会影响镜像
- 数据卷默认会一直存在,即使容器被删除
通过ls可以列出本地所有的数据卷:
$ docker volume create my-vol
my-vol
$ docker volume ls
DRIVER VOLUME NAME
local my-vol
<font style="color:rgb(53, 148, 247);">inspect</font>
命令也可以查看数据卷的具体信息:
在启动容器时,使用
$ docker volume inspect my-vol
[
{
"CreatedAt": "",
"Driver": "local",
"Labels": {},
"Mountpoint": "/data/programs/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
<font style="color:rgb(53, 148, 247);">--mount</font>
将数据卷挂载在容器的目录里(可以有多个挂载点):
借助上面的
$ docker run -d -P --name web --mount source=my-vol,target=/usr/share/nginx/html nginx
# 通过-v简写形式
$ docker run -d -P --name web -v my-vol:/usr/share/nginx/html nginx
<font style="color:rgb(43, 43, 43);">inspect</font>
命令查看容器的挂载信息:
数据卷是被设计用来持久化数据的,它的生命周期独立于容器;因此即使将容器删除了,数据卷的数据依然还是存在的,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的数据卷,可以在删除容器的时候使用
$ docker inspect -f "{{.Mounts}}" web
[{volume my-vol /data/programs/docker/volumes/my-vol/_data /usr/share/nginx/html local z true }]
<font style="color:rgb(53, 148, 247);">docker rm -v</font>
命令同时删除数据卷,或者手动来删除:
无主的数据卷可能会占据很多空间,要清理请使用以下命令(谨慎使用!):
$ docker volume rm my-vol
$ docker volume prune
挂载目录
可以发现上面数据卷挂载的目录都是在docker的安装路径下,不利于进行维护,可以直接挂载自定义的目录。挂载的本地目录的路径必须是绝对路径,不能是相对路径。 本地路径如果不存在,会自动生成。 默认挂载的主机目录的默认权限是读写,可以通过增加readonly指定为只读。
$ docker run -d -P --name web --mount source=/home/nginx,target=/usr/share/nginx/html nginx
# 通过-v简写形式
$ docker run -d -P --name web -v /home/nginx:/usr/share/nginx/html nginx
加readonly后,如果在容器内的/usr/share/nginx/html目录下修改文件或者新建文件就会报错。 需要注意的是,如果挂载本地目录,需要保证挂载的目录下面有程序运行所需要的文件,比如这里nginx容器需要在在本地目录/home/nginx下有index.html文件,如果没有的话会报403错误。
$ docker run -d -P --name web -v /home/nginx:/usr/share/nginx/html:ro nginx