CS-Cart – HTTP 500 エラー対策

目次

1. 本記事について
2. 環境情報
3. 問題発生時のリソース状況
4. 原因
5. 対処内容
6. 対処後のリソース状況

1. 本記事について


CS-Cart のレスポンス(サイト応答)が極端に劣化し、HTTP 500 エラーが発生するケースがあったため、確認すべき箇所と、その対処方法を記事にします。

本記事は CS-Cart 導入済みの方向けのため、
CS-Cart についての説明は割愛致します。
詳しくは公式サイトをご確認ください。

公式サイト
開発者ドキュメント

2. 環境情報


– OS/ソフトウェア
EC2(Amazon Linux 2, SSD, 64 ビット, t3.large)
RDS(MySQL 8.0.17, マルチAZ, db.r3.large)

CS-Cart マーケットプレイス版(4.9.2_JP_1)
PHP(7.2.30)
PHP-fpm(7.2.30)
Apache(2.4.39)

– AWS環境(ロードバランサ + EC2 + RDS のシンプル構成です)

3. 問題発生時のリソース状況


以下は CloudWatch ダッシュボードで表示したメトリクスです。
※ 表示されている値は 5 分間隔の平均値です。

問題が発生した 19:00 前後の状況を確認すると、

①EC2
 1. CPUUtilization(CPU 使用率)は特に問題なし
 2. CpuWait(CPU 待ち時間)が上がっている

②ApplicationELB(ロードバランサ)
 1. TargetResponseTime(ユーザへリクエストが返却されるまでの時間)が上がっている
 2. NewConnectionCount(新規接続数)は遅延が発生した時間帯以外と比較する限り問題なし

③RDS
 1. DatabaseConnections(DBコネクション数)が上がっている
 2. CPUUtilization(CPU 使用率)は特に問題なし
 3. ReadLatency(SelectSQLの待ち時間)が上がっている

上記のため、

・WEB サーバが忙しい訳ではない。
・DB コネクション数が上がっている時間帯に処理待ちが多く発生し、レスポンスタイムが遅くなっている。

という状況でした。


4. 原因


RDS の Performance Insights を確認したところ、問題が発生した時間帯で「Table_locks_waited(テーブルロックによる処理待ち)」が多数発生していました。

「Table_locks_waited MySQL」で調べると、テーブルのストレージエンジンが「MyISAM(マイアイサム)」の場合は テーブルロック方式という記述が、、

CS-Cart テーブルのストレージエンジンを確認したら「lock_keys」テーブルを除き、全て「MyISAM(マイアイサム)」でした。

※ 詳しい内容は後述致します。

テーブルのストレージエンジンを確認するためのSQLは以下です。

select TABLE_NAME, ENGINE from information_schema.TABLES where TABLE_SCHEMA like '%cscart%';

テーブルロックの原因となっているストレージエンジンの変更を検討したいですが、CS-Cart が MyISAM 以外に対応していない可能性があるため、「CS-Cart MyISAM」で調べたところ、
CS-Cart は InnoDB(イノディービー) にも対応しているので問題ない。という情報と、
CS-Cart 開発者が高速化の方法について説明している記事の中で InnoDB への変換を推奨している情報が見つかりました。

MySQLストレージエンジン
How to speed up CS-Cart. Server details

これで裏付けが取れましたので、以降の対処を行いました。


5. 対処内容


それぞれ以下の作業を行いました。

①RDS
 CS-Cart テーブルのストレージエンジンを「MyISAM(マイアイサム)」から「InnoDB(イノディービー)」へ変更

②EC2
 OPcache と APCu を導入
 ※ こちらはテーブルロックへの対処ではないですが、高速化の観点で CS-Cart 開発者から推奨されているため、実施しました。

①RDS

CS-Cart テーブルのストレージエンジンを「MyISAM(マイアイサム)」から「InnoDB(イノディービー)」へ変更

MyISAM はテーブルロックの方式なので、参照クエリや更新クエリが多くなるにつれ、処理待ちが発生します。

共有ロック
(READロック)
他のプロセスは読み込みはできるが、書き込みができなくなる
排他ロック
(WRITEロック)
他のプロセスは読み込みも書き込みもできない

本件の問題が発生した際はテーブルロックが多く発生しておりましたので、
処理待ちが発生した結果、ユーザへ HTTP 500 エラーが返却されてしまいました。

この問題を解消するため、テーブルエンジンを InnoDB へ変更します。

InnoDB を採用した理由は行ロック方式であることと、
CS-Cart は InnoDB でも適切に動作する旨、公式サイトに明記されているためです。
MySQL のテーブルエンジンは MyISAM や InnoDB の他にもありますが、特殊なユースケースで使用するタイプのため検討しておりません。
気になる方は公式サイトの「第16章代替ストレージエンジン」リンクを辿ってください。

公式サイト
第16章代替ストレージエンジン


MyISAM から InnoDB への変更は、

ALTER TABLE cscart_addon_descriptions ENGINE=InnoDB;

このように「ALTER TABLE」SQL で行います。
ひとつひとつ SQL を書くのが大変なので、以下のように一括で作成します。

select
concat('ALTER TABLE ', TABLE_NAME, ' ENGINE=InnoDB;') as 'sql'
from
information_schema.tables
where
table_schema like '%cscart%'
and
engine = 'MyISAM';

CS-Cart の全テーブルを MyISAM から InnoDB へ変更したら RDS への対処は完了です。

②EC2

OPcache と APCu を導入

PHP はインタプリタ言語のため、呼び出される度にプログラムの解析/解釈が行われ、オーバーヘッドがあります。
今回はクライアントへのレスポンス速度を改善する必要があるため、PHP の速度改善に寄与する「OPcache」と「APCu」をインストールします。

— 解説
OPcache を導入すると、コンピュータが解析済みの「バイトコード」をキャッシュして再利用してくれるため、処理が高速になります。
APCu は PHP が動的に作成したデータをキャッシュしてくれるので、ページの表示速度が改善されます。

本環境の PHP は「remi」リポジトリでインストールされているため、
インストールコマンドは以下になります。

$ sudo yum install --enablerepo=remi,remi-php72 php-opcache php-pecl-apcu

※ OPcache が php-opcache です。
※ APCu が php-pecl-apcu です。

インストールが終わりましたら、Apache を再起動しましょう。

$ sudo systemctl restart httpd

以下の表示となれば完了です。

OPcache(with Zend OPcache … の記述が追加されます)

$ php -v

PHP 7.2.30 (cli) (built: May 5 2020 18:04:45) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.30, Copyright (c) 1999-2018, by Zend Technologies

APCu(apcu が表示されます)

$ php -i | grep apc
/etc/php.d/40-apcu.ini,
apcu
MMAP File Mask => /tmp/apc.XXXXXX
apc.coredump_unmap => Off => Off
apc.enable_cli => Off => Off
apc.enabled => On => On
apc.entries_hint => 4096 => 4096
apc.gc_ttl => 3600 => 3600
apc.mmap_file_mask => /tmp/apc.XXXXXX => /tmp/apc.XXXXXX
apc.preload_path => no value => no value
apc.serializer => php => php
apc.shm_segments => 1 => 1
apc.shm_size => 32M => 32M
apc.slam_defense => On => On
apc.smart => 0 => 0
apc.ttl => 0 => 0
apc.use_request_time => On => On
apc.writable => /tmp => /tmp

本来、性能問題を解決するにあたっては、1つ1つ対応を試し、原因箇所の特定をし、対策をし、効果を確認するのが鉄則ですが、本記事では「それぞれどの程度効果があったのか」についての測定結果がありません。
急を要したため、テーブルのストレージエンジン変更とPHPのキャッシュ化を同時に行い解決を優先しました。


6. 対処後のリソース状況


※ 表示されている値は 5 分間隔の平均値です。

対応前後の状況を比較すると、

– ApplicationELB(ロードバランサ)
 1. TargetResponseTime(ユーザへリクエストが返却されるまでの時間)
   6.87 → 0.8
 2. NewConnectionCount(新規接続数)
   6.87 → 395

レスポンスタイムが改善されていることが確認できます。



本記事は以上です。
問題が発生した際に CS-Cart についての情報をインターネットで検索したのですが、なかなか欲しい情報が見つからず苦労したため、掲載致しました。
解決への一助となりましたら幸いです。

Dockerを使ったMagentoの開発環境構築方法

1. 概要

今回はECサイト構築プロダクトの代表例であるMagentoプラットフォームの開発環境をローカルでDockerを使って構築する方法を紹介します。Magentoとは利用者にユーザ管理、カートシステム、及び決済方法をはじめ、高度な機能と柔軟な変更性を提供したECサイトのプラットフォームです。本記事では、Magentoをダウンロードし、インストールする方法をまとめています。環境構築をポータブルで、扱いやすくするためにdocker-composeを使っています。

本記事内容

  • Magentoとは
  • 本記事を読む前提知識
  • 構築環境準備
  • 環境のデザイン・設計
  • 実装
  • 構築手順
  • 考察・まとめ

2. 本記事を読む前提知識

本記事は以下のような読者の方に向けて書きました。

  • ECサイトの構築、開発等に興味があり、新しいプラットフォームを探している人
  • 新しいフレームワーク、プラットフォーム勉強が好きな人
  • Magentoの開発、自作モジュールを勉強したい人

事前知識

以下の情報・知識について理解があることを前提としています

  • linuxの基本的な知識(ターミナル上コマンドで基本的な操作できる)
  • docker、 docker-composeコンテナー技術
    • dockerは、仮想化・インフラ分野で注目されている技術
    • docker-composeは複数のdockerコンテナーを一斉に管理できるようなツールです。複数のdockerコンテナーから構成される複雑システムをdockerのコマンドだけで操作・管理するのは膨大な作業になってしまう可能性があります。そこで、docker-composeがよく利用されている。docker-composeを使ったら1つのファイルに必要なコンテナーを記述して、docker-compose upという1つのコマンドを実行させるだけで環境が構築されます。
  • nginx、php、composer
    • nginxはサーバ上でウェブを立てるサービス
    • composerはphp依存関係ライブラリを管理するシステム
  • redis
    • インメモリーデータベースシステムで、アクセス性能がいいのでキャッシュ用に使用されていることが多いです。
  • elasticsearch
    • 大量のデータ中で早く検索できるような技術
  • microservice (マイクロサービス)
    • dockerといったコンテナー技術の流行により、大きなシステムを構築する際に、複数の小さいサービスの組み合わせで構築する考え方が注目されています。これをマイクロサービスとよんでいます。マイクロサービスの利点として、開発・変更にはシステム全体ではなくて一部のサービスだけが対象となり、アジャイル的な開発に向いています。

3. Magentoとは

MagentoはPHPで開発され、Symfony、Laminas (バージョン2.3.4まではZend)等を利用した大規模ECサイト向けのプラットフォームです。本プラットフォームはオープンソースプロジェクトで、2018年にAdobeに買収されました。ライセンスとして無料版(Community version)と有料版(Enterprise version)があり、有料版の方にセキュリティー、性能性、利用性の観点からモジュールが追加されています。BuiltWith®サイトの調査によるとMagentoは世界中で利用されているプラットフォームのうち3番目になっています。

Magentoの主なメリットとデメリットは以下の通りです。
【利点】
・大規模性
・ECサイトに必須な機能の全てが揃っています
・要件によって柔軟に変更できます
・オープンソースなので、Communityバージョンを無料でも利用可能
【弱点】
・小規模なデータの扱いには向いていません
・非常に複雑なプラットフォームなので習得に時間がかかります

4. 構築環境準備

  • dockerとdocker-composeのインストール
    • このサイトのガイドに沿ってdockerとdocker-composeのインストール
  • Magentoマーケットプレイスでアカウント作成
  • 作成したアカウントのアクセスキーを作成
    • ログインした後に、このサイトにアクセスし、アクセスキーを作成

5. 環境のデザイン・設計

MagentoのECサイトを構築する際、以下のサービスを立てる必要があります:

  • webサーバ構築に必要なサービス
    • nginxサービス
    • phpサービス
    • データベースサービス
  • Magentoサイトに利用するサービス
    • redis:キャッシュサービス、セッション用とキャッシュ用で2つ必要
    • elasticsearch:検索エンジン

したがって、今回はnginx、php、データベース、redis-session、redis-cache、elasticsearchといった6つのサービスをdockerで立てます。以下は環境のデザイン図になります。

以下にはブラウザからデータがどうやって流れて処理されていく手順を説明します:

  • ユーザはブラウザからアクセスするときに、リクエストはnginxに80ポートで送信されます。
  • nginxは、phpでの処理が必要なら(動的なコンテンツ)、phpに9000ポートで依頼を送信します。
  • Magentoのほとんどの処理がphpコンテナー上で行われるが、処理中にredis-session、redis-cache、elasticsearch、データベースに接続します。
  • 処理されたレスポンスがユーザのブラウザへ返却されます。

magentoのソースコードとデータベースのファイルをコンテナーと開発パソコンの間に共有した理由を以下に説明します:

  • 開発中に編集したファイルをすぐにMagentoのdocker環境に反映させ、テストするのは開発の流れである。したがって、magentoソースコードをnginx、phpコンテナーと共有しました。
  • データベースのボリュームを開発パソコンと共有することによって、この開発環境にデータベースも含めて一緒に他の開発者のパソコンに写したい場合、ファイルとしてコピーするだけで済みます。

6. 環境構築のdocker-composeファイル

本記事で使ったMagento開発環境構築のソースコードはこのgithubからアクセスできる。v0.1タグはちょうど本記事で利用したソースコードとなっています。上に書いてあるように、複数のdockerコンテナーを管理するためにdocker-composeを利用しました。以下に、docker-compose.ymlファイルを表示しています。

version: '3'
services:
  # magentoをダウンロードするため
  composer-install:
    build: ./composer
    volumes:
      - ./html:/html:delegated
      - ./config/composer/auth.json:/root/.composer/auth.json
  # ウェブサーバ
  nginx:
    image: nginx:1.12
    ports: 
      - 80:80
    # magentoコードとnginx設定ファイルの共有
    volumes:
      - ./html:/var/www/html:delegated
      - ./config/nginx/magento.conf:/etc/nginx/conf.d/default.conf:cached
    links:
      - php
    depends_on:
      - php
  # phpサーバ
  php:
    build: ./php
    volumes:
      - ./html:/var/www/html:delegated
    links:
      - db
      - redis-cache
      - redis-sessions
      - elasticsearch
    depends_on:
      - db
  db:
    image: mysql:5.7.31
    volumes:
      - ./db/data:/var/lib/mysql:delegated
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: magento
  elasticsearch:
    image: elasticsearch:7.9.0
    environment:
      - discovery.type=single-node
  redis-cache:
    image: redis:alpine
  redis-sessions:
    image: redis:alpine

nginxサービス

ベースはnginx:1.12イメージで、開発パソコンから80ポートで接続できるようにしています。magentoソースコード(html)をコンテナー上では/var/www/htmlにマウントしています。さらに、このコンテナーはphpコンテナーに接続します(links: -php)。

phpサービス

ベースはphp/Dockerfileになっています。自作のDockerfileでコンテナーを作成している理由は、magentoを動かすために複数のphpパッケージが必要となります。したがって、Dockerfileでphp:7.2-fpmコンテナー上にmagento動作に必要なパッケージをインストールしています。このコンテナーにもmagentoソースコードをマウントしています。さらに、phpコンテナーはdb、redis-cache、redis-sessions、elasticsearchコンテナーに接続します。

elasticsearch、redis-cache、redis-sessionsサービス

基本的にはデフォルトの設定で使うので、定義がベースとなるイメージを設定するだけで良いです。

7. 構築手順

本記事の作業は以下のようなラップトップを開発パソコンとして利用しました。

パソコンモデルMacBook Pro
プロセッサー2.3 GHz Dual-Core Intel Core i5
メモリー16 GB
オペレーティングシステムMacOS Catalina Version 10.15.7
Dockerアプリケーションdocker desktop v2.3.0.5

Magentoソースコードのダウンロード

まずは、このgithubから本記事で利用したソースコードをクローン・ダウンロードします。以下にはソースコードを置いた場所を「ルートフォルダ」とします。

以下には色々なdocker、docker-compose、シェルスクリプトのコマンドをターミナル上で動かす必要があるが、それらをターミナル上でcdコマンドでgithubからのソースコードをおいた「ルートフォルダ」の中で実行しています。したがって、読者はcdコマンドを使って予め、ターミナル上で現在フォルダを「ルートフォルダ」に変更しておけば良いです。

$ cd ルートフォルダ ← 現在フォルダをルートフォルダに変更します

1.magentoのソースコードをcomposerを使って「ルートフォルダ/html」の下にダウンロードします。composerを実行させるためのコンテナーをdocker-composeの中に準備してあります。composerの実行には認証が必要なので、「ルートフォルダ/config/composer/auth.json」ファイルの中身に4.で作成したアクセスキーをコピーする必要があります。

{
    "http-basic": {
        "repo.magento.com": {
            "username": "ここにパブリックキーをコピー",
            "password": "ここにプライベートキーをコピー"
        }
    }
}

2.以下のコマンドをターミナル上で「ルートフォルダ」の下に実行させてmagento 2.3.5バージョンをcomposerを使ってダウンロードできます。このスクリプトの中にはcomposer-installというdockerコンテナーをたてその上にcomposerコマンドでmagentoソースコードをダウンロードするコードが入っています。
※magentoは大きいプラットフォームなので、コマンド実行には数分ぐらいかかります

$ sh magento_download.sh

Magentoのインストール

ダウンロードした後、ターミナル上で「ルートフォルダ」の下にdocker-compose upコマンドで環境を立てます。

$ docker-compose up

その後、magentoをインストールする必要があります。そのためには、phpコンテナー上でmagentoインストールコマンドを実行させる必要があるが、そのためにmagento_install.shスクリプトを準備しています。以下のようにコマンドをターミナル上で「ルートフォルダ」の下に実行させれば、magentoのインストールができます。

$ sh magento_install.sh

シェルの中身は以下のようになっています。そこで、アドミン画面にログインする情報を自分で設定場合は、ADMIN_EMAILとADMIN_PASSWORDの値を変更すれば良いです。

#!/bin/sh

# 各サービスの名前の指定
# もし,.envファイルの中身を変更したら
# こちらも変更する必要がある
PHP=magento_php_1
# DATABASE=magento_db_1

# admin画面にログインするための情報
ADMIN_USER_NAME='admin'
ADMIN_PASSWORD='Password1234'
ADMIN_EMAIL='huchka@sbworks.jp'

docker exec -it ${PHP} php -d memory_limit=-1 /var/www/html/bin/magento setup:install \
  --db-host db --db-name magento --db-user root --db-password root --timezone 'Asia/Tokyo' --currency JPY --use-rewrites 1 --cleanup-database \
  --backend-frontname admin --admin-firstname AdminFirstName --admin-lastname AdminLastName --admin-email ${ADMIN_EMAIL} \
  --admin-user ${ADMIN_USER_NAME} --admin-password ${ADMIN_PASSWORD} --base-url 'http://127.0.0.1/' --language en_US \
  --session-save=redis --session-save-redis-host=redis-sessions --session-save-redis-port=6379 --session-save-redis-db=0 --session-save-redis-password='' \
  --cache-backend=redis --cache-backend-redis-server=redis-cache --cache-backend-redis-port=6379 --cache-backend-redis-db=0 \
  --page-cache=redis --page-cache-redis-server=redis-cache --page-cache-redis-port=6379 --page-cache-redis-db=1 \

# elasticsearchの設定
docker exec -it ${PHP} php /var/www/html/bin/magento config:set catalog/search/engine 'elasticsearch7'
docker exec -it ${PHP} php /var/www/html/bin/magento config:set catalog/search/elasticsearch7_server_hostname 'elasticsearch'

テスト、動作確認

以上で、magentoのダウンロードとインストールが完了したので、ブラウザで127.0.0.1にアクセスしたら以下のような画面が表示されるはずです。

さらに、admin画面に入りたい場合127.0.0.1/adminにアクセスし、インストール時に設定したユーザ名(ADMIN_USER_NAME)とパスワード(ADMIN_PASSWORD)でログインできます。

Magentoはindexer管理、キャッシュ管理、デプロイモード管理等の設定を楽にするためにcliコマンドを標準で提供しています。しかし、標準コマンドをphpコンテナーに入って(docker exec -itコマンドで)実行させる必要があり、一々コンテナーに入るのは無駄な作業になります。したがって、magento_command.shというシェルを本記事の作業で用意しました。例えば、キャッシュの状況を表示するmagentoのコマンドは”php bin/magento cache:status”です。これを実行させるためには以下のコマンドを打つだけで済みます。以下の結果から、全ての種類のデータをキャッシュするように設定されていることが分かります。

$ sh magento_command.sh cache:status
Current status:
                        config: 1
                        layout: 1
                    block_html: 1
                   collections: 1
                    reflection: 1
                        db_ddl: 1
               compiled_config: 1
                           eav: 1
         customer_notification: 1
            config_integration: 1
        config_integration_api: 1
                google_product: 1
                     full_page: 1
             config_webservice: 1
                     translate: 1
                        vertex: 1

8. 考察・まとめ

Magentoはサイズ的に膨大なphpスクリプトから構成されている大きなプラットフォームです。したがって、開発中や本番ライブでの性能が問題となってくることが多いです。性能を上げるためには、redis、varnish、elasticsearch等のキャッシュサービスと検索エンジンを利用することを強くお勧めします。さらに、CPU周波数が高くて、SSDを持っているサーバ、パソコンを利用した方が良いです。例えば、AWS上で本番のサーバを構築しているならC5系のインスタンスが適しています。magentoの処理は多数のphpファイルを渡って実行されるように設計されており、例えば一つのレスポンスに対し、100以上のphpスクリプトが実行されているということもあるからです。

ラップトップ上のdockerでMagentoの開発環境を何も考えずに構築すると、性能がとても低くなり、開発・作業できないぐらいなものになるケースもあります。そのときに最初に確認するべきは、dockerのリソースの設定になります。本記事で使った設定は以下の写真のように、CPUとMemoryはラップトップの半分ずつにしています。
次に、dockerコンテナーへのマウント(共有)する方法について確認します。つまり、htmlフォルダをどんなモードでマウントしているかということです。基本的には、volumeのマウントをdelegatedにした方が良いです。この設定をしたら、dockerコンテナーの方がファイル書き込みがホストのファイルに反映されるのに遅延を入れて性能をあげています。

本記事ではMagentoプラットフォームの開発環境を構築する方法をまとめました。環境構築にはマイクロサービスとして流行っているdocker (docker-compose)を利用しました。