0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenShift上の Pod のログを rsyslog に転送する

Posted at

はじめに

OpenShift上の Pod は、再作成されると Pod のログまで消失してしまいます。これでは運用に堪えないので、ログを外部に転送することを検討することにしました。

OpenShift上にLokiStackを構築してもいいのですが、検証環境は非力なスペックなので、syslogサーバーに転送することにしました。

検証環境は下記記事で作ったものです。

システム構成

  • ClusterLogForwarder
    • OpenShift では、ClusterLogForwarder リソースを使用してログを転送します
    • openshift-logging という namespace に作成します
  • rsyslog
    • 今回は bastionサーバーに構築します
    • /var/log/openshift/<namespace name>/<pod name>_<container name>.log という命名規則でログを出力することを目指します

構築手順

下記、公式手順を参考にしながら進めていきます。

今回は、ログをクラスタ外のみ転送し、クラスタの内部ログストアに格納しないため、内部ログストアとなる LokiStack は不要なので、 Loki Operator のインストール、 LokiStack の作成は不要です。

OpenShift側の設定

  1. openshift-logging namespace の作成
    oc create namespace openshift-logging
    
  2. OperatorGroup の作成
    01_OperatorGroup_openshift-logging.yaml
    apiVersion: operators.coreos.com/v1
    kind: OperatorGroup
    metadata:
      name: cluster-logging
      namespace: openshift-logging
    spec:
      upgradeStrategy: Default
    
    oc apply -f 01_OperatorGroup_openshift-logging.yaml
    
  3. Subscription の作成
    02_subscription_cluster-logging.yaml
    apiVersion: operators.coreos.com/v1alpha1
    kind: Subscription
    metadata:
      name: cluster-logging
      namespace: openshift-logging
    spec:
      channel: stable-6.2
      installPlanApproval: Automatic
      name: cluster-logging
      source: redhat-operators
      sourceNamespace: openshift-marketplace
    
    oc apply -f 02_subscription_cluster-logging.yaml
    
    なお、 channel の候補は以下で確認できます。
    実行例
    $ oc get packagemanifests cluster-logging -n openshift-marketplace -o jsonpath='{.status.channels[*].name}'
    stable-6.1 stable-6.2
    
  4. インストール状況の確認
    以下を確認します。
    # Operatorのインストール状況確認
    oc get csv -n openshift-logging
    
    # Podの状態確認
    oc get pods -n openshift-logging
    
    # CRD(Custom Resource Definition)の確認
    oc get crd | grep logging
    
    実行例
    $ oc get csv -n openshift-logging
    NAME                                    DISPLAY                     VERSION               REPLACES                 PHASE
    cluster-logging.v6.2.2                  Red Hat OpenShift Logging   6.2.2                 cluster-logging.v6.2.1   Succeeded
    metallb-operator.v4.18.0-202505081435   MetalLB Operator            4.18.0-202505081435                            Succeeded
    openstack-operator.v0.3.0               OpenStack                   0.3.0                                          Succeeded
    $ oc get pods -n openshift-logging
    NAME                                       READY   STATUS    RESTARTS   AGE
    cluster-logging-operator-b5d867f4c-mbcss   1/1     Running   0          2m17s
    $ oc get crd | grep logging
    logfilemetricexporters.logging.openshift.io                       2025-05-31T17:07:39Z
    loggings.telemetry.openstack.org                                  2025-05-31T16:06:09Z
    
  5. Servcice Accountの作成
    ログコレクターが使用するサービスアカウントを作成し、コレクターがログを収集して転送できるように、サービスアカウントに必要な権限を割り当てます。ここでは、コレクターにインフラストラクチャーログとアプリケーションログの両方を収集する権限を付与します。
    oc create sa logging-collector -n openshift-logging
    oc adm policy add-cluster-role-to-user logging-collector-logs-writer -z logging-collector -n openshift-logging
    oc adm policy add-cluster-role-to-user collect-application-logs -z logging-collector -n openshift-logging
    oc adm policy add-cluster-role-to-user collect-infrastructure-logs -z logging-collector -n openshift-logging
    
  6. ClusterLogForwarder の作成
    ClusterLogging が稼動したら、ClusterLogForwarderを作成します。
    04_clusterlogforwarder.yaml
    apiVersion: observability.openshift.io/v1
    kind: ClusterLogForwarder
    metadata:
      name: instance
      namespace: openshift-logging
    spec:
      serviceAccount:
        name: logging-collector
      outputs:
      - name: syslog-output
        type: syslog
        syslog:
          url: udp://172.18.0.253:514
          facility: local0
          severity: informational
          tag: "openshift"
          rfc: RFC5424
      pipelines:
      - name: application-logs
        inputRefs:
        - application
        outputRefs:
        - syslog-output
    

rsyslog側の設定

  1. rsyslogの設定ファイルの編集
    /etc/rsyslog.conf は最終的には以下のようになりました。試行錯誤の過程は後述します。
    --- /etc/rsyslog.conf.default   2025-05-07 22:14:04.000000000 +0900
    +++ /etc/rsyslog.conf   2025-06-01 02:45:21.909801001 +0900
    @@ -29,14 +29,19 @@
    
     # Provides UDP syslog reception
     # for parameters see http://www.rsyslog.com/doc/imudp.html
    -#module(load="imudp") # needs to be done just once
    -#input(type="imudp" port="514")
    +module(load="imudp") # needs to be done just once
    +input(type="imudp" port="514")
    
     # Provides TCP syslog reception
     # for parameters see http://www.rsyslog.com/doc/imtcp.html
     #module(load="imtcp") # needs to be done just once
     #input(type="imtcp" port="514")
    
    +#### TEMPLATES ####
    +template(name="DynaPodFile" type="string" string="/var/log/openshift/%$.extracted_hostname%/%$.extracted_namespace_name%/%$.extracted_pod_name%_%$.extracted_container_name%.log")
    +$template OpenShiftLogFormat,"%TIMESTAMP% %HOSTNAME% %.extracted_namespace_name% %.extracted_pod_name% %.extracted_container_name% | %.extracted_message%\n"
    +
     #### RULES ####
    
     # Log all kernel messages to the console.
    @@ -79,3 +84,33 @@
     # # Remote Logging (we use TCP for reliable delivery)
     # # remote_host is: name/ip, e.g. 192.168.0.1, port optional e.g. 10514
     #Target="remote_host" Port="XXX" Protocol="tcp")
    +
    +if $syslogfacility-text == 'local0' then {
    +    # Pod名を抽出(正規表現を使用)
    +    if re_match($msg, '.*"pod_name":"([^"]+)".*') then {
    +        set $.extracted_hostname = re_extract($msg, '"hostname":"([^"]+)"', 0, 1, "unknown");
    +        set $.extracted_namespace_name = re_extract($msg, '"namespace_name":"([^"]+)"', 0, 1, "unknown");
    +        set $.extracted_pod_name       = re_extract($msg, '"pod_name":"([^"]+)"', 0, 1, "unknown");
    +        set $.extracted_container_name = re_extract($msg, '"container_name":"([^"]+)"', 0, 1, "unknown");
    +        #set $.extracted_message        = re_extract($msg, '"message":"([^"]+)"', 0, 1, "unknown");
    +
    +        # messageの開始位置から、次の","msg_idまでを抽出
    +        set $.extracted_message = re_extract($msg, '"message":"(.*?)","msg_id"', 0, 1, "");
    +
    +        # 見つからない場合
    +        if ($.extracted_message == "") then {
    +            set $.extracted_message = "Message extraction failed";
    +        }
    +
    +        # Pod名が取得できた場合
    +        if ($.extracted_pod_name != "unknown") then {
    +            local0.* ?DynaPodFile;OpenShiftLogFormat
    +            stop
    +        }
    +    }
    +}
    
  2. ログディレクトリの作成
    sudo mkdir -p /var/log/openshift
    
  3. ログローテーション設定
    /etc/logrotate.d/openshift
    /var/log/openshift/*/*.log {
        daily
        rotate 30
        compress
        delaycompress
        missingok
        create 0644 syslog syslog
        postrotate
            systemctl reload rsyslog
        endscript
    }
    
  4. ファイアウォール設定
    sudo firewall-cmd --permanent --add-port=514/udp
    sudo firewall-cmd --permanent --add-port=514/tcp
    sudo firewall-cmd --reload
    
  5. SELinux設定
    sudo setsebool -P nis_enabled 1
    sudo semanage port -a -t syslogd_port_t -p udp 514
    sudo semanage port -a -t syslogd_port_t -p tcp 514
    
  6. rsyslogサービスの再起動
    sudo rsyslogd -N1 -f /etc/rsyslog.conf
    sudo systemctl restart rsyslog
    
    実行例
    $ sudo rsyslogd -N1 -f /etc/rsyslog.conf
    rsyslogd: version 8.2412.0-2.el9, config validation run (level 1), master config /etc/rsyslog.conf
    rsyslogd: End of config validation run. Bye.
    
  7. 出力されたログの確認
    実行例
    $ cd /var/log/openshift
    $ sudo tree .
    .
    └── crc
        ├── baremetal-operator-system
        │   ├── baremetal-operator-ironic-8677964577-lbsjj_ironic-httpd.log
        │   ├── baremetal-operator-ironic-8677964577-lbsjj_ironic.log
        │   └── baremetal-operator-ironic-8677964577-lbsjj_mariadb.log
        ├── cert-manager
        │   └── cert-manager-cainjector-645bd4478d-24x9q_cert-manager-cainjector.log
        ├── cert-manager-operator
        │   └── cert-manager-operator-controller-manager-698c6d7767-qr7k9_cert-manager-operator.log
        ├── hostpath-provisioner
        │   ├── csi-hostpathplugin-fp6dj_csi-provisioner.log
        │   └── csi-hostpathplugin-fp6dj_hostpath-provisioner.log
        ├── metallb-system
        │   ├── controller-764dff778f-sp6pf_controller.log
        │   ├── frr-k8s-jwx4m_controller.log
        │   ├── frr-k8s-jwx4m_frr.log
        │   ├── metallb-operator-controller-manager-564755966-6q58k_manager.log
        │   └── speaker-w2pbh_speaker.log
        ├── openstack
        │   ├── ceilometer-0_ceilometer-central-agent.log
        │   ├── ceilometer-0_ceilometer-notification-agent.log
        │   ├── ceilometer-0_proxy-httpd.log
        │   ├── cinder-api-0_cinder-api-log.log
        │   ├── cinder-api-0_cinder-api.log
    (snip)
        └── openstack-operators
            ├── barbican-operator-controller-manager-655f8448d8-59bdm_manager.log
            ├── cinder-operator-controller-manager-645dc58df8-4x97r_manager.log
            ├── glance-operator-controller-manager-669856f45d-s4f8p_manager.log
    (snip)
    
    以下のようなログが出力されました。
    May 31 05:39:02 crc baremetal-operator-system baremetal-operator-ironic-8677964577-lbsjj ironic-httpd | 127.0.0.1 - - [31/May/2025:05:39:01 +0000] \"GET / HTTP/1.1\" 200 474 \"-\" \"curl/7.76.1\"
    May 31 05:39:02 crc baremetal-operator-system baremetal-operator-ironic-8677964577-lbsjj ironic-httpd | [Sat May 31 05:39:01.617894 2025] [ssl:debug] [pid 146:tid 149] ssl_engine_io.c(1150): [client 127.0.0.1:50322] AH02001: Connection closed to child 193 with standard shutdown (server 192.168.122.10:6385)
    

rsyslog.conf のチューニング

Step1. そのまま出力

まずは local0 で受け取ったものを /var/log/openshift/openshift.log に出力してみます。

/etc/rsyslog.conf
$template OpenShiftLogFormat,"%TIMESTAMP% %HOSTNAME% %syslogtag %msg%\n"
local0.*    /var/log/openshift/openshift.log;OpenShiftLogFormat

以下のようなログが出力されました。

May 31 17:29:40 crc openstack_memcached-0_memcached[63591bb1-0722-4505-9616-fb5fd5dbdb26] {"@timestamp":"2025-05-31T17:29:40.425489219Z","app_name":"openstack_memcached-0_memcached","facility":"local0","hostname":"crc","kubernetes":{"annotations":{"k8s.ovn.org/pod-networks":"{\"default\":{\"ip_addresses\":[\"10.217.0.101/23\"],\"mac_address\":\"0a:58:0a:d9:00:65\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"169.254.0.5/32\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.101/23\",\"gateway_ip\":\"10.217.0.1\",\"role\":\"primary\"}}","k8s.v1.cni.cncf.io/network-status":"[{\n    \"name\": \"ovn-kubernetes\",\n    \"interface\": \"eth0\",\n    \"ips\": [\n        \"10.217.0.101\"\n    ],\n    \"mac\": \"0a:58:0a:d9:00:65\",\n    \"default\": true,\n    \"dns\": {}\n}]","openshift.io/scc":"anyuid"},"container_id":"cri-o://b19aa1c9e60596b9ad86d6649ba2bafc3827bf49e6eca9265bb660af35058479","container_image":"quay.io/podified-antelope-centos9/openstack-memcached@sha256:83b7d261083257b382f78e0a1190dd2c8d2e46c8d14dabd0ebb81c8c9be9fb4a","container_image_id":"quay.io/podified-antelope-centos9/openstack-memcached@sha256:83b7d261083257b382f78e0a1190dd2c8d2e46c8d14dabd0ebb81c8c9be9fb4a","container_iostream":"stderr","container_name":"memcached","labels":{"app":"memcached","apps_kubernetes_io_pod-index":"0","controller-revision-hash":"memcached-8b564785b","cr":"memcached","memcached_name":"memcached","memcached_namespace":"openstack","memcached_uid":"4d668ca9-c6b3-4bbc-af33-eef0da3c8d74","owner":"infra-operator","service":"memcached","statefulset_kubernetes_io_pod-name":"memcached-0"},"namespace_id":"69c7a12e-a245-4481-b3d9-fd9dccd6f001","namespace_labels":{"kubernetes_io_metadata_name":"openstack","pod-security_kubernetes_io_enforce":"privileged","security_openshift_io_scc_podSecurityLabelSync":"false"},"namespace_name":"openstack","pod_id":"63591bb1-0722-4505-9616-fb5fd5dbdb26","pod_ip":"10.217.0.101","pod_name":"memcached-0","pod_owner":"StatefulSet/memcached"},"level":"default","log_source":"container","log_type":"application","message":"<43 new auto-negotiating client connection","msg_id":"container","openshift":{"cluster_id":"6df651c0-eb51-42db-a77c-8a60f65389bc","sequence":1748712580660089637},"proc_id":"63591bb1-0722-4505-9616-fb5fd5dbdb26","severity":"informational","timestamp":"2025-05-31T17:29:40.425489219Z"}

ログを整形すると以下のような構造になっています。

{
  "@timestamp": "2025-05-31T17:29:40.425489219Z",
  "app_name": "openstack_memcached-0_memcached",
  "facility": "local0",
  "hostname": "crc",
  "kubernetes": {
    "annotations": {
      "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.101/23\"],\"mac_address\":\"0a:58:0a:d9:00:65\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"169.254.0.5/32\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.101/23\",\"gateway_ip\":\"10.217.0.1\",\"role\":\"primary\"}}",
      "k8s.v1.cni.cncf.io/network-status": "[{\n    \"name\": \"ovn-kubernetes\",\n    \"interface\": \"eth0\",\n    \"ips\": [\n        \"10.217.0.101\"\n    ],\n    \"mac\": \"0a:58:0a:d9:00:65\",\n    \"default\": true,\n    \"dns\": {}\n}]",
      "openshift.io/scc": "anyuid"
    },
    "container_id": "cri-o://b19aa1c9e60596b9ad86d6649ba2bafc3827bf49e6eca9265bb660af35058479",
    "container_image": "quay.io/podified-antelope-centos9/openstack-memcached@sha256:83b7d261083257b382f78e0a1190dd2c8d2e46c8d14dabd0ebb81c8c9be9fb4a",
    "container_image_id": "quay.io/podified-antelope-centos9/openstack-memcached@sha256:83b7d261083257b382f78e0a1190dd2c8d2e46c8d14dabd0ebb81c8c9be9fb4a",
    "container_iostream": "stderr",
    "container_name": "memcached",
    "labels": {
      "app": "memcached",
      "apps_kubernetes_io_pod-index": "0",
      "controller-revision-hash": "memcached-8b564785b",
      "cr": "memcached",
      "memcached_name": "memcached",
      "memcached_namespace": "openstack",
      "memcached_uid": "4d668ca9-c6b3-4bbc-af33-eef0da3c8d74",
      "owner": "infra-operator",
      "service": "memcached",
      "statefulset_kubernetes_io_pod-name": "memcached-0"
    },
    "namespace_id": "69c7a12e-a245-4481-b3d9-fd9dccd6f001",
    "namespace_labels": {
      "kubernetes_io_metadata_name": "openstack",
      "pod-security_kubernetes_io_enforce": "privileged",
      "security_openshift_io_scc_podSecurityLabelSync": "false"
    },
    "namespace_name": "openstack",
    "pod_id": "63591bb1-0722-4505-9616-fb5fd5dbdb26",
    "pod_ip": "10.217.0.101",
    "pod_name": "memcached-0",
    "pod_owner": "StatefulSet/memcached"
  },
  "level": "default",
  "log_source": "container",
  "log_type": "application",
  "message": "<43 new auto-negotiating client connection",
  "msg_id": "container",
  "openshift": {
    "cluster_id": "6df651c0-eb51-42db-a77c-8a60f65389bc",
    "sequence": 1748712580660089637
  },
  "proc_id": "63591bb1-0722-4505-9616-fb5fd5dbdb26",
  "severity": "informational",
  "timestamp": "2025-05-31T17:29:40.425489219Z"
}

今回は ELK などに送信したい訳ではないので、人間が見るにはかなり冗長です。JSONデータを parse して、message だけ出力するようにしてみます。また、namespace_name、container_name が kubernetes の中にあるので、その値も抽出する必要があります。

Step2. rsyslog の mmjsonparse を使ってみる

rsyslog.conf を以下のようにしてみました。

/etc/rsyslog.conf
module(load="mmjsonparse")
template(name="OpenShiftLogFormat" type="string" string="%TIMESTAMP% %HOSTNAME% %syslogtag% [%parsesuccess%] %msg%\n")
if $syslogtag contains "fluentd" then {
    # JSONを解析
    action(type="mmjsonparse")
    action(type="omfile" File="/var/log/openshift/openshift.log" template="OpenShiftLogFormat")
}

残念ながら、 %parsesuccess の値が FALSE になってしまい、 mmjsonparse では parse することはできませんでした。

Step3. 正規表現で必要なパラメータを抽出する

rsyslog.conf を以下のようにしてみました。

/etc/rsyslog.conf
#### TEMPLATES ####
template(name="DynaPodFile" type="string" string="/var/log/openshift/%$.extracted_hostname%/%$.extracted_namespace_name%/%$.extracted_pod_name%_%$.extracted_container_name%.log")
$template OpenShiftLogFormat,"%TIMESTAMP% %HOSTNAME% %.extracted_namespace_name% %.extracted_pod_name% %.extracted_container_name% | %.extracted_message%\n"

if $syslogtag contains "fluentd" then {
    # Pod名を抽出(正規表現を使用)
    if re_match($msg, '.*"pod_name":"([^"]+)".*') then {
        set $.extracted_hostname = re_extract($msg, '"hostname":"([^"]+)"', 0, 1, "unknown");
        set $.extracted_namespace_name = re_extract($msg, '"namespace_name":"([^"]+)"', 0, 1, "unknown");
        set $.extracted_pod_name       = re_extract($msg, '"pod_name":"([^"]+)"', 0, 1, "unknown");
        set $.extracted_container_name = re_extract($msg, '"container_name":"([^"]+)"', 0, 1, "unknown");

        # messageの開始位置から、次の","msg_idまでを抽出
        set $.extracted_message = re_extract($msg, '"message":"(.*?)","msg_id"', 0, 1, "");

        # 見つからない場合
        if ($.extracted_message == "") then {
            set $.extracted_message = "Message extraction failed";
        }

        # Pod名が取得できた場合
        if ($.extracted_pod_name != "unknown") then {
            local0.* ?DynaPodFile;OpenShiftLogFormat
            stop
        }
    }
}

message の内容に「"」が含まれていると、namespace_name などのように parse すると途中で切れてしまうことが分かったので、message の次の msg_id が現れるまでを抽出することにしました。

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?