Skip to content

Commit

Permalink
Add production configuration (#14)
Browse files Browse the repository at this point in the history
## Changes:
- Added production template
- Modify `Database.php` for custom deployment with environment variables
[ba23310], closes #15.
- Add custom nginx configuration for PHP-FPM & CodeIgniter
  • Loading branch information
rahmatnazali authored Feb 2, 2024
1 parent f3c9610 commit 3aa10a7
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 0 deletions.
Empty file removed configurations/.gitkeep
Empty file.
Empty file removed configurations/production/.gitkeep
Empty file.
55 changes: 55 additions & 0 deletions configurations/production/example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Place this at root folder of this repo

version: '3.7'

services:
php-fpm:
container_name: php-fpm
build:
context: ./source
environment:
- CI_ENVIRONMENT=production
- DB_DEFAULT_HOSTNAME=db-main
- DB_DEFAULT_DATABASE=main
- DB_DEFAULT_USERNAME=root
- DB_DEFAULT_PASSWORD=root
- DB_DEFAULT_DBDRIVER=MySQLi
- DB_DEFAULT_PORT=3306
- DB_TESTS_HOSTNAME=db-test
- DB_TESTS_DATABASE=test
- DB_TESTS_USERNAME=root
- DB_TESTS_PASSWORD=root
- DB_TESTS_DBDRIVER=MySQLi
- DB_TESTS_PORT=3306
volumes:
- source-code:/var/www
nginx:
image: nginx:latest
container_name: nginx
volumes:
- ./configurations/production/nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- source-code:/var/www
ports:
- "8000:80"
db-main:
image: mysql
volumes:
- main_mysql_data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=main
ports:
- "3310:3306"
db-test:
image: mysql
volumes:
- test_mysql_data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=test
ports:
- "3311:3306"
volumes:
main_mysql_data:
test_mysql_data:
source-code:
38 changes: 38 additions & 0 deletions configurations/production/nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# This file is taken from: https://www.codeigniter.com/user_guide/installation/running.html#default-conf
# Some key points:
# - enable URLSs to be accessed without "index.php"
# - Raise 404 for URLs ending with ".php"
# - Deny access to hidden files

server {
listen 80;
listen [::]:80;

server_name example.com;

root /var/www/codeigniter4/public;
index index.php index.html index.htm;

location / {
try_files $uri $uri/ /index.php$is_args$args;
}

location ~ \.php$ {
# point to the php-fpm address
fastcgi_pass php-fpm:9000;

# or, if nginx and php-fpm are on the same device, this can also be used
#fastcgi_pass unix:/run/php/php8.3-fpm.sock;

fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

error_page 404 /index.php;

# deny access to hidden files such as .htaccess
location ~ /\. {
deny all;
}
}
124 changes: 124 additions & 0 deletions configurations/production/production.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@

# Production Configuration

This file provides a working example of serving the source code of this repo
on a production grade environment.

Use case:
- The source code will be served with PHP-FPM
- NGINX will be used to do reverse proxy to the PHP-FPM (other tool can also be used, as long as it's compatible with FastCGI Protocol).

For the sake of simplicity, the architecture is detailed at [example.yml](example.yml).
It can then be converted to a vps-style serving, docker swarm, or kubernetes, as needed.

## Preparing the CodeIgniter 4 Image

### Building the image

[Dockerfile](../../source/Dockerfile) is supplied inside `/source`, so image can be built like so:

```
cd source
docker build -t my-application:1
```

The source code will be copied inside the official [PHP-FPM Image](https://hub.docker.com/_/php).

### Passing environment variables

Currently, there are 13 environment variables that can be adjusted:

| Variable name | Description |
|---------------------|----------------------------------------------------------|
| CI_ENVIRONMENT | Environment for the site (`development` or `production`) |
| DB_DEFAULT_HOSTNAME | Host name for the default database |
| DB_DEFAULT_DATABASE | Name for the default database |
| DB_DEFAULT_USERNAME | Username for the default database |
| DB_DEFAULT_PASSWORD | Password for the default database |
| DB_DEFAULT_DBDRIVER | DB Driver for the default database (`SQLite3`, `MySQLi`) |
| DB_DEFAULT_PORT | Port for the default database |
| DB_TESTS_HOSTNAME | Host name for the test database |
| DB_TESTS_DATABASE | Name for the test database |
| DB_TESTS_USERNAME | Username for the test database |
| DB_TESTS_PASSWORD | Password for the test database |
| DB_TESTS_DBDRIVER | DB Driver for the test database (`SQLite3`, `MySQLi`) |
| DB_TESTS_PORT | Port for the default database |

The `/source/.env` file will be ignored when creating the image.
Please use the appropriate way from the stack you've picked to pass an environment variables.

For example, in docker compose you would do it like this:

```
services:
fpm:
environment:
- CI_ENVIRONMENT=production
- DB_DEFAULT_HOSTNAME=db-main
- DB_DEFAULT_DATABASE=main
- DB_DEFAULT_USERNAME=root
- DB_DEFAULT_PASSWORD=root
- DB_DEFAULT_DBDRIVER=MySQLi
- DB_DEFAULT_PORT=3306
- DB_TESTS_HOSTNAME=db-test
- DB_TESTS_DATABASE=test
- DB_TESTS_USERNAME=root
- DB_TESTS_PASSWORD=root
- DB_TESTS_DBDRIVER=MySQLi
- DB_TESTS_PORT=3306
```

In Kubernetes, insert them inside the specification and mix its usage with `ConfigMap` or `Secret` as necessary.

## Preparing the NGINX

The [nginx.conf](nginx/nginx.conf) is provided as a baseline for serving CodeIgniter and PHP in general.
You may want to modify it as you need:

- Change `server_name` from `example.com` to the domain you intended to use.
- Confirm that `root` points to the `/source/public` folder, where CodeIgniter should start form.
If NGINX and PHP-FPM are not being installed on the same device,
you may want to make sure NGINX can access the source code inside the PHP-FPM.
- Confirm that `fastcgi_pass` points to the PHP-FPM's address.

## Additional Notes

### PHP-FPM related notes

We can make the image work without any configuration change.
But if needed, the PHP-FPM main configuration file is located at `/usr/local/etc/php-fpm.conf`.
In that file, it will include all other files located at `/usr/local/etc/php-fpm.d/`.

The most related configuration file is probably the `www.conf`,
in which it already set these by default, and you may want to change this according to the needs.

```
user = www-data
group = www-data
listen = 127.0.0.1:9000
;listen.owner = www-data
;listen.group = www-data
;listen.mode = 0660
;security.limit_extensions = .php .php3 .php4 .php5 .php7
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
```

In any case that the default configuration path is invalid,
you may need to get it by:

```
docker exec -ti fpm php-fpm -tt
```

### Useful reference

- https://github.com/atsanna/codeigniter4-docker
- https://github.com/LERUfic/codeigniter-docker
- https://github.com/firmanJS/Dockerize-Codeigniter-With-docker-compose
6 changes: 6 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,11 @@ composer run-script test
- Use the provided [Debug Toolbar](https://codeigniter4.github.io/userguide/tutorial/index.html#debug-toolbar)
- Error logs will be printed on `writable/logs`

## Production Configuration

This repo offers a working configuration for serving the source code on a production environment
with PHP-FPM and NGINX.
More about this can be found at [production.md](configurations/production/production.md).

## Useful external resources
- 25 minutes of [basic MVC in CodeIgniter 4](https://youtu.be/c8zHxE-mN4c?si=pNoCCJwCjGoRfYQp)
2 changes: 2 additions & 0 deletions source/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
./vendor
31 changes: 31 additions & 0 deletions source/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
FROM php:8.3.2-fpm

WORKDIR /var/www

RUN apt-get update && \
apt-get install -y \
git \
zip \
curl \
libicu-dev

RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install mysqli
RUN docker-php-ext-install intl

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

COPY composer.json codeigniter4/composer.json
COPY composer.lock codeigniter4/composer.lock
RUN cd codeigniter4 && composer install

COPY . codeigniter4

RUN chown -R www-data:www-data codeigniter4/
RUN chmod -R 755 codeigniter4/writable/

RUN apt-get clean && rm -rf /var/lib/apt/lists/*

EXPOSE 9000

CMD ["php-fpm"]
20 changes: 20 additions & 0 deletions source/app/Config/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,25 @@ public function __construct()
if (ENVIRONMENT === 'testing') {
$this->defaultGroup = 'tests';
}

// assign the default database configuration from environment variables (if given)
if (getenv('DB_DEFAULT_HOSTNAME')){
$this->default['hostname'] = getenv('DB_DEFAULT_HOSTNAME') ?? $this->default['hostname'];
$this->default['username'] = getenv('DB_DEFAULT_USERNAME') ?? $this->default['username'];
$this->default['password'] = getenv('DB_DEFAULT_PASSWORD') ?? $this->default['password'];
$this->default['database'] = getenv('DB_DEFAULT_DATABASE') ?? $this->default['database'];
$this->default['DBDriver'] = getenv('DB_DEFAULT_DBDRIVER') ?? $this->default['DBDriver'];
$this->default['port'] = getenv('DB_DEFAULT_PORT') ?? $this->default['port'];
}

// assign the test database configuration from environment variables (if given)
if (getenv('DB_TESTS_HOSTNAME')){
$this->tests['hostname'] = getenv('DB_TESTS_HOSTNAME') ?? $this->tests['hostname'];
$this->tests['username'] = getenv('DB_TESTS_USERNAME') ?? $this->tests['username'];
$this->tests['password'] = getenv('DB_TESTS_PASSWORD') ?? $this->tests['password'];
$this->tests['database'] = getenv('DB_TESTS_DATABASE') ?? $this->tests['database'];
$this->tests['DBDriver'] = getenv('DB_TESTS_DBDRIVER') ?? $this->tests['DBDriver'];
$this->tests['port'] = getenv('DB_TESTS_PORT') ?? $this->tests['port'];
}
}
}

0 comments on commit 3aa10a7

Please sign in to comment.