Docker 是用 Go 语言开发的, 基于 Linux 内核的 CGroup, namespace, 以及 AUFS 类的 Union FS 技术, 是对进程进行封装隔离的轻量级容器虚拟化技术之一。Docker 支持 Windows/Linux/Mac/AWS/Azure 多种平台的安装, 其中 Windows 需要 Win10+, Mac 需要 EI Captain+。Docker 是一个 C/S 架构的服务, 安装好 docker 之后需要启动 docker 软件后才能使用 docker 命令。
1. Docker 基础
- runc 是一个 Linux 命令行工具, 用于根据 OCI容器运行时规范 创建和运行容器。
- containerd 是一个守护程序, 它管理容器生命周期, 提供了在一个节点上执行容器和管理镜像的最小功能集。
Docker 主要有 Dockerfile, Image, Container, Repository 四个基本概念。通过 Dockerfile 我们可以生成 Docker Image(镜像)。自己制作的镜像可以上传到 Docker hub 平台, 也可以从平台上拉去我们需要的镜像。当镜像拉到本地之后, 我们就可以实例化这个镜像形成一个 Container(实例) 了。一个简单的镜像启动的命令是:
$ docker run [组织名称]/<镜像名称>:[镜像标签]
其中除了镜像名称, 其它的都是可选参数。组织名称不填默认为library, 镜像标签不填则默认为latest。例如经典的启动一个 Hello World 镜像的过程如下:
可以看到当我实例化hello-world这个镜像的时候, docker 发现本地没有这个镜像会先去 Docker hub 远端拉取镜像, 如刚才说的, 默认是latest标签。拉取后就会实例化执行入口命令了。我们除了可以使用 Docker hub 查找我们需要的镜像之外, 也可以使用docker search命令来查找。
1.1 安装
Docker 软件包已经包括在默认的 CentOS-Extras 软件源里。因此想要安装 docker, 只需要运行下面的 yum 命令:
# yum install docker
# 安装最新版
curl -fsSL https://get.docker.com/ | sh
1.2 启动 Docker 服务
安装完成后, 使用下面的命令来启动 docker 服务, 并将其设置为开机启动:
service docker start #systemctl start docker.service
chkconfig docker on #systemctl enable docker.service
Docker 使用 /etc/docker/daemon.json 来配置 Daemon。
下面我们就来看看如何运行一个 Nginx 容器实例:
$ docker run -d --rm -p 8080:80 -v "$PWD/workspace":/var/www/hello.world \
-v "$PWD/hello.world.conf":/etc/nginx/conf.d/hello.world.conf nginx
使用docker run
命令就能启动一个实例了, 其中-p表示将本机的 8080 端口映射到镜像实例内的 80 端口, 而-v表示将本地的$PWD/workspace文件夹映射到镜像实例里的/var/www/hello.world文件夹, 后面的同理。最后再指定一下镜像名称, 就能完成一次 Nginx 实例的启动了。此时访问http://hello.world:8080即可看到效果。
注: 千万不要在容器实例中存储内容, 实例销毁时实例内的所有内容都会被销毁,下次启动的时候又是全新的实例, 内容不会保存下来。如果需要存储服务需要使用挂载卷或者外部存储服务。
1.3 显示当前正在运行容器的列表
[root@iawen ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
743845f56051 centos "/bin/bash" 11 seconds ago Up 10 seconds flamboyant_shirley
1.4 Docker 其它的优点
基于 Docker 容器虚拟化技术除了以上说的解决部署环境之外, 还有一些其它的优点, 例如:
- 基于 Docker 的 CI 持续集成和 CD 持续支付
- 基于 Kubernetes, Docker Swarm 的集群弹性扩容和缩容
- CI/CD 对于现在的敏捷开发是非常重要的, 自动化任务帮助我们节省很多不必要的开发时间浪费, 具体可查看我之间的文章《基于Docker的CI工具》④。而 k8s 和 Docker Swarm 带来的弹性扩容和缩容让业务不在为流量问题而头疼。通过监控报警设置当出现峰值的时候自动扩容抗压, 当出现低谷的时候自动去除多余的容器来节省成本, 同时也将多余的资源给其它服务使用。
2. Docker 四个基本概念
2.1 Dockerfile
Dockerfile 是 Docker 比较重要的概念。它是 Docker 创建镜像的核心, 它的出现给 Docker 提供了两大好处:
- 文本化的镜像生成操作让其方便版本管理和自动化部署
- 每条命令对应镜像的一层, 细化操作后保证其可增量更新, 复用镜像块, 减小镜像体积
scratch 镜像, 是一个虚拟概念, 并不实际存在, 表示一个空白的镜像。很多基础镜像都是从 scrath 基础上构建而来。为了构建项目, 非构建所需要的文件尽量不用放在同一个目录下, 或者使用 .dockerignore 文件来排除出去。
Dockerfile 的一些编写规则主要如下:
命令 | 说明 |
---|---|
# | 注释 |
FROM | 指令告诉 Docker 使用哪个镜像作为基础 |
RUN | 开头的指令会在创建中运行, 比如安装一个软件包, 每个RUN 就是新建一层 |
COPY | 指令将文件复制进镜像中 |
ADD | 源路径可以是一个URL, 如果是压缩文件将自动解压, 相比COPY更强大, 但语义更复杂 |
WORKDIR | 指定工作目录 |
CMD/ENTRYPOINT | 容器启动执行命令. 当Dockerfile里有ENTRYPOINT时, ENTRYPOINT的值会是要执行的命令, 而CMD值会被当作ENTRYPOINT的选项. ENTRYPOINT指令无法被 docker run 后的指令覆盖。 |
ENV | 设置环境变量 |
VOLUME | 定义匿名卷, 命令映射(本地:VOLUME值)如: -v /data/registry:/var/lib/registry |
EXPOSE | 暴露端口, 这只是一个申明 |
HEALTHCHECK | 健康检查 |
RUN 和 CMD/ENTRYPOINT 都是执行命令, 区别在于 RUN 是在镜像构建过程中执行的, 而 CMD/ENTRYPOINT 是在镜像生成实例的时候执行的, 类似于 C/C++ 语言的头文件的正常代码的区别。而且后者在一个 Dockerfile 文件中只能有一个存在。CMD/ENTRYPOINT 的区别除了在写法上有区别之外, 还有在docker run命令后增加 CMD 参数的情况下有区别(CMD会被复写)。一般建议使用 ENTRYPOINT 会更方便点。一个简单的 Node 命令行脚本的 Dockerfile 文件如下:
FROM mhart/alpine-node:8.9.3 LABEL maintainer="lizheming <i@imnerd.org>"
org.label-schema.name="Drone Wechat Notification"
org.label-schema.vendor="lizheming"
org.label-schema.schema-version="1.1.0"
WORKDIR /wechat
COPY package.json /wechat/package.json
RUN npm install --production --registry=https://registry.npm.taobao.org
COPY index.js /wechat/index.js
ENTRYPOINT [ "node", "/wechat/index.js" ]
这里我认为依赖是比较固定的, 没有代码修改那么频繁, 所以将其提前了。最终保证了所以越稳定的变化的命令至于上层, 保证了每层打包出来的 Layer 能够尽可能的复用, 而不会徒增镜像的大小。最后我们使用如下命令就可以完成一个 Docker 镜像的构建了:
$ docker build lizheming/drone-wechat:latest .
参数和docker run是一样的。构建完成之后就可以开心的 push 到 Docker hub 上啦~
2.1.1 Go 程序打包到Docker
- 编译GO程序
CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' hello2.go
- Dockerfile 文件
From scratch
ADD ./hello2 ./ # COPY ./hello2 ./
ENTRYPOINT ["/hello2"] # CMD ["./hello2"]
docker build -t goweb2 .
2.2 镜像 Image
镜像由多个层组成, 每层叠加之后, 从外部看来就如一个独立的对象。镜像内部是一个精简的操作系统(OS), 同时还包含应用运行所必须的文件和依赖包。
首先需要先从镜像仓库服务中拉取镜像。常见的镜像仓库服务是 Docker Hub, 但是也存在其他镜像仓库服务。拉取操作会将镜像下载到本地 Docker 主机, 可以使用该镜像启动一个或者多个容器。
2.3 容器 Container
容器都是由镜像加载而来的. 在面向对象的编程语言中, 有类跟对象的概念。类是抽象的, 对象是类的具体实现。Image跟Container可以类比面向对象中的类跟对象, Image就相当于抽象的类, Container就相当于具体实例化的对象。运行容器有两种模式:
- 一种是交互模式
- 一种是后台模式
常用命令:
docker container port # 查看某个容器的端口映射
2.4 仓库 Registry
一个registry是一个存储和内容交付系统, 其中维护着若干命名的Docker镜像, 这些镜像有不同的标记版本。用户通过使用 docker push 和 docker pull 命令与 registry 进行交互。
2.4.1 设置 Registry 镜像地址
下载官方镜像点的镜像国内访问速度太慢, 在该配置文件(/etc/docker/daemon.json)中加入(没有该文件的话, 请先创建一个):
{
// http: server gave HTTP response to HTTPS client
"insecure-registries": ["192.168.137.3:5000"],
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
]
}
2.4.2 搭建私有 Docker Registry
官方的Docker hub是一个用于管理公共镜像的好地方, 我们可以在上面找到我们想要的镜像, 也可以把我们自己的镜像推送上去。但是, 有时候, 我们的使用场景需要我们拥有一个私有的镜像仓库用于管理我们自己的镜像。这个可以通过开源软件Registry来达成目的。
# 克隆 registry 代码到 $GOPATH/src/github.com/docker/ 目录下
git clone https://github.com/distribution/distribution
cd $GOPATH/src/github.com/docker/distribution
make binaries
cp bin/* /usr/local/bin/
cp cmd/registry/config-sample.yml /etc/docker/config-reigstry.yml
# 生成认证帐号
htpasswd -Bbn iawen 123456 > /etc/docker/registry
# 修改 registry 的配置文件, 启动并登录
resistry serve /etc/docker/config-reigstry.yml
docker login localhost:5000 -u iawen -p 123456
docker tag goweb localhost:5000/goweb:latest
docker push localhost:5000/goweb:latest
docker pull localhost:5000/goweb:latest
2.4.3 Harbor
Harbor以Docker公司开源的Registry为基础, 提供了管理Ul、基于角色的访问控制、AD/LDAP集成、日志审核等企业用户 需求的功能, 同时还原生支持中文。Harbor和Registry都是Docker的镜像仓库, 但是Harbor作为更多企业的选择, 是因为相比较于Regisrty来说, 它具有很多的优势。
- 提供分层传输机制, 优化网络传输
Docker镜像是是分层的, 而如果每次传输都使用全量文件(所以用FTP的方式并不适合), 显然不经济。必须提供识别分层传输的机制, 以层的UUID为标识, 确定传输的对象。 - 提供WEB界面, 优化用户体验
只用镜像的名字来进行上传下载显然很不方便, 需要有一个用户界面可以支持登陆、搜索功能, 包括区分公有、私有镜像。 - 支持水平扩展集群
当有用户对镜像的上传下载操作集中在某服务器, 需要对相应的访问压力作分解。 - 良好的安全机制
企业中的开发团队有很多不同的职位, 对于不同的职位人员, 分配不同的权限, 具有更好的安全性。 - Harbor提供了基于角色的访问控制机制, 并通过项目来对镜像进行组织和访问权限的控制。kubernetes中通过namespace来对资源进行隔离, 在企业级应用场景中, 通过将两者进行结合可以有效将kubernetes使用的镜像资源进行管理和访问控制, 增强镜像使用的安全性。尤其是在多租户场景下, 可以通过租户、namespace和项目相结合的方式来实现对多租户镜像资源的管理和访问控制。
3. Docker 三剑客
一个完整的项目肯定是不止依赖一个服务的, 而 Docker 镜像的 ENTRYPOINT 只能设置一个, 所以难道我们要使用docker run命令手动创建 N 个容器实例吗?
3.1 Docker Compose
Docker Compose 是一款容器编排程序, 使用 YAML 配置的形式将你需要启动的容器管理起来, 免去我们需要多次执行docker run命令的烦恼。
Git 项目地址: https://github.com/docker/compose
3.1.1 Compose 示例
一个简单的 docker-compose.yaml 配置文件大概如下:
version: "2"
services:
nginx:
depends_on:
- "php"
image: "nginx:latest"
volumes:
- "$PWD/src/docker/conf:/etc/nginx/conf.d"
- "$PWD:/home/q/system/m_look_360_cn"
ports:
- "8082:80"
container_name: "m.look.360.cn-nginx"
php:
image: "lizheming/php-fpm-yaf"
volumes:
- "$PWD:/home/q/system/m_look_360_cn"
container_name: "m.look.360.cn-php"
Docker Compose 的另外一个好处就是能够帮我们处理容器的依赖关系, 在每个容器中会将容器的 IP 和服务的名称使用 hosts 的方式绑定, 这样我们就能在容器中直接使用服务名称来接入对应的容器了。例如下面这个 Nginx 配置中的php:9000就是利用了这个原理。
server {
listen 80;
server_name dev.m.look.360.cn;
charset utf-8;
root /home/q/system/m_look_360_cn/public;
index index.html index.htm index.php;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ .php$ {
fastcgi_pass php:9000;
#fastcgi_pass unix:/tmp/fcgi.sock;
fastcgi_index index.php;
}
}
3.1.2 Compose 模板文件
模板文件是使用 Compose 的核心, 涉及到的指令关键字也比较多。默认的模板文件名称为 docker-compose.yml, 格式为 YAML 格式。每个服务都必须通过 image 指令指定镜像或 build 指令(需要 Dockerfile)等来自动构建生成镜像。
指令 | 说明 |
---|---|
build | 指定 Dockerfile 所在文件夹的路径(可以是绝对路径, 或者相对 docker-compose.yml 文件的路径)。Compose 将会利用它自动构建这个镜像, 然后使用这个镜像。 |
cap_add, cap_drop | 指定容器的内核能力(capacity)分配。 |
command | 覆盖容器启动后默认执行的命令。 |
cgroup_parent | 指定父 cgroup 组, 意味着将继承该组的资源限制。 |
container_name | 指定容器名称。默认将会使用 项目名称_服务名称_序号 这样的格式. 注意: 指定容器名称后, 该服务将无法进行扩展(scale) |
devices | 指定设备映射关系。 |
depends_on | 解决容器的依赖、 启动先后的问题。 |
dns | 自定义 DNS 服务器。可以是一个值, 也可以是一个列表 |
dns_search | 配置 DNS 搜索域。 可以是一个值, 也可以是一个列表 |
tmpfs | 挂载一个 tmpfs 文件系统到容器 |
env_file | 从文件中获取环境变量, 可以为单独的文件路径或列表 |
environment | 设置环境变量。你可以使用数组或字典两种格式 |
expose | 暴露端口, 但不映射到宿主机, 只被连接的服务访问 |
external_links | 链接到 docker-compose.yml 外部的容器, 甚至并非 Compose 管理的外部容器。 |
extra_hosts | 类似 Docker 中的 –add-host 参数, 指定额外的 host 名称映射信息 |
healthcheck | 通过命令检查容器是否健康运行 |
image | 指定为镜像名称或镜像 ID。如果镜像在本地不存在, Compose将会尝试拉取这个镜像 |
labels | 为容器添加 Docker 元数据(metadata) 信息 |
logging | 配置日志选项, 目前支持三种日志驱动类型: driver: “json-file” driver: “syslog” driver: “none” |
network_mode | 设置网络模式。 使用和 docker run 的 –network 参数一样的值 |
networks | 配置容器连接的网络 |
pid | 跟主机系统共享进程命名空间。打开该选项的容器之间, 以及容器和宿主机系统之间可以通过进程 ID 来相互访问和操作 |
ports | 暴露端口信息 |
secrets | 存储敏感数据, 例如 mysql 服务密码 |
security_opt | 指定容器模板标签(label)机制的默认属性(用户、 角色、 类型、 级别等)。例如配置标签的用户名和角色名 |
stop_signal | 设置另一个信号来停止容器。在默认情况下使用的是 SIGTERM 停止容器 |
sysctls | 配置容器内核参数 |
ulimits | 指定容器的 ulimits 限制值 |
volumes | 数据卷所挂载路径设置。可以设置为宿主机路径(HOST:CONTAINER)或者数据卷名称(VOLUME:CONTAINER), 并且可以设置访问模式( HOST:CONTAINER:ro) |
configs | 仅用于 Swarm mode |
deploy | 仅用于 Swarm mode |
此外, 还有包括domainname, entrypoint, hostname, ipc, mac_address, privileged, read_only, shm_size, restart等指令, 基本跟 docker run 中对应参数的功能一致。
Compose 模板文件支持动态读取主机的系统环境变量和当前目录下的 .env 文件中的变量。
3.2 Docker Swarm
Swarm 是 Docker 社区提供的原生支持 Docker 集群的工具。 它可以把多个 Docker 主机组成的系统转换成为单一的虚拟 Docker 主机。Swarm 对外提供两种 API。一种是标准的 Docker API, 例如 Dokku、Compose、Krane、Flynn、Deis、Jenkins等; 另一种是 Swarm 的集群管理 API, 用于集群的管理。
Docker 1.12 Swarm mode (opens new window)已经内嵌入 Docker 引擎, 成为了 docker 子命令 docker swarm。请注意与旧的 Docker Swarm 区分开来。
Swarm mode 内置 kv 存储功能, 提供了众多的新特性, 比如: 具有容错能力的去中心化设计、内置服务发现、负载均衡、路由网格、动态伸缩、滚动更新、安全传输等。使得 Docker 原生的 Swarm 集群具备与 Mesos、Kubernetes 竞争的实力。
3.2.1 搭建集群
# Manager Node1
docker swarm init
docker swarm join-token work # Work Node Used
docker swarm join-token manager # Manager Reachable Node Used
# Work Node2
docker swarm join --token xxxx 192.168.137.4:2377
# Manager Node3
docker swarm join --token xxxx 192.168.137.4:2377
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
pdnk8ew3s1codgccft1po75ql pc3.iawen.com Ready Active Reachable 20.10.12
na99vaxxbcrqskcbt9pz9b5oj * pc4.iawen.com Ready Active Leader 20.10.12
3podpin9x95xin0pgpn0y6qvi pc5.iawen.com Ready Active 20.10.12
3.2.2 创建服务
docker login -u xxxx -p xxxx http://192.168.137.3:5000
docker service create --with-registry-auth --name=goweb-service --replicas=2 192.168.137.3:5000/iawen/goweb:latest
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
urek1alw5r4e mygoweb replicated 3/3 192.168.137.3:5000/iawen/goweb:latest *:12345->12345/tcp
3.3 Docker Machine
Docker Machine 是 Docker 官方编排(Orchestration)项目之一, 负责在多种平台上快速安装 Docker 环境。Docker Machine 项目基于 Go 语言实现, 目前在 Github 上进行维护.
项目地址: https://github.com/docker/machine
4. Docker 相关
4.1 Docker命令
命令 | 说明 |
---|---|
docker pull name[:tag] | 从网络上下载镜像, 如docker pull ubuntu[:latest] |
docker run -t -i ubuntu /bin/bash | 使用镜像, 如(利用镜像创建一个容器, 在其中运行bash应用) -t 让Docker分配一个伪终端并绑定到容器的标准输入上 -i 让容器的标准输入保持的打开 -d 让Docker容器在后台以守护态(Daemonized)形式守护 -v 宿主机卷:容器卷 –volumes-from=container 数据容器卷 –privileged=true 特权模式 |
docker images | 列出本地主机上已有的镜像 |
docker tag | 为本地镜像添加新的标签, 如: docker tag dl.dockerpool.com:5000/ubuntu:latest ubuntu:latest |
docker inspect | 返回一个JSON格式的消息, 如果只要其中一项内容时, 可以使用-f参数来指定。在指定镜像ID的时候, 通常使用该ID的前若干个字符组成的可区分字串来替代完整的ID。 |
docker search TERM | 可以搜索远端仓库中共享的镜像, 默认搜索Docker Hub官方仓库中的镜像。选项: –automated=false 仅显示自动创建的镜像 –no-trunc=false 输出信息不截断显示 -s, –starts=0 抒写仅显示评价为指定星级以上的镜像 |
docker rmi IMAGE [IMAGE …] | 删除镜像, IMAGE可以为标签或ID, 当有该镜像创建的容器存在时, 镜像文件默认是无法被删除的! |
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] | 基于已经镜像的容器创建, 主要选项包括: -a, –author="" 作者信息 -m, –message="" 提交消息 -p, –pause=true 提交时暂停容器运行 |
docker stop | 停止容器运行, `docker stop [-t |
docker attach docker exec -ti ID /bin/bash |
进入容器 |
docker rm [OPTIONS] CONTAINER [CONTAINER …] | 删除容器: -f, –force=false 强制终止并删除 -l, –link=false 删除容器的连接 -v, –volumes=false 删除容器挂载的数据卷 |
docker export/import CONTAINER/IMAGE | 导出容器/导入镜像, 如: `cat test_for_run.tar |
docker create | 创建容器且不运行, 选项基本与docker run一致 |
备注:
- 基于本地模板导入(推荐使用OpenVZ提供的模板:http://openvz.org):
cat ubuntu-14.0.04-x86_64-minimal.tar.gz | docket import - ubuntu:14.04
- 启动容器有两种方式:
- 1、基于镜像新建一个容器并启动
- 2、将在终止状态的容器启动
4.2 清理Docker占用的磁盘空间
4.2.1 Docker System命令
docker system df
命令, 类似于Linux上的df命令, 用于查看Docker的磁盘使用情况:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0 B 0 B
Containers 0 0 0 B 0 B
Local Volumes 4 0 315.3 MB 315.3 MB (100%)
docker system prune
命令可以用于清理磁盘, 删除关闭的容器、无用的数据卷和网络, 以及dangling镜像(即无tag的镜像)。docker system prune -a
命令清理得更加彻底, 可以将没有容器使用Docker镜像都删掉。注意, 这两个命令会把你暂时关闭的容器, 以及暂时没有用到的Docker镜像都删掉了……所以使用之前一定要想清楚吶。
执行docker system prune -a命令之后, Docker占用的磁盘空间减少了很多:
docker system prune -a
4.2.2 手动清理Docker镜像/容器/数据卷
对于旧版的Docker(版本1.13之前), 是没有Docker System命令的, 因此需要进行手动清理。这里给出几个常用的命令:
# 删除所有关闭的容器
docker ps -a | grep Exit | cut -d ' ' -f 1 | xargs docker rm
# 删除所有dangling镜像(即无tag的镜像, 又叫虚悬镜像)
docker rmi $(docker image ls -f dangling=true | awk "{print $3}") # 或者
docker rmi $(docker images | grep "^<none>" | awk "{print $3}") # 或者
docker image prune
# 删除所有dangling数据卷(即无用的Volume)
docker volume rm $(docker volume ls -qf dangling=true) # 或者
docker volume prune
# 清理 Build Cache 缓存
docker builder prune
4.2.3 限制容器的日志大小
有一次, 当我使用1与2提到的方法清理磁盘之后, 发现并没有什么作用, 于是, 我进行了一系列分析。
在Ubuntu上, Docker的所有相关文件, 包括镜像、容器等都保存在/var/lib/docker/目录中:
$ du -hs /var/lib/docker/
97G /var/lib/docker/
Docker竟然使用了将近100GB磁盘, 这也是够了。使用du命令继续查看, 可以定位到真正占用这么多磁盘的目录:
92G /var/lib/docker/containers/a376aa694b22ee497f6fc9f7d15d943de91c853284f8f105ff5ad6c7ddae7a53
由docker ps可知, Nginx容器的ID恰好为a376aa694b22, 与上面的目录/var/lib/docker/containers/a376aa694b22的前缀一致:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a376aa694b22 192.168.59.224:5000/nginx:1.12.1 "nginx -g 'daemon off" 9 weeks ago Up 10 minutes nginx
因此, Nginx容器竟然占用了92GB的磁盘。进一步分析可知, 真正占用磁盘空间的是Nginx的日志文件。那么这就不难理解了。
使用truncate命令, 可以将Nginx容器的日志文件“清零”:
truncate -s 0 /var/lib/docker/containers/a376aa694b22ee497f6fc9f7d15d943de91c853284f8f105ff5ad6c7ddae7a53/*-json.log
当然, 这个命令只是临时有作用, 日志文件迟早又会涨回来。要从根本上解决问题, 需要限制Nginx容器的日志文件大小。这个可以通过配置日志的max-size来实现, 下面是Nginx容器的docker-compose配置文件:
nginx:
image: nginx:1.12.1
restart: always
logging:
driver: "json-file"
options:
max-size: "5g"
重启Nginx容器之后, 其日志文件的大小就被限制在5GB, 再也不用担心了~
4.2.4 重启Docker
还有一次, 当我清理了镜像、容器以及数据卷之后, 发现磁盘空间并没有减少。根据Docker disk usage提到过的建议, 我重启了Docker, 发现磁盘使用率从83%降到了19%。根据高手指点, 这应该是与内核3.13相关的Bug, 导致Docker无法清理一些无用目录:
it's quite likely that for some reason when those container shutdown, docker couldn't remove the directory because the shm device was busy. This tends to happen often on 3.13 kernel. You may want to update it to the 4.4 version supported on trusty 14.04.5 LTS.
The reason it disappeared after a restart, is that daemon probably tried and succeeded to clean up left over data from stopped containers.
我查看了一下内核版本, 发现真的是3.13:
$ uname -r
3.13.0-86-generic
如果你的内核版本也是3.13, 而且清理磁盘没能成功, 不妨重启一下Docker。当然, 这个晚上操作比较靠谱。
4.2.4 清理日志
echo "" > $(docker inspect --format='{{.LogPath}}' <容器ID>)
4.2.5 权限组
groupadd docker
gpasswd -a $USER docker #将当前用户添加到docker用户组
newgrp docker
5. 其他
5.1 一图说完docker 命令
5.2 一些经典的镜像
- busybox
- alpine
5.3 overlayfs 与 d_type
overlayfs是一种文件系统, 也是目前dokcer在使用的最新的文件系统, 其他的文件系统还有:aufs、device mapper等。而 overlayfs 其实和 aufs 是类似的。更准确的说, overlayfs, 其实是 Linux 文件系统的一种上层文件系统。
如何查看当前操作是否支持overlay: lsmod | grep over
开启overlay: modprobe overlay
docker官方, 建议使用 overlay2, 而不是 overlay, 因为 overlay2 更高效。要使用 overlay2的话, 需要 Linux 内核在版本4以上。
要想使用overlay2, Docker 版本必须高于 17.06.02。
指定docker的overlay2驱动, 需要在启动docker的时候, 指定 –storage-driver 参数, 或者, 在配置文件 /etc/docker/daemon.json 中 , 指定驱动配置
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
不论是 overlay, 还是 overlay2, 它们的底层文件系统都是 overlayfs 文件系统。而 overlayfs 文件系统, 就会用到 d_type 这个东西用来文件的操作是被正确的处理了。换句话说, docker只要使用 overlay 或者 overlay2, 就等于在用 overlayfs, 也就一定会用到 d_type。
docker info
检测当前的文件系统, 是否支持 d_type: xfs_info /
备份, 加参数重新格式化, 恢复: mkfs.xfs -n ftype=1 /path/to/your/device
挂载配置中推荐开启 pquota, 这样可以防止某个容器写文件溢出导致整个容器目录空间被占满。写入到 /etc/fstab 中的内容如下:
$UUID /var/lib/docker xfs defaults,pquota 0 0