Backend Team Monthly vol.3【 kintone / Linux / PHP / MinIO 】

弊ロジカルスタジオでは、エンジニアの業務ごとにチームに分かれて活動しています。
バックエンドチームでは、活動の一環としてMonthlyを発信しています!

日々の業務でぶち当たったことや疑問に思ったこと、調べたことをベースに、 自己学習やたまたま見かけた面白い情報なども交えながら、情報共有できればと思っています。

前回の記事はこちらになります。よろしければ併せてご覧ください!

Backend Team Monthly vol.2【 Laravel 】


 

1.【kintone】カテゴリー機能で分かりやすくレコード管理

設定画面で有効化できる、カテゴリーという機能をご存じでしょうか?

 

このカテゴリー機能を使うと、レコード登録画面でそのレコードが属するカテゴリーを選択できるようになり・・・

一覧画面の左側にカテゴリーの表示領域が追加され、ワンクリックで絞り込みができるようになります。

 

設定は簡単です。

設定画面→カテゴリーから設定画面へ遷移します。

デフォルトではこんな感じで無効になっているので、有効化してください。

有効化すると、カテゴリーの設定が可能になります。

最大で5階層まで設定できます。

 

設定したら、保存を押すだけです。

再度無効化しても設定内容は消えないので、気軽に試すこともできます。

細かくしすぎると逆に管理しにくいので、気を付けましょう!

 


 

2.【Linux】tarコマンドで圧縮したファイルが問題ないかのチェックする方法

ファイルやフォルダを圧縮するときに便利なtarコマンドです。

リリースの時には、ログやコードのバックアップするために使用することが多いですね。

tarコマンドで生成された圧縮ファイルが、実際のファイルと乖離がないかをチェックする方法をご紹介します。

サーバーのエラーなどで、実際はバックアップが取れていなかったという事態をさけれます。

1.容量の確認する

サーバにコードの圧縮したファイルが生成されるので、サーバの容量が逼迫する可能性があります。

dhコマンドで容量を確認しておきましょう

dh -f

上記を実行すると、下記のようなデータが出力されるので、圧縮しても問題ないかの確認ができます。

Filesystem       Size  Used Avail Use% Mounted on
/dev/root         58G   13G   45G  22% /
tmpfs            956M     0  956M   0% /dev/shm

2.容量の確認する

サーバー内のmasterディレクトリを圧縮する想定のコマンドとなります。

tar -czvf master_backup20260105.tar.gz master

tオプションを付けているので、圧縮されるファイルが一覧で表示されます。

問題がなければ、エラーが表示されずに、「master_backup20260105.tar.gz」が生成されます。

念のために、生成されているかを確認しておきましょう

ll master_backup20260105.tar.gz

2.既存ディレクトリのファイルパスの取得

masterディレクトリの全てのファイルを取得して、ソートして、master.txtに保存します。

find master -print | sort > master.txt

3.圧縮ファイルのファイルパスの取得

圧縮されたファイルは、findコマンドで取得できないので、tarコマンドで一覧を保存します。

tar -tzf master_backup20260105.tar.gz > master_backup20260105.txt

4.それぞれのテキストファイルの差分をなくす

この状態で、master.txtとmaster_backup20260105.txtのdiffを確認できれば楽なのですが、取得したコマンドが違うため、下記のような差分が出ます。

  • ディレクトリの場合の最後のスラッシュありなし
  • tarコマンドでは、日本語はバイト列になる

下記コマンドで解消可能で、「master_backup20260105_convert.txt」が生成されます。

printf '%b' "$(cat master_backup20260105.txt)" | sed 's:/$::' | sort > master_backup20260105_convert.txt

5.ファイルの差分を確認する

最後に、WinMergeなどで下記の差分の確認を行い、差分が出なければ問題ありません。

  • master.txt
  • master_backup20260105_convert.txt

※本番であれば下記の差分が出ることもありますので、目視確認が必要な場合もあります。

  • キャッシュ関連
  • 画像アップロード機能

 


 

3.【PHP】yieldって何?

#[Test]
#[DataProvider('dataProviderInvalidPayload')]
public function バリデーションエラー(array $payload, array $errors): void
{
    [
        'account' => $account,
    ] = $this->setUpRecords();

    $this->actingAsRecord($account);

    $response = $this->callEndpoint($payload);
    $response->assertSessionHasErrors($errors);
}

public static function dataProviderInvalidPayload(): Generator
{
    yield 'idが未指定' => [ // <-これ
	    ...

案件で別ファイルのソースコードを参考にしながら実装していく中で当たり前に使ってたのですが

よく考えたらyieldって何?となったので調べてみました。

yieldとは

メモリを節約しながら、大量のデータを少しずつ処理するための仕組み。

普通のreturnを使った関数だと一気にまとめて返しますが、yield を使った関数(ジェネレータといいます)は、結果を一つずつ小出しに返します。

動きとしてはyieldが一度実行されたら次を待って、また呼び出されたら次を実行する感じです。

return(通常の関数) yield(ジェネレータ)
返し方 全データを配列などにまとめて一気に返す 必要な分だけ1つずつ生成して返す
メモリ消費 データ量が多いとメモリ消費も大きい 最小限のメモリしか使わない
処理の中断 実行したらそこで終了 値を返した後、一時停止して次を待つ

yieldはわんこそばみたいなイメージですかね。(1杯ずつ出すので机の上のスペースを消費しない)

普通のは、そば100杯分全部できるまで待ってまとめて出すのでテーブルが器で埋まってしまう、的な・・・

実際どれくらいメモリ節約できるのか

ループで10万個の配列を返す処理を普通の関数とジェネレータで書いて、それぞれの動きを見てみます。

普通の関数

// --- 比較1: 通常関数 ---
function getArray($max) {
    $result = [];
    for ($i = 0; $i < $max; $i++) {
        $result[] = $i;
    }
    return $result;
}

memory_reset_peak_usage();

$data = getArray(100000);

echo "消費メモリ: " . round(memory_get_peak_usage() / (1024 * 1024), 3) . " MB";
// 消費メモリ: 2.332 MB

ジェネレータ

// --- 比較2: ジェネレータ (yield) ---
function yieldNumbers($max) {
    for ($i = 0; $i < $max; $i++) {
        yield $i;
    }
}

memory_reset_peak_usage();

$data = yieldNumbers(100000);

foreach ($data as $number) {
    // 処理をシミュレートするために何かを行う(ここでは何もしない)
}

echo "【yield】消費メモリ: " . round(memory_get_peak_usage() / (1024 * 1024), 3) . " MB";
// 【yield】消費メモリ: 0.392 MB

大幅にメモリ節約できました。

少ない配列だとどうなるのでしょうか。10万個を100個にすると、以下の結果になりました。

// 消費メモリ: 0.328 MB
// 【yield】消費メモリ: 0.326 MB

ほとんど同じです。やはり大きいデータだと効果も大きいようです。

使い道

以下のような場面で使われているのを実案件で見かけました。

  • PHPUnitのDataProviderのように、テストケースを1つずつ供給したい場合
  • 行数の多いCSVを読み込んで1件ずつ処理したい場合
  • バッチ処理や夜間ジョブなど、メモリ使用量を抑えたい処理

さいごに

普段、当たり前に使っているものを見直す良いきっかけになりました。

PHPで「大量のデータを扱うけどメモリが心配・・・」という場面に出会ったら、yieldを思い出してみてください。

 


 

4.【MinIO】自前でAmazon S3互換のオブジェクトストレージを構築する

MinIO(ミンアイオー)とは?

画像や動画などの膨大なデータを保存する場合、Amazon S3のようなクラウドサービスを利用するケースも多いと思います。

ローカルの開発環境等で動かしたいというニーズに対して、S3と同じ仕組みを自分のサーバーで作れるようにするのがMinIOです。

S3と互換性があるため、S3向けに作ったアプリの設定を書き換えるだけで、そのままMinIOに接続できます。

下記は、Docker、Laravel、MinIOで環境を構築する手順です。

設定ファイル

docker-compose.yml

services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    ports:
      - "8080:80"
    volumes:
      - ./src:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
    networks:
      - app-network

  php:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    container_name: php
    volumes:
      - ./src:/var/www/html
    depends_on:
      - mysql
      - minio
    networks:
      - app-network

  mysql:
    image: mysql:8.0
    container_name: mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: laravel
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - app-network

  minio:
    image: quay.io/minio/minio:latest
    container_name: minio
    ports:
      - "9000:9000"
      - "9001:9001"
    volumes:
      - minio-data:/data
    environment:
      MINIO_ROOT_USER: admin
      MINIO_ROOT_PASSWORD: password123
    command: server /data --console-address ":9001"
    networks:
      - app-network

  createbuckets:
    image: quay.io/minio/mc:latest
    container_name: createbuckets
    depends_on:
      - minio
    restart: on-failure
    entrypoint: >
      /bin/sh -c "
      sleep 5;
      /usr/bin/mc alias set myminio <http://minio:9000> admin password123;
      /usr/bin/mc mb myminio/mybucket --ignore-existing;
      /usr/bin/mc anonymous set download myminio/mybucket;
      exit 0;
      "
    networks:
      - app-network

volumes:
  mysql-data:
    driver: local
  minio-data:
    driver: local

networks:
  app-network:
    driver: bridge

docker/nginx/default.conf

server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;
    index index.php index.html;

    client_max_body_size 100M;

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

    location ~ \\.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\\.(?!well-known).* {
        deny all;
    }
}

docker/php/Dockerfile

FROM php:8.4-fpm

RUN apt-get update && apt-get install -y \\
    git \\
    curl \\
    libpng-dev \\
    libonig-dev \\
    libxml2-dev \\
    libzip-dev \\
    zip \\
    unzip \\
    && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip \\
    && apt-get clean \\
    && rm -rf /var/lib/apt/lists/*

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

RUN useradd -G www-data,root -u 1000 -d /home/devuser devuser
RUN mkdir -p /home/devuser/.composer && \\
    chown -R devuser:devuser /home/devuser

USER devuser

使い方

Laravelプロジェクトの作成

# srcディレクトリにLaravelをインストール
docker run --rm -v $(pwd)/src:/app composer create-project laravel/laravel .

環境変数の設定

.envの値を修正します。

AWS_ACCESS_KEY_ID=admin
AWS_SECRET_ACCESS_KEY=password123
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=mybucket
AWS_ENDPOINT=http://minio:9000
AWS_USE_PATH_STYLE_ENDPOINT=true

Dockerコンテナの起動

docker compose up -d --build

S3ライブラリのインストール

docker compose exec php composer require league/flysystem-aws-s3-v3

Laravelの初期設定

docker compose exec php php artisan key:generate
docker compose exec php php artisan migrate

動作確認

コードでの使用例

use Illuminate\\Support\\Facades\\Storage;

// アップロードStorage::disk('s3')->put('path/to/file.txt', 'content');

// ファイルオブジェクトからアップロード
Storage::disk('s3')->putFileAs('uploads', $file, 'filename.jpg');

// 存在確認
Storage::disk('s3')->exists('path/to/file.txt');

// 取得
$content = Storage::disk('s3')->get('path/to/file.txt');

// 削除
Storage::disk('s3')->delete('path/to/file.txt');

// 署名付きURL(30分有効)
$url = Storage::disk('s3')->temporaryUrl('path/to/file.txt', now()->addMinutes(30));

開発環境と本番環境の注意点

  • MinIOのライセンスはAGPLv3です。
  • 環境ごとに求められるコンプライアンスと運用コストが大きく異なります。
  • 組織内部での利用や、外部ユーザーがアクセスしないテスト環境であれば、コード公開義務は発生しないと考えられます。
  • 本番・商用環境にMinIOを組み込む場合、AGPLv3の公開義務に抵触する可能性が非常に高くなります。

 


 

最後までお読みいただきありがとうございました。

Backend Team Monthly は今後も更新予定ですので、お楽しみに!

また、ロジカルスタジオでは、エンジニアを募集中です。

ぜひ下のバナーから確認してみて下さい!