🔖

docker compose upだけでSchemaSpyを同時起動!!

2023/12/21に公開

はじめに

この記事は エンジニア組織の開発生産性・開発者体験向上の取り組みをシェアしよう! by Findy Advent Calendar 2023 シリーズ3 の21日目の記事となります。

株式会社DELTAでエンジニアをしている長田(おさだ)です。
https://note.com/delta_sevenrich/n/n15f551a4d7a5

既存のデータベースからER図を出力してくれるSchemaSpyを導入しようとしたものの、『docker compose up[1]の一発実行で他のサービスと一緒にSchemaSpyも同時に起動できるようにする』を実現させようとしているうちに、色々とハマって時間を溶かしてしまったので備忘録として残したいと思います。

背景

私たちの開発チームではdocker compose upの一発実行だけでデータベースやAPIサーバー・フロントエンドまで全てのサービスを起動させることで、開発環境のセットアップを効率化して開発者間の環境差異も最小限にしています。
今回はSchemaSpyも同じ様に導入しようと思ったものの、別々に起動する方法の記事が多かったことと、個人的にもDockerについて知らない部分を学ぶ機会になったので記事にしました。
とはいえ、SchemaSpyの導入方法を主軸にしていますので、SchemaSpyを導入しようと思ったときに役に立てれば幸いです。

結果

以下の構成にすることで同時起動が可能になります

実行環境

docker compose version
Docker Compose version v2.22.0-desktop.2
docker --version
Docker version 24.0.6, build ed223bc820

ディレクトリ構成

├── compose.yml
└── schemaspy
   ├── Dockerfile.dev
   ├── output // SchemaSpyによって生成されたドキュメント
   │   ├── anomalies.js
   │   ├── bower
   │   ├── column.js
   │   ├── constraint.js
   │   ├── favicon.png
   │   ├── fonts
   │   ├── images
   │   ├── index.html
   │   ├── main.js
   │   ├── public
   │   ├── relationships.js
   │   ├── routines
   │   ├── routines.js
   │   ├── schemaSpy.css
   │   ├── schemaSpy.js
   │   └── tables
   └── schemaspy.properties
compose.yml
services:
  db:
    container_name: postgres_db
    image: postgres:15.3
    restart: always
    ports:
      - 5432:5432
    networks:
      - app_network
    environment:
      - TZ=Asia/Tokyo
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    volumes:
      - database:/var/lib/postgresql/data
    healthcheck:
      test: "psql -U postgres"
      interval: 3s
      timeout: 3s
      retries: 5

  schemaspy:
    build:
      context: .
      dockerfile: ./schemaspy/Dockerfile.dev
    container_name: schemaspy
    image: schemaspy/schemaspy
    networks:
      - app_network
    volumes:
      - ./schemaspy/output:/schemaspy_output
      - ./schemaspy/schemaspy.properties:/schemaspy.properties
    command: "java -jar schemaspy.jar -vizjs"
    depends_on:
      db:
        condition: service_healthy

  nginx_schemaspy:
    image: nginx
    container_name: "nginx_schemaspy"
    depends_on:
      - schemaspy
    ports:
      - "8088:80"
    volumes:
      - ./schemaspy/output:/usr/share/nginx/html:ro
      
volumes:
  database:

networks:
  app_network:
schemaspy/Dockerfile.dev
FROM openjdk:11-jdk

RUN apt-get update && apt-get install -y fontconfig fonts-dejavu && \
    wget -O schemaspy.jar https://github.com/schemaspy/schemaspy/releases/download/v6.2.4/schemaspy-6.2.4.jar && \
    wget -O /postgresql.jar https://jdbc.postgresql.org/download/postgresql-42.6.0.jar && \
    rm -rf /var/lib/apt/lists/*
schemaspy/schemaspy.properties
# データベースのタイプをpgsqlで指定
schemaspy.t=pgsql
# schemaspyのコンテナ内でのJDBCドライバのパス
# Dockerfile.devで`wget -O /postgresql.jar~~~`とルート直下で指定している
schemaspy.dp=/postgresql.jar
# データベースのプロパティを指定
# hostはサービス名を指定(コンテナ名ではない)
schemaspy.host=db
schemaspy.port=5432
# dbはコンテナ作成時に指定していないためデフォルトのpostgres
schemaspy.db=postgres
schemaspy.u=postgres
schemaspy.p=postgres
# schemaspyのコンテナ内での出力先
schemaspy.o=./schemaspy_output
# どのデータベーススキーマに対して生成するか指定。pgsqlはpublicと指定
schemaspy.schemas=public

結論

  • SchemaSpyの起動
  • データベースとSchemaSpyのコンテナ間アクセス
  • SchemaSpyコンテナの出力とホスト側へのデータ受け渡し
  • SchemaSpyの生成したデータへホスティング

を順番に整理していくことでdocker compose upの一発起動による開発環境の構築が可能になりました。

動作確認と一部解説

動作確認

上記「結果」の構成をまるっと導入することでSchemaSpyによって生成されたHTMLにNginxを通じてアクセス(localhost:8088)できるようになります。が、テーブル定義がないためリレーションの画面では下記のようなエラーが出ています。
テーブルが作成されてないので当然です。

テーブル定義

そこで、テーブル作成して動作確認をします。

以下のコマンドでpostgres_dbへアクセスして

docker exec -it postgres_db psql -U postgres

以下のSQLを流します

CREATE TABLE IF NOT EXISTS clinics (
   "id" serial PRIMARY KEY,
   "name" VARCHAR (256) NOT NULL,
   "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS departments (
   "id" serial PRIMARY KEY,
   "name" VARCHAR (256) NOT NULL,
   "clinic_id" INT,  -- 外部キーとしてクリニックIDを追加
   "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   FOREIGN KEY ("clinic_id") REFERENCES clinics ("id") ON DELETE SET NULL  -- 外部キー制約を追加
);

CREATE TABLE IF NOT EXISTS patients (
   "id" serial PRIMARY KEY,
   "name" VARCHAR (256) NOT NULL,
   "sex" VARCHAR (256) NOT NULL,
   "clinic_id" INT,  -- 外部キーとしてクリニックIDを追加
   "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   FOREIGN KEY ("clinic_id") REFERENCES clinics ("id") ON DELETE SET NULL
);

CREATE TABLE IF NOT EXISTS treatments (
   "id" serial PRIMARY KEY,
   "name" VARCHAR (256) NOT NULL,
   "department_id" INT,  -- 外部キーとして診療科IDを追加
   "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   FOREIGN KEY ("department_id") REFERENCES departments ("id") ON DELETE SET NULL
);
SchemaSpyコンテナの再起動

SchemaSpyにはデータベースの変更を自動的に検出して出力を更新する機能がないので再読み込みをする必要がありますので、SchemaSpyコンテナを再起動します。

docker-compose restart schemaspy

http://localhost:8088/public/relationships.htmlにアクセスしてみることで以下のようにER図が作成されていることが確認できます。


SchemaSpyを起動してPostgreSQLにアクセスできるようにする

SchemaSpyのDockerfile.dev

SchemaSpy本体の他にPostgreSQLのJDBC Driverが必要になります。
Dockerfile.devでSchemaSpyのコンテナ内にダウンロードして、schemaspy.propertiesで使用するための指定をしています。

上記のDockerfile.devとschemaspy.propertiesの該当部分を再記載
schemaspy/Dockerfile.dev
# SchemaSpyのダウンロード
    wget -O schemaspy.jar https://github.com/schemaspy/schemaspy/releases/download/v6.2.4/schemaspy-6.2.4.jar && \
# PostgreSQLのJDBC Driverのダウンロード
    wget -O /postgresql.jar https://jdbc.postgresql.org/download/postgresql-42.6.0.jar && \
    rm -rf /var/lib/apt/lists/*
schemaspy/schemaspy.properties
# schemaspyのコンテナ内でのJDBCドライバのパス
# Dockerfile.devで`wget -O /postgresql.jar~~~`とルート直下で指定している
schemaspy.dp=/postgresql.jar

データベースとSchemaSpyのコンテナ間アクセス

schemaspy.propertiesのhost

schemaspy.propertiesのコメントでも記載していますが、サービス名を指定します。
コンテナ名を指定していたため、アクセスできずに時間を溶かしました。

schemaspy.propertiesの該当部分を再記載
schemaspy/schemaspy.properties
# データベースのプロパティを指定
# hostはサービス名を指定(コンテナ名のpostgres_dbではない)
schemaspy.host=db
schemaspy.port=5432
# dbはコンテナ作成時に指定していないためデフォルトのpostgres
schemaspy.db=postgres
schemaspy.u=postgres
schemaspy.p=postgres

SchemaSpyで出力されたドキュメントをホストへ受け渡し

ここは基本的なコンテナとホストのデータ受け渡しについて理解が曖昧でハマりました。
識別しやすいようにホスト側を/schemaspy/outputで、SchemaSpy側を/schemaspy_outputとしています。

schemaspy.propertiesの該当部分を再記載
schemaspy/schemaspy.properties
# schemaspyのコンテナ内での出力先
schemaspy.o=./schemaspy_output
compose.yml
  schemaspy:
    build:
      context: .
      dockerfile: ./schemaspy/Dockerfile.dev
    container_name: schemaspy
    image: schemaspy/schemaspy
    networks:
      - app_network
    volumes:
    # ここでホスト側へ受け渡し
      - ./schemaspy/output:/schemaspy_output
      - ./schemaspy/schemaspy.properties:/schemaspy.properties
    command: "java -jar schemaspy.jar -vizjs"
    depends_on:
      db:
        condition: service_healthy

SchemaSpyのホスティング

もともとローカル環境の開発で使用しているものに追加していて、記載しているものの他ににAPIサーバーやnext.jsの起動もしています。
またCloudBeaver8080で起動していたため、8088というポート番号にしています。

schemaspy.propertiesの該当部分を再記載
compose.yml
  nginx_schemaspy:
    image: nginx
    container_name: "nginx_schemaspy"
    depends_on:
      - schemaspy
    ports:
      - "8088:80"
    volumes:
      - ./schemaspy/output:/usr/share/nginx/html:ro

おわりに

SchemaSpy導入を主軸に、私たちの開発チームが開発環境を効率化していることについて書かせてもらいました。これまで私はサーバーサイドのアプリケーション開発が中心でしたが、チームの変化に伴い、ミドルウェアの実装やビジネスサイドとの対話など、新たな役割にも取り組むようになっています。

手を動かすような実装タスクが減って技術から離れているわけではなく。むしろ、他の開発者が快適に作業できる環境を整えること、将来的なサービス展開を見据えた設計を考えることで、技術のキャッチアップの観点が変わり、より総合的な技術力の向上につながっていると思っています。

Docker Composeを使った開発環境の自動化は、単に作業を楽にするだけではなく、チーム全体の生産性を高め、より創造的な作業に集中できるようにしてくれます。他の開発チームやエンジニアにとって、少しでも参考になれば幸いです。

We're Hiring!

最後までお読みいただきありがとうございます。
株式会社DELTAでは仲間を募集中です!!

まずはカジュアル面談からできればと思います、ぜひPittaから
またはGoogle Formから お申込みください!

脚注
  1. 今はハイフンなしのcomposeになったようです。docker-compose upで止まっていたので勉強になりました。Docker Compose 概要 ↩︎

DELTAテックブログ

Discussion