想要在容器之间进行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内置服务器。

实施的过程

    1. 希望将文件进行轮换的容器(以下称为nginx容器)挂载目录(更改容器启动选项)

 

    1. 创建logrotate容器

 

    1. 进行操作验证(中间过程)

 

    1. 创建logrotate容器和nginx容器通信的docker网络 ←从这篇文章开始

 

    1. 创建nginx用于重新打开文件的API

 

    1. 更改nginx容器的配置

 

    1. 更改主机配置

 

    1. 更改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,重新打开日志。

image.png

我会简单地解释程序的运行。

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。
如果对某人有所帮助,我会感到幸福。
希望能在某个地方再次相见。

广告
将在 10 秒后关闭
bannerAds