uncategorized

前端工程师应该懂的 Docker 常用操作及概念散记

记录Docker常用知识,在运维的不归路上越走越远

Docker 运行时操作

detach mode

不占用当前输入输出,类似于 deamon

拷贝文件

1
docker cp my-file <dockerid>:/home/path/to/location

查看日志

$ docker logs --tail 300 -f
-f 获得持续输出 –tail 从最后300行开始(防止日志文件很长)

Docker 设置自动重启

  1. 启动时配置 docker run [image ID] --restart=aways
  2. 已启动的容器增加配置: docker update --restart=always <CONTAINER ID>
    Docker 重启选项说明
    To configure the restart policy for a container, use the –restart flag when using the docker run command. The value of the –restart flag can be any of the following:
Flag Description
no Do not automatically restart the container. (the default)
on-failure Restart the container if it exits due to an error, which manifests as a non-zero exit code.
always Always restart the container if it stops. If it is manually stopped, it is restarted only when Docker daemon restarts or the container itself is manually restarted. (See the second bullet listed in restart policy details)
unless-stopped Similar to always, except that when the container is stopped (manually or otherwise), it is not restarted even after Docker daemon restarts.

配置完可以查看到重启配置的结果:

docker inspect [container ID]

1
2
3
4
"RestartPolicy": {
"Name": "always",
"MaximumRetryCount": 0
}

防止容器退出

例如想启动一个 centOS 容器来做一些内部操作的时候,如果只是单纯 run 这个镜像,但不要求它做一些操作的话,刚 run 起来就会 exit, 因为容器已经没有事情要做了,这时可以在启动的时候要求容器持续做一个事情来保证它不退出,如 tail 流式监听文件变化

1
docker run -d --name centos centos tail -f /dev/null

Dockerfile & Build

Dockerfile 指令

Dockerfile 中每一条指令都会生成一个 Layer

Docker构建中的缓存

在从上往下逐层构建的过程中,如果 docker 发现当前层的依赖没有改变,没有触发重新缓存机制,则会直接使用缓存,利用此机制可以缓存每次在构建时的依赖安装操作,单独将 ADD package*.json 写为一行,与其他操作解耦,这样的话就可以尽可能地缓存安装依赖的操作,同样类似的,一些静态操作,如 apt-get 一类的,尽量放到靠前的位置,因为如果放到靠后的位置,前面某一层发生变动,后面所有层都不会触发缓存,都得重新执行

列出 Docker images 中无用镜像

docker images -f "dangling=true" -a

-f参数意为过滤符合特定条件的条目, dangling=true 表示已经不被引用

进一步结合 docker rmi 一次性删除这些无用镜像,rmi 除了接受单个id参数,也可以接受一组id,所以可以利用 –filter 参数,过滤除了id以外的其他输出,结果直接导入 docker rmi

1
docker rmi `docker images -f "dangling=true" --format="{{.ID}}"`

其他支持的格式化参数:

.ID Image ID
.Repository Image repository
.Tag Image tag
.Digest Image digest
.CreatedSince Elapsed time since the image was created
.CreatedAt Time when the image was created
.Size Image disk size

关于 alpine 镜像和依赖加速

在基于 linux 的镜像中,基础镜像可以认为就是一整个操作系统,但很多场景下实际不需要 linux 的所有能力,alpine 项目就实现了构建最小化的 linux 镜像(~5Mb),来支持最常用的能力,同时保持镜像size尽可能地小;因此在使用该镜像的过程中,很有可能需要安装业务相关的依赖,如在 storm-cellar 项目中,需要依赖 imagemagick 来处理图像,需要使用 apk --update add imagemagick 来安装依赖

但默认的依赖管理工具请求的是默认的软件源地址,速度非常慢,我们可以替换使用阿里源,在执行 apk add 之前,先执行源地址字符串的替换命令:

sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories

EXPOSE 字段

EXPOSE 字段指明了容器希望暴露的端口号(容器的诉求),比如我构建了一个基于Nginx的容器,EXPOSE 指明暴露 80端口,这样一来当我启动容器,使用 -P 参数来发布所有暴露的端口的时候,Docker会将容器希望暴露的端口映射到主机的随机端口上;如28378 -> 80/tcp,这有点像是一个主机遵从容器意愿来实现内外互联的一个过程,但更多时候,我们会使用 -p 参数来显式指定内外端口映射关系,例如 -p 10080:80 将外部主机的 10080 端口映射到容器内部的80,当然这样的过程就更多地像一个强制性过程,使用该参数来指定映射关系的人一定要自己确保,容器内部 80 是他想要的端口

Docker Volume

volume 是Docker中一个非常有用和常用的特性,它提供了一种在容器和宿主机之间的数据互通和保存重用机制

数据的保存和迁移

例如我使用Docker启动了一个JIRA程序,但我很有可能会在使用一段时间后将JIRA迁移到别的机器上,这是我只要想办法将之前JIRA产生的数据保存下来,在别的机器上启动新的JIRA容器时,要求它使用这份历史数据即可达到程序与数据迁移的目的。

那么我如何知道哪些数据是JIRA运行必备的呢?一般来说,容器对应的镜像里会做这么一段声明:

1
VOLUME ["/var/atlassian/jira", "/opt/atlassian/jira/logs"]

这个声明就表示,启动该容器时,Docker Daemon会自动创建两个数据卷,一个与容器内的/var/atlassian/jira目录绑定,一个与 /opt/atlassian/jira/logs 绑定,也就是在容器内这俩目录下产生和变化的数据,都会反映到Docker的卷上(即存储在Docker host机器上的物理文件),某种意义上,也就相当于声明了,除了这两个位置的文件,其他地方的数据都是不重要的,或是镜像和新容器可以完全重现的,只有这两个地方的数据是独特的,需要用户小心持久化的

一般来说,如果是这样自动创建的数据卷,是随机命名的(一串很长的随机字符,使用 docker inspect [container-id] 可以查到挂载的具体是哪些卷),以我的JIRA程序为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"Mounts": [
{
"Type": "volume",
"Name": "3cec51a9185d4acf3bf20860398b4db4fe45dabdd710003cbad31eaba98c2552",
"Source": "/var/lib/docker/volumes/3cec51a9185d4acf3bf20860398b4db4fe45dabdd710003cbad31eaba98c2552/_data",
"Destination": "/var/atlassian/jira",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "31e1ec20b28b26e33499a05c8d74ed6ab2603235830060972f796cce12c950ec",
"Source": "/var/lib/docker/volumes/31e1ec20b28b26e33499a05c8d74ed6ab2603235830060972f796cce12c950ec/_data",
"Destination": "/opt/atlassian/jira/logs",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]

Mounts 这个数组告诉我,我的JIRA挂载了 3cec... & 31e1e... 这两个数据卷, docker volume ls可以查到,并且可以使用 docker volume inspect [volume id] 查到该卷的具体物理位置(当然前面 inspect container 也告诉我们了)

如果我需要迁移JIRA数据,我只需要将这两个 “Source” 字段指定的数据卷物理位置下的_data目录拷贝 / 保存 / 移动 到新的机器上,然后在新的机器上 docker create volume [volume name] 来新建空数据卷,最后将对应的数据拷贝到空数据卷的对应物理目录下的_data里即完成了数据的迁移

在新的机器上启动新的JIRA容器时,通过 -v [volume-name]:/var/allanssian/jira 来指定容器使用我刚刚复制过来的新数据卷即可

查找哪些容器使用了指定的卷

1
docker ps -a --filter volume=VOLUME_NAME_OR_MOUNT_POINT

我创建的卷自动被移除了

当启动容器时使用了 --rm 参数的时候,如果容器被停止,相应的容器创建的匿名卷会被自动移除,相当于运行了 docker rm -v [container id]

Docker Compose

  1. service 下指定使用哪个 volume 时,需要在顶层配置中定义所有可能涉及到的 volume, network也一样
  2. 使用已经存在的 volume / network 时,要配置 external: true flag

Docker Daemon

Docker镜像加速

修改 docker daemon 的配置文件 vim /etc/docker/daemon.json (如果不存在则创建一下即可)

增加(或修改)配置:

1
2
3
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}

数组里也可以继续填入其他的源地址,在前面的源不可用时会逐一尝试使用

Share