想要在容器之间进行logrotate Part 2
首先
本文介绍了如何使用另一个用于logrotate的容器(以下简称logrotate容器)来实现nginx(openresty)日志文件的日志轮转。
※此文是第2部分。我认为如果您能先阅读第1部分,会更容易理解。
第1部分:https://qiita.com/toyo_mura/items/4e8b0849df2cb8208f8c
part1复习
-
- logrotateコンテナはできた
-
- ローテーション後のファイルにログを出力してくれない問題が発生
- コンテナ間でのローテーションを完成させるには、logrotateを実行したあと、nginxにログファイルを開きなおさせる(以下: reopen)かnginxの再起動をする必要がある。
目标
我们的目标是通过logrotate容器来集中管理和轮换NGINX日志,这些日志分散在多个容器中。
重新打开已经考虑过的日志文件并重新启动nginx
前提是,在完成容器之间的轮转后,需要在运行logrotate之后重新打开nginx的日志文件,或者重新启动nginx。
具体而言,需要在nginx容器中运行openresty -s reopen命令,或者重新启动nginx容器。
为了实现这一点,我们考虑了以下内容。
docker.sockをlogrotateコンテナにマウントしてdockerコマンドを使う
セキュリティ的に問題あり
参考:https://qiita.com/YuasaJunki/items/8da1ef708a6f826f5915
dockerをリモートから制御できるようにして、logrotateコンテナからdockerコマンドを使う
セキュリティ的に問題あり
参考:リモートサーバーの中のDockerにローカルから接続する
https://qiita.com/YuasaJunki/items/8da1ef708a6f826f5915
nginxコンテナにsshdを起動してlogrotateコンテナからsshで実施
若干セキュリティ的に問題あり
参考:https://postd.cc/docker-ssh-considered-evil/
これだけのためにsshdを起動するのは無駄かも
LUAを使ってAPI(CGI)を実装(nginxにファイルをreopenさせるAPI)
作るのが少しめんどくさい
既存コンテナの変更が必要
pythonを使ってAPI(CGI)を実装(nginxにファイルをreopenさせるAPI)
作るのが少しめんどくさい
既存コンテナの変更が必要
LUAよりも汎用性がありそう
根据以上考虑,我们曾经考虑过几种方法。例如挂载docker.sock或启动sshd似乎很容易实现,但由于安全问题,我们不能在运营中采用这些方法,因此排除了这些选项。
当只剩下两个选择时,LUA还是Python时,出于以下理由,我得出了使用Python来开发API的结论:LUA虽然在openresty中可用,但在nginx中不可用;而Python几乎在所有环境中都可用,无论与应用相关与否,只需启动一个CGI服务器即可。这样看来,Python更具通用性,因此我认为Python更适合用于开发API。
想要执行的操作如下:
在nginx容器中持续运行reopen的API(CGI)。
在logrotate容器完成日志轮换后,使用logrotate的功能向nginx容器发送带有reopen信号的HTTP请求。
然后,正在nginx容器中运行的CGI服务器将运行CGI。
CGI将发送从HTTP请求获取的信号到nginx,文件将被reopen,这是机制。
※CGI服务器是Python内置服务器。
实施的过程
-
- 希望将文件进行轮换的容器(以下称为nginx容器)挂载目录(更改容器启动选项)
-
- 创建logrotate容器
-
- 进行操作验证(中间过程)
-
- 创建logrotate容器和nginx容器通信的docker网络 ←从这篇文章开始
-
- 创建nginx用于重新打开文件的API
-
- 更改nginx容器的配置
-
- 更改主机配置
-
- 更改logrotate容器的配置
- 进行操作验证
环境
-
- ホスト
OS: CentOS 7.9.2009
Docker: 20.10.9
HOSTNAME: development
nginxコンテナ
OS: CentOS 7.6.1810
nginx: openresty/1.19.3.1
HOSTNAME: development_c
logrotateコンテナ
OS: ubuntu 20.04
HOSTNAME: 5d76504e84c1
创建一个Docker网络,用于logrotate容器和nginx容器之间的通信。
为了使logrotate容器和nginx容器能够进行通信,我们需要使用docker network create命令在主机上创建docker-network。如果不进行这个操作,logrotate容器将无法通过容器名称向nginx容器发送curl命令,因此这个工作非常重要。请注意,此命令在主机上执行,而不是在容器内执行。
[root@development ~]# docker network create openresty-network
我会确认能否完成。
[root@development ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
2a90d0faac26 bridge bridge local
e091384cb914 host host local
9151cbd0142e none null local
0a1ad870469e openresty-network bridge local
看起来能够顺利完成。
创建一个用于nginx重新打开文件的API。
接下来我们将创建CGI程序。这次我们将使用Python2来实现(因为它是nginx容器的默认版本)。文件名可以任意取,但为了方便理解,我们将其命名为reopen.py。
以下是CGI文件的内容。
(为了增加通用性,本次reopen功能中也包含了不必需的部分。)
#!/usr/bin/env python
import os
import signal
import cgi
p_pid = None
p_sig = None
s_sig = None
form = cgi.FieldStorage()
if form.has_key("pid"):
p_pid = form["pid"].value
if form.has_key("sig"):
p_sig = form["sig"].value
sigs = {
"USR1": signal.SIGUSR1,
"HUP" : signal.SIGHUP
}
if sigs.has_key(p_sig):
s_sig = sigs[p_sig]
if p_pid != None and int(p_pid) > 0 and s_sig != None:
print("Content-Type: text/plain\n")
print("pid: " + p_pid)
print("sig: " + p_sig)
print("signal: " + str(s_sig))
try:
os.kill(int(p_pid), s_sig)
print("res: OK")
except OSError as e:
print("res: " + str(e))
else:
print("Status: 400 Bad Request\n")
print("param error")
参考:
cgi.FieldStorage
has_key
CGI
内置服务器是固定的200
内置服务器
我将解释整体运动的情况。
-
- logrotateコンテナからcurlでpid=1とsig=USR1を乗せたhttpリクエストを送信
-
- (USR1はnginxにログファイルをreopenさせるシグナル)
-
- nginxコンテナで起動しているCGIサーバがhttpリクエストを受け付けて、pid=1とsig=USR1をreopen.pyに渡す
-
- 受け取ったプログラムは、pidが1のプロセス(ここではnginx)にUSR1のシグナルを送信
- これによってnginxのログがreopenされる
可以参考的内容是,发送 USR1 信号给 nginx,重新打开日志。

我会简单地解释程序的运行。
pid=1とsig=USR1を受け取る(cgi.FieldStorageのおかげでクライアントから送られるフォームの内容を取得できる)
受け取った値がp_pid,s_sigにセットされる
os.kill(int(p_pid),s_sig)の部分でnginxにUSR1シグナルを送信
パーミッションエラーなどの場合はエラーを吐く
现在的情况是如此。
另外,在sigs = {…}的部分限定了可以接收的信号。
参考链接:https://qiita.com/pythonista/items/ef1cbbf8991e3a5921ff
只需要一种方式,请将上述内容用中文进行简述。
更改nginx容器的配置
接下来,我们将更改nginx容器的设置。
尽管已经创建了CGI程序,但它不能如预期那样工作。如果nginx容器继续以root用户运行CGI,即使发送reopen信号给nginx也会导致权限错误。原因是执行CGI的用户不是root。如果以nginx用户启动nginx容器,它将正常工作。因此,需要将nginx容器的启动用户从root更改为nginx。
因此,我們將對nginx容器進行更改。
以下是作业的内容。
-
- nginxコンテナのDockerfileを編集
-
- entrypoint.shを編集
- コンテナ起動時のオプション変更
修改nginx容器的Dockerfile
为了使用nginx用户启动nginx容器,需要编辑Dockerfile。
在使用nginx用户启动时,还需要更改目录的权限等设置,所以也会记录这些配置。
我认为每个人的nginx容器的Dockerfile都各不相同,但在这里我将描述我添加的内容。
我会添加以下项目。
:
COPY entrypoint.sh /tmp/
COPY reopen.py /var/tmp/cgi-bin/
RUN chmod 707 /etc/nginx/conf
RUN chmod 707 /usr/local/openresty/nginx/logs
RUN chmod 707 /usr/local/openresty/nginx
RUN chmod 707 /usr/local/openresty/nginx/logs/access
RUN chmod 707 /usr/local/openresty/nginx/logs/error
RUN chmod 755 /var/tmp/cgi-bin/reopen.py
RUN useradd -m nginx
ENTRYPOINT ["sh","/tmp/entrypoint.sh"]
USER nginx
CMD ["/usr/bin/openresty", "-g", "daemon off;"]
差分 – Differential
[root@development nginx]# diff Dockerfile_old Dockerfile
4a5,6
> COPY entrypoint.sh /tmp/
> COPY reopen.py /var/tmp/cgi-bin/
13a16,26
> RUN chmod 707 /etc/nginx/conf
> RUN chmod 707 /usr/local/openresty/nginx/logs
> RUN chmod 707 /usr/local/openresty/nginx
> RUN chmod 707 /usr/local/openresty/nginx/logs/access
> RUN chmod 707 /usr/local/openresty/nginx/logs/error
> RUN chmod 755 /var/tmp/cgi-bin/reopen.py
>
> RUN useradd -m nginx
>
> ENTRYPOINT ["sh","/tmp/entrypoint.sh"]
> USER nginx
编辑entrypoint.sh
下一步,编辑entrypoint.sh文件。
entrypoint.sh文件在Dockerfile中的ENTRYPOINT命令中使用。
具体内容如下。
#!/bin/sh
cd /var/tmp/
nohup python -m CGIHTTPServer &
exec "$@"
在nginx容器中始终运行CGI服务器,使用nohup python -m CGIHTTPServer &命令。
在启动容器时更改选项
由于nginx容器的启动用户不再是root,因此无法使用小于1024的端口。因此,我们需要更改端口。另外,我们还需要添加选项以使用之前创建的docker-network。请按照如下描述进行操作。
docker run -d \
-p 80:8080 \ #80:80だと使えない
:
-v /hogehoge/nginx/access/:/usr/local/openresty/nginx/logs/access/ \
-v /hogehoge/nginx/error/:/usr/local/openresty/nginx/logs/error/ \
:
--network openresty-network \
--name nginx1 toyo_mura/nginx:latest
[root@development nginx]# diff run.sh_old run.sh
17c17
< -p 80:80 \
---
> -p 80:8080 \ #80:80だと使えない
30a31
> --network openresty-network \
主机配置的修改
我会稍微更改主机的设置。更改内容如下:
- ログが出力されるディレクトリのパーミッション変更
更改输出日志的目录权限
如果不改变存储日志的主机目录的权限,将无法写入日志,因此需要进行更改。
[root@development ~]# chmod 707 /hogehoge/nginx/access
[root@development ~]# chmod 707 /hogehoge/nginx/error
logrotateコンテナの設定変更
logrotateコンテナの設定がpart1で設定した内容のままだと動かないので追記を行います。
作業内容は以下です。
-
- logrotateコンテナのDockerfileにcurlコマンドインストール行を追記
-
- /etc/logrotate.d/に持っていっているnginxのログ用logrotate定義ファイルにcurlコマンドを追記
- コンテナ起動時のオプション変更
logrotateコンテナのDockerfileにcurlコマンドインストール行を追記
logrotateコンテナからnginxコンテナにcurlコマンドでhttpリクエストを送信したいのでlogrotateコンテナのDockerfileを下記のようにします。
FROM ubuntu:20.04
COPY entrypoint.sh /tmp/
##### rsyslog settings #####
RUN apt-get update
RUN apt-get -y install rsyslog
RUN sed -i -e "s/^#cron/cron/" /etc/rsyslog.d/50-default.conf
##### vim settings #####
RUN apt-get -y install vim
RUN echo 'set encoding=utf-8\n\
set fileencodings=iso-2022-jp,euc-jp,sjis,utf-8\n\
set fileformats=unix,dos,mac' >> ~/.vimrc
##### cron setting #####
RUN apt-get -y install cron
##### logrotate settings #####
RUN apt-get -y install logrotate
COPY ./nginx /etc/logrotate.d/
##### curl setting #####
RUN apt-get -y install curl
##### timezone settings #####
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get -y install tzdata
ENV TZ Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENTRYPOINT ["sh","/tmp/entrypoint.sh"]
CMD ["cron", "-f"]
分差
[root@development logrotate]# diff Dockerfile_old Dockerfile
22a23,25
> ##### curl setting #####
> RUN apt-get -y install curl
>
将curl命令添加到将nginx日志用logrotate定义文件复制到/etc/logrotate.d/中。
logrotate終了後にcurlコマンドを自動で叩いてほしいので、/etc/logrotate.d/に持っていっているnginxのログ用logrotate定義ファイルを下記のように編集します。
/var/log/nginx/access/*.log
/var/log/nginx/error/*.log {
ifempty
dateext
missingok
compress
delaycompress
daily
rotate 10
lastaction
curl -v "nginx1:8000/cgi-bin/reopen.py?pid=1&sig=USR1"
endscript
}
分歧或区别
[root@development logrotate]# diff nginx_old nginx
9a10,12
> lastaction
> curl -v "nginx1:8000/cgi-bin/reopen.py?pid=1&sig=USR1"
> endscript
lastaction/endscript是一个在所有日志轮换之后只执行一次的脚本。
参考:https://hackers-high.com/linux/man-jp-logrotate/#lastactionendscript
在这里的curl部分,我向nginx容器发送了一个带有pid=1和sig=USR1信号的http请求。
通过增加curl命令的行数,也可以向多个容器发送http请求。
另外,使用8000作为发送端口是因为python的CGIHTTPServer监听端口是8000。
由于nginx容器和logrotate容器可以通过openresty-network进行通信,因此不需要进行特别的端口开放等操作。
在容器启动时更改选项
添加以下用于使用先前创建的docker网络的选项:
您可以按照以下方式进行描述。
[root@development logrotate]# diff run.sh_old run.sh
6a7
> --network openresty-network \
确认行动
首先,我们需要分别构建nginx容器和logrotate容器,然后运行这些容器以重新创建它们。(以便应用编辑后的配置)
当重新制作容器后,应该在Nginx容器中运行CGI服务器,我们会确认其是否真正运行。
[root@development logrotate]# docker exec nginx1 ps -ef
UID PID PPID C STIME TTY TIME CMD
nginx 1 0 0 15:10 ? 00:00:01 nginx: master process /usr/bin/openresty -g daemon off;
nginx 9 1 0 15:10 ? 00:00:02 python -m CGIHTTPServer
nginx 37 1 0 15:11 ? 00:00:00 nginx: worker process
nginx 38 0 0 18:34 ? 00:00:00 ps -ef
似乎是在运行PID9。
顺便一提,在nginx容器中使用ss命令,可以看到CGI服务器正在8000端口监听。
[root@development logrotate]# docker exec nginx1 ss -nltp
:
LISTEN 0 5 *:8000 *:* users:(("python",pid=9,fd=3))
:
接下来,我们将检查reopen.py是否能正常运行。我们将通过从logrotate容器中使用curl命令发送带有信号的HTTP请求来尝试。
日志轮转容器
root@5d76504e84c1:/# curl -v "nginx1:8000/cgi-bin/reopen.py?pid=1&sig=USR1"
* Trying 172.21.0.3:8000...
* TCP_NODELAY set
* Connected to nginx1 (172.21.0.3) port 8000 (#0)
> GET /cgi-bin/reopen.py?pid=1&sig=USR1 HTTP/1.1
> Host: nginx1:8000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 Script output follows
< Server: SimpleHTTP/0.6 Python/2.7.5
< Date: Fri, 18 Mar 2022 06:20:51 GMT
< Content-Type: text/plain
<
pid: 1
sig: USR1
signal: 10
res: OK
* Closing connection 0
root@5d76504e84c1:/#
目前看来,reopen.py运行正常,没有出现问题。
然后,我们将进行实际的日志轮换以及确认是否能重新打开日志文件。
请重新启动nginx容器。
在完成到part1的实施中,应该可以实现日志文件的轮换。
在这里,我们将确认是否可以将数据正确写入轮换后的文件中。
首先,与part1相似,我们需要确认轮换情况。
[root@development logrotate]# docker exec -it logrotate bash
root@5d76504e84c1:/# ls /var/log/nginx/access/
hogehoge.com.log fugafuga.com.log piyopiyo.com.log
hogehoge.com.log-20220226.gz fugafuga.com.log-20220226.gz piyopiyocom.log-20220226.gz
hogehoge.com.log-20220227.gz fugafuga.com.log-20220227.gz piyopiyo.com.log-20220227.gz
hogehoge.com.log-20220228.gz fugafuga.com.log-20220228.gz piyopiyo.com.log-20220228.gz
hogehoge.com.log-20220301.gz fugafuga.com.log-20220301.gz piyopiyo.com.log-20220301.gz
hogehoge.com.log-20220302.gz fugafuga.com.log-20220302.gz piyopiyo.com.log-20220302.gz
hogehoge.com.log-20220303.gz fugafuga.com.log-20220303.gz piyopiyo.com.log-20220303.gz
hogehoge.com.log-20220304.gz fugafuga.com.log-20220304.gz piyopiyo.com.log-20220304.gz
hogehoge.com.log-20220305.gz fugafuga.com.log-20220305.gz piyopiyo.com.log-20220305.gz
hogehoge.com.log-20220306.gz fugafuga.com.log-20220306.gz piyopiyo.com.log-20220306.gz
hogehoge.com.log-20220307 fugafuga.com.log-20220307 piyopiyo.com.log-20220307
看起来没有问题可以进行轮换了。
然后,我们将检查是否成功抓取了轮换后的文件。
(通过这个检查,同时也会验证logrotate的lastaction/endscript的执行情况)。
[root@development logrotate]# docker exec nginx1 ls -l /proc/1/fd
total 0
lrwx------ 1 nginx nginx 64 Jan 14 15:37 0 -> /dev/null
l-wx------ 1 nginx nginx 64 Jan 14 15:37 1 -> pipe:[25985041]
l-wx------ 1 nginx nginx 64 Mar 7 14:55 10 -> /usr/local/openresty/nginx/logs/access/hogehoge.com.log
l-wx------ 1 nginx nginx 64 Mar 7 14:55 11 -> /usr/local/openresty/nginx/logs/error/hogehoge.com.log
:
看起来您已经顺利获取了轮换后的文件。
我将确认日志是否被正确输出。
[root@development logrotate]# curl -I 127.0.0.1
:
[root@development logrotate]# docker exec -it nginx1 cat /usr/local/openresty/nginx/logs/access/hogehoge.com.log
172.21.0.1 - - [10/Mar/2022:17:00:05 +0900] "HEAD / HTTP/1.1" 403 0 "-" "curl/7.29.0" "-"
我确认了日志的输出。
我們順便嘗試創建另一個nginx容器,以確認它是否可以在多個容器中運行。
我們將以下內容添加到將nginx日誌文件複製到/etc/logrotate.d/的logrotate定義文件中。
[root@development logrotate]# diff nginx_old nginx
12a13
> curl -v "nginx2:8000/cgi-bin/reload.py?pid=1&sig=USR1"
我会跳过轮换的确认。
我会确认是否拿到轮换后的文件。
[root@development logrotate]# docker exec nginx2 ls -l /proc/1/fd
total 0
lrwx------ 1 nginx nginx 64 Mar 28 15:11 0 -> /dev/null
l-wx------ 1 nginx nginx 64 Mar 28 15:11 1 -> pipe:[50709858]
l-wx------ 1 nginx nginx 64 Mar 28 18:10 10 -> /usr/local/openresty/nginx/logs/access/fugafuga.com.log
l-wx------ 1 nginx nginx 64 Mar 28 18:10 11 -> /usr/local/openresty/nginx/logs/error/fugafuga.com.log
:
我們最後確認一下是否真的輸出了日誌。
[root@development logrotate]# curl -I 127.0.0.1:8080
:
[root@development logrotate]# docker exec -it nginx2 cat /usr/local/openresty/nginx/logs/access/fugafuga.com.log
172.21.0.1 - - [28/Mar/2022:16:14:02 +0900] "HEAD / HTTP/1.1" 403 0 "-" "curl/7.29.0" "-"
我确认即使有多个容器,日志也能够正常输出。
总结
-
- logrotateコンテナとnginxコンテナ間でlogrotateを実装することができた
- logrotateを実装するためには既存のnginxコンテナに変更を加える必要あり
结束
非常感谢您阅读至此的part1、part2。
如果对某人有所帮助,我会感到幸福。
希望能在某个地方再次相见。