将在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日志,它还会方便地保存容器名称等信息!

广告
将在 10 秒后关闭
bannerAds