【C】接続元IPアドレスの JSON を返すだけの Apache モジュールを作ってみる

こんにちは。
普段まだまだ業務でも使う機会の多い Apache ですが、そういえばモジュール周りってどういう風に実装されているんだろうと調べてみた備忘録です。
雛型自体は apxs コマンドで自動的に生成されるそうなので、接続元 IP アドレスの JSON を返すだけの API みたいなよく分からない Apache モジュールを作ってみます。

※ PHP だったら下記2行で終わるんですけどね。

<?php
        $json = ['remote_ip' => $_SERVER["REMOTE_ADDR"]];
        echo json_encode($json);

環境構築

はい、勿論Apacheのモジュールなんで、Apacheの本体と、apxsコマンドを使うので開発ツールを導入します。
ただそれだけ~。

yum groupinstall "Development tools"
yum install httpd httpd-devel

apxsコマンドで雛型を作ります。
cd /usr/local/src
apxs -g -n info
cd info/

下記みたいなディレクトリとファイルが生成されるんで、mod_info.c に処理を書いていきます。
[root@test-server1-1 src]# tree
.
└── info
    ├── Makefile
    ├── mod_info.c
    └── modules.mk

1 directory, 3 files

コード

はい、雛型に1行加えただけです…

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

/* The sample content handler */
static int info_handler(request_rec *r)
{
    if (strcmp(r->handler, "info")) {
        return DECLINED;
    }
    r->content_type = "text/html";

    if (!r->header_only) {
        /* 接続元IPを表示する */
        ap_rprintf(r,"{\"client_ip\":\"%s\"}\n",r->connection->client_ip);
    }
    return OK;
}

static void info_register_hooks(apr_pool_t *p)
{
    ap_hook_handler(info_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA info_module = {
    STANDARD20_MODULE_STUFF,
    NULL,                  /* create per-dir    config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    NULL,                  /* table of config file commands       */
    info_register_hooks  /* register hooks                      */
};

接続元 IP は構造体のポインタ r , connection のメンバである client_ip に格納されているので、ap_rprintf 関数でこれを表示させます。
/**
*Output data to the client in a printf format
*
*@Parameters
*      r      The current request
*      fmt      The format string
*      ...      The arguments to use to fill out the format string
*
*@Returns
*      The number of bytes sent
*/
int ap_rprintf( request_rec * 	r,
                const char *    fmt,
                                ... 
)

コンパイル

こちらも apxs コマンドでモジュールのコンパイルと Apache へのモジュールのロード設定まで自動で完了します。

# ディレクトリ移動&コンパイル
cd /usr/local/src/info
apxs -c -i -a mod_info.c

# Apache設定追加
cat >> /etc/httpd/conf/httpd.conf << EOF
<Location "/info">
    SetHandler info
</Location>
EOF

# Apache起動
systemctl start httpd

curl で叩いたら json が返ってきますね。OKです。

keisuke@DESKTOP-MOGIJIA:~$ curl http://192.168.33.10/info
{"client_ip":"192.168.33.1"}

CentOS7 を入れた Raspberry Pi に Laravel8 環境を構築する

こんにちは。
CentOS7 を入れたラズパイに Laravel8 環境を構築してみます。
デフォルトのリポジトリからだと、古い PHP と MariaDB が入っちゃうので、それぞれ工夫して導入します。

PHP

デフォルトのリポジトリだと PHP5 系が入っちゃいます。
armv7l 用の remi リポジトリがあるけども PHP7.2 なので、こちらもダメです。
ついては phpenv でソースから PHP7.4 をビルドします。

# EPEL リポジトリ導入
cat > /etc/yum.repos.d/epel.repo << EOF
[epel]
name=Epel rebuild for armhfp
baseurl=https://armv7.dev.centos.org/repodir/epel-pass-1/
enabled=1
gpgcheck=0
EOF

# 関連パッケージ 導入
yum groupinstall "Development tools"
yum install libicu libicu-devel oniguruma oniguruma-devel cmake cmake3
yum install gcc libxml2 libxml2-devel libcurl libcurl-devel libpng libpng-devel libmcrypt libmcrypt-devel libtidy libtidy-devel libxslt libxslt-devel openssl-devel bison libjpeg-turbo-devel readline-devel autoconf sqlite-devel bzip2-devel nginx

# phpenv 導入
cd /usr/local/src/
git clone https://github.com/CHH/phpenv.git
cd phpenv/bin/
./phpenv-install.sh
git clone https://github.com/CHH/php-build.git ~/.phpenv/plugins/php-build

vi ~/.bashrc
======================================
export PATH="/root/.phpenv/bin:$PATH"
eval "$(phpenv init -)"
======================================

source ~/.bashrc

# libzip 導入
cd /usr/local/src/
wget https://libzip.org/download/libzip-1.7.3.tar.gz
tar xzvf libzip-1.7.3.tar.gz 
cd libzip-1.7.3
cmake3 -DCMAKE_INSTALL_PREFIX=/usr/local/libzip
make
make install

export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/libzip/lib/pkgconfig"

# PHP 導入
phpenv install --list
phpenv install 7.4.14
phpenv global 7.4.14

# PHP-FPM 設定
vi ~/.phpenv/versions/7.4.14/etc/php-fpm.d/www.conf
==========================================================
[www]
user = nginx
group = nginx
listen = /var/run/www.sock
listen.owner = nginx
listen.group = nginx
==========================================================

vi ~/.phpenv/versions/7.4.14/etc/php-fpm.conf
==========================================================
pid = /var/run/php-fpm.pid
==========================================================

ln -s /root/.phpenv/versions/7.4.14/sbin/php-fpm /usr/local/bin/php-fpm

# Unit ファイル作成
vi /usr/lib/systemd/system/php-fpm.service
==============================================================
[Unit]
Description=The PHP FastCGI Process Manager
After=syslog.target network.target

[Service]
Type=forking
PIDFile=/var/run/php-fpm.pid
ExecStartPre=/usr/bin/rm -f /run/php-fpm.pid
ExecStart=/usr/local/bin/php-fpm -D
ExecReload=/bin/kill -USR2 $MAINPID
PrivateTmp=true
RuntimeDirectory=php-fpm
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
==============================================================

# PHP-FPM 起動
systemctl daemon-reload
mkdir -p /var/run/php
systemctl enable php-fpm
systemctl start php-fpm

MariaDB

こちらもデフォルトのリポジトリからだと 5.5 系が入っちゃうのでパスです。
一旦 yum で 5.5 系を導入して初期設定を行って、その後にソースから最新版をビルドし、Unit ファイルの書き換えで最新版にアップデートします。

# 5.5 系の MariaDB 導入
yum install mariadb-server
systemctl start mariadb
mysql_secure_installation

# 10.5.8 の MariaDB ソースをビルド
cd /usr/local/src/
wget https://downloads.mariadb.org/interstitial/mariadb-10.5.8/source/mariadb-10.5.8.tar.gz/from/https%3A//mirror.yongbok.net/mariadb/
mv index.html mariadb.tar.gz
tar -zxvf mariadb.tar.gz
cd mariadb-10.5.8/
cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mariadb
make
make install

# Unit ファイル書き換え
systemctl stop mariadb
vi /usr/lib/systemd/system/mariadb.service
================================================
#ExecStart=/usr/bin/mysqld_safe --basedir=/usr
ExecStart=/usr/local/mariadb/bin/mysqld_safe
================================================

# 10.5.8 にて MariaDB 起動
systemctl daemon-reload
systemctl start mariadb

Composer

Composer は公式に則って普通に導入です。

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

補足

EASE の Nginx 設定ファイル例も記載しておきます。
自分用の備忘録ですしお寿司。

upstream php-fpm-ease {
  ip_hash;
  server unix:/var/run/www.sock;
}

server {
     listen 80;
     server_name ease.local;
     root         /var/www/vhosts/ease/public;
     index        index.php index.html index.htm;
     access_log   /var/log/nginx/ease.local_access.log;
     error_log    /var/log/nginx/ease.local_error.log;

     fastcgi_read_timeout 999999;
     proxy_read_timeout 999999;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass  php-fpm-ease;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_buffer_size  128k;
        fastcgi_buffers  256 16k;
        fastcgi_busy_buffers_size  256k;
        fastcgi_temp_file_write_size  256k;
        include  fastcgi_params;
    }

}

【Ansible】Playbook を WEB で管理できるツールを作ってみた【Laravel】

こんにちは。
Ansible の Playbook 管理ツールといえば、 Ansible Tower / AWX / Ansible Semaphore 等々がございます。
これらを業務フローに組み込めないかな、と試してみたのですが、いずれもあまりピンとこず。

ないなら自分で作ってしまえと思い、年末の勢いでツールを作ってみました。
https://github.com/snkk1210/ease

実現したかったこと

ツールを作成するに辺り、実現したかったポイントは下記 7 点です。

・WEB ブラウザ上で Playbook が管理できる。
・WEB ブラウザ上で Playbook に定義された処理を実行できる。
・処理実行 ( プロビジョニング ) 履歴を保持できる。
・既に存在する Playbook ( リポジトリ ) の資産を流用できる。
・ユーザ毎に細かな権限設定が可能であり、組織的に管理できる。
・プロビジョニング時に公開鍵認証/パスワード認証の両方が使用できる。
・外部ファイルをアップロードし、処理実行時に利用することができる。

社内でのユースケースとしては、下記のようなことを実現したく実装しました。

だいたいの使い方

1. プロビジョニング対象への接続認証を登録

サイドメニューから「Make Auth」を選択し、認証名、パスワード、秘密鍵を登録

2. Playbook を作成

サイドメニューから「Make Playbook」を選択し、下記項目をそれぞれ登録

・Playbook
→ Playbook の名前

・repository
→ Playbookに使うリポジトリ名

・認証
→ 作成した接続認証

・private_key
→ 認証に使う秘密鍵
※ここで設定した秘密鍵情報が優先される(空にすると接続認証の情報を使用)

・inventory
→ インベントリファイル

・vars
→ 変数ファイル

・main
→ レポジトリに存在する role を include する Playbook

3. Playbook を選択

サイドメニューから「Playbooks」を選択し、一覧の中から使用する Playbook の「Run」を選択

4. Playbookを実行

セレクトボックスから「鍵認証」「パスワード認証」を選択して、「ドライラン」または「実行」を選択

権限周り

実装する権限は下記 3 ロールに絞りました。
※ もっと細かく調整できるように改修したいですね。

admin → 全てのリソースの操作が可能/ユーザの作成が可能
read-only → 全てのリソースの「閲覧」のみ可能
他ユーザ → 自分で作成したリソースのみ操作が可能

終わりに

余力があれば、アカウント権限操作周りの機能と、WEB ベースでのリポジトリ追加機能を実装する予定です。
始めて Laravel 使いましたが、こんな感じに実装できるんですね。
色々と勉強になりました。

awk で特定の行の値を取得して計算してみる(Linux のモニタリングツールを作る)

Linux のモニタリングツールといえば、dstat、sarなんかが有名だと思いますが、環境によってはツールが入っていないこともしばしば。
というわけで、linux にプリインストールされているコマンドで必要最低限のモニタリングツールを用意してみます。

ログ調査とかテキストの整形とかでお世話になる awk ですが、実は計算が出来るんですよね。
例えば、 cat /proc/meminfo の出力結果から実メモリの使用率を計算してみようと思います。

/proc/meminfo

ちなみに meminfo の内容はこちら。
MemTotal、MemFree、MemAvailable の値を取得できれば、メモリ使用率を求めることができそうですね。
※ 今回はawkの挙動を確認したいので、あえて MemTotal と MemAvailable を取得してみます。

[root@verienv01 ~]# cat /proc/meminfo | head
MemTotal:         498672 kB
MemFree:           77104 kB
MemAvailable:     363416 kB
Buffers:               0 kB
Cached:           261200 kB
SwapCached:            4 kB
Active:           147176 kB
Inactive:         150668 kB
Active(anon):        492 kB
Inactive(anon):    43400 kB

awkでメモリ使用率を求める

awk で行を指定したい場合は「NR」を使えばよくて、1行目と3行目で且つ、スペースで区切った左から2列目の値を取得したいのでこうですね。

[root@verienv01 ~]# cat /proc/meminfo | awk 'NR==3 {print $2} NR==1 {print $2}'
498672
363332

取得した値をワンライナーで変数ava(MemAvailable)と変数total(MemTotal)に入れて、計算まで行う場合はこうです!
[root@verienv01 ~]# cat /proc/meminfo | awk 'NR==3 {ava=$2} NR==1 {total=$2} {per = ava / total}END{print 100 - per * 100}'
27.1136

小数点以下は切り捨てます。
これでバッファキャッシュも考慮したメモリ使用率を出力できましたね。
[root@verienv01 ~]# cat /proc/meminfo | awk 'NR==3 {ava=$2} NR==1 {total=$2} {per = ava / total}END{print 100 - per * 100}' | sed s/\.[0-9,]*$//g
27

モニタリングツール

というわけで、CPU使用率、メモリ使用率、ロードアベレージ、タイムスタンプを出力するシェルスクリプトを用意します。

#!/bin/bash

MEMORY=$(cat /proc/meminfo | awk 'NR==3 {ava=$2} NR==1 {total=$2} {per = ava / total}END{print 100 - per * 100}' | sed s/\.[0-9,]*$//g)
CPU=$(vmstat | awk 'NR==3 {print $13,$14}')
LA=$(uptime | awk '{print $8,$9,$10}')
TIME=$(vmstat -t | awk 'NR==3 {print $18,$19}')

echo "--cpu-- --mem--  --------la--------  --------time--------"
echo " " $CPU "    " $MEMORY "   " $LA " " $TIME

いろいろとツッコミ処があると思いますが、、まぁいいでしょう。
[root@verienv01 ~]# ./monitor.sh
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-05 23:56:43

whileでループすれば、モニタリングツールの出来上がり(小並感)
[root@verienv01 ~]# while true; do ./monitor.sh ; sleep 1; done
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:42
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:43
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:44
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:45
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:46
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:48
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:49

終わりに

正直 dstat と sar と vmstat でいいですね。

【サーバレス】Lambda + API Gateway + Flask で Web ツールを作ってみる【AWS】

こんにちは。
仕事でサーバレス環境を扱うことがありまして、勉強がてら少し触ってみようと思ったんですね。
先日に SSL/TLS 証明書の整合性チェックツールを Flask で作ったんで、こちらをサーバレス環境で動かしてみます。
https://github.com/keisukesanuki/certificate_verify_flask_lambda

zappa

コードのデプロイ用に zappa というツールがあるそうです。。
下記手順で簡単に Lambda + API Gateway の環境構築とアプリケーションのデプロイが完了するようです。

# 必要なパッケージの導入
yum groupinstall "Development tools" -y
yum install zlib-devel openssl-devel sqlite-devel -y

# pyenvの導入
git clone https://github.com/pyenv/pyenv.git ~/.pyenv
echo 'export PATH="$HOME/.pyenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
source ~/.bash_profile

# pyenv-virtualenvの導入
git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile
exec $SHELL -l

# 3.6系のpythonを導入
pyenv install 3.6.5
pyenv global 3.6.5

# awscliの導入/設定
pip install -U pip
pip install awscli
aws configure

# ソースの取得
pyenv virtualenv 3.6.5 lambda
git clone https://github.com/keisukesanuki/certificate_verify_flask_lambda.git 
cd certificate_verify_flask_lambda
pyenv local lambda

# モジュールの導入
pip install -U pip
pip install flask
pip install zappa

# lambdaにデプロイ
zappa init
===========================================================================
What do you want to call this environment (default 'dev'): certificateCheck
===========================================================================
zappa deploy

JMeter5.3 で The JVM should have exited but did not のエラーがでた話

JMeter5.3 のクラスタ環境を作って、CLI から JMeter を実行すると表題のエラーが出たんですね。

... end of run
The JVM should have exited but did not.
The following non-daemon threads are still running (DestroyJavaVM is OK):
Thread[DestroyJavaVM,5,main], stackTrace:
Thread[AWT-EventQueue-0,6,main], stackTrace:sun.misc.Unsafe#park
java.util.concurrent.locks.LockSupport#park at line:175
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject#await at line:2039
java.awt.EventQueue#getNextEvent at line:554
java.awt.EventDispatchThread#pumpOneEventForFilters at line:187
java.awt.EventDispatchThread#pumpEventsForFilter at line:116
java.awt.EventDispatchThread#pumpEventsForHierarchy at line:105
java.awt.EventDispatchThread#pumpEvents at line:101
java.awt.EventDispatchThread#pumpEvents at line:93
java.awt.EventDispatchThread#run at line:82

Thread[AWT-Shutdown,5,system], stackTrace:java.lang.Object#wait
sun.awt.AWTAutoShutdown#run at line:314
java.lang.Thread#run at line:748

どうも、シナリオ内にスクリプトレコーダを入れていると、CLI モードで実行した際に処理が正常に終了しなくなる不具合があるとのことです。
https://bz.apache.org/bugzilla/show_bug.cgi?id=64479

対応としては、シナリオ中のスクリプトレコーダを削除すればよいようです。

【fdisk】LVM 環境を構築した後に拡張してみる【備忘録】

こんにちは。
fdisk にあまり馴染みがなかったので、勉強がてら LVM 構築/拡張の検証をしてみました。

“【fdisk】LVM 環境を構築した後に拡張してみる【備忘録】” の続きを読む

【Raspberry Pi】semaphore を導入してみる【Ansible】

ラズパイのリソースが余っていたので、semaphore を導入して遊んでみようと思います。
※ 事前に下記を導入する必要ありです。
・MySQL >= 5.6.4/MariaDB >= 5.3
・git >= 2.x

“【Raspberry Pi】semaphore を導入してみる【Ansible】” の続きを読む

【Lambda】CloudWatch の通知を Lambda で Chatwork に飛ばしてみる【Python】

こんにちは。
表題の通り、CloudWatch の Alarm 通知を Chatwork に通知してみます。
ランタイムは前回と同じく python 3.7 です。
ROOMNO にメッセージを通知するルームナンバ、 TOKEN に ChatWorkToken を定義すれば動きます。

import boto3
import json
import logging
import os
import urllib
 
from base64 import b64decode
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

# Chatwork のルームナンバ定義
ROOMNO = 'xxxxxxxxxxxxxxxx'
 
# WEB_HOOKURL
URL = f'https://api.chatwork.com/v2/rooms/{ROOMNO}/messages'

# ChatWorkToken 定義
TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

logger = logging.getLogger()
logger.setLevel(logging.INFO)
 
 
def lambda_handler(event, context):
    logger.info("Event: " + str(event))

    # メッセージ取得
    message = json.loads(event['Records'][0]['Sns']['Message'])
    logger.info("Message: " + str(message))
 
    alarm_name = message['AlarmName']
    new_state = message['NewStateValue']
    reason = message['NewStateReason']

    # ヘッダ情報
    headers = {
        'X-ChatWorkToken': TOKEN,
    }  
 
    # 通知内容
    sns_message = {
        'body': "%s state is now %s: %s" % (alarm_name, new_state, reason),
    }
    
    # エンコード
    msns_message = urllib.parse.urlencode(sns_message)
    msns_message = msns_message.encode('utf-8') 
    
    # リクエスト発行
    req = Request(URL, data=msns_message, headers=headers)
    with urlopen(req) as res:
        result = json.loads(res.read().decode("utf-8"))

【Python】JMeter の実行結果をスプレッドシートに出力するスクリプトを書いてみた【gspread】

こんにちは。
JMeter が出力する結果を Google のスプレッドシートに出力してみようと考えまして、調べてみると Python に便利なライブラリがあったんで作ってみました。
※ 下記の JMeter デプロイ用 Playbook にも入れているのでよければご利用くださいませ。
https://github.com/keisukesanuki/jmeter-MS

Python スクリプト

#!/usr/bin/python3

import gspread
import json
import csv
import sys
import itertools

# シークレットキーを絶対パスで指定
SECRETJSON = "/usr/local/jmeter/bin/sacred-drive.json"
# スプレッドシートキーを定義
SPREADSHEET_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

############################################################################
## 関数

# 数字と文字コンバーター
def num2alpha(num):
    if num<=26:
        return chr(64+num)
    elif num%26==0:
        return num2alpha(num//26-1)+chr(90)
    else:
        return num2alpha(num//26)+chr(64+num%26)

#############################################################################
## 認証

# お約束
from oauth2client.service_account import ServiceAccountCredentials
scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive']

# ダウンロードした json ファイルを定義
credentials = ServiceAccountCredentials.from_json_keyfile_name(SECRETJSON, scope)

# Google API にログイン
gc = gspread.authorize(credentials)

# スプレッドシートのシート1を開く
worksheet = gc.open_by_key(SPREADSHEET_KEY).sheet1


##############################################################################
## 処理

# コマンドライン引数を取得
args = sys.argv
csvfile = args[1]

# CSVファイルの内容を配列に代入
with open(csvfile) as fp:
    results_list_ex = list(csv.reader(fp))

# 2次元配列を1次元配列に変換
results_list = list(itertools.chain.from_iterable(results_list_ex))

# カウント変数初期化
COUNT_NUM = 1
# 空白行探索
while str(len(worksheet.cell(COUNT_NUM, 1).value)) != "0":
        COUNT_NUM += 1

# 編集する範囲を指定
cell_list = worksheet.range('A'+str(COUNT_NUM)+':'+num2alpha(len(results_list))+str(COUNT_NUM))

# cell_listにresults_listの配列を代入
for i,cell in enumerate(cell_list):
    cell.value = results_list[i]

# 結果の保存
worksheet.update_cells(cell_list)

第一引数に csv ファイルを指定することで、結果をスプレッドシートに出力するスクリプトです。
このスクリプトを後述の JMeter 起動用スクリプトで利用します。

シェルスクリプト

#!/bin/sh

DATE=$(date +"%Y%m%d")
OPTIME=$(date +"%Y%m%d-%H%M%S")
# 結果の出力先ディレクトリを指定
LOGDIR=/var/www/html/${DATE}
# JMXファイルを指定
FILE_JMX=/usr/local/jmeter/bin/templates/build-web-test-plan.jmx

# 日付ディレクトリの作成
mkdir -p ${LOGDIR}

# JMeter 起動
/usr/local/jmeter/bin/jmeter -Dsun.net.inetaddr.ttl=0 -n -t ${FILE_JMX} -j ${LOGDIR}/${OPTIME}.log -l ${LOGDIR}/${OPTIME}.jtl -e -o ${LOGDIR}/${OPTIME}_th${JMETER_THREAD}${2}/ -r

# CSV ファイルの作成
cat ${LOGDIR}/${OPTIME}_th${JMETER_THREAD}${2}/statistics.json | jq  -r ". [] | [.transaction,.sampleCount,.errorCount,.errorPct,.meanResTime,.minResTime,.maxResTime,.pct1ResTime,.pct2ResTime,.pct3ResTime,.throughput,.receivedKBytesPerSec,.sentKBytesPerSec] | @csv" | grep "Total" > ${LOGDIR}/${OPTIME}_th${JMETER_THREAD}${2}/statistics.csv

# スプレッドシートに結果を出力
/usr/local/bin/main.py ${LOGDIR}/${OPTIME}_th/statistics.csv

JMeter は json で結果を出力するので jq で無理やり csv に変換してます。

Python だと簡単に実装できて楽ですね。

補足

スクリプトの実行に下記のパッケージ導入が必要です。

yum install python3 python-devel jq
pip3 install gspread
pip3 install oauth2client