オープンソースERPの「iDempiere」を日本の商習慣に対応させた「JPiere」を、DockerComposeで構築してみました。企業級ERPシステムのDocker化は想像以上に複雑で、特にJetty設定の動的生成やデータベース初期化の自動化などめちゃムズかったです
JPiereとは何か
JPiereは、世界的に利用されているオープンソースERP「iDempiere」を日本の商習慣に適応させたERPシステムです。消費税計算、日本の会計基準、商取引慣行などに対応しており、中小企業から大企業まで幅広く利用されているそうです。
主な特徴
- 販売管理、購買管理、在庫管理、財務会計の統合システム
- 日本の消費税法に完全対応
- Webベースのユーザーインターフェース
- 多言語対応(日本語、英語など)
- プラグインによる機能拡張
今回構築する環境
システム構成
アプリケーションサーバー
- JPiere 11(iDempiere 11ベース)
- Java 17(Eclipse Temurin)
- Jetty Webサーバー
データベース
- PostgreSQL 13
- 日本語対応設定(UTF-8、Locale C)
プロジェクト構成
実際のファイル構成は以下の通りです。
jpieredocker/
├── Dockerfile # JPiereアプリケーション用イメージ
├── docker-compose.yaml # サービス構成定義
├── docker-entrypoint.sh # 起動時スクリプト
└── files/
├── JPiereServer11.gtk.linux.x86_64.zip # JPiereサーバー本体
└── ExpDat.jar # データベース初期データ
files/
ディレクトリにダウンロードしたJPiereファイルを配置しました。
Docker Composeの詳細設定
本当は環境変数を使うべきでしょうが簡易的に…
PostgreSQLサービス
postgres:
image: postgres:13
platform: linux/amd64
hostname: "postgres"
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init-postgres.sql:/docker-entrypoint-initdb.d/init-postgres.sql
ports:
- "5432:5432"
networks:
- jpiere_network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
重要なポイント:
JPiereサービス
jpiere:
build:
context: .
dockerfile: Dockerfile
platform: linux/amd64
image: jpiere:11
hostname: "jpiere"
ports:
- "8080:8080" # Web UI
- "12612:12612" # Telnet管理コンソール
environment:
- TZ=Asia/Tokyo
- DB_NAME=idempiere
- DB_HOST=postgres
- DB_PORT=5432
- DB_USER=adempiere
- DB_PASS=adempiere
- DB_ADMIN_PASS=postgres
- JAVA_OPTS=-Xms256M -Xmx2048M
- MIGRATE_EXISTING_DATABASE=false
- DISABLE_SSL=true
volumes:
- jpiere_config:/opt/idempiere/configuration
- jpiere_plugins:/opt/idempiere/plugins
- jpiere_log:/opt/idempiere/log
depends_on:
postgres:
condition: service_healthy
networks:
- jpiere_network
重要なポイント:
SSL無効化: 開発環境での簡単な接続のため
Dockerfileの詳細解析
基盤イメージとパッケージ
FROM --platform=linux/amd64 eclipse-temurin:17-jdk-jammy
ENV IDEMPIERE_VERSION 11
ENV IDEMPIERE_HOME /opt/idempiere
ENV IDEMPIERE_PLUGINS_HOME $IDEMPIERE_HOME/plugins
ENV IDEMPIERE_LOGS_HOME $IDEMPIERE_HOME/log
# 必要なパッケージのインストール
RUN apt-get update && \
apt-get install -y --no-install-recommends nano postgresql-client unzip && \
rm -rf /var/lib/apt/lists/*
Eclipse Temurin 17を使用することで、Oracle JDKのライセンス問題を回避しています。
JPiereサーバーの展開処理
# ローカルファイルをコンテナにコピー
COPY files/JPiereServer11.gtk.linux.x86_64.zip /tmp/jpiere-server.zip
COPY files/ExpDat.jar /tmp/ExpDat.jar
# JPiereサーバーのセットアップ
RUN echo "=== JPiere Server Setup Start ===" && \
echo "Hash: $(md5sum /tmp/jpiere-server.zip)" > $IDEMPIERE_HOME/MD5SUMS && \
echo "Date: $(date)" >> $IDEMPIERE_HOME/MD5SUMS && \
echo "=== Extracting JPiere Server ===" && \
cd /tmp && \
unzip -q jpiere-server.zip && \
echo "=== Finding jpiere-server directory ===" && \
JPIERE_DIR=$(find /tmp -maxdepth 1 -name "jpiere-server*" -type d | head -1) && \
echo "Found JPiere directory: $JPIERE_DIR" && \
if [ -n "$JPIERE_DIR" ] && [ -d "$JPIERE_DIR" ]; then \
cp -r "$JPIERE_DIR"/* "$IDEMPIERE_HOME"/ && \
rm -rf "$JPIERE_DIR" && \
rm -f /tmp/jpiere-server.zip; \
else \
echo "ERROR: JPiere server directory not found!" && \
exit 1; \
fi
この部分で重要なのは:
- MD5ハッシュ記録: ビルド時のファイル整合性を記録
- 動的ディレクトリ検索: ZIPファイル内のディレクトリ名を自動検出
- エラーハンドリング: 展開に失敗した場合はビルド停止
データベース初期データの処理
# ExpDat.jarの解凍(JPiere用PostgreSQLダンプファイル)
RUN echo "=== ExpDat.jar Processing Start ===" && \
cd /tmp && \
if [ -f ExpDat.jar ]; then \
unzip ExpDat.jar && \
ls -la ExpDat.dmp && \
echo "ExpDat.dmpファイルを/tmp/に配置完了" && \
rm ExpDat.jar; \
else \
echo "ERROR: ExpDat.jar not found!" && \
exit 1; \
fi
ExpDat.jarには、JPiere用のPostgreSQLダンプファイル(ExpDat.dmp)が含まれています。これがJPiereのマスターデータと初期設定データです。
環境設定ファイルの生成
# myEnvironment.shの作成と必要ファイルの確認
RUN echo "=== Environment Setup ===" && \
if [ ! -d "$IDEMPIERE_HOME/utils" ]; then \
mkdir -p "$IDEMPIERE_HOME/utils"; \
fi && \
if [ ! -f "$IDEMPIERE_HOME/utils/myEnvironment.sh" ]; then \
echo "#!/bin/bash" > "$IDEMPIERE_HOME/utils/myEnvironment.sh" && \
echo "export JAVA_HOME=${JAVA_HOME}" >> "$IDEMPIERE_HOME/utils/myEnvironment.sh" && \
echo "export IDEMPIERE_HOME=${IDEMPIERE_HOME}" >> "$IDEMPIERE_HOME/utils/myEnvironment.sh" && \
echo "export PATH=\$PATH:\$JAVA_HOME/bin" >> "$IDEMPIERE_HOME/utils/myEnvironment.sh" && \
chmod +x "$IDEMPIERE_HOME/utils/myEnvironment.sh"; \
fi
iDempiereが期待する環境設定ファイルを動的に生成しています。
エントリーポイントスクリプトの複雑さ
docker-entrypoint.shは、このプロジェクトで最も複雑で重要な部分です。この部分の実装に最も時間がかかりました。
環境変数の設定
# 環境変数設定
JAVA_OPTIONS=${JAVA_OPTIONS:-""}
KEY_STORE_PASS=${KEY_STORE_PASS:-myPassword}
KEY_STORE_ON=${KEY_STORE_ON:-idempiere.org}
KEY_STORE_OU=${KEY_STORE_OU:-"JPiere Docker"}
HOST=${HOST:-0.0.0.0}
IDEMPIERE_PORT=${IDEMPIERE_PORT:-8080}
IDEMPIERE_SSL_PORT=${IDEMPIERE_SSL_PORT:-8443}
TELNET_PORT=${TELNET_PORT:-12612}
DB_HOST=${DB_HOST:-postgres}
DB_PORT=${DB_PORT:-5432}
DB_NAME=${DB_NAME:-idempiere}
DB_USER=${DB_USER:-adempiere}
DB_PASS=${DB_PASS:-adempiere}
DB_ADMIN_PASS=${DB_ADMIN_PASS:-postgres}
MIGRATE_EXISTING_DATABASE=${MIGRATE_EXISTING_DATABASE:-false}
DISABLE_SSL=${DISABLE_SSL:-false}
これらの環境変数は、iDempiereのconsole-setup.sh
に自動入力するために使用されます。
PostgreSQL接続待機
Postgresとの接続に何度も失敗したので接続確認するようにしました
# PostgreSQL接続待機
RETRIES=30
until PGPASSWORD=$DB_ADMIN_PASS psql -h $DB_HOST -U postgres -c "\q" > /dev/null 2>&1 || [[ $RETRIES == 0 ]]; do
echo "Waiting for postgres server, $((RETRIES--)) remaining attempts..."
sleep 1
done
if [[ $RETRIES == 0 ]]; then
echo "PostgreSQL connection failed. Shutting down..."
exit 1
fi
Jetty設定の動的生成(最難関部分)
Jettyサーバー設定ファイルの動的生成もめちゃくちゃややこしかったです。
setup_jetty_config() {
echo "=== Setting up Jetty configuration files ==="
local jetty_etc_dir="$IDEMPIERE_HOME/jettyhome/etc"
local template_dir="$IDEMPIERE_HOME/org.adempiere.server-feature/jettyhome/etc"
# テンプレートディレクトリが存在しない場合は、現在のディレクトリをテンプレートとして使用
if [[ ! -d "$template_dir" ]]; then
template_dir="$jetty_etc_dir"
fi
# テンプレートファイルの処理
for template_file in "$template_dir"/*-template.xml; do
if [[ -f "$template_file" ]]; then
local basename=$(basename "$template_file")
local target_file="$jetty_etc_dir/${basename/-template/}"
# プレースホルダーの置換
sed -e "s|@ADEMPIERE_SSL_PORT@|$IDEMPIERE_SSL_PORT|g" \
-e "s|@ADEMPIERE_PORT@|$IDEMPIERE_PORT|g" \
-e "s|@ADEMPIERE_WEB_PORT@|$IDEMPIERE_PORT|g" \
-e "s|@ADEMPIERE_WEB_SSL_PORT@|$IDEMPIERE_SSL_PORT|g" \
-e "s|@ADEMPIERE_HOST@|$HOST|g" \
-e "s|@ADEMPIERE_KEYSTORE@|$jetty_etc_dir/keystore|g" \
-e "s|@ADEMPIERE_KEYSTOREPASS@|$KEY_STORE_PASS|g" \
-e "s|@ADEMPIERE_KEYPASS@|$KEY_STORE_PASS|g" \
"$template_file" > "$target_file"
fi
done
}
- テンプレートファイルの場所: JPiereのディレクトリ構造が複雑で、テンプレートファイルの場所を特定するのに時間がかかりました
- プレースホルダー置換: 一つでも置換し忘れるとJettyが起動しません
- SSL設定の処理: 最近はゲートウェイを使うのが普通だと思う。SSLを無効化したい
SSL無効化の処理
# SSL設定の無効化(オプション)
if [[ "${DISABLE_SSL:-false}" == "true" ]]; then
echo "=== Disabling SSL configuration (DISABLE_SSL=true) ==="
# SSL設定ファイルを削除するのではなく、最小限の設定ファイルを作成
if [[ -f "$jetty_etc_dir/jetty-ssl.xml" ]]; then
cat > "$jetty_etc_dir/jetty-ssl.xml" << 'EOF'
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!-- SSL disabled configuration -->
<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory$Server">
<Set name="Provider"><Property name="jetty.sslContext.provider"/></Set>
</Configure>
EOF
fi
fi
SSL設定ファイルを単純に削除するとJettyが起動しないため、最小限の内容で上書きする必要がありました。
console-setup.shの自動実行
# コンソールセットアップ実行
echo "Executing console-setup..."
echo -e "$JAVA_HOME\n$JAVA_OPTIONS\n$IDEMPIERE_HOME\n$KEY_STORE_PASS\n$KEY_STORE_ON\n$KEY_STORE_OU\n$KEY_STORE_O\n$KEY_STORE_L\n$KEY_STORE_S\n$KEY_STORE_C\n$HOST\n$IDEMPIERE_PORT\n$IDEMPIERE_SSL_PORT\nN\n2\n$DB_HOST\n$DB_PORT\n$DB_NAME\n$DB_USER\n$DB_PASS\n$DB_ADMIN_PASS\n$MAIL_HOST\n$MAIL_USER\n$MAIL_PASS\n$MAIL_ADMIN\nY\n" | ./console-setup.sh
iDempiereの対話式セットアップを自動化するため、改行区切りで全ての入力値を事前に用意してecho -e
でパイプしています。
データベース初期化の自動判定
# データベース初期化チェック
if ! PGPASSWORD=$DB_PASS psql -h $DB_HOST -U $DB_USER -d $DB_NAME -c "\q" > /dev/null 2>&1 ; then
echo "Database '$DB_NAME' not found or user '$DB_USER' doesn't exist, initializing..."
# PostgreSQLユーザー作成
PGPASSWORD=$DB_ADMIN_PASS psql -h $DB_HOST -U postgres -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" 2>/dev/null
# データベース作成
PGPASSWORD=$DB_ADMIN_PASS psql -h $DB_HOST -U postgres -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;" 2>/dev/null
# JPiere固有: ExpDat.dmpでデータベース初期化
echo "Importing JPiere database from ExpDat.dmp..."
PGPASSWORD=$DB_ADMIN_PASS psql -h $DB_HOST -U postgres -d $DB_NAME -f /tmp/ExpDat.dmp
# データベース同期
cd utils
./RUN_SyncDB.sh
cd ..
# データベース署名
./sign-database-build.sh
fi
この部分では、データベースとユーザーの存在確認、作成、ExpDat.dmpからの初期化、スキーマ同期、署名という一連の処理を自動化しています。
まとめ
JPiereのDockerCompose環境構築を通じて学んだことを率直に共有します。
苦労した点
1. 企業級ERPの複雑さ
単純なWebアプリケーションとは次元の違う複雑さでした。特に:
- Jetty設定ファイルの動的生成
- データベース初期化の自動化
- SSL設定の適切な無効化
- iDempiereのconsole-setup.shの自動化
これらの実装に、予想以上の時間がかかりました。
2. ドキュメントの不足
JPiereやiDempiereのDocker化に関する情報は非常に限られており、エラーメッセージもJavaスタックトレース中心で根本原因の特定が困難でした。
3. Apple Siliconでの性能問題
DockerでRosettaを有効にしてx86_64エミュレーションしています。
あとがき
本当にこんなにインストール大変なんでしょうか
正しい方法をご存知でしたらご教示ください