Skip to main content

JodGig Overview

Repository

Existing Branches

  • merge
  • merge-qa2
  • merge-demo
  • merge-prod

You branch out from merge to create a new feature branch.

Your branches are merged

Future


Install PHP

MacOS (Apple Silicon)

We will use homebrew to manage Mac dependencies.

As of writing this, the latest version is php@8.3.

  • We will install this version.
brew install php

You will get the following output:

To enable PHP in Apache add the following to httpd.conf and restart Apache:
LoadModule php_module /opt/homebrew/opt/php/lib/httpd/modules/libphp.so

<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>

Finally, check DirectoryIndex includes index.php
DirectoryIndex index.php index.html

The php.ini and php-fpm.ini file can be found in:
/opt/homebrew/etc/php/8.3/

To start php now and restart at login:
brew services start php
Or, if you don't want/need a background service you can just run:
/opt/homebrew/opt/php/sbin/php-fpm --nodaemonize

Check if your system picks up php by checking the version installed.

php -v

Output:

PHP 8.3.9 (cli) (built: Jul  2 2024 14:10:14) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.9, Copyright (c) Zend Technologies
with Zend OPcache v8.3.9, Copyright (c), by Zend Technologies

You can see where php is installed with which

which php

Output:

/opt/homebrew/bin/php

Installing PHP minor version

If your codebase is not running on the latest and greatest version, for example php v8.1, you can install it via:

brew install php@8.1

Output:

==> php@8.1
To enable PHP in Apache add the following to httpd.conf and restart Apache:
LoadModule php_module /opt/homebrew/opt/php@8.1/lib/httpd/modules/libphp.so

<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>

Finally, check DirectoryIndex includes index.php
DirectoryIndex index.php index.html

The php.ini and php-fpm.ini file can be found in:
/opt/homebrew/etc/php/8.1/

php@8.1 is keg-only, which means it was not symlinked into /opt/homebrew,
because this is an alternate version of another formula.

If you need to have php@8.1 first in your PATH, run:
echo 'export PATH="/opt/homebrew/opt/php@8.1/bin:$PATH"' >> ~/.zshrc
echo 'export PATH="/opt/homebrew/opt/php@8.1/sbin:$PATH"' >> ~/.zshrc

For compilers to find php@8.1 you may need to set:
export LDFLAGS="-L/opt/homebrew/opt/php@8.1/lib"
export CPPFLAGS="-I/opt/homebrew/opt/php@8.1/include"

To start php@8.1 now and restart at login:
brew services start php@8.1
Or, if you don't want/need a background service you can just run:
/opt/homebrew/opt/php@8.1/sbin/php-fpm --nodaemonize

You will get an error while checking if your system picks up php.

> php -v
zsh: command not found: php
important

Installing specific cask with homebrew When installing a specific version with homebrew, homebrew does not create symbolic link (symlinks) for the binaries that were installed in /opt/homebrew/Cellar/php@8.1/8.1.x. Ensure the binary path of PHP is

So your system will not see php since it is not in the system's executable search path (e.g. /opt/homebrew/bin).

  • Your shell (i.e. bash, zsh, etc) cannot find the php executable
  • PHP executable is not in any of the directories listed in your PATH environment variables.
  • This leads to the error above.

Install PHP's dependency manager

PHP uses composer which is a dependency manager.

MacOS: Use homebrew to install this.

> brew install composer
______
/ ____/___ ____ ___ ____ ____ ________ _____
/ / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
/_/
Composer version 2.7.7 2024-06-10 22:11:12
> which composer
/opt/homebrew/bin/composer

Checking Installed PHP Extensions

Ensure that the following packages are installed by running php -m

• bcmath • ctype • fileinfo • json • mbstring • openssl • pdo • tokenizer • xml

Running php -m will list all the PHP modules.

> php -m
[PHP Modules]
bcmath
bz2
calendar
# shortened...

Install Dependencies

Navigate into the project folder root to install the project dependencies using composer

note

Dependencies are listed in composer.json file, similar to NodeJs package.json

composer install

No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.
Loading composer repositories with package information
Updating dependencies
Lock file operations: 185 installs, 0 updates, 0 removals
- Locking asm89/stack-cors (v2.2.0)
- Locking aws/aws-crt-php (v1.2.6)
# ...shortened output

Install Xdebug for debugging

For Linux and Windows, you can use xdebug's installation guide.

Assuming you installed php via homebrew, you can run this command.

pecl install xdebug

Output final lines:

Build process completed successfully
Installing '/opt/homebrew/Cellar/php@8.1/8.1.29/pecl/20210902/xdebug.so'
install ok: channel://pecl.php.net/xdebug-3.3.2
Extension xdebug enabled in php.ini

To check Xdebug was successfully installed:

php -v

Output:

PHP 8.1.29 (cli) (built: Jun  5 2024 05:51:57) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.29, Copyright (c) Zend Technologies
with Xdebug v3.3.2, Copyright (c) 2002-2024, by Derick Rethans
with Zend OPcache v8.1.29, Copyright (c), by Zend Technologies

Install PHP Debug VSCode extension

Look for PHP Debug extension in VSCode Marketplace by xdebug.

Install it.

Look for your php.ini file:

php --ini

If you installed php via homebrew, you should see:

Configuration File (php.ini) Path: /opt/homebrew/etc/php/8.1
Loaded Configuration File: /opt/homebrew/etc/php/8.1/php.ini
Scan for additional .ini files in: /opt/homebrew/etc/php/8.1/conf.d
Additional .ini files parsed: /opt/homebrew/etc/php/8.1/conf.d/ext-opcache.ini

Assuming you installed xdebug v3, open up the php.ini file, and add in

xdebug.mode = debug
xdebug.start_with_request = yes

Set up VS Code Launch Config

Create a Launch Configuration in VSCode for PHP

Replace the generated Listen for Xdebug with the following configurations:

    {
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"runtimeExecutable": "php",
"runtimeArgs": [
"-dxdebug.mode=debug", // tells Xdebug to start in debug mode. -d flag used to set php.ini settings
"-dxdebug.start_with_request=yes", // tells Xdebug to start debugging immediately when a request is received
"artisan", // Laravel command line to interact with the PHP app
"serve", // Artisan command to start the Laravel development server
"--host=localhost", // Host to bind the server to
"--port=8001" // Port to bind the server to
],
"cwd": "${workspaceRoot}", // Tell debugger to run `php` in the Current Working Directory (cwd)
"env": {
"XDEBUG_MODE": "debug", // Ensure xdebug can read it during runtime, and not only startup
"XDEBUG_CONFIG": "client_port=${port}"
}
},

JodGig Project Setup

Esnure you have PHP v8.3 installed in your local machine.

Install passport

If you tried running the server immediately after installing PHP, you might have encountered the following error:

[2024-10-17 18:54:28] local.ERROR: Invalid key supplied {"exception":"[object] (LogicException(code: 0): Invalid key supplied at /Users/alaay/projects/jodgig/jodgig-api/vendor/league/oauth2-server/src/CryptKey.php:67)
[stacktrace]

#0 /Users/username/projects/jodgig/jodgig-api/vendor/laravel/passport/src/PassportServiceProvider.php(275): League\\OAuth2\\Server\\CryptKey->__construct('file:///Users/a...', NULL, false)
#1 /Users/username/projects/jodgig/jodgig-api/vendor/laravel/passport/src/PassportServiceProvider.php(256): Laravel\\Passport\\PassportServiceProvider->makeCryptKey('public')

This is because we have not installed passport locally.

The project uses passport for OAuth2 authentication (for some reason).

php artisan passport:install

Output:

Encryption keys generated successfully.
Personal access client created successfully.
Client ID: 5
Client secret: <client secret key>
Password grant client created successfully.
Client ID: 6
Client secret: <client secret key>

passport:install will do two things:

  1. Creates two files

    • ./storage/oauth-private.key
    • ./storage/oauth-public.key
  2. Inserts into the database table oauth_clients

    • Depending on what sql dump you got to load your DB, you're Client ID in your output will be different.

Overview

Start the local server

This will start the server on your machine running at localhost:8001

compose start

This will run the start script defined in composer.json

"start": "php artisan serve --host=localhost --port=8001"

Dockerising JodGig for Deployment Notes

These are notes based on recent deployment of JodGig onto AWS EC2 by Ali.

Laravel depends on the following key components:

  • PHP-FPM (FastCGI Process Manager)
  • Nginx (web server)

PHP-FPMis the core component that handles PHP code execution and interacts with the web server.

  • It's responsible for managing PHP processes, optimizing performance, and handling requests efficiently.

Nginx acts as a reverse proxy server, routing incoming requests to the appropriate PHP-FPM process.

Docker Strategy

Use docker compose to setup the services

  • Nginx
  • PHP-FPM

Nginx will receive the HTTP requests and forward them to PHP-FPM.

  • PHP-FPM cannot accept HTTP requests directly since it only supports FastCGI protocol.

Configuration Files

Inside docker container with php-8.1

/usr/local/etc/
pear.conf
php-fpm.conf
php-fpm.conf.default

php-fpm.d/
docker.conf
www.conf
www.conf.default
zz-docker.conf

php/
php.ini-development
php.ini-production
conf.d/
docker-fpm.ini
docker-php-ext-bcmath.ini
docker-php-ext-exif.ini
docker-php-ext-gd.ini
docker-php-ext-intl.ini
docker-php-ext-pcntl.ini
docker-php-ext-pdo_mysql.ini
docker-php-ext-redis.ini
docker-php-ext-sodium.ini
docker-php-ext-zip.ini

Two config files we need to take note of when building the Docker image with Dockerfile:

  • www.conf
  • php.ini-production

www.conf

An important configuration file for php-fpm (FastCGI Process Manager) that controls how PHP processes are managed when running PHP apps like Laravel.

Configures:

  • php-fpm pool settings
  • controls how PHP processes are managed
    • e.g. number of child processes, process manager settings, etc.
  • php-fpm logging and error handling
  • defines resource limits (e.g. memory, CPU) for PHP processes

www.config configs examples

configdescription
pm.max_childrenMaximum number of child processes that can be spawned by php-fpm.
pm.start_serversMinimum number of child processes that will be started by php-fpm when the pool is first initialized.
pm.min_spare_serversMinimum number of idle child processes that should be kept running by php-fpm.
pm.max_spare_serversMaximum number of idle child processes that should be kept running by php-fpm.
pm.process_idle_timeoutMaximum number of seconds a child process can remain idle before it is terminated.

php.ini-production


JodGig Deployment Overview

JodGig consists of two services:

  • Frontend
  • API

Both are deployed directly into two different EC2 instances.

  • jodgig.<env>.frontend
  • jodgig.<env>.api

Where <env> is the environment:

  • qa
  • demo
  • production

Overview

  • User makes HTTP requests which get routed to haproxy via Cloudflare DNS.
  • HTTP requests with the url jodapp.com will be forwarded to the frontend service.
  • HTTP requests with the url jodaopp.com/api will be forwarded to the api service.

API

Inside API EC2 instance, we have 2 docker containers running:

  • php-fpm_YYYYMMDD_HHMMSS
  • nginx

Nginx

We require nginx to receive HTTP requests from Haproxy. php-fpm by itself cannot receive HTTP requests. It is a FastCGI process.

nginx will receive the HTTP request, and then forward a FastCGI request over TCP/IP to php-fpm through the docker network api_default.

note

Traditionally PHP-FPM might use Unix sockets for communication when on the same machine. In our case, we are using TCP/IP to communicate between nginx and php-fpm via the docker network api_default.

PHP-FPM

PHP-FPM is a FastCGI process. It is used to process PHP files. nginx will forward PHP requests to php-fpm.

It will handle the PHP requests that come from nginx.

Frontend

Inside Frontend EC2 instance, we do not have any docker containers running. Instead we have pm2, a node process manager running. It only handles a single node process called serve.

  • serve is a lightweight server for serving static files.

JodGig Deployment Process

The new deployment process is designed in a way to fix previous issues faced when deploying to production:

  • Deployment takes ~8-15minutes
  • Build constantly fails due to LetsEncrypt quota reaching limit
  • Deployment has to happen during off-peak hours

Overview

We use simple bash scripts to automate deployment for both API and Frontend.

JodGig API

~/deploy/deploy.sh is the file that we use to deploy the API.

  1. Determine if oauth keys exist. If not, exit the script.
  2. Create the docker network if it doesn't exist
    • This is for nginx container to forward requests to php-fpm container
  3. Build the php-fpm docker image.
  4. Run the new php-fpm container using the newly built image alongside the old php-fpm container
  5. In a loop, check the health the new php-fpm container until it's ready.
  6. Once ready, run database migrations in the new php-fpm container.

At this point, we need to point nginx to the new php-fpm container.

  1. Update ~/api/deploy/nginx/conf.d/default.conf to point to the new php-fpm container.
  2. Restart the nginx container to pick up the new configuration.
  3. Stop the old php-fpm container

Visualisation

You can view the visualisation in this draw.io..