将在docker容器内执行的命令输出到docker日志的方法
只需要一个选项就能解决。
如果在容器内击打这个就好了。
echo 1 > /proc/1/fd/1
我想做的事情。
-
- チームメンバーにCLIコマンドを提供した
GUI 作る暇がない
ACLの関係で指定サーバからしか叩けない
security的にproxyなど使いたくなかったため
serverへのlogin権限さえあれば叩ける = 権限管理は既存のアレでok
host OS の環境は汚したくないのでコンテナで提供
docker exec はうざいので、aliasでhost側から実行させる
alias cli=’docker exec container_name python run.py $@’
実行した結果をelasticsearchに保存したい
将命令结果输入到docker日志的方法 – 前提知识
打开两个终端窗口。
我会记录下来
$ docker-compose logs -f
Attaching to notip
我创建了一个名为”notip”的容器。
在容器中进行回显时
docker-compose exec echo 1
Docker日志没有任何反应。
$ docker-compose logs -f
Attaching to notip
<---------------- 何も出ない
我曾想过将其发送到/dev/stdout,但那个是符号链接到/proc/self/fd/1的。
进一步跟踪到/dev/pts/0,也就是与登录的bash进程的tty相关联的。
$ ll /dev/stdout
lrwxrwxrwx 1 root root 15 Jul 3 09:39 /dev/stdout -> /proc/self/fd/1
$ ll /proc/self/fd/1
lrwx------ 1 root root 64 Jul 3 10:31 /proc/self/fd/1 -> /dev/pts/0
$ ll /dev/pts/0
crw--w---- 1 root tty 136, 0 Jul 3 10:31 /dev/pts/0 ( ← キャラクタデバイス)
$ tty
/dev/pts/0 <----- 一緒だね
那么,docker log接收什么样的输入呢?看起来,docker log似乎接收来自PID=1的stdout/err的输入。
在内部,在pid=1的文件描述符中直接输出
docker exec -it notip /bin/bash
echo 1 > /proc/1/fd/1
$ docker-compose logs -f
Attaching to notip
notip| 1 <--------------出た!
换言之,如果将在容器内执行的命令结果流向 /proc/1/fd/1,那么它将被视为 Docker 日志。
我在 Docker 的源代码的哪里可以找到这个信息呢?有人知道吗?
我找不到正式的文件,但这里有一些提示。
官方的httpd驱动程序会改变httpd应用程序的配置,使其将正常输出直接写入/proc/self/fd/1(即STDOUT),将错误输出写入/proc/self/fd/2(即STDERR)。请查看Dockerfile。
https://docs.docker.com/config/containers/logging/
Apache的映像正在将日志输出到/proc/self/fd/1。因为httpd进程的PID为1,所以其他进程(PID)只需将其发送到/proc/1/fd/1即可。明白了。
使用 Python 记录器将输出写入 Docker 日志。
ここでは python の実行結果を /proc/1/fd/1 に流してみます。肝は、出力を docker log だけに出すと コマンド叩いた人に結果が何も見えなくなっちゃう ので、 stdout && docker log 両方に出すことです。
感谢神的报道。
ファイルへのログ出力、Logger生成
用Python编写一个日志记录器。
python の logging モジュールには、ハンドラという概念があります
ハンドラは「出力先」として理解しています。fileだったり、画面(console)だったり、データベースだったり。
fluentdみたいに、loggerに入れたものを様々な場所に出力することができます。
普通は console に表示するだけですが、ハンドラを複数作ると 画面+ファイル とか 画面+データベース に同時に送ることができます。
这次的目标是 ↓。
-
- handler1 = stdout
コマンドを叩いた人に結果が見えるように
handler2 = /proc/1/fd/1
docker logにも出力(これを監査/debug用に記録)
import logging
logger = logging.getLogger("logger") #logger名loggerを取得
logger.setLevel(logging.DEBUG) #loggerとしてはDEBUGで
#handler1を作成
handler1 = logging.StreamHandler()
handler1.setFormatter(logging.Formatter("%(asctime)s %(threadName)s %(levelname)8s %(message)s"))
#handler2を作成
handler2 = logging.FileHandler(filename="/proc/1/fd/1") #handler2はdocker logが使うPID=1のstdoutファイルへ出力
handler2.setFormatter(logging.Formatter("%(asctime)s %(threadName)s %(levelname)8s %(message)s"))
#loggerに2つのハンドラを設定
logger.addHandler(handler1)
logger.addHandler(handler2)
#出力処理
logger.debug("debug message")
logger.info("info message")
logger.warning("warn message")
logger.error("error message")
thread使うため、 formatterに threadName を追加しています
どちらも DEBUG levelで出力
在执行之前,先使用`tail`命令查看Docker日志。
docker-compose logs -f
Attaching to notip
我将尝试运行logger。
$ docker exec notip python logger.py
2021-07-03 10:23:36,738 MainThread DEBUG debug message
2021-07-03 10:23:36,738 MainThread INFO info message
2021-07-03 10:23:36,738 MainThread WARNING warn message
2021-07-03 10:23:36,738 MainThread ERROR error message
结果已经在stdout中输出。
在docker日志中也输出了相同的内容!目标达成!
docker-compose logs -f
Attaching to notip
notip | 2021-07-03 10:23:36,738 MainThread DEBUG debug message
notip | 2021-07-03 10:23:36,738 MainThread INFO info message
notip | 2021-07-03 10:23:36,738 MainThread WARNING warn message
notip | 2021-07-03 10:23:36,738 MainThread ERROR error message
/proc的注意事项
副産物として、 linux の host OS から直接 /proc/1/fd/1 を叩くと、permission denied になるようです. systemdのfdはさわれないのか。
[vagrant@localhost ~]$ sudo echo 1 > /proc/1/fd/1
-bash: /proc/1/fd/1: Permission denied
看来这样也能防止一些操作失误。Docker容器已经调整了这个方面的内容。增长知识。
顺便提一句,如果你从除了 Linux 以外的操作系统直接操作,由于不存在 /proc 目录,会导致失败。
mac$ echo 1 > /proc/1/fd/1
-bash: /proc/1/fd/1: No such file or directory
这个有点不方便进行开发,所以可以考虑创建一个符号链接来解决。也可以在Python方面切换文件描述符。
$ sudo mkdir -p /proc/1/fd/
$ sudo ln -s $(tty) /proc/1/fd/1
$ echo 1 > /proc/1/fd/1
1
将数据发送到Elasticsearch的方式。
从这里开始是额外的内容。
首先解释将Docker日志发送到Elasticsearch的设置(通过Fluentd)
记录:Docker容器
使用Docker日志驱动程序,将Docker日志发送到Elasticsearch。
version: '2'
services:
web:
restart: always
build:
context: .
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: "docker.logging_driver.production"
记录: Fluentd
我使用此处的fluentd部分来建立容器。
https://github.com/myoshimi/es-docker-logging/blob/master/docker-compose.yml 的中文翻译如下:
version: '3.7'
services:
fluentd:
build: ./fluentd
restart: always
command: >
/usr/bin/fluentd -c /fluentd/etc/fluent.conf -v
ports:
- "24224:24224"
- "24224:24224/udp"
volumes:
- ${PWD}/log:/fluentd/log
- ${PWD}/fluent.conf:/fluentd/etc/fluent.conf:ro
使用fluent.conf将日志转发到elasticsearch.
<source>
@type forward
@id input1
@label @mainstream
port 24224
</source>
<filter **>
@type stdout
</filter>
<label @mainstream>
<match docker.**>
@type copy
<store>
@type file
@id output_docker1
path /fluentd/log/docker.*.log
symlink_path /fluentd/log/docker.log
append true
time_slice_format %Y%m%d
time_slice_wait 1m
time_format %Y%m%dT%H%M%S%z
</store>
<store>
@type elasticsearch
hosts https://<user>:<pw>@<host>:12000 # <------ ES 接続情報
logstash_format true
logstash_prefix application_name # <-- ES index 名になる. kibanaは application_name.* で表示すればok
</store>
</match>
<match **>
@type file
@id output1
path /fluentd/log/data.*.log
symlink_path /fluentd/log/data.log
append true
time_slice_format %Y%m%d
time_slice_wait 10m
time_format %Y%m%dT%H%M%S%z
</match>
</label>
现在,已经实现了将在容器内执行的python命令的结果自动保存到elasticsearch中。
通过使用docker日志,它还会方便地保存容器名称等信息!