docker的日志管理工作和时区本地化设置(docker日志过大,造成磁盘空间被占满的两种处理方法,以及时区的准确设置)
说来话长,某次迁移服务器,该服务器跑的是一个Python项目,由于原服务器和目标服务器操作系统不一样,因此,迁移工作并不顺利,因此,决定使用docker把该Python项目容器化。
说干就干,docker确实是效率神器,半天功夫就迁移完毕了,但是前方有两个坑,很可惜,我掉进去了。第一个坑是docker的日志,第二个坑是docker容器化后的时区问题。
首先说一说docker的日志,docker的日志分为两种,第一种是docker引擎的日志,第二种是docker容器运行时的服务日志,默认为json-file格式,json-file日志驱动 记录从容器的 STOUT/STDERR的输出 ,用 JSON 的格式写到文件中,日志中不仅包含着 输出日志,还有时间戳和 输出格式。
所有的驱动如下:
none | 运行的容器没有日志,docker logs也不返回任何输出。 |
local | 日志以自定义格式存储,旨在实现最小开销。 |
json-file | 日志格式为JSON。Docker的默认日志记录驱动程序。 |
syslog | 将日志消息写入syslog。该syslog守护程序必须在主机上运行。 |
journald | 将日志消息写入journald。该journald守护程序必须在主机上运行。 |
gelf | 将日志消息写入Graylog扩展日志格式(GELF)端点,例如Graylog或Logstash。 |
fluentd | 将日志消息写入fluentd(转发输入)。该fluentd守护程序必须在主机上运行。 |
awslogs | 将日志消息写入Amazon CloudWatch Logs。 |
splunk | 使用HTTP事件收集器将日志消息写入splunk。 |
etwlogs | 将日志消息写为Windows事件跟踪(ETW)事件。仅适用于Windows平台。 |
gcplogs | 将日志消息写入Google Cloud Platform(GCP)Logging。 |
logentries | 将日志消息写入Rapid7 Logentries。
|
常用的驱动是json-file,syslog ,journald 这三种,json-file 是默认的日志驱动。
docker的日志通常默认是保存在 /var/lib/docker/containers/容器名称/ 目录下,如果是默认的json-file 驱动,那么日志文件名称为 容器名称-json-log,当然,docker日志也有级别,通常级别为info,如果为debug的话,那么日志增长会非常快的。
回到前面所说的迁移项目,我并没有对日志做任何限制和改动,也就是所有的都是默认的,迁移完成的比较顺利,也就等待验收了,然后大概过了四五天时间,需要在迁移的机器上做一点小改动,登陆服务器后cd 切换目录报错:bash: cannot create temp file for here-document: No space left on device,这就让人很奇怪了,什么也没干怎么就没磁盘空间了??
df -ah 命令一看, 根目录使用了百分百。(这个不是服务器的,是我的实验机)
怎么办?find / -type f -size +100M -print0 | xargs -0 du -h | sort -nr 这个命令查找大于100M的文件,发现/var/lib/docker/containers/容器名称/容器名称-json.log 文件500多g大,然后就使用了错误的命令 rm -rf 文件名 删除了,没想到仍然是磁盘占用百分百,遂重启docker服务,重启容器,仍然是百分百,无奈,重启服务器后恢复正常的磁盘使用。
后面查阅资料发现,日志文件清空一般不是删除rm命令,而是 > 文件名这样的清空方式。
那么,docker日志野蛮增长的情况应该如何处理呢?
有如下两个办法,第一,定时任务清空日志,但,该方法治标不治本,第二,限制日志的大小,指定日志只能多大,超过了设定值就不增加。这个方法是通过docker自身的引擎来实现的。可以根除日志无限增长的问题。第三,更改 docker的工作目录到一个磁盘空间多的分区,结合第一个方法,经常性的定时任务清理日志。
那么,第一个方法可以考虑放弃,自然使用最有效率的第二种方法啦。
通常,在centos系列操作系统中,docker是使用systemctl 管理服务的,docker当然也有这么一个服务管理脚本文件,找到它,修改它的工作路径到另一个分区。找这么一个文件也比较简单,
systemctl status docker 输出如下:
vim /etc/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
[Service]
Type=notify
ExecStart=/usr/local/bin/dockerd --graph=/opt/docker
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TimeoutStartSec=0
Delegate=yes
KillMode=process
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s
[Install]
WantedBy=multi-user.target
比如,现在我将docker的工作目录更改到了 /opt/docker 目录下,然后 重启docker服务,重启命令为 systemctl daemon-reload&&systemctl restart docker
[root@centos11 ~]# cd /opt/docker/
[root@centos11 docker]# ls
builder buildkit containerd containers image network overlay2 plugins runtimes swarm tmp trust volumes
将原来的目录移动到新目录下即可,通常原目录为 /var/lib/docker/ ,移动前请先停止docker服务。 然后编写crontab 定时任务,这个就不演示了。移动完毕后在启动docker服务即可。以上是第三种方法结合第一种方法,只是转移日志文件目录并且定期删除日志,这样其实并不好。应该使用第二种方法的哦。
第二种方法限制日志增长,可以分为三种形式,1,全局限制,也就是所有的docker容器统一使用一个限定值,不管是原来已启动的镜像还是以后将要启动的镜像,统统限制日志大小,2,docker run 阶段,某一个容器单独限定日志大小。3,docker-compose 编排启动一组容器时组内每个容器日志限制大小。
全局限制:新建/etc/docker/daemon.json,若有就不用新建了。添加log-dirver和log-opts参数,
{
"registry-mirrors": ["https://b0j89uo8.mirror.aliyuncs.com"],
"exec-opts":["native.cgroupdriver=systemd"],
"log-driver":"json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
max-size=500m,意味着一个容器日志大小上限是500M,max-file=3,意味着一个容器有三个日志,分别是id+.json、id+1.json、id+2.json。使用的驱动是json-file驱动,如果是为了试验看到效果,那么max-size的值修改为2M,文件数量修改为1,我试验的时候是使用的mysql镜像,反复重启容器后,很快日志就达到了512k,并且不在继续增长,证明该方法确实可行。
容器run时单独限制:
--log-driver json-file --log-opt max-size=10m,比如,将这一段加在docker run 后面,那么以这条命令启动的容器日志将会限定在10m大小,并且使用的日志驱动是json-file。
[root@centos11 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hub.c.163.com/library/mysql latest 9e64176cd8a2 3 years ago 407MB
[root@centos11 ~]# docker run -itd --name mysql1 --log-driver json-file --log-opt max-size=10m -e MYSQL_ROOT_PASSWORD=123456 hub.c.163.com/library/mysql
bf402735afc0d18b0a72c68850a0b1f3579e810594a1bf9267e7183a6c47e379
这个MySQL容器的日志将会保持在10m大小。使用的日志驱动是json-file。
docker-compose 编排文件限制:比如一个nginx容器,限制它的日志大小为2G。
nginx:
image: nginx:1.12.1
restart: always
logging:
driver: “json-file”
options:
max-size: “2g”
关于时区问题:
时区问题是大问题,时间没统一好,业务会乱套。究其原因,大部分 Docker 镜像都是基于 Alpine,Ubuntu,Debian,CentOS 等基础镜像制作而成。基本上都采用 UTC 时间,默认时区为零时区。
1,通过传递环境变量改变容器时区
- 适用于基于 Debian 基础镜像, CentOS 基础镜像 制作的 Docker 镜像
- 不适用于基于 Alpine 基础镜像, Ubuntu 基础镜像 制作的 Docker 镜像
比如前面的MySQL镜像这样启动:
root@centos11 ~]# docker run -it --name mysql2 -e TZ=Asia/Shanghai -e MYSQL_ROOT_PASSWORD=12345 -p 3306:3306 hub.c.163.com/library/mysql /bin/bash
root@d0d8ba4c6135:/# date
Sat Jan 23 16:31:39 CST 2021
root@d0d8ba4c6135:/# exit
[root@centos11 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hub.c.163.com/library/mysql latest 9e64176cd8a2 3 years ago 407M
2,在docker-compose 编排时指定环境变量,同样的只适用于debian系列操作系统镜像,还是以上面所使用的MySQL镜像为例:
version: '3'
services:
mysql:
container_name: mysql_57
image: hub.c.163.com/library/mysql:5.7.18
restart: always
environment:
MYSQL_USER: admin
MYSQL_PASSWORD: admins
MYSQL_DATABASE: database
MYSQL_ROOT_PASSWORD: admin
TZ: Asia/Shanghai
volumes:
- /usr/local/mysql/data:/var/lib/mysql
ports:
- 3306:3306
network_mode: host
3,彻底的解决时区问题,通过dockerfile重新制作镜像
(1). Alpine
将以下代码添加到 Dockerfile 中:
ENV TZ Asia/Shanghai
RUN apk add tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& apk del tzdata
(2). Debian
Debian 基础镜像 中已经安装了 tzdata 包,我们可以将以下代码添加到 Dockerfile 中:
ENV TZ=Asia/Shanghai \
DEBIAN_FRONTEND=noninteractive
RUN ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& dpkg-reconfigure --frontend noninteractive tzdata \
&& rm -rf /var/lib/apt/lists/*
(3). Ubuntu
Ubuntu 基础镜像中没有安装了 tzdata 包,因此我们需要先安装 tzdata 包。
我们可以将以下代码添加到 Dockerfile 中。
ENV TZ=Asia/Shanghai \
DEBIAN_FRONTEND=noninteractive
RUN apt update \
&& apt install -y tzdata \
&& ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& dpkg-reconfigure --frontend noninteractive tzdata \
&& rm -rf /var/lib/apt/lists/*
(4). CentOS
CentOS 基础镜像 中已经安装了 tzdata 包,我们可以将以下代码添加到 Dockerfile 中。
ENV TZ Asia/Shanghai
RUN ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone