Skip to content

Commit

Permalink
feat: frankenphp in prod
Browse files Browse the repository at this point in the history
  • Loading branch information
Kocal committed Mar 10, 2024
1 parent 6538f51 commit 6326873
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 0 deletions.
93 changes: 93 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#syntax=docker/dockerfile:1.4

# Versions
FROM dunglas/frankenphp:1-php8.2 AS frankenphp_upstream

# The different stages of this Dockerfile are meant to be built into separate images
# https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage
# https://docs.docker.com/compose/compose-file/#target


# Base FrankenPHP image
FROM frankenphp_upstream AS frankenphp_base

WORKDIR /app

# persistent / runtime deps
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends \
acl \
file \
gettext \
git \
&& rm -rf /var/lib/apt/lists/*

RUN set -eux; \
install-php-extensions \
@composer \
apcu \
intl \
opcache \
zip \
imagick \
;

# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
ENV COMPOSER_ALLOW_SUPERUSER=1

###> recipes ###
###< recipes ###

COPY --link frankenphp/conf.d/app.ini $PHP_INI_DIR/conf.d/
COPY --link --chmod=755 frankenphp/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
COPY --link frankenphp/Caddyfile /etc/caddy/Caddyfile

ENTRYPOINT ["docker-entrypoint"]

HEALTHCHECK --start-period=60s CMD curl -f http://localhost:2019/metrics || exit 1
CMD [ "frankenphp", "run", "--config", "/etc/caddy/Caddyfile" ]

# TODO: We use Symfony CLI and local PHP for development, but I may be reconsidering this approach in the future
## Dev FrankenPHP image
#FROM frankenphp_base AS frankenphp_dev
#
#ENV APP_ENV=dev XDEBUG_MODE=off
#VOLUME /app/var/
#
#RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
#
#RUN set -eux; \
# install-php-extensions \
# xdebug \
# ;
#
#COPY --link frankenphp/conf.d/app.dev.ini $PHP_INI_DIR/conf.d/
#
#CMD [ "frankenphp", "run", "--config", "/etc/caddy/Caddyfile", "--watch" ]

# Prod FrankenPHP image
FROM frankenphp_base AS frankenphp_prod

ENV APP_ENV=prod
ENV FRANKENPHP_CONFIG="import worker.Caddyfile"

RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

COPY --link frankenphp/conf.d/app.prod.ini $PHP_INI_DIR/conf.d/
COPY --link frankenphp/worker.Caddyfile /etc/caddy/worker.Caddyfile

# prevent the reinstallation of vendors at every changes in the source code
COPY --link composer.* symfony.* ./
RUN set -eux; \
composer install --no-cache --prefer-dist --no-dev --no-autoloader --no-scripts --no-progress

# copy sources
COPY --link . ./
RUN rm -Rf frankenphp/

RUN set -eux; \
mkdir -p var/cache var/log; \
composer dump-autoload --classmap-authoritative --no-dev; \
composer dump-env prod; \
composer run-script --no-dev post-install-cmd; \
chmod +x bin/console; sync;
9 changes: 9 additions & 0 deletions compose.prod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: '3.6'

services:
php:
build:
context: .
target: frankenphp_prod
environment:
APP_SECRET: ${APP_SECRET}
55 changes: 55 additions & 0 deletions frankenphp/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
{$CADDY_GLOBAL_OPTIONS}

frankenphp {
{$FRANKENPHP_CONFIG}
}

# https://caddyserver.com/docs/caddyfile/directives#sorting-algorithm
order mercure after encode
order vulcain after reverse_proxy
order php_server before file_server
}

{$CADDY_EXTRA_CONFIG}

{$SERVER_NAME:localhost} {
log {
# Redact the authorization query parameter that can be set by Mercure
format filter {
wrap console
fields {
uri query {
replace authorization REDACTED
}
}
}
}

root * /app/public
encode zstd br gzip

mercure {
# Transport to use (default to Bolt)
transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db}
# Publisher JWT key
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}
# Subscriber JWT key
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG}
# Allow anonymous subscribers (double-check that it's what you want)
anonymous
# Enable the subscription API (double-check that it's what you want)
subscriptions
# Extra directives
{$MERCURE_EXTRA_DIRECTIVES}
}

vulcain

{$CADDY_SERVER_EXTRA_DIRECTIVES}

# Disable Topics tracking if not enabled explicitly: https://github.com/jkarlin/topics
header ?Permissions-Policy "browsing-topics=()"

php_server
}
5 changes: 5 additions & 0 deletions frankenphp/conf.d/app.dev.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
; See https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host
; See https://github.com/docker/for-linux/issues/264
; The `client_host` below may optionally be replaced with `discover_client_host=yes`
; Add `start_with_request=yes` to start debug session on each request
xdebug.client_host = host.docker.internal
13 changes: 13 additions & 0 deletions frankenphp/conf.d/app.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
expose_php = 0
date.timezone = UTC
apc.enable_cli = 1
session.use_strict_mode = 1
zend.detect_unicode = 0

; https://symfony.com/doc/current/performance.html
realpath_cache_size = 4096K
realpath_cache_ttl = 600
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.memory_consumption = 256
opcache.enable_file_override = 1
2 changes: 2 additions & 0 deletions frankenphp/conf.d/app.prod.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
opcache.preload_user = root
opcache.preload = /app/config/preload.php
60 changes: 60 additions & 0 deletions frankenphp/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/bin/sh
set -e

if [ "$1" = 'frankenphp' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
# Install the project the first time PHP is started
# After the installation, the following block can be deleted
if [ ! -f composer.json ]; then
rm -Rf tmp/
composer create-project "symfony/skeleton $SYMFONY_VERSION" tmp --stability="$STABILITY" --prefer-dist --no-progress --no-interaction --no-install

cd tmp
cp -Rp . ..
cd -
rm -Rf tmp/

composer require "php:>=$PHP_VERSION" runtime/frankenphp-symfony
composer config --json extra.symfony.docker 'true'

if grep -q ^DATABASE_URL= .env; then
echo "To finish the installation please press Ctrl+C to stop Docker Compose and run: docker compose up --build -d --wait"
sleep infinity
fi
fi

if [ -z "$(ls -A 'vendor/' 2>/dev/null)" ]; then
composer install --prefer-dist --no-progress --no-interaction
fi

if grep -q ^DATABASE_URL= .env; then
echo "Waiting for database to be ready..."
ATTEMPTS_LEFT_TO_REACH_DATABASE=60
until [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ] || DATABASE_ERROR=$(php bin/console dbal:run-sql -q "SELECT 1" 2>&1); do
if [ $? -eq 255 ]; then
# If the Doctrine command exits with 255, an unrecoverable error occurred
ATTEMPTS_LEFT_TO_REACH_DATABASE=0
break
fi
sleep 1
ATTEMPTS_LEFT_TO_REACH_DATABASE=$((ATTEMPTS_LEFT_TO_REACH_DATABASE - 1))
echo "Still waiting for database to be ready... Or maybe the database is not reachable. $ATTEMPTS_LEFT_TO_REACH_DATABASE attempts left."
done

if [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ]; then
echo "The database is not up or not reachable:"
echo "$DATABASE_ERROR"
exit 1
else
echo "The database is now ready and reachable"
fi

if [ "$( find ./migrations -iname '*.php' -print -quit )" ]; then
php bin/console doctrine:migrations:migrate --no-interaction
fi
fi

setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var
setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var
fi

exec docker-php-entrypoint "$@"
4 changes: 4 additions & 0 deletions frankenphp/worker.Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
worker {
file ./public/index.php
env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime
}

0 comments on commit 6326873

Please sign in to comment.