Rootless Docker/Optimized build (#932)
* Rootless Dockerfile/Optimized build Add unneeded files to .dockerignore Split Dockerfile into more stages to allow Composer/Yarn to run concurrently Don't log supervisord to a file, as file logging in a Docker container makes no sense Redirect process output to container output for log processors Run all processes as non-root Minimize files with write permission for non-root user Move docker folder out of .github, as it has nothing to do with GitHub * Remove install-php-extensions utility after use and name final stage * Test arm64 runner * Allow Docker workflow caching multi-arch separately * Fix Docker publish workflow branches * Move Caddyfile/crontab config into docker directory, remove redundant supervisord user * Further restrict permissions * Supervisord logs
This commit is contained in:
parent
37ba62410f
commit
6a4963200c
@ -1,10 +1,29 @@
|
||||
**.DS_Store
|
||||
.env
|
||||
.devcontainer
|
||||
.dockerignore
|
||||
.editorconfig
|
||||
.git
|
||||
node_modules
|
||||
vendor
|
||||
.github
|
||||
**.gitignore
|
||||
.php-cs-fixer.dist.php
|
||||
.prettierrc.json
|
||||
.vscode
|
||||
Dockerfile
|
||||
bounties.md
|
||||
compose.yml
|
||||
contributing.md
|
||||
contributor_license_agreement.md
|
||||
database/database.sqlite
|
||||
docker/README.md
|
||||
node_modules
|
||||
phpstan.neon
|
||||
phpunit.xml
|
||||
readme.md
|
||||
storage/debugbar/*.json
|
||||
storage/logs/*.log
|
||||
storage/framework/cache/data/*
|
||||
storage/framework/sessions/*
|
||||
storage/framework/testing
|
||||
storage/framework/views/*.php
|
||||
storage/logs/*.log
|
||||
vendor
|
||||
|
6
.github/workflows/docker-publish.yml
vendored
6
.github/workflows/docker-publish.yml
vendored
@ -71,6 +71,8 @@ jobs:
|
||||
VERSION=${{ steps.build_info.outputs.version_tag }}
|
||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||
cache-from: type=gha,scope=tagged${{ matrix.os }}
|
||||
cache-to: type=gha,scope=tagged${{ matrix.os }},mode=max
|
||||
|
||||
- name: Build and Push (main)
|
||||
uses: docker/build-push-action@v6
|
||||
@ -84,5 +86,5 @@ jobs:
|
||||
VERSION=dev-${{ steps.build_info.outputs.short_sha }}
|
||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
cache-from: type=gha,scope=${{ matrix.os }}
|
||||
cache-to: type=gha,scope=${{ matrix.os }},mode=max
|
||||
|
108
Dockerfile
108
Dockerfile
@ -1,69 +1,103 @@
|
||||
# syntax=docker.io/docker/dockerfile:1.7-labs
|
||||
# Pelican Production Dockerfile
|
||||
|
||||
# ================================
|
||||
# Stage 1: Build PHP Dependencies
|
||||
# Stage 1: Build PHP Base Image
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine AS composer
|
||||
FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine AS base
|
||||
|
||||
ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
|
||||
|
||||
RUN install-php-extensions bcmath gd intl zip opcache pcntl posix pdo_mysql
|
||||
|
||||
RUN rm /usr/local/bin/install-php-extensions
|
||||
|
||||
# ================================
|
||||
# Stage 2-1: Composer Install
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH base AS composer
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY . ./
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
# Install required libraries and PHP extensions
|
||||
RUN apk update && apk add --no-cache \
|
||||
libpng-dev libjpeg-turbo-dev freetype-dev libzip-dev icu-dev \
|
||||
zip unzip curl \
|
||||
&& docker-php-ext-install bcmath gd intl zip opcache pcntl posix pdo_mysql
|
||||
# Copy bare minimum to install Composer dependencies
|
||||
COPY composer.json composer.lock ./
|
||||
|
||||
RUN composer install --no-dev --optimize-autoloader
|
||||
RUN composer install --no-dev --no-interaction --no-autoloader --no-scripts
|
||||
|
||||
# ================================
|
||||
# Stage 2: Build Frontend Assets
|
||||
# Stage 2-2: Yarn Install
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH node:20-alpine AS yarn
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY --from=composer /build .
|
||||
# Copy bare minimum to install Yarn dependencies
|
||||
COPY package.json yarn.lock ./
|
||||
|
||||
RUN yarn config set network-timeout 300000 \
|
||||
&& yarn install --frozen-lockfile \
|
||||
&& yarn run build
|
||||
&& yarn install --frozen-lockfile
|
||||
|
||||
# ================================
|
||||
# Stage 3: Build Final Application Image
|
||||
# Stage 3-1: Composer Optimize
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
|
||||
FROM --platform=$TARGETOS/$TARGETARCH composer AS composerbuild
|
||||
|
||||
# Copy full code to optimize autoload
|
||||
COPY --exclude=Caddyfile --exclude=docker/ . ./
|
||||
|
||||
RUN composer dump-autoload --optimize
|
||||
|
||||
# ================================
|
||||
# Stage 3-2: Build Frontend Assets
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH yarn AS yarnbuild
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy full code
|
||||
COPY --exclude=Caddyfile --exclude=docker/ . ./
|
||||
COPY --from=composer /build .
|
||||
|
||||
RUN yarn run build
|
||||
|
||||
# ================================
|
||||
# Stage 4: Build Final Application Image
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH base AS final
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
# Install additional required libraries
|
||||
RUN apk update && apk add --no-cache \
|
||||
libpng-dev libjpeg-turbo-dev freetype-dev libzip-dev icu-dev \
|
||||
zip unzip curl caddy ca-certificates supervisor
|
||||
caddy ca-certificates supervisor supercronic
|
||||
|
||||
# Copy PHP extensions and configuration from Composer stage
|
||||
COPY --from=composer /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
|
||||
COPY --from=composer /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
|
||||
COPY --chown=root:www-data --chmod=640 --from=composerbuild /build .
|
||||
COPY --chown=root:www-data --chmod=640 --from=yarnbuild /build/public ./public
|
||||
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
COPY --from=yarn /build .
|
||||
|
||||
RUN touch .env
|
||||
|
||||
# Set permissions for Laravel directories
|
||||
RUN chmod -R 755 storage bootstrap/cache \
|
||||
&& chown -R www-data:www-data ./
|
||||
|
||||
# Add Laravel scheduler to crontab
|
||||
RUN echo "* * * * * php /var/www/html/artisan schedule:run >> /dev/null 2>&1" | crontab -u www-data -
|
||||
# Set permissions
|
||||
# First ensure all files are owned by root and restrict www-data to read access
|
||||
RUN chown root:www-data ./ \
|
||||
&& chmod 750 ./ \
|
||||
# Files should not have execute set, but directories need it
|
||||
&& find ./ -type d -exec chmod 750 {} \; \
|
||||
# Symlink to env/database path, as www-data won't be able to write to webroot
|
||||
&& ln -s /pelican-data/.env ./.env \
|
||||
&& ln -s /pelican-data/database/database.sqlite ./database/database.sqlite \
|
||||
# Create necessary directories
|
||||
&& mkdir -p /pelican-data /var/run/supervisord /etc/supercronic \
|
||||
# Finally allow www-data write permissions where necessary
|
||||
&& chown -R www-data:www-data /pelican-data ./storage ./bootstrap/cache /var/run/supervisord \
|
||||
&& chmod -R u+rwX,g+rwX,o-rwx /pelican-data ./storage ./bootstrap/cache /var/run/supervisord
|
||||
|
||||
# Configure Supervisor
|
||||
RUN cp .github/docker/supervisord.conf /etc/supervisord.conf && \
|
||||
mkdir /var/log/supervisord/
|
||||
COPY docker/supervisord.conf /etc/supervisord.conf
|
||||
COPY docker/Caddyfile /etc/caddy/Caddyfile
|
||||
# Add Laravel scheduler to crontab
|
||||
COPY docker/crontab /etc/supercronic/crontab
|
||||
|
||||
COPY docker/entrypoint.sh ./docker/entrypoint.sh
|
||||
|
||||
HEALTHCHECK --interval=5m --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost/up || exit 1
|
||||
@ -72,5 +106,7 @@ EXPOSE 80 443
|
||||
|
||||
VOLUME /pelican-data
|
||||
|
||||
ENTRYPOINT [ "/bin/ash", ".github/docker/entrypoint.sh" ]
|
||||
USER www-data
|
||||
|
||||
ENTRYPOINT [ "/bin/ash", "docker/entrypoint.sh" ]
|
||||
CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ]
|
||||
|
@ -28,6 +28,7 @@ x-common:
|
||||
services:
|
||||
panel:
|
||||
image: ghcr.io/pelican-dev/panel:latest
|
||||
build: .
|
||||
restart: always
|
||||
networks:
|
||||
- default
|
||||
|
1
docker/crontab
Normal file
1
docker/crontab
Normal file
@ -0,0 +1 @@
|
||||
* * * * * php /var/www/html/artisan schedule:run
|
@ -1,14 +1,11 @@
|
||||
#!/bin/ash -e
|
||||
|
||||
#mkdir -p /var/log/supervisord/ /var/log/php8/ \
|
||||
|
||||
## check for .env file and generate app keys if missing
|
||||
if [ -f /pelican-data/.env ]; then
|
||||
## check for .env file or symlink and generate app keys if missing
|
||||
if [ -f /var/www/html/.env ]; then
|
||||
echo "external vars exist."
|
||||
rm -rf /var/www/html/.env
|
||||
else
|
||||
echo "external vars don't exist."
|
||||
rm -rf /var/www/html/.env
|
||||
# webroot .env is symlinked to this path
|
||||
touch /pelican-data/.env
|
||||
|
||||
## manually generate a key because key generate --force fails
|
||||
@ -26,10 +23,7 @@ else
|
||||
echo -e "APP_INSTALLED=false" >> /pelican-data/.env
|
||||
fi
|
||||
|
||||
mkdir /pelican-data/database
|
||||
ln -s /pelican-data/.env /var/www/html/
|
||||
chown -h www-data:www-data /var/www/html/.env
|
||||
ln -s /pelican-data/database/database.sqlite /var/www/html/database/
|
||||
mkdir /pelican-data/database /var/www/html/storage/logs/supervisord 2>/dev/null
|
||||
|
||||
if ! grep -q "APP_KEY=" .env || grep -q "APP_KEY=$" .env; then
|
||||
echo "Generating APP_KEY..."
|
||||
@ -45,10 +39,6 @@ php artisan migrate --force
|
||||
echo -e "Optimizing Filament"
|
||||
php artisan filament:optimize
|
||||
|
||||
## start cronjobs for the queue
|
||||
echo -e "Starting cron jobs."
|
||||
crond -L /var/log/crond -l 5
|
||||
|
||||
export SUPERVISORD_CADDY=false
|
||||
|
||||
## disable caddy if SKIP_CADDY is set
|
||||
@ -59,7 +49,5 @@ else
|
||||
export SUPERVISORD_CADDY=true
|
||||
fi
|
||||
|
||||
chown -R www-data:www-data /pelican-data/.env /pelican-data/database
|
||||
|
||||
echo "Starting Supervisord"
|
||||
exec "$@"
|
@ -4,16 +4,14 @@ username=dummy
|
||||
password=dummy
|
||||
|
||||
[supervisord]
|
||||
logfile=/var/log/supervisord/supervisord.log ; supervisord log file
|
||||
logfile=/var/www/html/storage/logs/supervisord/supervisord.log ; supervisord log file
|
||||
logfile_maxbytes=50MB ; maximum size of logfile before rotation
|
||||
logfile_backups=2 ; number of backed up logfiles
|
||||
loglevel=error ; info, debug, warn, trace
|
||||
pidfile=/var/run/supervisord.pid ; pidfile location
|
||||
nodaemon=false ; run supervisord as a daemon
|
||||
pidfile=/var/run/supervisord/supervisord.pid ; pidfile location
|
||||
nodaemon=true ; run supervisord as a daemon
|
||||
minfds=1024 ; number of startup file descriptors
|
||||
minprocs=200 ; number of process descriptors
|
||||
user=root ; default user
|
||||
childlogdir=/var/log/supervisord/ ; where child log files will live
|
||||
|
||||
[rpcinterface:supervisor]
|
||||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||||
@ -30,7 +28,6 @@ autorestart=true
|
||||
|
||||
[program:queue-worker]
|
||||
command=/usr/local/bin/php /var/www/html/artisan queue:work --tries=3
|
||||
user=www-data
|
||||
autostart=true
|
||||
autorestart=true
|
||||
|
||||
@ -39,5 +36,12 @@ command=caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
autostart=%(ENV_SUPERVISORD_CADDY)s
|
||||
autorestart=%(ENV_SUPERVISORD_CADDY)s
|
||||
priority=10
|
||||
stdout_events_enabled=true
|
||||
stderr_events_enabled=true
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
|
||||
[program:supercronic]
|
||||
command=supercronic /etc/supercronic/crontab
|
||||
autostart=true
|
||||
autorestart=true
|
||||
redirect_stderr=true
|
Loading…
x
Reference in New Issue
Block a user