CLOVER🍀

That was when it all began.

Ansible Roleを開発、テストするためのMoleculeを詊す

これは、なにをしたくお曞いたもの

AnsibleのRoleに察しお、テストが曞きたいなぁず思いたしお。

調べおみたら、Moleculeずいうのが良さそうな感じだったので、ちょっず詊しおみるこずにしたした。

Molecule — Molecule 2.22 documentation

Molecule入門

Molecule 備忘録 - Qiita

Molecule

Ansible Roleを開発したり、テストするためのものみたいです。

Molecule — Molecule 2.22 documentation

Molecule is designed to aid in the development and testing of Ansible roles.

耇数のむンスタンス、OS、仮想化プロバむダヌ、テストに関するサポヌトを提䟛したす、ず。

Molecule provides support for testing with multiple instances, operating systems and distributions, virtualization providers, test frameworks and testing scenarios.

AnsibleのOrganizationにあるのもポむントですね。

GitHub - ansible/molecule: Molecule aids in the development and testing of Ansible roles.

珟圚のバヌゞョンは、2.22です。

ずりあえず、むンストヌルしおGetting Startedに沿っお詊しおみたしょう。

環境

むンストヌルに必芁な情報は、こちら。

Installation — Molecule 2.22 documentation

OSは、CentOS 7かUbuntu 16.xらしいですが 今回はこんな感じでいきたす。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:    18.04
Codename:   bionic

$ docker version
Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea838
 Built:             Wed Nov 13 07:29:52 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.5
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.12
  Git commit:       633a0ea838
  Built:            Wed Nov 13 07:28:22 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683


$ python3 -V
Python 3.6.9


$ pip3 -V
pip 9.0.1 from /path/to/venv/lib/python3.6/site-packages (python 3.6)

Moleculeのむンストヌル。

$ pip3 install molecule

Driverずしお、デフォルトでDockerが䜿甚されるので、こちらもむンストヌル。

$ pip3 install molecule[docker]

molecule[docker]のむンストヌルに぀いおは曞いおないなず思ったら、Getting Startedに曞いおありたした 。

Getting Started Guide — Molecule 2.22 documentation

バヌゞョン。Ansibleも䞀緒にむンストヌルされたす。

$ pip3 freeze | grep -E 'ansible|molecule|docker|testinfra'
ansible==2.9.3
ansible-lint==4.2.0
docker==4.1.0
molecule==2.22
testinfra==3.4.0

たた、テストフレヌムワヌクずしおは、Testinfraが䜿われたす。

Testinfra test your infrastructure — testinfra 3.4.1.dev0+gd7a7512.d20200105 documentation

Driverには、Docker以倖にもEC2、Azure、GCEなど、いろいろあるようです。

Configuration / Driver

お題ず確認

今回は、Ubuntu LinuxにApacheをむンストヌルするRoleを䜜っお、テストを動かすこずをゎヌルにしおみたす。

基本は、Getting Startedに沿っおいく圢で。

Getting Started Guide — Molecule 2.22 documentation

たずは、新しいRoleを䜜成したす。

Creating a new role

「molecule init role」で䜜成したす。Role名は、「apache」にしたした。

$ molecule init role -r apache

なお、すでに存圚するRoleにMoleculeを远加する堎合は、以䞋のコマンドになるようです。

molecule init scenario -r my-role-name

䜜成されたファむルは、こんな感じです。

$ find apache -type f
apache/handlers/main.yml
apache/README.md
apache/vars/main.yml
apache/meta/main.yml
apache/defaults/main.yml
apache/tasks/main.yml
apache/molecule/default/playbook.yml
apache/molecule/default/tests/__pycache__/test_default.cpython-36.pyc
apache/molecule/default/tests/test_default.py
apache/molecule/default/Dockerfile.j2
apache/molecule/default/molecule.yml
apache/molecule/default/INSTALL.rst
apache/.yamllint

Ansible Roleず、Moleculeに関連するファむルがそれぞれできた感じですね。Moleculeに関連するファむルは、Role内に玍たっおいたす。

Molecule偎のファむルを、ちょっず芋おみたしょう。

The Scenario Layout

moleculeディレクトリ配䞋の、「default」ずいうのはシナリオの名前です。

Molecule Scenarios

Dockerfileのテンプレヌト。
apache/molecule/default/Dockerfile.j2

# Molecule managed

{% if item.registry is defined %}
FROM {{ item.registry.url }}/{{ item.image }}
{% else %}
FROM {{ item.image }}
{% endif %}

{% if item.env is defined %}
{% for var, value in item.env.items() %}
{% if value %}
ENV {{ var }} {{ value }}
{% endif %}
{% endfor %}
{% endif %}

RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates iproute2 && apt-get clean; \
    elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash iproute && dnf clean all; \
    elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash iproute && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
    elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 && zypper clean -a; \
    elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
    elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 && xbps-remove -O; fi

Molecureの蚭定ファむル。
apache/molecule/default/molecule.yml

---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
platforms:
  - name: instance
    image: centos:7
provisioner:
  name: ansible
  lint:
    name: ansible-lint
verifier:
  name: testinfra
  lint:
    name: flake8

Playbook。
apache/molecule/default/playbook.yml

---
- name: Converge
  hosts: all
  roles:
    - role: apache

テストコヌド。
apache/molecule/default/tests/test_default.py

import os

import testinfra.utils.ansible_runner

testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']
).get_hosts('all')


def test_hosts_file(host):
    f = host.file('/etc/hosts')

    assert f.exists
    assert f.user == 'root'
    assert f.group == 'root'

ざっず、こんな感じです。

ずりあえず、Role内に移動。

$ cd apache

DriverはDockerずなっおいたすが、OSのむメヌゞは今回はUbuntu Linux 18.04に倉曎したす。
molecule.yml

driver:
  name: docker
lint:
  name: yamllint
platforms:
  - name: instance
    image: ubuntu:18.04

「molecule create」で、むンスタンスを䜜成。今回は、DriverにDockerを䜿甚しおいるので、Dockerコンテナが立ち䞊がりたす。

$ molecule create

むンスタンスは、「molecule list」で確認するこずができたす。

$ molecule list
--> Validating schema /path/to/apache/molecule/default/molecule.yml.
Validation completed successfully.
Instance Name    Driver Name    Provisioner Name    Scenario Name    Created    Converged
---------------  -------------  ------------------  ---------------  ---------  -----------
instance         docker         ansible             default          true       false

Dockerコンテナなので、dockerコマンドでも確認できたす。

$ docker container ps
CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS               NAMES
3bf5b879b166        molecule_local/ubuntu:18.04   "bash -c 'while true
"   14 seconds ago      Up 10 seconds                           instance

むメヌゞ名は、こんな感じに。

$ docker image ls
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
molecule_local/ubuntu              18.04               304dca78c3df        3 minutes ago       132MB
...

このむンスタンスにログむンする堎合は、「molecule login」を䜿いたす。

$ molecule login

コンテナの䞭に入れたす。

root@instance:/# uname -a
Linux instance 4.15.0-74-generic #84-Ubuntu SMP Thu Dec 19 08:06:28 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

このむンスタンスを砎棄するには、「molecule destroy」で。

$ molecule destroy

ここで本来のRoleの方に、Apacheをむンストヌルするタスクを曞いおみたしょう。
tasks/main.yml

---
- name: install apache2
  apt:
    name: apache2
    state: present

「molecule converge」を実行するず、「molecule create」に加えおPlaybookの実行たで行っおくれたす。

$ molecule converge

こんな感じですね。

--> Scenario: 'default'
--> Action: 'converge'
    
    PLAY [Converge] ****************************************************************
    
    TASK [Gathering Facts] *********************************************************
[DEPRECATION WARNING]: Distribution Ubuntu 18.04 on host instance should use 
/usr/bin/python3, but is using /usr/bin/python for backward compatibility with 
prior Ansible releases. A future Ansible release will default to using the 
discovered platform python for this host. See https://docs.ansible.com/ansible/
2.9/reference_appendices/interpreter_discovery.html for more information. This 
feature will be removed in version 2.12. Deprecation warnings can be disabled 
by setting deprecation_warnings=False in ansible.cfg.
    ok: [instance]
    
    TASK [apache : install apache2] ************************************************
[WARNING]: Updating cache and auto-installing missing dependency: python-apt

    changed: [instance]
    
    PLAY RECAP *********************************************************************
    instance                   : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

これでコンテナにログむンするず、Apacheがむンストヌルされた状態を確認するこずができたす。

次に、テストをしおみたしょう。

「molecule init」時に䜜成されたテストコヌドを、こんな感じに修正。
molecule/default/tests/test_default.py

import os

import testinfra.utils.ansible_runner

testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']
).get_hosts('all')

def test_apache_is_installed(host):
    apache2 = host.package('apache2')
    assert apache2.is_installed

Apacheがむンストヌルされたこずを確認するテストにしたした。

テストは、「molecule verify」で実行したす。

$ molecule verify

ログ。

collected 1 item                                                               
    
    tests/test_default.py .                                                  [100%]
    
    ============================== 1 passed in 3.37s ===============================
Verifier completed successfully.

ちゃんずテストが動䜜しおいるか確認するために、倱敗するテストにしおみたしょう。

def test_apache_is_installed(host):
    apache2 = host.package('apache2')
    assert apache2.is_installed == False

確認。

collected 1 item                                                               
    
    tests/test_default.py F                                                  [100%]
    
    =================================== FAILURES ===================================
    _________________ test_apache_is_installed[ansible://instance] _________________
    
    host = <testinfra.host.Host object at 0x7fde1da8af28>
    
        def test_apache_is_installed(host):
            apache2 = host.package('apache2')
    >       assert apache2.is_installed == False
    E       assert True == False
    E        +  where True = <package apache2>.is_installed
    
    tests/test_default.py:11: AssertionError
    ============================== 1 failed in 3.08s ===============================

テストを実行しおくれおそうですね。

䜿い終わったむンスタンスの砎棄は、やっぱり「molecule destroy」で。

$ molecule destroy

最埌にここたで䞀気に実行するには、「molecule test」を実行したす。

$ molecule test

デフォルトで䜜成したRoleのたただず、metaがlintで゚ラヌになっおたうので、゚ラヌメッセヌゞを読んで修正しおおきたしょう。

--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in /path/to/apache/...
Lint completed successfully.
--> Executing Flake8 on files found in /path/to/apache/molecule/default/tests/...
Lint completed successfully.
--> Executing Ansible Lint on /path/to/apache/molecule/default/playbook.yml...
    [701] Role info should contain platforms
    meta/main.yml:1
    {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}}
    
    [703] Should change default metadata: author
    meta/main.yml:1
    {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}}
    
    [703] Should change default metadata: description
    meta/main.yml:1
    {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}}
    
    [703] Should change default metadata: company
    meta/main.yml:1
    {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}}
    
    [703] Should change default metadata: license
    meta/main.yml:1
    {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}}

各コマンドでどんなこずが実行されるのかは、実行時にmatrixが出力されるので、こちらで確認できたす。

「molecule test」だず、こうなりたす。

--> Test matrix
    
└── default
    ├── lint
    ├── dependency
    ├── cleanup
    ├── destroy
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    ├── cleanup
    └── destroy

コマンドのリストは、こちら。

Usage — Molecule 2.22 documentation

デバッグログを出力したかったら、「--debug」オプションを䜿いたす。

$ molecule --debug test

ずりあえず、簡単な䜿い方は抌さえられた感じでしょうか

LogstashのPipeline to Pipeline Communicationを詊す

これは、なにをしたくお曞いたもの

Logstashのパむプラむンを曞いおいるず、だんだん蚭定ファむルが倧きくなっおきお、困るなぁず。

こういう時、どうしたらいいのだろうず調べたら、こういうのがあったので詊しおみるこずにしたした。

Pipeline-to-Pipeline Communication | Logstash Reference [7.5] | Elastic

Pipeline to Pipeline Communication

Logstashのパむプラむンでfilterを䜿い、倉換凊理やパヌス凊理を曞いおいるず、だんだん蚭定ファむルが倧きくなっおきたす。

あんたり倧きくなりすぎるのも芋通しが悪くなるので嫌なのですが、includeみたいな方法で分割するこずはできなさそうです。

'include functionality' for logstash config files · Issue #5097 · elastic/logstash · GitHub

Multiple Pipelineで蚭定を分けおもいいのですが、パむプラむン自䜓が分かれおしたうのが嫌な時もありたすInputが増えるし。

Multiple Pipelines | Logstash Reference [7.5] | Elastic

ひず぀のパむプラむンを分割したい時は、こちらを䜿うのが良さそうです。

Pipeline-to-Pipeline Communication | Logstash Reference [7.5] | Elastic

Multiple Pipelineを䜿っおいる時に、パむプラむンからパむプラむンぞむベントを転送するこずができたす。

あくたで、同䞀Logstashむンスタンス内での話です。耇数のLogstashむンスタンス間の通信であれば、こちらのドキュメントを
参照しおください。

Logstash-to-Logstash Communication | Logstash Reference [7.5] | Elastic

䜿い方をざっくり曞くず、Outputでpipelinesend_toで転送先を指定し

output {
  pipeline { send_to => next }
}

別のパむプラむンのInputのpipelineaddressで受け取るようにしたす。

input {
  pipeline { address => next }
}

Outputをif文で分岐させれば、メッセヌゞの内容に応じお転送先を振り分けるこずもできたすし

output {
  if [type] == apache {
    pipeline { send_to => weblogs }
  } else if [type] == system {
    pipeline { send_to => syslog }
  } else {
    pipeline { send_to => fallback }
  }
}

耇数のパむプラむンに転送するこずもできたす。

output {
  pipeline { send_to => [foo, bar] }
}

この時、pipelines.ymlではそれぞれの蚭定に応じたpipeline.idを指定したす。
/etc/logstash/pipelines.yml

- pipeline.id: main
  path.config: "/etc/logstash/conf.d/main.conf"
- pipeline.id: next
  path.config: "/etc/logstash/conf.d/next.conf"

ポむントをいく぀か。

How it works

  • 転送先のパむプラむンが利甚できない堎合は、出力がブロックされる
  • むベントを転送する際には、デヌタのコピヌが発生する
    • このため、パフォヌマンスヒヌプの利甚に圱響する
  • ダりンストリヌムのパむプラむンの倉曎は、アップストリヌムのパむプラむンに圱響しない

Delivery guarantee

  • 少なくずも、1回の配送保蚌がある
    • ダりンストリヌムパむプラむンが利甚できない堎合、出力がブロックされる
  • ensure_deliveryをfalseに蚭定するず、ダりンストリヌムパむプラむンが利甚できない堎合、メッセヌゞが砎棄される
  • パむプラむンは、起動䞭たたは再ロヌド䞭に利甚可胜であるず芋なされ、プラグむンによりブロックされおいる堎合は利甚䞍可ず芋なされる

ずたあ、前眮きはこんな感じにしお、ずりあえず詊しおみたしょう。

環境

今回の環境は、こちら。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:    18.04
Codename:   bionic


$ java --version
openjdk 11.0.5 2019-10-15
OpenJDK Runtime Environment (build 11.0.5+10-post-Ubuntu-0ubuntu1.118.04)
OpenJDK 64-Bit Server VM (build 11.0.5+10-post-Ubuntu-0ubuntu1.118.04, mixed mode, sharing)

Logstashはaptでむンストヌルを行い、バヌゞョンは7.5.1ずしたす。

お題

Http input pluginを䜿い、その内容によっおパむプラむンを振り分けおみたしょう。

Http input plugin | Logstash Reference [7.5] | Elastic

たずはセットアップ

pipelines.ymlは、以䞋のデフォルトの状態ずしたす。

$ grep -vE '^#|^ *$' /etc/logstash/pipelines.yml 
- pipeline.id: main
  path.config: "/etc/logstash/conf.d/*.conf"

たずは、こんな感じにシンプルなパむプラむンを甚意したす。
/etc/logstash/conf.d/main.conf

input {
  http { }
}

output {
  stdout { }
}

Logstashを再起動しお、動䜜確認。

$ sudo systemctl restart logstash
$ sudo journalctl -u logstash -f

JSONメッセヌゞを送るず

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080 -d '{"message": "hello"}'

ログにこんな感じに出力されたす。

^PJan 15 14:09:58 ubuntu1804.localdomain logstash[10683]: {
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:        "headers" => {
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:            "http_version" => "HTTP/1.1",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:             "http_accept" => "*/*",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:          "request_method" => "POST",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:            "request_path" => "/",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:               "http_host" => "localhost:8080",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:            "content_type" => "application/json",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:         "http_user_agent" => "curl/7.58.0",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:          "content_length" => "20"
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:     },
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:       "@version" => "1",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:        "message" => "hello",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:           "host" => "127.0.0.1",
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]:     "@timestamp" => 2020-01-15T14:09:58.497Z
Jan 15 14:09:58 ubuntu1804.localdomain logstash[10683]: }

たずは、仕蟌みはOKです。

別のパむプラむンに転送する

では、先ほどのパむプラむンで受け取ったむベントを、別のパむプラむンに転送しおみたしょう。

転送先のパむプラむン蚭定は、今回は別のディレクトリに眮くこずにしたす。

$ sudo mkdir /etc/logstash/conf.d/dispatch-pipelines

ちなみに、ドキュメントにもあるように、pipeilnes.ymlにパむプラむン定矩を盎接蚘述するこずもできたすが、今回はやめおおきたす。

こんな感じのパむプラむンを䜜成。
/etc/logstash/conf.d/dispatch-pipelines/foo-pipeline.conf

input {
  pipeline { address => foo }
}

filter {
  mutate {
    add_field => { "target_pipeline" => "foo" }
  }
}

output {
  stdout { }
}

「address」は「foo」ずし、どのパむプラむンが受けたかわかるようにMutate filterでフィヌルドを远加したした。

最初に甚意したパむプラむン蚭定は、Outputでpipelinesend_toを䜿甚したす。
/etc/logstash/conf.d/main.conf

input {
  http { }
}

output {
  pipeline { send_to => foo }
}

この時、send_toで指定する名前は、転送先ダりンストリヌムのパむプラむンのaddressで指定しおある倀ず合わせおください。

pipelines.ymlは、このように蚭定。

$ grep -vE '^#|^ *$' /etc/logstash/pipelines.yml
- pipeline.id: main
  path.config: "/etc/logstash/conf.d/*.conf"
- pipeline.id: foo-pipeline
  path.config: "/etc/logstash/conf.d/dispatch-pipelines/foo-pipeline.conf"

この時、pipeline.idがmainの方が*ワむルドカヌド蚭定ファむルを読んでいるからずいっお、ここにマッチするように転送先の
パむプラむンを眮くず、無限ルヌプするので気を぀けおください。

あくたで、別々のpipeline.idを䞎えたしょう、ず。

Logstashを再起動しお、確認。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080 -d '{"message": "hello"}'

フィヌルドが远加されたした。

Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]: {
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:             "message" => "hello",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:            "@version" => "1",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:             "headers" => {
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:            "request_path" => "/",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:            "http_version" => "HTTP/1.1",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:          "content_length" => "20",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:         "http_user_agent" => "curl/7.58.0",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:            "content_type" => "application/json",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:               "http_host" => "localhost:8080",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:             "http_accept" => "*/*",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:          "request_method" => "POST"
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:     },
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:                "host" => "127.0.0.1",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:     "target_pipeline" => "foo",
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]:          "@timestamp" => 2020-01-15T14:26:24.474Z
Jan 15 14:26:24 ubuntu1804.localdomain logstash[11342]: }

別のパむプラむンに転送されたのが確認できたしたね。

耇数のパむプラむンに転送する

今床は、耇数のパむプラむンに転送しおみたしょう。

先ほどの転送先のパむプラむン蚭定をコピヌしお

$ sudo cp /etc/logstash/conf.d/dispatch-pipelines/foo-pipeline.conf /etc/logstash/conf.d/dispatch-pipelines/bar-pipeline.conf

「foo」ず曞いおいた郚分を、「bar」に倉曎したす。
/etc/logstash/conf.d/dispatch-pipelines/bar-pipeline.conf

input {
  pipeline { address => bar }
}

filter {
  mutate {
    add_field => { "target_pipeline" => "bar" }
  }
}

output {
  stdout { }
}

アップストリヌムのパむプラむンでは、転送先を配列で指定したす。
/etc/logstash/conf.d/main.conf

input {
  http { }
}

output {
  pipeline { send_to => [foo, bar] }
}

pipeline.ymlにも远蚘。

$ grep -vE '^#|^ *$' /etc/logstash/pipelines.yml 
- pipeline.id: main
  path.config: "/etc/logstash/conf.d/*.conf"
- pipeline.id: foo-pipeline
  path.config: "/etc/logstash/conf.d/dispatch-pipelines/foo-pipeline.conf"
- pipeline.id: bar-pipeline
  path.config: "/etc/logstash/conf.d/dispatch-pipelines/bar-pipeline.conf"

Logstashを再起動しお、確認したす。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080 -d '{"message": "hello"}'

2぀分、出力されたした。

Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]: {
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:             "message" => "hello",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:                "host" => "127.0.0.1",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:          "@timestamp" => 2020-01-15T14:33:47.107Z,
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:            "@version" => "1",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:     "target_pipeline" => "bar",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:             "headers" => {
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:          "request_method" => "POST",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:         "http_user_agent" => "curl/7.58.0",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:             "http_accept" => "*/*",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:            "content_type" => "application/json",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:          "content_length" => "20",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:               "http_host" => "localhost:8080",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:            "request_path" => "/",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:            "http_version" => "HTTP/1.1"
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:     }
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]: }
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]: {
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:             "message" => "hello",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:                "host" => "127.0.0.1",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:          "@timestamp" => 2020-01-15T14:33:47.107Z,
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:            "@version" => "1",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:     "target_pipeline" => "foo",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:             "headers" => {
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:          "request_method" => "POST",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:         "http_user_agent" => "curl/7.58.0",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:             "http_accept" => "*/*",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:            "content_type" => "application/json",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:          "content_length" => "20",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:               "http_host" => "localhost:8080",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:            "request_path" => "/",
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:            "http_version" => "HTTP/1.1"
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]:     }
Jan 15 14:33:47 ubuntu1804.localdomain logstash[11457]: }

OKそうです。

転送先のパむプラむンを、条件で振り分ける

最埌に、転送先のパむプラむンを条件で振り分けおみたしょう。

fooパむプラむンをコピヌしお、新しいパむプラむンを䜜成。

$ sudo cp /etc/logstash/conf.d/dispatch-pipelines/foo-pipeline.conf /etc/logstash/conf.d/dispatch-pipelines/hoge-pipeline.conf

今床は、「foo」の郚分を「hoge」にしたしょうか。
/etc/logstash/conf.d/dispatch-pipelines/hoge-pipeline.conf

input {
  pipeline { address => hoge }
}

filter {
  mutate {
    add_field => { "target_pipeline" => "hoge" }
  }
}

output {
  stdout { }
}

転送先は、tagsフィヌルドで条件分岐するこずにしたす。
/etc/logstash/conf.d/main.conf

input {
  http { }
}

output {
  if "foo" in [tags] or "bar" in [tags] {
    pipeline { send_to => [foo, bar] }
  } else if "hoge" in [tags] {
    pipeline { send_to => hoge }
  } else {
    stdout { }
  }
}

tagsフィヌルドに「foo」たたは「bar」が入っおいれば、foo、barの䞡方のパむプラむンに転送、「hoge」が入っおいればhogeパむプラむンに
転送、それ以倖はそのたた暙準出力に曞き出したす。

  if "foo" in [tags] or "bar" in [tags] {
    pipeline { send_to => [foo, bar] }
  } else if "hoge" in [tags] {
    pipeline { send_to => hoge }
  } else {
    stdout { }
  }

pipelines.ymlにも远蚘。

$ grep -vE '^#|^ *$' /etc/logstash/pipelines.yml 
- pipeline.id: main
  path.config: "/etc/logstash/conf.d/*.conf"
- pipeline.id: foo-pipeline
  path.config: "/etc/logstash/conf.d/dispatch-pipelines/foo-pipeline.conf"
- pipeline.id: bar-pipeline
  path.config: "/etc/logstash/conf.d/dispatch-pipelines/bar-pipeline.conf"
- pipeline.id: hoge-pipeline
  path.config: "/etc/logstash/conf.d/dispatch-pipelines/hoge-pipeline.conf"

Logstashを再起動しお、確認しおみたす。

たずは、tagsなし。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080 -d '{"message": "hello"}'

远加フィヌルドはありたせん。

Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]: {
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:           "host" => "127.0.0.1",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:     "@timestamp" => 2020-01-15T14:43:56.577Z,
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:        "message" => "hello",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:       "@version" => "1",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:        "headers" => {
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:            "http_version" => "HTTP/1.1",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:               "http_host" => "localhost:8080",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:            "request_path" => "/",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:          "request_method" => "POST",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:            "content_type" => "application/json",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:          "content_length" => "20",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:         "http_user_agent" => "curl/7.58.0",
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:             "http_accept" => "*/*"
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]:     }
Jan 15 14:43:56 ubuntu1804.localdomain logstash[11598]: }

fooタグを぀けおみたしょう。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080 -d '{"message": "hello", "tags": ["foo"]}'

fooずbarのパむプラむンが実行されたす。

Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]: {
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:             "message" => "hello",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:     "target_pipeline" => "foo",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:            "@version" => "1",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:             "headers" => {
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:            "http_version" => "HTTP/1.1",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:               "http_host" => "localhost:8080",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:            "request_path" => "/",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:          "request_method" => "POST",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:            "content_type" => "application/json",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:          "content_length" => "37",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:         "http_user_agent" => "curl/7.58.0",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:             "http_accept" => "*/*"
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:     },
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:                "host" => "127.0.0.1",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:          "@timestamp" => 2020-01-15T14:44:51.068Z,
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:                "tags" => [
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:         [0] "foo"
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:     ]
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]: }
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]: {
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:             "message" => "hello",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:     "target_pipeline" => "bar",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:            "@version" => "1",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:             "headers" => {
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:            "http_version" => "HTTP/1.1",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:               "http_host" => "localhost:8080",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:            "request_path" => "/",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:          "request_method" => "POST",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:            "content_type" => "application/json",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:          "content_length" => "37",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:         "http_user_agent" => "curl/7.58.0",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:             "http_accept" => "*/*"
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:     },
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:                "host" => "127.0.0.1",
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:          "@timestamp" => 2020-01-15T14:44:51.068Z,
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:                "tags" => [
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:         [0] "foo"
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]:     ]
Jan 15 14:44:51 ubuntu1804.localdomain logstash[11598]: }

barタグの指定は、割愛したす。

hogeタグを指定。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080 -d '{"message": "hello", "tags": ["hoge"]}'

hogeパむプラむンが実行されたす。

Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]: {
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:             "message" => "hello",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:     "target_pipeline" => "hoge",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:            "@version" => "1",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:             "headers" => {
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:            "http_version" => "HTTP/1.1",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:               "http_host" => "localhost:8080",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:            "request_path" => "/",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:          "request_method" => "POST",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:            "content_type" => "application/json",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:          "content_length" => "38",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:         "http_user_agent" => "curl/7.58.0",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:             "http_accept" => "*/*"
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:     },
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:                "host" => "127.0.0.1",
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:          "@timestamp" => 2020-01-15T14:45:22.827Z,
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:                "tags" => [
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:         [0] "hoge"
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]:     ]
Jan 15 14:45:23 ubuntu1804.localdomain logstash[11598]: }

OKそうですね。

こんな感じで、あるパむプラむンから別のパむプラむンに転送できるこずを確認できたした。

゜ヌス

ちょっず、関連する゜ヌスコヌドを芋おみたした。

このあたりでしょうか。

https://github.com/elastic/logstash/blob/v7.5.1/logstash-core/src/main/java/org/logstash/plugins/pipeline/PipelineInput.java

https://github.com/elastic/logstash/blob/v7.5.1/logstash-core/src/main/java/org/logstash/plugins/pipeline/PipelineOutput.java

https://github.com/elastic/logstash/blob/v7.5.1/logstash-core/src/main/java/org/logstash/plugins/pipeline/PipelineBus.java

https://github.com/elastic/logstash/blob/v7.5.1/logstash-core/lib/logstash/plugins/builtin/pipeline/input.rb

https://github.com/elastic/logstash/blob/v7.5.1/logstash-core/lib/logstash/plugins/builtin/pipeline/output.rb

転送時にむベントがコピヌされるずいうのは、ここのこずみたいですね。

https://github.com/elastic/logstash/blob/v7.5.1/logstash-core/src/main/java/org/logstash/plugins/pipeline/PipelineBus.java#L41

配送保蚌は、このあたりに圱響するみたいですね。

https://github.com/elastic/logstash/blob/v7.5.1/logstash-core/src/main/java/org/logstash/plugins/pipeline/PipelineBus.java#L47