2021年03月

/proc/sys/net/netfilter/nf_conntrackのレコードが着々と増える

/proc/sys/net/netfilter/nf_conntrackのレコードが着々と増える事象が発生しました。
結論から書くと、「conntrack -F」でconntrackテーブル(でいいのかな…)をflushすることで回復しました。

[状況]
Screenshot_11
Screenshot_13


[対処]
実施した内容は以下のとおりです。

現在のnf_conntrackのレコード数を確認
$ cat /proc/sys/net/netfilter/nf_conntrack_count
1263

現在のnf_conntrackのレコード数を1秒ごとに確認
$ watch cat /proc/sys/net/netfilter/nf_conntrack_count

nf_conntrackの最大数を確認
$ cat /proc/sys/net/netfilter/nf_conntrack_max
262144

firewalldが悪さをしている可能性を考えてrestartするも変化なし
# systemctl restart firewalld

nftablesが悪さをしている可能性を考えてrestartするも変化なし
# systemctl restart nftables.service

conntrackテーブル(でいいのかな…)をflushしてレコードが着々と増えないことを確認
# conntrack -F

「yum module install php:remi-8.0」をansibleで書く

CentOS8にphp:remi-8.0をインストールするコマンドは以下のとおりです。
# sudo rpm -ivh http://rpms.remirepo.net/enterprise/remi-release-8.rpm
# sudo rpm --import http://rpms.remirepo.net/RPM-GPG-KEY-remi
# sudo yum config-manager --set-enabled remi
# sudo yum module install php:remi-8.0
これをansibleで書くと以下のようになります。
- name: phpのインストール
  dnf:
    name: "@php:remi-8.0"
    enablerepo: remi
    state: present
php:remi-8.0の前の@については、ansible.builtin.dnf – Manages packages with the dnf package managerのExamplesの「Install a modularity appstream with defined stream and profile」を参照。

Unable to find a virtual host listening on port 80 which is currently needed for Certbot to prove to the CA that you control your domain. Please add a virtual host for port 80.

/usr/bin/certbot renew
でLet's Encryptの証明書を更新しようとしたところ以下のエラーが発生して証明書が更新できませんでした。
Failed to renew certificate erogamescape.dyndns.org with error: Unable to find a virtual host listening on port 80 which is currently needed for Certbot to prove to the CA that you control your domain. Please add a virtual host for port 80.
ErogameScapeは
erogamescape.dyndns.org
erogamescape.org
の2つでアクセスできるようにしています。
virtual host の設定は443番ポートについては設定しているのですが、80番ポートには設定していませんでした。
今までは特に問題なく証明書が更新できていたので、何かがかわったのだと思います。
以下のように80番ポートにもVirtualHostを設定して証明書の更新ができました。
<VirtualHost *:80>
    ServerName erogamescape.org:80
</VirtualHost>
erogamescape.dyndns.org
erogamescape.org
の2つを設定する必要はなく、1つだけ設定することで証明書の更新ができました。
DocumentRootとServerAdminは設定する必要はありませんでした。

certbotコマンドを試す場合は
/usr/bin/certbot renew --dry-run
とオプションに--dry-runを追加するとステージング環境に接続するので、アクセス回数制限を気にせずに試すことができます。

PHP Fatal error: Array and string offset access syntax with curly braces is no longer supported in /usr/share/pear/PEAR/Config.php on line 2095

CentOS 8 Streamにおいて、yum module install php:remi-8.0した際、以下のエラーメッセージが発生しました。
結論から書くと、dnf update php-pearすることで、PHP Fatal errorがでなくなることを確認しました。

[状況]
CentOS Linux 8 - AppStreamのPHP7.3を運用中。
Remi's Modular repository for Enterprise Linux 8のPHP8.0に切り替え。

[手順]
# yum update epel-release
# yum module list php
# sudo rpm -ivh http://rpms.remirepo.net/enterprise/remi-release-8.rpm
# sudo rpm --import http://rpms.remirepo.net/RPM-GPG-KEY-remi
# sudo yum config-manager --set-enabled remi
# sudo yum module reset php
# sudo yum module list php
# sudo yum module install php:remi-8.0


[エラーメッセージの状況]
# sudo yum module install php:remi-8.0
Last metadata expiration check: 0:00:28 ago on Sun 14 Mar 2021 08:51:28 AM JST.
Dependencies resolved.
============================================================================================================================
 Package                            Architecture        Version                             Repository                 Size
============================================================================================================================
Upgrading:
 libzip                             x86_64              1.7.3-1.el8.remi                    remi-modular               66 k
 php-cli                            x86_64              8.0.3-1.el8.remi                    remi-modular              4.7 M
 php-common                         x86_64              8.0.3-1.el8.remi                    remi-modular              1.2 M
     replacing  php-json.x86_64 7.3.20-1.module_el8.2.0+498+4deef2f1
 php-devel                          x86_64              8.0.3-1.el8.remi                    remi-modular              1.2 M
 php-fpm                            x86_64              8.0.3-1.el8.remi                    remi-modular              1.6 M
 php-gd                             x86_64              8.0.3-1.el8.remi                    remi-modular              100 k
 php-mbstring                       x86_64              8.0.3-1.el8.remi                    remi-modular              523 k
 php-opcache                        x86_64              8.0.3-1.el8.remi                    remi-modular              762 k
 php-pdo                            x86_64              8.0.3-1.el8.remi                    remi-modular              153 k
 php-pecl-zip                       x86_64              1.19.2-1.el8.remi.8.0               remi-modular               71 k
 php-pgsql                          x86_64              8.0.3-1.el8.remi                    remi-modular              144 k
 php-process                        x86_64              8.0.3-1.el8.remi                    remi-modular              104 k
 php-xml                            x86_64              8.0.3-1.el8.remi                    remi-modular              236 k
Installing dependencies:
 libxml2-devel                      x86_64              2.9.7-8.el8                         appstream                 1.0 M
 oniguruma5php                      x86_64              6.9.6-1.el8.remi                    remi-safe                 208 k
 php-fedora-autoloader              noarch              1.0.1-2.el8.remi                    remi-modular               13 k
 xz-devel                           x86_64              5.2.4-3.el8                         baseos                     62 k
Installing weak dependencies:
 php-nikic-php-parser4              noarch              4.10.4-1.el8.remi                   remi                      164 k
Installing module profiles:
 php/common
Enabling module streams:
 php                                                    remi-8.0

Transaction Summary
============================================================================================================================
Install   5 Packages
Upgrade  13 Packages
(中略)
  Cleanup          : libzip-1.5.2-1.module_el8.2.0+314+53b99e08.x86_64                                                32/32
  Running scriptlet: libzip-1.5.2-1.module_el8.2.0+314+53b99e08.x86_64                                                32/32
PHP Fatal error:  Array and string offset access syntax with curly braces is no longer supported in /usr/share/pear/PEAR/Config.php on line 2095
(中略)

[PHP Fatal errorが出る原因]
/usr/share/pear/PEAR/Config.phpの2095行目は以下のようになっていました。
} elseif ($prepend{0} != '\\') {
PHP7.4で上記の書き方は廃止され、
} elseif ($prepend[0] != '\\') {
と書く必要があります。
修正すると、PHP Fatal errorが解消されることを確認しました。
pearのVerをあげれば解消されるとふんだので、以下のようにpearをupdateしました。
# pear list
PHP Fatal error:  Array and string offset access syntax with curly braces is no longer supported in /usr/share/pear/PEAR/Config.php on line 2095
# rpm -qa | grep pear
php-pear-1.10.9-1.module_el8.2.0+314+53b99e08.noarch
# dnf update php-pear
Last metadata expiration check: 2:56:06 ago on Sun 14 Mar 2021 03:45:12 PM JST.
Dependencies resolved.
============================================================================================================================
 Package                  Architecture           Version                                 Repository                    Size
============================================================================================================================
Upgrading:
 php-pear                 noarch                 1:1.10.12-5.el8.remi                    remi-modular                 365 k

# pear list
Installed packages, channel pear.php.net:
=========================================
Package          Version State
Archive_Tar      1.4.12  stable
Console_Getopt   1.4.3   stable
PEAR             1.10.12 stable
PEAR_Manpages    1.10.0  stable
Structures_Graph 1.1.1   stable
XML_Util         1.4.5   stable
pear listコマンドもPHP Fatal errorで実行できませんでしたが、php-pearを1:1.10.12-5.el8.remiにすることでPHP Fatal errorが解消いたしました。

Cannot change a fulfilled promise to rejected

phpを7.3からphp8.0にあげるためのスクリプト確認中に以下の事象が発生しました。

[事象]
paapi5-php-sdkを使ったスクリプトでAmazonのProduct Advertising APIから情報を取得しようとすると以下のエラーメッセージを出力してスクリプトが停止する。
Cannot change a fulfilled promise to rejected
[詳細]
paapi5-php-sdkのGetItems.phpのfunction getItemsAsync()において、$exceptionの詳細を確認するため以下のようにスクリプトを変更しました。
    try {
        $promise = $apiInstance->getItemsAsync($getItemsRequest);
        $response = $promise->wait();
        $promise->then(
            function ($response) {
                return $response;
            },
            function (\Exception $exception) {
                return FALSE;
            }
        );

        echo $response, PHP_EOL;
    } catch (ApiException $exception) {
      return FALSE;
    } catch (Exception $exception) {
      var_dump($exception); → 追加
      return FALSE;
    }
$exceptionの内容は以下のとおりでした。
object(LogicException)#20 (7) {
  ["message":protected]=>
  string(45) "Cannot change a fulfilled promise to rejected"
  ["string":"Exception":private]=>
  string(0) ""
  ["code":protected]=>
  int(0)
  ["file":protected]=>
  string(72) "/paapi5-php-sdk/vendor/guzzlehttp/promises/src/Promise.php"
  ["line":protected]=>
  int(130)
  ["trace":"Exception":private]=>
  array(13) {
    [0]=>
    array(6) {
      ["file"]=>
      string(72) "/paapi5-php-sdk/vendor/guzzlehttp/promises/src/Promise.php"
      ["line"]=>
      int(118)
      ["function"]=>
      string(6) "settle"
      ["class"]=>
      string(26) "GuzzleHttp\Promise\Promise"
      ["type"]=>
      string(2) "->"
      ["args"]=>
      array(2) {
        [0]=>
        string(8) "rejected"
        [1]=>
        object(TypeError)#98 (7) {
          ["message":protected]=>
          string(90) "method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given"
          ["string":"Error":private]=>
          string(0) ""
          ["code":protected]=>
          int(0)
          ["file":protected]=>
          string(72) "/paapi5-php-sdk/vendor/guzzlehttp/promises/src/Promise.php"
          ["line":protected]=>
          int(151)
Promise.phpの151行目は以下のとおりでした。
if (!method_exists($value, 'then')) {
PHP8かPHP7.4で、method_existsの第一引数にobject/string以外の型を渡すとerrorになるように変更されたのかな…
if (!method_exists((object)$value, 'then')) {
と書くと動くことを確認しました。
おそらく最新のguzzlehttpなら動くと思い、guzzlehttpを最新のリビジョン(6.5.5)にあげたところ動作しました。
該当部分は
if (!is_object($value) || !method_exists($value, 'then')) {
と修正されていました。
自分がpaapi5-php-sdkを使い始めたのはProduct Advertising API 5の導入時期でした。
今のpaapi5-php-sdkを確認したところ、同梱されているguzzlehttpは6.5.3でしたので問題ないです。
自分と同じようにpaapi5-php-sdkを初期に導入して何もしていない…場合は、PHPを7.3から8.0にあげると同じエラーがでるかなと思います。





undefined reference to `pthread_getspecific'

Google Test の使い方 - ゼロから学ぶ C++のビルドを実行した際に以下のエラーが発生しました。

環境は以下のとおりです。
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"

$ g++ --version
g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ビルドの結果は以下の通りです。
$ g++ -std=c++11 sample.cc sample_test.cc -o test -L/usr/local/lib -lgtest -lgtest_main
/usr/local/lib/libgtest.a(gtest-all.cc.o): In function `testing::internal::ThreadLocal<testing::TestPartResultReporterInterface*>::~ThreadLocal()':
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEED2Ev[_ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEED5Ev]+0x24): undefined reference to `pthread_getspecific'
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEED2Ev[_ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEED5Ev]+0x39): undefined reference to `pthread_key_delete'
/usr/local/lib/libgtest.a(gtest-all.cc.o): In function `testing::internal::ThreadLocal<std::vector<testing::internal::TraceInfo, std::allocator<testing::internal::TraceInfo> > >::~ThreadLocal()':
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEED2Ev[_ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEED5Ev]+0x24): undefined reference to `pthread_getspecific'
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEED2Ev[_ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEED5Ev]+0x39): undefined reference to `pthread_key_delete'
/usr/local/lib/libgtest.a(gtest-all.cc.o): In function `testing::internal::ThreadLocal<std::vector<testing::internal::TraceInfo, std::allocator<testing::internal::TraceInfo> > >::GetOrCreateValue() const':
gtest-all.cc:(.text._ZNK7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE16GetOrCreateValueEv[_ZNK7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE16GetOrCreateValueEv]+0x25): undefined reference to `pthread_getspecific'
gtest-all.cc:(.text._ZNK7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE16GetOrCreateValueEv[_ZNK7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE16GetOrCreateValueEv]+0x88): undefined reference to `pthread_setspecific'
/usr/local/lib/libgtest.a(gtest-all.cc.o): In function `testing::internal::ThreadLocal<testing::TestPartResultReporterInterface*>::CreateKey()':
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE9CreateKeyEv[_ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE9CreateKeyEv]+0x27): undefined reference to `pthread_key_create'
/usr/local/lib/libgtest.a(gtest-all.cc.o): In function `testing::internal::ThreadLocal<std::vector<testing::internal::TraceInfo, std::allocator<testing::internal::TraceInfo> > >::CreateKey()':
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE9CreateKeyEv[_ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE9CreateKeyEv]+0x27): undefined reference to `pthread_key_create'
/usr/local/lib/libgtest.a(gtest-all.cc.o): In function `testing::internal::ThreadLocal<testing::TestPartResultReporterInterface*>::GetOrCreateValue() const':
gtest-all.cc:(.text._ZNK7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE16GetOrCreateValueEv[_ZNK7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE16GetOrCreateValueEv]+0x25): undefined reference to `pthread_getspecific'
gtest-all.cc:(.text._ZNK7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE16GetOrCreateValueEv[_ZNK7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE16GetOrCreateValueEv]+0x88): undefined reference to `pthread_setspecific'
collect2: error: ld returned 1 exit status
以下のとおり、「-pthread」を追加するとビルドできます。
g++ -std=c++11 sample.cc sample_test.cc -o test -L/usr/local/lib -pthread -lgtest -lgtest_main
参考

着々とinodesの値が増え始め、サーバーの応答が遅くなる事象について

[概要]
着々とinodesの値が増え始め、サーバーの応答が遅くなる事象が発生しました。
結論から書くと、GoogleBotの巡回がある日から多くなり、GoogleBotによるアクセスを捌ききれなくなりました。
GoogleBotが実行に時間のかかるスクリプトにアクセスした場合に、404を返すようにしました。
※500や429が適切のような気もするのですが、200をかえせるようになる見込みがないので、あらかじめそのスクリプトはなかったことにして、様子見いたします。

[詳細]
一週間前からinodeの使用量が着々と増え始めました。
Screenshot_6

時期がちょっとだけ違うのですが、得点とゲームの対応表(グラフィカル版)をリリースしたので、GoogleBotがそこにアクセスしてキャッシュファイルを生成しているからかな…と思っていました。
しかし、だんだん負荷が増えて、ついにサーバーの応答が遅くなるまでになってしましいました。
Screenshot_7
得点とゲームの対応表(グラフィカル版)へのアクセスだけでは、こんなに負荷があがるはずがないので、ソフト不良を疑い、各サーバーを再起動するも負荷は高いままでした。

iftopコマンドの出力を眺めていても、Dos的なものだったり、頻度が多いクローラーがきているわけでもなく、ただGoogleBotが多い気がする…というものでした。
試しにGoogleBotのアクセスを遮断したところ負荷が改善しました。

なぜ、GoogleBotのアクセスが多くなったのかは分からなかったのですが、すぐにGoogleBotのアクセスを減らす必要があったので、Googlebot のクロール頻度の変更を見るも、すぐにクロール頻度を低くする方法はありませんでした。
簡単な解決策としては、robots.txt を使用して、過負荷になっているエージェント(Googlebot、AdsBot など)に対するクロールをブロックします。ただし、有効になるまで最大 1 日程度かかることがあります。
すぐに効果があるのは以下の対応でした。
負荷の増大を動的に検出して対応できる場合は、配信制限に近づいた時点で HTTP 5XX / 429 を返します。ただし、5XX や 429 を返すのは 2~3 日程度までにすべきです。それを超えてしまうと、Google がサイトをクロールする頻度がいずれ少なくなってきます。

そこで、いくつかのスクリプトでGooglebotがきた際に429を返すようにしました。
if(preg_match("/Googlebot/", $_SERVER['HTTP_USER_AGENT']))
{
    header("HTTP/1.1 429 Too Many Requests");
    exit;   
}
これにより、アクセス数が減りました。
Screenshot_8

また負荷も下がりました。
Screenshot_9

なぜGoogleBotのクロール頻度が増えたのかについて、Google Serch Consoleを確認したところ、カバレッジの有効なページの数が突然14万ページ増えていました。
Screenshot_10
この影響でクロールが増えたのかと推測しますが、なぜ突然増えたのかさっぱり分かりません。

現在、特定のスクリプトで429ではなく404を返すようにしました。
一時的ではなく今後もGooglebotに巡回して欲しくない…と思っているからです。

負荷は下がったので、恒久的な対策として、robor.txtに特定のスクリプトを記載し、そもそもクロールしないように設定をいたします。

記事検索