feat: Docker FrankenPHP and Nginx examples (#40)

This commit is contained in:
Ray Blair 2024-09-04 08:15:54 +01:00 committed by GitHub
parent db3ff52223
commit 038db48f75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 531 additions and 0 deletions

View File

@ -0,0 +1,35 @@
# Use a specific version of the PHP image for consistency and reliability
FROM dunglas/frankenphp:builder-php8.3-alpine
# Set the server name, replace "your-domain-name.example.com" with your actual domain
# For HTTP-only setups, use ":80"
ENV SERVER_NAME=:80
# Enable PHP production settings for optimized performance and security
# RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Set the working directory for the application
WORKDIR /app
# Copy the entire project directory to the container's working directory
COPY . /app
# Copy Composer binary from the official Composer image for dependency management
COPY --from=composer:2.2 /usr/bin/composer /usr/bin/composer
# Install Composer dependencies with optimized options for production
RUN composer install \
--ignore-platform-reqs \
--no-scripts \
--no-progress \
--no-ansi \
--no-dev
# Install required PHP extensions for the application
RUN install-php-extensions \
pdo \
pdo_mysql \
opcache
# Optional: Set the FRANKENPHP_CONFIG environment variable to start FrankenPHP with a specific worker script
# ENV FRANKENPHP_CONFIG="worker ./public/index.php"

View File

@ -0,0 +1,59 @@
# Docker FrankenPHP Example
This repository provides a basic example of setting up FrankenPHP in a Docker environment using Dumbo. The provided configuration is intended as a starting point for development and production deployments.
## Prerequisites
- Ensure that you have [Docker](https://www.docker.com/) installed on your system.
## Running the Example
### 1. Install Dependencies
Before building the Docker images, you need to install the project dependencies using Composer:
```bash
composer install
```
### 2. Build and Start Docker Containers
#### Development Environment
To build and start the Docker containers for development:
1. Build the Docker images:
```bash
docker-compose -f docker-compose.yml build
```
2. Start the Docker containers:
```bash
docker-compose up --build app
```
3. Navigate to [localhost](https://localhost).
#### Production Environment
To build and start the Docker containers for production:
1. Build the Docker image:
```bash
docker build --tag notrab/dumbo:docker-frankenphp-example .
```
2. Run the Docker container:
```bash
docker run notrab/dumbo:docker-frankenphp-example
```
## Further Documentation
For more detailed information on FrankenPHP, refer to the official documentation:
- [FrankenPHP Documentation](https://frankenphp.dev/)

View File

@ -0,0 +1,12 @@
{
"require": {
"notrab/dumbo": "@dev"
},
"repositories": [
{
"type": "path",
"url": "../../"
}
],
"prefer-stable": false
}

View File

@ -0,0 +1,41 @@
services:
# Main application service using the FrankenPHP image
app:
image: dunglas/frankenphp
networks:
- app_network # Connect the service to the defined network
ports:
- "80:80" # Map port 80 on the host to port 80 in the container (HTTP)
- "443:443" # Map port 443 on the host to port 443 in the container (HTTPS)
- "443:443/udp" # Map UDP traffic on port 443 (useful for QUIC and HTTP/3)
volumes:
- ./:/app # Mount the current directory to /app in the container
- caddy_data:/data # Persist Caddy data, such as certificates, in a named volume
- caddy_config:/config # Persist Caddy configuration files in a named volume
# Uncomment the following line during development to enable interactive terminal for readable logs
# tty: true
depends_on:
- composer # Ensure the composer service runs before starting the app service
# Composer service for managing PHP dependencies
composer:
image: composer:2.2 # Use the official Composer image, version 2.2
working_dir: /app # Set the working directory inside the container to /app
volumes:
- ./:/app # Mount the current directory to /app in the container
entrypoint:
- composer # Override the default entrypoint to run Composer commands
- "--ignore-platform-reqs" # Ignore platform requirements during installation
- "--no-progress" # Disable the progress display for a cleaner output
- "--no-ansi" # Disable ANSI colors in the output
command: ["install"] # Install PHP dependencies defined in composer.json
# Define a custom network for the services to communicate with each other
networks:
app_network:
driver: bridge # Use the bridge driver for creating an isolated network
# Define named volumes for persisting data outside of the container lifecycle
volumes:
caddy_data: # Volume to store Caddy's data (like SSL certificates)
caddy_config: # Volume to store Caddy's configuration files

View File

@ -0,0 +1,110 @@
<?php
require __DIR__ . "/../vendor/autoload.php";
use Dumbo\Dumbo;
use Dumbo\HTTPException;
$app = new Dumbo();
$user = new Dumbo();
$userData = [
[
"id" => 1,
"name" => "Jamie Barton",
"email" => "jamie@notrab.dev",
],
];
$user->get("/", function ($c) use ($userData) {
return $c->json($userData);
});
$user->get("/:id", function ($c) use ($userData) {
$id = (int) $c->req->param("id");
$user =
array_values(array_filter($userData, fn ($u) => $u["id"] === $id))[0] ??
null;
if (!$user) {
return $c->json(["error" => "User not found"], 404);
}
return $c->json($user);
});
$user->post("/", function ($c) use ($userData) {
$body = $c->req->body();
if (!isset($body["name"]) || !isset($body["email"])) {
return $c->json(["error" => "Name and email are required"], 400);
}
$newId = max(array_column($userData, "id")) + 1;
$newUserData = array_merge(["id" => $newId], $body);
return $c->json($newUserData, 201);
});
$user->delete("/:id", function ($c) use ($userData) {
$id = (int) $c->req->param("id");
$user =
array_values(array_filter($userData, fn ($u) => $u["id"] === $id))[0] ??
null;
if (!$user) {
return $c->json(["error" => "User not found"], 404);
}
return $c->json(["message" => "User deleted successfully"]);
});
$app->get("/greet/:greeting", function ($c) {
$greeting = $c->req->param("greeting");
$name = $c->req->query("name");
return $c->json([
"message" => "$greeting, $name!",
]);
});
$app->route("/users", $user);
$app->use(function ($ctx, $next) {
$ctx->set("message", "Dumbo");
return $next($ctx);
});
$app->use(function ($c, $next) {
$c->header("X-Powered-By", "Dumbo");
return $next($c);
});
$app->get("/redirect", function ($c) {
$message = $c->get("message");
return $c->redirect("/greet/hello?name=$message", 301);
});
$app->get("/", function ($c) {
$message = $c->get("message");
return $c->html("<h1>Hello from $message!</h1>", 200, [
"X-Hello" => "World",
]);
});
$app->get("/error", function ($c) {
$customResponse = $c->html("<h1>Something went wrong</h1>", 404);
throw new HTTPException(
statusCode: 404,
message: "Something went wrong",
customResponse: $customResponse
);
});
$app->run();

View File

@ -0,0 +1,22 @@
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass app:9000; # This matches the PHP-FPM service in docker-compose
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}

View File

@ -0,0 +1,35 @@
# Use the official PHP image with FPM (FastCGI Process Manager)
FROM php:8.3-fpm
# Install necessary PHP extensions
RUN docker-php-ext-install mysqli pdo pdo_mysql
# Install Git we'll need it for Composer
RUN apt-get update \
&& apt-get install -y --no-install-recommends git
# Install Composer
COPY --from=composer:2.2 /usr/bin/composer /usr/bin/composer
# Set the working directory in the container
WORKDIR /var/www/html
# Copy the application code to the working directory
COPY . /var/www/html
# Install Composer dependencies with optimized options for production
RUN composer install \
--ignore-platform-reqs \
--no-scripts \
--no-progress \
--no-ansi \
--no-dev
# Set proper permissions for the web server
RUN chown -R www-data:www-data /var/www/html
# Expose port 9000 for PHP-FPM
EXPOSE 9000
# Start PHP-FPM
CMD ["php-fpm"]

View File

@ -0,0 +1,53 @@
# Docker Nginx Example
This repository provides a basic example of setting up Nginx in a Docker environment using Dumbo. The provided configuration is intended as a starting point for development and production deployments.
## Prerequisites
- Ensure that you have [Docker](https://www.docker.com/) installed on your system.
## Running the Example
### 1. Install Dependencies
Before building the Docker images, you need to install the project dependencies using Composer:
```bash
composer install
```
### 2. Build and Start Docker Containers
#### Development Environment
To build and start the Docker containers for development:
1. Build the Docker images:
```bash
docker-compose -f docker-compose.yml build
```
2. Start the Docker containers:
```bash
docker-compose up --build web
```
3. Navigate to [localhost](http://localhost:8080).
#### Production Environment
To build and start the Docker containers for production:
1. Build the Docker image:
```bash
docker build --tag notrab/dumbo:docker-nginx-example .
```
2. Run the Docker container:
```bash
docker run notrab/dumbo:docker-nginx-example
```

View File

@ -0,0 +1,12 @@
{
"require": {
"notrab/dumbo": "@dev"
},
"repositories": [
{
"type": "path",
"url": "../../"
}
],
"prefer-stable": false
}

View File

@ -0,0 +1,42 @@
services:
# Nginx Web Server
web:
image: nginx:latest # Use the latest official Nginx image
ports:
- "8080:80" # Map port 8080 on the host to port 80 in the container
volumes:
- .:/var/www/html # Mount the current directory to the web root inside the container
- ./Docker/nginx.conf:/etc/nginx/conf.d/default.conf # Use custom Nginx configuration
depends_on:
- app # Ensure the PHP-FPM service is started before Nginx
networks:
- app_network
# PHP-FPM Service
app:
build:
context: . # Use the current directory to build the Dockerfile
dockerfile: Dockerfile # Use the specified Dockerfile
networks:
- app_network
volumes:
- .:/var/www/html # Mount the current directory to the web root inside the container
depends_on:
- composer # Ensure the composer service runs before starting the app service
# Composer service for managing PHP dependencies
composer:
image: composer:2.2 # Use the official Composer image, version 2.2
working_dir: /var/www/html # Set the working directory inside the container to /var/www/html
volumes:
- .:/var/www/html # Mount the current directory to /var/www/html in the container
entrypoint:
- composer # Override the default entrypoint to run Composer commands
- "--ignore-platform-reqs" # Ignore platform requirements during installation
- "--no-progress" # Disable the progress display for a cleaner output
- "--no-ansi" # Disable ANSI colors in the output
command: ["install"] # Install PHP dependencies defined in composer.json
networks:
app_network:
driver: bridge # Use the bridge network driver

View File

@ -0,0 +1,110 @@
<?php
require __DIR__ . "/../vendor/autoload.php";
use Dumbo\Dumbo;
use Dumbo\HTTPException;
$app = new Dumbo();
$user = new Dumbo();
$userData = [
[
"id" => 1,
"name" => "Jamie Barton",
"email" => "jamie@notrab.dev",
],
];
$user->get("/", function ($c) use ($userData) {
return $c->json($userData);
});
$user->get("/:id", function ($c) use ($userData) {
$id = (int) $c->req->param("id");
$user =
array_values(array_filter($userData, fn ($u) => $u["id"] === $id))[0] ??
null;
if (!$user) {
return $c->json(["error" => "User not found"], 404);
}
return $c->json($user);
});
$user->post("/", function ($c) use ($userData) {
$body = $c->req->body();
if (!isset($body["name"]) || !isset($body["email"])) {
return $c->json(["error" => "Name and email are required"], 400);
}
$newId = max(array_column($userData, "id")) + 1;
$newUserData = array_merge(["id" => $newId], $body);
return $c->json($newUserData, 201);
});
$user->delete("/:id", function ($c) use ($userData) {
$id = (int) $c->req->param("id");
$user =
array_values(array_filter($userData, fn ($u) => $u["id"] === $id))[0] ??
null;
if (!$user) {
return $c->json(["error" => "User not found"], 404);
}
return $c->json(["message" => "User deleted successfully"]);
});
$app->get("/greet/:greeting", function ($c) {
$greeting = $c->req->param("greeting");
$name = $c->req->query("name");
return $c->json([
"message" => "$greeting, $name!",
]);
});
$app->route("/users", $user);
$app->use(function ($ctx, $next) {
$ctx->set("message", "Dumbo");
return $next($ctx);
});
$app->use(function ($c, $next) {
$c->header("X-Powered-By", "Dumbo");
return $next($c);
});
$app->get("/redirect", function ($c) {
$message = $c->get("message");
return $c->redirect("/greet/hello?name=$message", 301);
});
$app->get("/", function ($c) {
$message = $c->get("message");
return $c->html("<h1>Hello from $message!</h1>", 200, [
"X-Hello" => "World",
]);
});
$app->get("/error", function ($c) {
$customResponse = $c->html("<h1>Something went wrong</h1>", 404);
throw new HTTPException(
statusCode: 404,
message: "Something went wrong",
customResponse: $customResponse
);
});
$app->run();