【Docker从入门到实战】笔记

什么是docker

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。

Docker采用 C/S架构 Docker daemon 作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。 客户端和服务端既可以运行在一个机器上,也可通过 socket 或者RESTful API 来进行通信。
Docker daemon 一般在宿主主机后台运行,等待接收来自客户端的消息。 Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker daemon 交互。

docker与传统虚拟机的比较

传统虚拟机技术是虚拟出一套硬件 后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程 直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。

Image镜像

Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文 件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。分层存储:镜像由多层文件系统联合组成。分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为 基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

Container容器

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、 暂停等。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。

前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层, 在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容 器存储层。容器不应该向其存储层内写入任何数据,容器存储层要保持无 状态化。所有的文件写入操作,都应该使用数据卷(Volume)

Docker Registry仓库

镜像构建完成后,可以很容易的在当前宿主上运行,但是如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry就是这样的服务。 一个Docker Registry中可以包含多个仓库(Repository);每个仓库可以包含多个标签 (Tag);每个标签对应一个镜像。

镜像加速器

国内访问 Docker Hub 有时会遇到困难,此时可以配置镜像加速

docker命令

docker version 查看当前docker版本

docker pull 拉取镜像

image

docker run 运行镜像

1
docker run -it --rm ubuntu:14.04 bash

image

docker images 列出镜像

  • 镜像体积:Docker Hub中显示的体积是压缩后的体积。在镜像下载和上传过程中镜像是 保持着压缩状态的,因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。而docker images显示的是镜像下载到本地后,展开的大小。

需要注意的是,列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于Docker镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为 使用相同的基础镜像,从而拥有共同的层。由于Docker使用Union FS,相同的层只需要保 存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多

  • 虚悬镜像:拉新镜像时,原有镜像名已存在则会被覆盖为新的,旧的就成为虚悬镜像\,可以用下面命令删除这类镜像
1
docker rmi $(docker images -q -f dangling=true)
  • 中间层镜像:为了加速镜像构建、重复利用资源,Docker 会利用中间层镜像。所以在使用一段时间后,可能会看到一些依赖的中间层镜像,使用命令 -a
1
docker images -a

docker images 还支持强大的过滤器参数 –filter ,或者简写 -f,想查看某个位置之前的镜像也可以,只需要把 since 换成 before 即可

image

-q 参数列出镜像ID

image

可以使用 docker exec 命令进入容器,修改其内容

image

可以通过 docker diff 命令看到容器的具体改动

image

容器修改后需要docker commit 成新的镜像

其中 –author 是指定修改的作者,而 –message 则是记录本次修改的内容

image

docker history 可以查看历史提交记录

image

注意:尽量不要使用commit来修改镜像,这会使得镜像臃肿和复杂化,定制镜像应该使用Dockerfile来完成

docker rmi 删除镜像 docker rm 删除容器

容器是以镜像为基础, 再加一层容器存储层

Untagged和Deleted

image

使用Dockerfile定制镜像

image

FROM 指定镜像基础

所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 nginx 镜像的容器,再进行修改一样,基础镜像是必须指定的,而且必须是第一条指令

Docker Hub 上有非常多的高质量的官方镜像

Docker还存在一个特殊的镜像,名为scratch。这个镜像 是虚拟的概念,并不实际存在,它表示一个空白的镜像

RUN 执行命令

image

Dockerfile中每一个指令都会建立一层,RUN 也不例外。每一个 RUN 的行为, 就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束 后,commit 这一层的修改,构成新的镜像

正确写法

image

镜像臃肿

image

docker build 构建镜像

image

在 Dockerfile 文件所在目录执行:

image

镜像构建上下文(Context)

如果注意,会看到 docker build,-t指定镜像名:版本,命令最后有一个“.”,表示当前目录,而 Dockerfile就在当前目录,因此不少初学者以为这个路径是在指定 Dockerfile 所在路径,这么理解其实是不准确的

image

客户端:client;服务端:daemon

在这种客户端/服务 端的架构中,如何才能让服务端获得本地文件呢,这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker引擎。这样 Docker引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件

image

其他docker build的用法

  • 直接用 Git repo 进行构建

image

  • 用给定的tar压缩包构建

image

COPY复制文件

image

ADD 更高级的复制文件

ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能

在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD

CMD 容器启动命令

CMD指令就是用于指定默认的容器主进程的启动命令

image

如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行

image

ENTRYPOINT 入口点

image

通过使用CMD,我们可以让镜像变成像命令一样使用,查询公网ip

image

如果我们的CMD命令需要传参数的话,这时候就需要采用ENTRYPOINT,比如我们需要加入-i参数

image

image

还可以将CMD作为参数传递给ENTRYPOINT

image

注意

在镜像中,ENTRYPOINT只生效最后一个,之前的会被覆盖,而CMD可以存在多个

ENV 设置环境变量

image

image

ARG 构建参数

构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的 环境变量,在将来容器运行时是不会存在这些环境变量的

image

VOLUME 定义匿名卷

image

EXPOSE 声明端口

image

要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来

  • -p:是映射宿主端口容器端口
  • -P:是映射随机端口EXPOSE端口

WORKDIR 指定工作目录

image

使用WORKDIR指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改 为指定的目录,该目录需要已经存在,WORKDIR 并不会帮你建立目录。因此如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令

USER 指定当前用户

image

HEALTHCHECK 健康检查

image

ONBUILD 为他人做嫁衣裳

image

参考文献

docker save 和 docker load

image

docker容器

docker run 启动容器

image

下面的命令则启动一个 bash 终端,允许用户进行交

image

  • -t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上
  • -i 则让容器的标准输入保持打开

使用 -d 参数可以后台运行容器

image

docker start 启动已停止的容器

可以使用docker start命令,直接将一个已经终止的容器启动运行

docker stop 终止容器

可以使用docker stop命令,来终止一个运行中的容器

终止状态的容器可以用docker ps -a命令看到,docker restart可以重启一个容器

docker exec 进入容器

可以使用docker attachdocker exec来进入容器

因为使用docker attach进入容器后执行exit会导致容器停止,而使用docker exec进入容器后执行exit不会导致容器停止,因此推荐使用exec

image

docker export 导出容器

image

docker import 导入容器

image

docker container rm 删除容器

docker rm 可以删除一个处于终止状态的容器,加 -f 参数可以删除一个运行中的容器

image

docker ps 查看运行中的容器

docker ps -a 查看所有容器,包括终止的容器

docker container prune 清理所有处于终止状态的容器

image

docker inspect 容器id 查看容器的信息(端口、网络、数据卷等)

docker仓库

仓库(Repository)是集中存放镜像的地方,一个容易混淆的概念是注册服务器(Registry)。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。如对于仓库地址dl.dockerpool.com/ubuntu 来说,dl.dockerpool.com 是注册服务器地址,ubuntu 是仓库名

Docker Hub

docker login 本地登录docker hub的仓库

docker search 关键词 查找仓库上的镜像

docker pull 拉取镜像

docker push 推镜像

docker tag 标记镜像

使用docker tagba58这个镜像标记为192.168.7.26:5000/test

1
docker tag ba58 192.168.7.26:5000/test

docker数据管理

image

image

数据卷(Volumes)

创建一个数据卷

1
$ docker volume create my-vol

查看所有数据卷

1
2
$ docker volume ls
local my-vol

查看数据卷信息

1
$ docker volume inspect my-vol

启动一个挂载数据卷的容器

1
2
3
4
5
6
$ docker run -d -P \
--name web \
# -v my-vol:/wepapp \
--mount source=my-vol,target=/webapp \
training/webapp \
python app.py

查看容器中数据卷的信息

1
$ docker inspect web
1
2
3
4
5
6
7
8
9
10
11
12
"Mounts": [
{
"Type": "volume",
"Name": "my-vol",
"Source": "/var/lib/docker/volumes/my-vol/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]

删除数据卷

1
docker volume rm my-vol

数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用docker rm -v这个命令。

无主的数据卷可能会占据很多空间,要清理请使用以下命令

1
docker volume prune

挂载主机目录(Bind mounts)

挂载一个主机目录作为数据卷

使用 –mount 标记可以指定挂载一个本地主机的目录到容器中去。

1
2
3
4
5
6
$ docker run -d -P \
--name web \
# -v /src/webapp:/opt/webapp \
--mount type=bind,source=/src/webapp,target=/opt/webapp \
training/webapp \
python app.py

查看数据卷的具体信息

1
$ docker inspect web
1
2
3
4
5
6
7
8
9
10
"Mounts": [
{
"Type": "bind",
"Source": "/src/webapp",
"Destination": "/opt/webapp",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]

挂载一个本地主机文件作为数据卷

1
2
3
4
5
6
7
8
9
$ docker run --rm -it \
# -v $HOME/.bash_history:/root/.bash_history \
--mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
ubuntu:18.04 \
bash

root@2affd44b4667:/# history
1 ls
2 diskutil list

使用网络

外部访问容器

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。

当使用 -P 标记时,Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。

image

docker port 查看映射端口配置

image

容器互联

使用link链接容器(不推荐使用)

docker network 使用网络互联容器(推荐使用)

  • 新建网络
1
$ docker network create -d bridge my-net

-d 参数指定 Docker 网络类型,有 bridge overlay

  • 连接容器

运行一个容器并连接到新建的 my-net 网络

1
$ docker run -it --rm --name busybox1 --network my-net busybox sh

连接相同网络的容器,网络是互通的

参考文献