LoginSignup
3
7

Dockerで環境構築(Next.js+Nginx+CakePHP+PostgreSQL+adminer)

Last updated at Posted at 2024-02-25

はじめに

初めまして!
エンジニアになって数年、今まで本を読むだけでしたが、もっとプライベートで楽しみながら成長したい!自分が学んだ足跡を残していきたい!と思い記事を書きました!
最終的には自在に開発できるようになりたいと思っています。:triumph:
いろいろな記事を参考にさせてもらっています。:bow_tone2:
その中でもこれってどういう意味?とかつまづいたところを念入りに書いていこうかと思います。:fist:

今回の目的

今までの総決算をします!

他、記事の中で紹介させていただきます!

使用したものや事前準備

・Macbook Pro
・ChatGPT
・Github Copilot
・Docker
・Visual Studio Code
・Next.js(React)
・CakePHP(PHP)
・PostgreSQL
・adminer

すぐに試したい方へ〜Github〜

パッケージ類を抜いていますが、今回作成した部分はあげております!

実際に使う方はclone後に下記を実施してください。
・webフォルダに移動し、コマンドの実施

 npm install
 npm run build

・api/srcフォルダに移動し、コマンドの実施

composer install

・api/src/config/app_local.phpのソースを修正(composer install実行後に表示されます)

       'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Postgres',
            'persistent' => false,
            'host' => 'db',
            'username' => 'app',
            'password' => 'app',
            'database' => 'app',
            'encoding' => 'utf8',
            'timezone' => 'UTC',
            'cacheMetadata' => true,
        ],

・プロジェクトフォルド直下に移動し、コマンドの実施

docker compose up

・下記にアクセスして、Helloテーブル>Helloカラム(character 10)を作成し、データも作成
http://localhost:8080/?pgsql=db&server=db&username=app&db=app
パスワード:app
http://localhost:80 にアクセス
※DBで失敗する場合は、 http://localhost:80/hello でアクセスしてエラー出てないか確認

全体構成

目指した機能

・フロントからAPI呼び出せる。
・APIからDBにアクセスしデータを取り出せる。
・APIからフロントにJSONデータを渡せる。
・返却されたJSONデータを画面に表示できる。

技術構成

フロント(静的ページ):Next.js
バック(動的ページ):CakePHP
DB:PostgreSQL
webサーバ:Nginx
アプリケーションWebサーバ:PHP-FPM
DB管理ツール:adminer

フォルダ構成

スクリーンショット 2024-02-26 4.57.37.png

作成手順

各コンテナサービスごとに進めていきます。

apiフォルダ

apiフォルダを作成し、その配下にDockerfileを作成しましょう。

スクリーンショット 2024-02-26 1.09.19.png

Dockerfile作成&ビルド

RUNコマンド
 ・パッケージリスト更新
 ・PHP拡張機能をコンパイルしてインストールするコマンド
 ・CakePHP5をインストールするために必要なintlとZIP関係、依存関係のインストール
 ・PostgreSQLデータベースに接続するための拡張機能、intlとZIPのインストール

COPYコマンド
・Dockerイメージのcomposerを使用

FROM php:8.3-fpm-bookworm

ENV TZ Asia/Tokyo

RUN apt-get update && \
  apt-get install -y libonig-dev libicu-dev libpq-dev libzip-dev zip && \
  docker-php-ext-install pdo_pgsql intl zip

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

WORKDIR /var/www

参考記事:

ターミナルを開いて、apiフォルダに移動しビルドします。

cd api
docker build -t api .

イメージができたか確認しましょう。

docker image ls

中を確認したい人はこちらもみましょう。戻りたい時はexitで戻ってこれます。

docker run -it --rm api:latest /bin/bash 

一旦ここで、abi部分は止めておきます。
apiのプロジェクト作成は後半で行います。

dbフォルダ

compose.ymlを作成し、dbサービスとadminerサービスの設定情報を記述します。

スクリーンショット 2024-02-26 1.10.00.png

compose.yml作成

compose.ymlの記述内容です。

※段階的に理解して進めていくために記述しており、後でまとめて再掲するのでいらない部分は消していただいて構いません。

version: '3'
services:
  db:
    image: postgres:12.18-bookworm
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: app
    volumes:
      - postgres:/var/lib/postgresql/data
    #networks:
      #- api-db-network
      #- db-gui-network
  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
    #networks:
      #- db-gui-network
    depends_on:
      - db

volumes:
  postgres:
#networks:
  # web-api-network:
  #   driver: bridge
  # api-db-network:
  #   driver: bridge
  # db-gui-network:
  #   driver: bridge

:writing_hand:dbのvolumesに設定するパスについて

データの保存先は、使用しているLinuxディストリビューションやPostgreSQLのバージョン等に依存します。
Volumeが二つできた等、不具合が発生した場合は要確認しましょう。

/var/lib/postgresql/data

Debianベースのディストリビューション(Ubuntuを含む)でDebianやUbuntuのパッケージマネージャー(apt)を使用してPostgreSQLをインストールした場合、使用されることが多いです。

/var/lib/pgsql/data

Red Hatベースのディストリビューション(Fedora、CentOS、RHELなど)でよくみられます。

PGDATAの位置については下記を参照ください。

起動確認

compose.ymlのあるフォルダに移動してから、コマンドを実行しましょう。
起動後にログインできることを確認します。

docker compose up -d --build

データベース種類:PostgreSQL
サーバ:db
ユーザ名:app
パスワード:app
データベース:app

スクリーンショット 2024-02-25 12.28.46.png

ログインできたら、試しにテーブルを作成したり、コンテナを再起動してみましょう。テーブルが残っていることが確認できるかと思います。

スクリーンショット 2024-02-25 12.29.24.png

起動したコンテナは終了させます。

docker compose down

webフォルダ

next.jsをビルドしてデプロイするパターンを作成します。
開発環境用に、ホットリロードしながら開発するパターンはまたいずれ・・・。

ローカルマシンにNode.jsが入っていることが前提で進めていきます。
MacBookの方はこちらの記事を参考にして導入してください。

Node.jsのバージョンは20.9.0(LTS)を使用します。

webプロジェクトの作成

スクリーンショット 2024-02-26 1.10.44.png

説明がわかりづらくて申し訳ないですが、、、:sob:
全体のプロジェクト直下で下記のコマンドを実行し、 Webプロジェクトを作成します。
設問が出てきます。project nameはwebにし、他の設定はデフォルト(下記の画像の通り)に進めましょう。

npx create-next-app@latest --typescript

スクリーンショット 2024-02-25 17.58.59.png

少し待って、作成が完了したら実行できるか確認してみましょう。

cd web
npm run dev

スクリーンショット 2024-02-25 18.04.36.png

ホットリロードで変更がかかるのも確認できると尚いいです。
web/app/page.tsxの18行目に自分の名前を入れてみて表示が変わるか確認してみてください。

スクリーンショット 2024-02-26 1.00.44.png

スクリーンショット 2024-02-26 1.02.03.png

ある程度堪能したらcontrol+c(ctrl+c)で終了します。
次の手順に進みましょう。:thumbsup:

ビルド設定変更

Next.jsには静的部分を担当してもらうので、ビルド時にreactと同じように配置するだけで動く設定に変更します。

参考:

(動的な部分が必要な場合には、Nginx使う場合どう構成するのだろうとか、そもそもPHP以外に使うものなのかも整理できていないなとか色々思いましたが、とりあえず今回は置いておきます。。:rolling_eyes:

ちなみに今回の設定をしないと、Next.jsはreactのみの時と違い、ビルド後もサーバー上でnpm startが必要です。Vercelを使用する方法がよくみられたのでいずれ調べることができたらと思います。

webフォルダ直下の next.config.mjs を開いてください。
nextConfig に output: 'export' を追加して保存してください。

output: 'export'

スクリーンショット 2024-02-25 17.43.13.png

ビルドした時に、outフォルダが作成されることを確認します。(今回のNginxサーバーに配置する資材です。)
webフォルダに遷移し、下記コマンドを実行してみてください。

npm run build

スクリーンショット 2024-02-25 18.08.43.png

次にいきましょう。:ok_hand:

nginxフォルダ

全体のプロジェクト直下にnginxフォルダを追加しましょう。

スクリーンショット 2024-02-26 1.15.04.png

下記記事を参考にして設定します。

Nginxのバージョンはメジャーバージョン(mainline)の1.25.4を設定します。

Nginx関連ファイル修正

compose.yml

下記設定に変更しましょう。
・- ./web/out:/usr/share/nginx/htmlは、Web部分で行なったビルドファイルと紐づけています。

version: '3'
services:
  nginx:
    image: nginx:1.25.4-bookworm
    ports:
      - 80:80
    volumes:
      - ./web/out:/usr/share/nginx/html
      - ./nginx/fastcgi_params:/etc/nginx/fastcgi_params
      - ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
    # depends_on:
    #   - api
    # networks:
    #   - web-api-network

fastcgi_params

nginxフォルダ直下にファイルを作成し、下記内容を追加しましょう。

fastcgi_param   QUERY_STRING            $query_string;
fastcgi_param   REQUEST_METHOD          $request_method;
fastcgi_param   CONTENT_TYPE            $content_type;
fastcgi_param   CONTENT_LENGTH          $content_length;

fastcgi_param   SCRIPT_FILENAME         $document_root$fastcgi_script_name;
fastcgi_param   SCRIPT_NAME             $fastcgi_script_name;
fastcgi_param   PATH_INFO               $fastcgi_path_info;
fastcgi_param   PATH_TRANSLATED         $document_root$fastcgi_path_info;
fastcgi_param   REQUEST_URI             $request_uri;
fastcgi_param   DOCUMENT_URI            $document_uri;
fastcgi_param   DOCUMENT_ROOT           $document_root;
fastcgi_param   SERVER_PROTOCOL         $server_protocol;

fastcgi_param   GATEWAY_INTERFACE       CGI/1.1;
fastcgi_param   SERVER_SOFTWARE         nginx/$nginx_version;

fastcgi_param   REMOTE_ADDR             $remote_addr;
fastcgi_param   REMOTE_PORT             $remote_port;
fastcgi_param   SERVER_ADDR             $server_addr;
fastcgi_param   SERVER_PORT             $server_port;
fastcgi_param   SERVER_NAME             $server_name;

fastcgi_param   HTTPS                   $https;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param   REDIRECT_STATUS         200;

default.conf

nginx/conf.d直下にファイルを作成し下記内容を追加しましょう。
・location / {〜は、Next.jsを見にいくように作成しています。
・location /Hello {は、apiサービスが完成していないため、一時的に127.0.0.1へ変更しています。

server {
    listen       80;
    server_name  localhost;

    location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    }

    location /Hello {
       root /var/www/src/webroot/index.php;
       fastcgi_param HTTP_PROXY "";
       fastcgi_pass 127.0.0.1:9000;
       include fastcgi_params;
    }
}

コンテナ起動確認

全体のプロジェクト直下で下記のコマンドを実行してください。
コンテナを起動し、webプロジェクトのビルドファイルで画面を表示できるか確認します。

cd ../
docker compose up -d --build

下記にアクセスしてNode.js画面が表示されたら成功です。

先ほどと同様に修正しても更新がかからないことも確認してみるといいでしょう。

起動したコンテナは終了させます。

docker compose down

:writing_hand:NginxイメージにNode.jsを入れたいときは

Dockerfileを作成してこちらの設定を入れましょう。

RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \
  export NVM_DIR="$HOME/.nvm" && \
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \
  [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  && \
  nvm install 20.9.0

コンテナ起動準備

全体の確認が取れたので協調して動くようにプロジェクト作成と再度修正します。

CakePHPプロジェクト作成

compose.ymlにapiの設定を追加します。他は全てコメントアウトか削除してください。

version: '3'
services:
  api:
    build: ./api
    volumes:
      - ./api:/var/www/
    # networks:
    #   - web-api-network

apiサービスのみ起動します。
compose.ymlがあるフォルダでコマンドを実行しましょう。

docker compose up -d --build

実行コマンドにsrcを追加し、compose.ymlがあるフォルダでコマンドを実行します。(srcフォルダは自動追加されます。)
※Dockerfileがある階層では作成するとフォルダがからじゃないと怒られてエラーが発生します

docker compose exec api bash -c 'composer create-project --prefer-dist cakephp/app:5 ./src'

パーミッションの設定を自動でやってくれるかですがデフォルトのまま(Y)で進めます。

スクリーンショット 2024-02-26 2.05.55.png

フォルダが作成されました。

スクリーンショット 2024-02-26 2.07.27.png

起動したコンテナは終了させます。

docker compose down

設定ファイル&ソースファイル修正

default.conf

パス:nginx/conf.d/default.conf

nginxサービスがapiサービスに繋げられるようにfastcgi_passのポート番号を api に変更します。

    location /Hello {
       root /var/www/src/webroot/index.php;
       fastcgi_param HTTP_PROXY "";
       fastcgi_pass api:9000;
       include fastcgi_params;
    }

app.php

接続設定情報は下記ページの設定項目を参考にしています。

    'default' => [
        'className' => 'Cake\Database\Connection',
        'driver' => 'Cake\Database\Driver\Postgres',
        'persistent' => false,
        'host' => 'db',
        'username' => 'app',
        'password' => 'app',
        'database' => 'app',
        'encoding' => 'utf8',
        'timezone' => 'UTC',
        'cacheMetadata' => true,
    ],

検証ソースファイル&検証データ作成

api(CakePHP)

HelloController.php

api/src/src/Controller 配下に HelloController.php を作成します。
http://localhost:80/Hello でアクセスすると HelloController.php が対応してくれます。
アクセスしてきた時にテーブル取得結果のJSONデータを返却するように作成します。

<?php

namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Http\Response;
use Cake\Datasource\ConnectionManager;

class HelloController extends Controller
{
    public function index(): Response
    {

        $connection = ConnectionManager::get('default');

        // SQLクエリを実行
        $results = $connection->execute('SELECT "Hello" FROM "Hello"')->fetchAll('assoc');
        $message = 'Hello';
        $this->set(compact('results'));
        $this->viewBuilder()->setOption('serialize', ['results']);

        return $this->response->withType('application/json')->withStringBody(json_encode(compact('results')));
    }
}

web(Next.js)

web/app/page.tsxを変更します。おしゃれなページで名残惜しいですがすっきり消しちゃいましょう!

コード補足

"use client"でクライアント処理であることを明示しています。これがないとuseEffectとuseStateが使えません。(Next.js 13以降からの仕組み)

ルーティングの仕組みは、app配下のpage.tsxがindex.html扱いになります。
他のページにアクセスさせたい場合は、app配下でフォルダを作成しpage.tsxを配置すればルーティングできます。
(例:/testでアクセスした時はtest/page.tsxが呼ばれる。)

Nginx経由でやる方法が地味に理解するのが難しかったです。:sweat:
今後改善していきたいと思います。

page.tsx

"use client";

import React, { useEffect, useState } from 'react';
import axios from 'axios';

const HelloWorld: React.FC = () => {
  const [data, setData] = useState('');

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get('http://localhost:80/Hello');
        console.log("ok");
        setData(response.data);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchData();
  }, []);

  return (
    <div>
      <h1>Hello, World!</h1>
      <p>{JSON.stringify(data)}</p>
    </div>
  );
};

export default HelloWorld;

axiosインストール

不足しているパッケージをインストールします。

cd web
npm install axios

db(PostgreSQL+adminer)

テーブルとデータを作成します。

compose.yml修正

compose.ymlをdbサービスとadminerサービスのみにします

version: '3'
services:
  db:
    image: postgres:12.18-bookworm
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: app
    volumes:
      - postgres:/var/lib/postgresql/data
    networks:
      # - api-db-network
      - db-gui-network
  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
    networks:
      - db-gui-network
    depends_on:
      - db

volumes:
  postgres:
networks:
  # web-api-network:
  #   driver: bridge
  # api-db-network:
  #   driver: bridge
  db-gui-network:
    driver: bridge

サービス起動 & adminer接続

compose.ymlが配置されているフォルダでコマンドを実行します。

cd ../
docker compose up -d --build

adminerに接続します。

ユーザ名:app
パスワード:app
データベース:app

テーブル作成

左のメニューの「テーブル作成」を押下します。
HelloテーブルにHelloカラムを追加し「保存」を押下します。
テーブル名:Hello
列名:Hello
型:character
長さ:10

スクリーンショット 2024-02-26 2.32.45.png

データ作成

作成後、左画面から「テーブル名(Hello)」を押下します。
「項目の作成」を押下します。データの内容はなんでも構わないので入力後、「保存」を押下します。
(例:DBから出てきました)

スクリーンショット 2024-02-25 23.58.42.png

SQL確認

左に表示されている「SQLコマンド」を押下します。
下記のSQLをコピペし、「実行」を押下します。

SELECT "Hello" FROM "Hello"

データが表示されればOKです。:raised_hands:

スクリーンショット 2024-02-26 0.00.10.png

起動したコンテナは終了させます。

docker compose down

compose.yml完成

全ての確認が取れたので、compose.ymlを完成させます。
コピーして貼り付けましょう。

version: '3'
services:
  nginx:
    image: nginx:1.25.4-bookworm
    ports:
      - 80:80
    volumes:
      - ./web/out:/usr/share/nginx/html
      - ./nginx/fastcgi_params:/etc/nginx/fastcgi_params
      - ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - api
    networks:
      - web-api-network
  api:
    build: ./api
    volumes:
      - ./api:/var/www/
    networks:
      - web-api-network
      - api-db-network
  db:
    image: postgres:12.18-bookworm
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: app
    volumes:
      - postgres:/var/lib/postgresql/data
    networks:
      - api-db-network
      - db-gui-network
  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
    networks:
      - db-gui-network
    depends_on:
      - db

volumes:
  postgres:
networks:
  web-api-network:
    driver: bridge
  api-db-network:
    driver: bridge
  db-gui-network:
    driver: bridge

下記、サービスごとの補足説明です。

追加で設定しているのは2点です。
depends_on : apiサービスの後に起動を開始するようにしています
networks : web-api-networkを設定しているサービスとのみ通信するようにしています。

nginxサービス

ここに記載はありませんが、adminerとポート番号が被らないように注意しましょう。

    depends_on:
      - api
    networks:
      - web-api-network

apiサービス

    networks:
      - web-api-network

dbサービス

    networks:
      - api-db-network
      - db-gui-network

adiminer

    networks:
      - db-gui-network
    depends_on:
      - db

その他

ネットワークの依存関係を追加しています。

networks:
  web-api-network:
    driver: bridge
  api-db-network:
    driver: bridge
  db-gui-network:
    driver: bridge

コンテナ起動&動作確認

だいぶ長くなりました。お疲れ様でした。:upside_down:
ここで全体の稼働確認をします。

nginx/conf.d/default.conf

127.0.0.1のままになっていないか念のため確認しましょう。変更していなかった場合は api に変更します。

コンテナ起動

webプロジェクトのビルドを行います。

cd web
npm run build

ビルドが完了したら起動しましょう。

cd ../
docker compose up -d --build

Docker Desktopがある方はコンテナが全て稼働しているか確認しやすいです。

スクリーンショット 2024-02-26 0.08.55.png

アクセスして、結果が表示されれば成功です。

スクリーンショット 2024-02-26 0.18.04.png

最後に

なるべく分解して取り組むことにとても悩みました。
入り組んでくるとややこしくなり把握できなくなるため、段階踏んでやりたかったですがなかなかできず気づいたら鳥の声が聞こえていました。:sunny:
後半に至っては知らないことばかりで、ChatGPTとレスバトルを繰り広げるなど、AIいてくれてよかったと思います。一人ではとてもできなかったです!
開発環境をホットリロードで実施できたらなと思うので引き続きやっていきたいと思います!

おまけ

開発環境整備してみた

3
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
7