Building a General Purpose
PHP Docker Image
Robert Lemke
Robert Lemke
Flownative Managing Partner
Neos CMS Project Founder
Robert Lemke
Flownative Managing Partner
Neos CMS Project Founder
Robert Lemke
Co-Founder Flownative
Founder Neos CMS Project
Creating software since 1985
PHP since 1998
Robert Lemke
Flownative Managing Partner
Neos CMS Project Founder
PHP Image
for Development
and Production
Dev Toolbox vs.
Prod Parity
Base Image
Base Image
- standards and tooling for other images
- common tools for application usage
! create your own base image
! reduce to a minimum, but not less
Which OS?
Alpine Linux
- smaller filesystem footprint
- more secure?
- only relevant in container context
- uses musl instead of glibc "
Which OS?
Debian
- larger filesystem footprint
- well-established security team
- wide-spread, therefore more first-hand
experience
- high compatibility due to glibc
Which OS?
! You need to feel comfortable working
with the OS and it must support all the
software you want to install
0
30
60
90
120
ubuntu:jammy debian:bullseye bitnami/minideb:bullseye alpine:latest
5,29 MB
72 MB
118 MB
69 MB
Does the image size
really matter?
Due to layering not so much if most
containers use the same base image
https://github.com/wagoodman/dive
dive
bash:$
Hello World
Base Image
FROM bitnami/minideb:bullseye
COPY root-files /
RUN /build.sh "# rm /build.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD [ "run" ]
FROM bitnami/minideb:bullseye
LABEL org.opencontainers.image.authors="Robert Lemke <robert@flownative.com>"
ARG BUILD_DATE
LABEL com.flownative.base-image-build-date=$BUILD_DATE
COPY root-files /
RUN /build.sh "# rm /build.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD [ "run" ]
FROM bitnami/minideb:bullseye
LABEL org.opencontainers.image.authors="Robert Lemke <robert@flownative.com>"
ARG BUILD_DATE
LABEL com.flownative.base-image-build-date=$BUILD_DATE
ENV FLOWNATIVE_LIB_PATH="/opt/flownative/lib" 
LOG_DEBUG=true
COPY "$from=flownative/bash-library:1.13.5 /lib $FLOWNATIVE_LIB_PATH
COPY root-files /
RUN /build.sh "# rm /build.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD [ "run" ]
!"/bin/bash
# Load helper libraries
. "${FLOWNATIVE_LIB_PATH}/log.sh"
. "${FLOWNATIVE_LIB_PATH}/banner.sh"
. "${FLOWNATIVE_LIB_PATH}/packages.sh"
set -o errexit
set -o nounset
set -o pipefail
# ---------------------------------------------------------------------------------------
# Main routine
export FLOWNATIVE_LOG_PATH_AND_FILENAME=/dev/stdout
banner_flownative 'Demo Base Image'
packages_install dpkg apt-utils ca-certificates syslog-ng logrotate
# Clean up
rm -rf 
/var/cache"% 
/var/log"%
!"/bin/bash
set -o errexit
set -o nounset
set -o pipefail
. "${FLOWNATIVE_LIB_PATH}/banner.sh"
banner_flownative "Demo Base Image"
sleep 3600
Supervisor
FROM flownative/base:bullseye
LABEL org.opencontainers.image.authors="Robert Lemke <robert@flownative.com>"
ENV PHP_BASE_PATH="/opt/flownative/php" 
PATH="/opt/flownative/php/bin:$PATH" 
LOG_DEBUG="false"
COPY root-files /
RUN export FLOWNATIVE_LOG_PATH_AND_FILENAME=/dev/stdout 
"# /build.sh init 
"# /build.sh clean
WORKDIR ${PHP_BASE_PATH}
ENTRYPOINT [ "/entrypoint.sh" ]
CMD [ "run" ]
!"/bin/bash
set -o errexit
set -o nounset
set -o pipefail
# Load lib
. "${FLOWNATIVE_LIB_PATH}/syslog-ng.sh"
. "${FLOWNATIVE_LIB_PATH}/supervisor.sh"
. "${FLOWNATIVE_LIB_PATH}/banner.sh"
banner_flownative "Demo Base Image"
eval "$(syslog_env)"
syslog_initialize
syslog_start
eval "$(supervisor_env)"
supervisor_initialize
supervisor_start
trap 'supervisor_stop; syslog_stop' SIGINT SIGTERM
if [[ "$*" = *"run"* ]]; then
supervisor_pid=$(supervisor_get_pid)
info "Entrypoint: Start up complete"
# We can't use "wait" because supervisord is not a direct child of this shell:
while [ -e "/proc/${supervisor_pid}" ]; do sleep 1.1; done
info "Good bye #"
else
"$@"
fi
Pod
Structure
PHP-FPM Image
FROM flownative/base:bullseye
LABEL org.opencontainers.image.authors="Robert Lemke <robert@flownative.com>"
# -----------------------------------------------------------------------------
# PHP
# Latest versions: https:"#www.php.net/downloads.php
ARG PHP_VERSION
ENV PHP_VERSION ${PHP_VERSION}
ENV PHP_BASE_PATH="/opt/flownative/php" 
PATH="/opt/flownative/php/bin:$PATH" 
LOG_DEBUG="false"
USER root
COPY root-files /
RUN export FLOWNATIVE_LOG_PATH_AND_FILENAME=/dev/stdout 
"# /build.sh init 
"# /build.sh clean
USER 1000
EXPOSE 9000 9001
WORKDIR ${PHP_BASE_PATH}
ENTRYPOINT [ "/entrypoint.sh" ]
CMD [ "run" ]
!"/bin/bash
[…]
# ---------------------------------------------------------------------------------------
# build_create_directories() - Create directories and set access rights accordingly
#
# @global PHP_BASE_PATH
# @global BEACH_APPLICATION_PATH
# @return void
#
build_create_directories() {
mkdir -p 
"${PHP_BASE_PATH}/bin" 
"${PHP_BASE_PATH}/etc/conf.d" 
"${PHP_BASE_PATH}/ext" 
"${PHP_BASE_PATH}/tmp" 
"${PHP_BASE_PATH}/log"
}
# ---------------------------------------------------------------------------------------
# build_adjust_permissions() - Adjust permissions for a few paths and files
#
# @global PHP_BASE_PATH
# @return void
#
build_adjust_permissions() {
chown -R root:root "${PHP_BASE_PATH}"
chmod -R g+rwX "${PHP_BASE_PATH}"
chown -R 1000 
"${PHP_BASE_PATH}/etc" 
"${PHP_BASE_PATH}/tmp"
}
# ---------------------------------------------------------------------------------------
# Main routine
case $1 in
init)
banner_flownative 'PHP'
build_create_directories
"&
clean)
build_adjust_permissions
"&
esac
!"/bin/bash
set -o errexit
set -o nounset
set -o pipefail
# Load lib
. "${FLOWNATIVE_LIB_PATH}/syslog-ng.sh"
. "${FLOWNATIVE_LIB_PATH}/supervisor.sh"
. "${FLOWNATIVE_LIB_PATH}/banner.sh"
banner_flownative PHP
eval "$(syslog_env)"
syslog_initialize
syslog_start
eval "$(supervisor_env)"
supervisor_initialize
supervisor_start
trap 'supervisor_stop; syslog_stop' SIGINT SIGTERM
if [[ "$*" = *"run"* ]]; then
supervisor_pid=$(supervisor_get_pid)
info "Entrypoint: Start up complete"
# We can't use "wait" because supervisord is not a direct child of this shell:
while [ -e "/proc/${supervisor_pid}" ]; do sleep 1.1; done
info "Good bye #"
else
"$@"
fi
# ---------------------------------------------------------------------------------------
# Main routine
case $1 in
init)
banner_flownative 'PHP'
if [[ ! "${PHP_VERSION}" =~ ^8.[0-2] ]]; then
error "$ Unsupported PHP version '${PHP_VERSION}'"
exit 1
fi
build_create_directories
"&
prepare)
packages_install $(build_get_runtime_packages) 1>$(debug_device)
packages_install $(build_get_build_packages) 1>$(debug_device)
"&
build)
build_compile_php
"&
clean)
build_adjust_permissions
packages_remove $(build_get_build_packages) 1>$(debug_device)
packages_remove $(build_get_unnecessary_packages) 1>$(debug_device)
packages_remove_docs_and_caches 1>$(debug_device)
build_clean
"&
esac
# ------------------------------------------------------------------------------------------
# build_get_build_packages() - Returns a list of packages which are only needed for building
#
# @global PHP_BASE_PATH
# @return List of packages
#
build_get_build_packages() {
local packages="
autoconf
bison
build-essential
cmake
curl
file
pkg-config
re2c
unzip
libcurl4-openssl-dev
libfreetype6-dev
libgmp-dev
libicu-dev
libjpeg62-turbo-dev
libltdl-dev
libmariadb-dev
libmcrypt-dev
libonig-dev
libpng-dev
libpspell-dev
libpq-dev
libreadline6-dev
libsqlite3-dev
libssl-dev
libwebp-dev
libxml2-dev
libzip-dev
libbz2-dev
"
echo $packages
}
# ---------------------------------------------------------------------------------------
# build_get_runtime_packages() - Returns a list of packages which are needed during runtime
#
# @return List of packages
#
build_get_runtime_packages() {
local packages="
libcurl4
libjpeg62
libonig5
libpq5
libreadline8
libsodium-dev
libssl1.1
libzip4
libbz2-1.0
libncurses6
libpng16-16
libwebp6
libxml2
libsqlite3-0
"
echo $packages
}
# ---------------------------------------------------------------------------------------
# build_get_unnecessary_packages() - Not needed packages, can be removed
#
# @return List of packages
#
build_get_unnecessary_packages() {
local packages="
cmake
"
echo $packages
}
# ---------------------------------------------------------------------------------------
# Main routine
case $1 in
init)
banner_flownative 'PHP'
if [[ ! "${PHP_VERSION}" =~ ^8.[0-2] ]]; then
error "$ Unsupported PHP version '${PHP_VERSION}'"
exit 1
fi
build_create_directories
"&
prepare)
packages_install $(build_get_runtime_packages) 1>$(debug_device)
packages_install $(build_get_build_packages) 1>$(debug_device)
"&
build)
build_compile_php
"&
clean)
build_adjust_permissions
packages_remove $(build_get_build_packages) 1>$(debug_device)
packages_remove $(build_get_unnecessary_packages) 1>$(debug_device)
packages_remove_docs_and_caches 1>$(debug_device)
build_clean
"&
esac
# ---------------------------------------------------------------------------------------
# Main routine
case $1 in
init)
banner_flownative 'PHP'
if [[ ! "${PHP_VERSION}" =~ ^8.[0-2] ]]; then
error "$ Unsupported PHP version '${PHP_VERSION}'"
exit 1
fi
build_create_directories
"&
prepare)
packages_install $(build_get_runtime_packages) 1>$(debug_device)
packages_install $(build_get_build_packages) 1>$(debug_device)
"&
build)
build_compile_php
"&
clean)
build_adjust_permissions
packages_remove $(build_get_build_packages) 1>$(debug_device)
packages_remove $(build_get_unnecessary_packages) 1>$(debug_device)
packages_remove_docs_and_caches 1>$(debug_device)
build_clean
"&
esac
# ---------------------------------------------------------------------------------------
# build_compile_php() -
#
# @global PHP_BASE_PATH
# @return void
#
build_compile_php() {
local php_source_url
php_source_url="https:"'www.php.net/distributions/php-${PHP_VERSION}.tar.gz"
info "$ Downloading source code for PHP ${PHP_VERSION} from ${php_source_url} ""("
with_backoff "curl -sSL ${php_source_url} -o php.tar.gz" "15" ") (
error "Failed downloading PHP source from ${php_source_url}"
exit 1
)
mkdir -p "${PHP_BASE_PATH}/src"
tar -xf php.tar.gz -C "${PHP_BASE_PATH}/src" "$strip-components=1
rm php.tar.gz*
cd "${PHP_BASE_PATH}/src"
info "$ Generating build configuration ""("
./buildconf "$force >$(debug_device)
if [[ ! -f configure ]]; then
error "$ Failed generating build configuration, 'configure' not found"
# shellcheck disable=SC2012
ls | output
exit 1
fi
…
# ---------------------------------------------------------------------------------------
# build_compile_php() -
#
# @global PHP_BASE_PATH
# @return void
#
build_compile_php() {
local php_source_url
php_source_url="https:"'www.php.net/distributions/php-${PHP_VERSION}.tar.gz"
info "$ Downloading source code for PHP ${PHP_VERSION} from ${php_source_url} ""("
with_backoff "curl -sSL ${php_source_url} -o php.tar.gz" "15" ") (
error "Failed downloading PHP source from ${php_source_url}"
exit 1
)
mkdir -p "${PHP_BASE_PATH}/src"
tar -xf php.tar.gz -C "${PHP_BASE_PATH}/src" "$strip-components=1
rm php.tar.gz*
cd "${PHP_BASE_PATH}/src"
info "$ Generating build configuration ""("
./buildconf "$force >$(debug_device)
if [[ ! -f configure ]]; then
error "$ Failed generating build configuration, 'configure' not found"
# shellcheck disable=SC2012
ls | output
exit 1
fi
…
…
# For GCC warning options see: https:"#gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/Warning-Options.html
export CFLAGS='-Wno-deprecated-declarations -Wno-stringop-overflow -Wno-implicit-function-declaration'
if [[ "${PHP_VERSION}" =~ ^8.[0-2] ]]; then
./configure 
"$prefix=${PHP_BASE_PATH} 
"$with-config-file-path="${PHP_BASE_PATH}/etc" 
"$with-config-file-scan-dir="${PHP_BASE_PATH}/etc/conf.d" 
"$enable-bcmath 
"$disable-cgi 
"$enable-calendar 
"$enable-exif 
"$enable-fpm 
"$enable-ftp 
"$enable-gd 
"$enable-intl 
"$enable-mbstring 
"$enable-pcntl 
"$enable-soap 
"$enable-sockets 
"$with-curl 
"$with-freetype 
"$with-gmp 
"$with-jpeg 
"$with-mysqli 
"$with-openssl 
"$with-pdo-pgsql 
"$with-pdo-mysql 
"$with-readline 
"$with-sodium 
"$with-system-ciphers 
"$with-webp 
"$with-zip 
"$with-zlib 
"$with-bz2 
"$without-pear 
>$(debug_device)
else
error "$ No configure call available for PHP version ${PHP_VERSION}"
exit 1
fi
…
info "$ Compiling PHP ""("
make -j"$(nproc)" >$(debug_device)
make install >$(debug_device)
info "$ Cleaning up ""("
make clean >$(debug_device)
rm -rf /tmp/pear
}
# ---------------------------------------------------------------------------------------
# Main routine
case $1 in
init)
banner_flownative 'PHP'
if [[ ! "${PHP_VERSION}" =~ ^8.[0-2] ]]; then
error "$ Unsupported PHP version '${PHP_VERSION}'"
exit 1
fi
build_create_directories
"&
prepare)
packages_install $(build_get_runtime_packages) 1>$(debug_device)
packages_install $(build_get_build_packages) 1>$(debug_device)
"&
build)
build_compile_php
"&
clean)
build_adjust_permissions
packages_remove $(build_get_build_packages) 1>$(debug_device)
packages_remove $(build_get_unnecessary_packages) 1>$(debug_device)
packages_remove_docs_and_caches 1>$(debug_device)
build_clean
"&
esac
!"/bin/bash
# shellcheck disable=SC1090
set -o errexit
set -o nounset
set -o pipefail
# Load lib
. "${FLOWNATIVE_LIB_PATH}/syslog-ng.sh"
. "${FLOWNATIVE_LIB_PATH}/supervisor.sh"
. "${FLOWNATIVE_LIB_PATH}/banner.sh"
. "${FLOWNATIVE_LIB_PATH}/php-fpm.sh"
banner_flownative PHP
eval "$(syslog_env)"
syslog_initialize
syslog_start
eval "$(php_fpm_env)"
eval "$(supervisor_env)"
php_fpm_initialize
supervisor_initialize
supervisor_start
trap 'supervisor_stop; syslog_stop' SIGINT SIGTERM
if [[ "$*" = *"run"* ]]; then
supervisor_pid=$(supervisor_get_pid)
info "Entrypoint: Start up complete"
# We can't use "wait" because supervisord is not a direct child of this shell:
while [ -e "/proc/${supervisor_pid}" ]; do sleep 1.1; done
info "Good bye #"
else
"$@"
fi
# ---------------------------------------------------------------------------------------
# php_fpm_env() - Load global environment variables for configuring PHP
#
# @global PHP_* The PHP_ environment variables
# @return "export" statements which can be passed to eval()
#
php_fpm_env() {
cat "*"EOF"
export PHP_BASE_PATH="${PHP_BASE_PATH}"
export PHP_CONF_PATH="${PHP_CONF_PATH:-${PHP_BASE_PATH}/etc}"
export PHP_TMP_PATH="${PHP_TMP_PATH:-${PHP_BASE_PATH}/tmp}"
export PHP_LOG_PATH="${PHP_LOG_PATH:-${PHP_BASE_PATH}/log}"
export PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-750M}"
export PHP_DATE_TIMEZONE="${PHP_DATE_TIMEZONE:-UTC}"
export PHP_DISPLAY_ERRORS="${PHP_DISPLAY_ERRORS:-off}"
export PHP_ERROR_REPORTING="${PHP_ERROR_REPORTING:-2147483647}"
export PHP_ERROR_LOG="${PHP_ERROR_LOG:-/dev/stderr}"
export PHP_OPCACHE_PRELOAD="${PHP_OPCACHE_PRELOAD:-}"
export PHP_XDEBUG_ENABLE="${PHP_XDEBUG_ENABLE:-false}"
export PHP_XDEBUG_MODE="${PHP_XDEBUG_MODE:-develop}"
export PHP_XDEBUG_DISCOVER_CLIENT_HOST="${PHP_XDEBUG_DISCOVER_CLIENT_HOST:-false}"
export PHP_XDEBUG_CLIENT_HOST="${PHP_XDEBUG_CLIENT_HOST:-}"
export PHP_XDEBUG_CONFIG="${PHP_XDEBUG_CONFIG:-}"
export XDEBUG_CONFIG="${XDEBUG_CONFIG:-${PHP_XDEBUG_CONFIG}}"
export PHP_XDEBUG_MAX_NESTING_LEVEL="${PHP_XDEBUG_MAX_NESTING_LEVEL:-512}"
export PHP_IGBINARY_ENABLE="${PHP_IGBINARY_ENABLE:-false}"
export PHP_FPM_USER="1000"
export PHP_FPM_GROUP="1000"
export PHP_FPM_PORT="${PHP_FPM_PORT:-9000}"
export PHP_FPM_PM_MODE="${PHP_FPM_PM_MODE:-ondemand}"
export PHP_FPM_MAX_CHILDREN="${PHP_FPM_MAX_CHILDREN:-20}"
export PHP_FPM_ERROR_LOG_PATH="${PHP_FPM_ERROR_LOG_PATH:-/opt/flownative/log/php-fpm-error.log}"
export PHP_FPM_ACCESS_LOG_PATH="${PHP_FPM_ACCESS_LOG_PATH:-/opt/flownative/log/php-fpm-access.log}"
EOF
}
[global]
error_log = ${PHP_FPM_ERROR_LOG_PATH}
pid = ${PHP_TMP_PATH}/php-fpm.pid
; don't daemonize, because we want to start PHP as a child process of
; the shell running php-fpm.sh, so we can wait for it with "wait":
daemonize = no
[www]
access.log = ${PHP_FPM_ACCESS_LOG_PATH}
listen = ["+]:${PHP_FPM_PORT}
pm = ${PHP_FPM_PM_MODE}
pm.max_children = ${PHP_FPM_MAX_CHILDREN}
pm.status_path = /php-fpm-status
clear_env = no
# ---------------------------------------------------------------------------------------
# php_fpm_initialize() - Initialize PHP configuration and check required files and dirs
#
# @global PHP_* The PHP_* environment variables
# @return void
#
php_fpm_initialize() {
if [[ $(id "$user) ", 0 ]]; then
error "PHP-FPM: Container is running as root, but only unprivileged users are supported"
exit 1
fi;
info "PHP-FPM: Initializing configuration ""("
envsubst < "${PHP_CONF_PATH}/php-fpm.conf.template" > "${PHP_CONF_PATH}/php-fpm.conf"
if is_boolean_yes "${PHP_XDEBUG_ENABLE}"; then
info "PHP-FPM: Xdebug is enabled"
mv "${PHP_CONF_PATH}/conf.d/php-ext-xdebug.ini.inactive" "${PHP_CONF_PATH}/conf.d/php-ext-xdebug.ini"
else
info "PHP-FPM: Xdebug is disabled"
export PHP_XDEBUG_MODE="off"
fi
if is_boolean_yes "${PHP_IGBINARY_ENABLE}"; then
# igbinary might have been enabled already by scripts in an Docker image which is based on this one
if [ -f "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini.inactive" ]; then
info "PHP-FPM: igbinary is enabled"
mv -f "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini.inactive" "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini"
fi
else
info "PHP-FPM: igbinary is disabled"
fi
# Create a file descriptor for the PHP-FPM log output and clean up the log lines a bit:
exec 4> >(sed -e "s/^([0-9"--]* [0-9:,]*)".1 OUTPUT PHP-FPM:/")
}
# ---------------------------------------------------------------------------------------
# php_fpm_initialize() - Initialize PHP configuration and check required files and dirs
#
# @global PHP_* The PHP_* environment variables
# @return void
#
php_fpm_initialize() {
if [[ $(id "$user) ", 0 ]]; then
error "PHP-FPM: Container is running as root, but only unprivileged users are supported"
exit 1
fi;
info "PHP-FPM: Initializing configuration ""("
envsubst < "${PHP_CONF_PATH}/php-fpm.conf.template" > "${PHP_CONF_PATH}/php-fpm.conf"
if is_boolean_yes "${PHP_XDEBUG_ENABLE}"; then
info "PHP-FPM: Xdebug is enabled"
mv "${PHP_CONF_PATH}/conf.d/php-ext-xdebug.ini.inactive" "${PHP_CONF_PATH}/conf.d/php-ext-xdebug.ini"
else
info "PHP-FPM: Xdebug is disabled"
export PHP_XDEBUG_MODE="off"
fi
if is_boolean_yes "${PHP_IGBINARY_ENABLE}"; then
# igbinary might have been enabled already by scripts in an Docker image which is based on this one
if [ -f "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini.inactive" ]; then
info "PHP-FPM: igbinary is enabled"
mv -f "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini.inactive" "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini"
fi
else
info "PHP-FPM: igbinary is disabled"
fi
# Create a file descriptor for the PHP-FPM log output and clean up the log lines a bit:
exec 4> >(sed -e "s/^([0-9"--]* [0-9:,]*)".1 OUTPUT PHP-FPM:/")
}
[program:php-fpm]
process_name=%(program_name)s
command="%(ENV_PHP_BASE_PATH)s/sbin/php-fpm"
autostart=true
autorestart=true
numprocs=1
Docker Compose
version: '3.7'
services:
webserver:
image: flownative/nginx:latest
container_name: ipc_muc_2023_webserver
ports:
- "8080"
volumes:
- ./:/application
environment:
- BEACH_PHP_FPM_HOST=ipc_muc_2023_php
php:
image: flownative/php:8.2
container_name: ipc_muc_2023_php
ports:
- "9000"
- "9003"
security_opt:
- no-new-privileges
volumes:
- ./:/application
environment:
- PHP_DISPLAY_ERRORS=on
<?php
echo "Hello world!";
Application Image
FROM scratch
MAINTAINER Flownative <support@flownative.com>
VOLUME /build
COPY DistributionPackages /build/DistributionPackages
COPY Packages /build/Packages
COPY bin /build/bin
COPY Web /build/Web
COPY flow /build/flow
COPY composer.json /build/composer.json
COPY composer.lock /build/composer.lock
COPY Configuration /build/Configuration
FROM scratch busybox:latest
MAINTAINER Flownative <support@flownative.com>
VOLUME /build
COPY DistributionPackages /build/DistributionPackages
COPY Packages /build/Packages
COPY bin /build/bin
COPY Web /build/Web
COPY flow /build/flow
COPY composer.json /build/composer.json
COPY composer.lock /build/composer.lock
COPY Configuration /build/Configuration
Google Cloud
https://cloud.google.com/artifact-registry/
Harbor
https://goharbor.io
GitLab Container Registry
https://gitlab.com
Builds
%
Nightly Rebuilds
&
Nightly Rebuilds
announce:
runs-on: ubuntu-latest
needs: build
steps:
- name: Dispatch to beach-php
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.FLOWNATIVE_BOT_TOKEN }}
repository: flownative/docker-beach-php
event-type: php-images-built
client-payload: '{"image_name": "flownative/docker-php/php"}'
Build Cascade
name: Build Docker images
on:
repository_dispatch:
types: [php-images-built]
push:
branches-ignore:
- '**'
tags:
- 'v*.*.*'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
Build Cascade
Image Security
- don't run as root (use SecurityContext)
- disable privilege escalation
- automatically scan images
- redeploy automatically, frequently
- only include minimal amount of
software – do you really need a shell?
https://github.com/robertlemke/ipc-muc-2023
Demo Repository
https://github.com/flownative/?q=docker-
flownative/docker-php
https://github.com/flownative/?q=docker-
flownative/bash-library
And what about …?
ddev
performance
Mutagen
SSH
TLS
Xdebug
extensions
Kubernetes
Colima
logging
Your thoughts?
slideshare.net/robertlemke
robert@flownative.com
www.flownative.com
@robert@flownative.social
@lem.ke

A General Purpose Docker Image for PHP

  • 1.
    Building a GeneralPurpose PHP Docker Image Robert Lemke
  • 2.
    Robert Lemke Flownative ManagingPartner Neos CMS Project Founder
  • 3.
    Robert Lemke Flownative ManagingPartner Neos CMS Project Founder
  • 5.
    Robert Lemke Co-Founder Flownative FounderNeos CMS Project Creating software since 1985 PHP since 1998
  • 6.
    Robert Lemke Flownative ManagingPartner Neos CMS Project Founder
  • 8.
  • 9.
  • 10.
  • 11.
    Base Image - standardsand tooling for other images - common tools for application usage ! create your own base image ! reduce to a minimum, but not less
  • 12.
    Which OS? Alpine Linux -smaller filesystem footprint - more secure? - only relevant in container context - uses musl instead of glibc "
  • 13.
    Which OS? Debian - largerfilesystem footprint - well-established security team - wide-spread, therefore more first-hand experience - high compatibility due to glibc
  • 14.
    Which OS? ! Youneed to feel comfortable working with the OS and it must support all the software you want to install
  • 15.
  • 16.
    Does the imagesize really matter? Due to layering not so much if most containers use the same base image
  • 17.
  • 18.
  • 19.
  • 20.
    FROM bitnami/minideb:bullseye COPY root-files/ RUN /build.sh "# rm /build.sh ENTRYPOINT ["/entrypoint.sh"] CMD [ "run" ]
  • 21.
    FROM bitnami/minideb:bullseye LABEL org.opencontainers.image.authors="RobertLemke <robert@flownative.com>" ARG BUILD_DATE LABEL com.flownative.base-image-build-date=$BUILD_DATE COPY root-files / RUN /build.sh "# rm /build.sh ENTRYPOINT ["/entrypoint.sh"] CMD [ "run" ]
  • 22.
    FROM bitnami/minideb:bullseye LABEL org.opencontainers.image.authors="RobertLemke <robert@flownative.com>" ARG BUILD_DATE LABEL com.flownative.base-image-build-date=$BUILD_DATE ENV FLOWNATIVE_LIB_PATH="/opt/flownative/lib" LOG_DEBUG=true COPY "$from=flownative/bash-library:1.13.5 /lib $FLOWNATIVE_LIB_PATH COPY root-files / RUN /build.sh "# rm /build.sh ENTRYPOINT ["/entrypoint.sh"] CMD [ "run" ]
  • 23.
    !"/bin/bash # Load helperlibraries . "${FLOWNATIVE_LIB_PATH}/log.sh" . "${FLOWNATIVE_LIB_PATH}/banner.sh" . "${FLOWNATIVE_LIB_PATH}/packages.sh" set -o errexit set -o nounset set -o pipefail # --------------------------------------------------------------------------------------- # Main routine export FLOWNATIVE_LOG_PATH_AND_FILENAME=/dev/stdout banner_flownative 'Demo Base Image' packages_install dpkg apt-utils ca-certificates syslog-ng logrotate # Clean up rm -rf /var/cache"% /var/log"%
  • 24.
    !"/bin/bash set -o errexit set-o nounset set -o pipefail . "${FLOWNATIVE_LIB_PATH}/banner.sh" banner_flownative "Demo Base Image" sleep 3600
  • 27.
  • 28.
    FROM flownative/base:bullseye LABEL org.opencontainers.image.authors="RobertLemke <robert@flownative.com>" ENV PHP_BASE_PATH="/opt/flownative/php" PATH="/opt/flownative/php/bin:$PATH" LOG_DEBUG="false" COPY root-files / RUN export FLOWNATIVE_LOG_PATH_AND_FILENAME=/dev/stdout "# /build.sh init "# /build.sh clean WORKDIR ${PHP_BASE_PATH} ENTRYPOINT [ "/entrypoint.sh" ] CMD [ "run" ]
  • 29.
    !"/bin/bash set -o errexit set-o nounset set -o pipefail # Load lib . "${FLOWNATIVE_LIB_PATH}/syslog-ng.sh" . "${FLOWNATIVE_LIB_PATH}/supervisor.sh" . "${FLOWNATIVE_LIB_PATH}/banner.sh" banner_flownative "Demo Base Image" eval "$(syslog_env)" syslog_initialize syslog_start eval "$(supervisor_env)" supervisor_initialize supervisor_start trap 'supervisor_stop; syslog_stop' SIGINT SIGTERM if [[ "$*" = *"run"* ]]; then supervisor_pid=$(supervisor_get_pid) info "Entrypoint: Start up complete" # We can't use "wait" because supervisord is not a direct child of this shell: while [ -e "/proc/${supervisor_pid}" ]; do sleep 1.1; done info "Good bye #" else "$@" fi
  • 30.
  • 31.
  • 32.
    FROM flownative/base:bullseye LABEL org.opencontainers.image.authors="RobertLemke <robert@flownative.com>" # ----------------------------------------------------------------------------- # PHP # Latest versions: https:"#www.php.net/downloads.php ARG PHP_VERSION ENV PHP_VERSION ${PHP_VERSION} ENV PHP_BASE_PATH="/opt/flownative/php" PATH="/opt/flownative/php/bin:$PATH" LOG_DEBUG="false" USER root COPY root-files / RUN export FLOWNATIVE_LOG_PATH_AND_FILENAME=/dev/stdout "# /build.sh init "# /build.sh clean USER 1000 EXPOSE 9000 9001 WORKDIR ${PHP_BASE_PATH} ENTRYPOINT [ "/entrypoint.sh" ] CMD [ "run" ]
  • 33.
    !"/bin/bash […] # --------------------------------------------------------------------------------------- # build_create_directories()- Create directories and set access rights accordingly # # @global PHP_BASE_PATH # @global BEACH_APPLICATION_PATH # @return void # build_create_directories() { mkdir -p "${PHP_BASE_PATH}/bin" "${PHP_BASE_PATH}/etc/conf.d" "${PHP_BASE_PATH}/ext" "${PHP_BASE_PATH}/tmp" "${PHP_BASE_PATH}/log" } # --------------------------------------------------------------------------------------- # build_adjust_permissions() - Adjust permissions for a few paths and files # # @global PHP_BASE_PATH # @return void # build_adjust_permissions() { chown -R root:root "${PHP_BASE_PATH}" chmod -R g+rwX "${PHP_BASE_PATH}" chown -R 1000 "${PHP_BASE_PATH}/etc" "${PHP_BASE_PATH}/tmp" } # --------------------------------------------------------------------------------------- # Main routine case $1 in init) banner_flownative 'PHP' build_create_directories "& clean) build_adjust_permissions "& esac
  • 34.
    !"/bin/bash set -o errexit set-o nounset set -o pipefail # Load lib . "${FLOWNATIVE_LIB_PATH}/syslog-ng.sh" . "${FLOWNATIVE_LIB_PATH}/supervisor.sh" . "${FLOWNATIVE_LIB_PATH}/banner.sh" banner_flownative PHP eval "$(syslog_env)" syslog_initialize syslog_start eval "$(supervisor_env)" supervisor_initialize supervisor_start trap 'supervisor_stop; syslog_stop' SIGINT SIGTERM if [[ "$*" = *"run"* ]]; then supervisor_pid=$(supervisor_get_pid) info "Entrypoint: Start up complete" # We can't use "wait" because supervisord is not a direct child of this shell: while [ -e "/proc/${supervisor_pid}" ]; do sleep 1.1; done info "Good bye #" else "$@" fi
  • 36.
    # --------------------------------------------------------------------------------------- # Mainroutine case $1 in init) banner_flownative 'PHP' if [[ ! "${PHP_VERSION}" =~ ^8.[0-2] ]]; then error "$ Unsupported PHP version '${PHP_VERSION}'" exit 1 fi build_create_directories "& prepare) packages_install $(build_get_runtime_packages) 1>$(debug_device) packages_install $(build_get_build_packages) 1>$(debug_device) "& build) build_compile_php "& clean) build_adjust_permissions packages_remove $(build_get_build_packages) 1>$(debug_device) packages_remove $(build_get_unnecessary_packages) 1>$(debug_device) packages_remove_docs_and_caches 1>$(debug_device) build_clean "& esac
  • 37.
    # ------------------------------------------------------------------------------------------ # build_get_build_packages()- Returns a list of packages which are only needed for building # # @global PHP_BASE_PATH # @return List of packages # build_get_build_packages() { local packages=" autoconf bison build-essential cmake curl file pkg-config re2c unzip libcurl4-openssl-dev libfreetype6-dev libgmp-dev libicu-dev libjpeg62-turbo-dev libltdl-dev libmariadb-dev libmcrypt-dev libonig-dev libpng-dev libpspell-dev libpq-dev libreadline6-dev libsqlite3-dev libssl-dev libwebp-dev libxml2-dev libzip-dev libbz2-dev " echo $packages }
  • 38.
    # --------------------------------------------------------------------------------------- # build_get_runtime_packages()- Returns a list of packages which are needed during runtime # # @return List of packages # build_get_runtime_packages() { local packages=" libcurl4 libjpeg62 libonig5 libpq5 libreadline8 libsodium-dev libssl1.1 libzip4 libbz2-1.0 libncurses6 libpng16-16 libwebp6 libxml2 libsqlite3-0 " echo $packages }
  • 39.
    # --------------------------------------------------------------------------------------- # build_get_unnecessary_packages()- Not needed packages, can be removed # # @return List of packages # build_get_unnecessary_packages() { local packages=" cmake " echo $packages }
  • 40.
    # --------------------------------------------------------------------------------------- # Mainroutine case $1 in init) banner_flownative 'PHP' if [[ ! "${PHP_VERSION}" =~ ^8.[0-2] ]]; then error "$ Unsupported PHP version '${PHP_VERSION}'" exit 1 fi build_create_directories "& prepare) packages_install $(build_get_runtime_packages) 1>$(debug_device) packages_install $(build_get_build_packages) 1>$(debug_device) "& build) build_compile_php "& clean) build_adjust_permissions packages_remove $(build_get_build_packages) 1>$(debug_device) packages_remove $(build_get_unnecessary_packages) 1>$(debug_device) packages_remove_docs_and_caches 1>$(debug_device) build_clean "& esac
  • 41.
    # --------------------------------------------------------------------------------------- # Mainroutine case $1 in init) banner_flownative 'PHP' if [[ ! "${PHP_VERSION}" =~ ^8.[0-2] ]]; then error "$ Unsupported PHP version '${PHP_VERSION}'" exit 1 fi build_create_directories "& prepare) packages_install $(build_get_runtime_packages) 1>$(debug_device) packages_install $(build_get_build_packages) 1>$(debug_device) "& build) build_compile_php "& clean) build_adjust_permissions packages_remove $(build_get_build_packages) 1>$(debug_device) packages_remove $(build_get_unnecessary_packages) 1>$(debug_device) packages_remove_docs_and_caches 1>$(debug_device) build_clean "& esac
  • 42.
    # --------------------------------------------------------------------------------------- # build_compile_php()- # # @global PHP_BASE_PATH # @return void # build_compile_php() { local php_source_url php_source_url="https:"'www.php.net/distributions/php-${PHP_VERSION}.tar.gz" info "$ Downloading source code for PHP ${PHP_VERSION} from ${php_source_url} ""(" with_backoff "curl -sSL ${php_source_url} -o php.tar.gz" "15" ") ( error "Failed downloading PHP source from ${php_source_url}" exit 1 ) mkdir -p "${PHP_BASE_PATH}/src" tar -xf php.tar.gz -C "${PHP_BASE_PATH}/src" "$strip-components=1 rm php.tar.gz* cd "${PHP_BASE_PATH}/src" info "$ Generating build configuration ""(" ./buildconf "$force >$(debug_device) if [[ ! -f configure ]]; then error "$ Failed generating build configuration, 'configure' not found" # shellcheck disable=SC2012 ls | output exit 1 fi …
  • 43.
    # --------------------------------------------------------------------------------------- # build_compile_php()- # # @global PHP_BASE_PATH # @return void # build_compile_php() { local php_source_url php_source_url="https:"'www.php.net/distributions/php-${PHP_VERSION}.tar.gz" info "$ Downloading source code for PHP ${PHP_VERSION} from ${php_source_url} ""(" with_backoff "curl -sSL ${php_source_url} -o php.tar.gz" "15" ") ( error "Failed downloading PHP source from ${php_source_url}" exit 1 ) mkdir -p "${PHP_BASE_PATH}/src" tar -xf php.tar.gz -C "${PHP_BASE_PATH}/src" "$strip-components=1 rm php.tar.gz* cd "${PHP_BASE_PATH}/src" info "$ Generating build configuration ""(" ./buildconf "$force >$(debug_device) if [[ ! -f configure ]]; then error "$ Failed generating build configuration, 'configure' not found" # shellcheck disable=SC2012 ls | output exit 1 fi …
  • 44.
    … # For GCCwarning options see: https:"#gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/Warning-Options.html export CFLAGS='-Wno-deprecated-declarations -Wno-stringop-overflow -Wno-implicit-function-declaration' if [[ "${PHP_VERSION}" =~ ^8.[0-2] ]]; then ./configure "$prefix=${PHP_BASE_PATH} "$with-config-file-path="${PHP_BASE_PATH}/etc" "$with-config-file-scan-dir="${PHP_BASE_PATH}/etc/conf.d" "$enable-bcmath "$disable-cgi "$enable-calendar "$enable-exif "$enable-fpm "$enable-ftp "$enable-gd "$enable-intl "$enable-mbstring "$enable-pcntl "$enable-soap "$enable-sockets "$with-curl "$with-freetype "$with-gmp "$with-jpeg "$with-mysqli "$with-openssl "$with-pdo-pgsql "$with-pdo-mysql "$with-readline "$with-sodium "$with-system-ciphers "$with-webp "$with-zip "$with-zlib "$with-bz2 "$without-pear >$(debug_device) else error "$ No configure call available for PHP version ${PHP_VERSION}" exit 1 fi …
  • 45.
    info "$ CompilingPHP ""(" make -j"$(nproc)" >$(debug_device) make install >$(debug_device) info "$ Cleaning up ""(" make clean >$(debug_device) rm -rf /tmp/pear }
  • 46.
    # --------------------------------------------------------------------------------------- # Mainroutine case $1 in init) banner_flownative 'PHP' if [[ ! "${PHP_VERSION}" =~ ^8.[0-2] ]]; then error "$ Unsupported PHP version '${PHP_VERSION}'" exit 1 fi build_create_directories "& prepare) packages_install $(build_get_runtime_packages) 1>$(debug_device) packages_install $(build_get_build_packages) 1>$(debug_device) "& build) build_compile_php "& clean) build_adjust_permissions packages_remove $(build_get_build_packages) 1>$(debug_device) packages_remove $(build_get_unnecessary_packages) 1>$(debug_device) packages_remove_docs_and_caches 1>$(debug_device) build_clean "& esac
  • 48.
    !"/bin/bash # shellcheck disable=SC1090 set-o errexit set -o nounset set -o pipefail # Load lib . "${FLOWNATIVE_LIB_PATH}/syslog-ng.sh" . "${FLOWNATIVE_LIB_PATH}/supervisor.sh" . "${FLOWNATIVE_LIB_PATH}/banner.sh" . "${FLOWNATIVE_LIB_PATH}/php-fpm.sh" banner_flownative PHP eval "$(syslog_env)" syslog_initialize syslog_start eval "$(php_fpm_env)" eval "$(supervisor_env)" php_fpm_initialize supervisor_initialize supervisor_start trap 'supervisor_stop; syslog_stop' SIGINT SIGTERM if [[ "$*" = *"run"* ]]; then supervisor_pid=$(supervisor_get_pid) info "Entrypoint: Start up complete" # We can't use "wait" because supervisord is not a direct child of this shell: while [ -e "/proc/${supervisor_pid}" ]; do sleep 1.1; done info "Good bye #" else "$@" fi
  • 49.
    # --------------------------------------------------------------------------------------- # php_fpm_env()- Load global environment variables for configuring PHP # # @global PHP_* The PHP_ environment variables # @return "export" statements which can be passed to eval() # php_fpm_env() { cat "*"EOF" export PHP_BASE_PATH="${PHP_BASE_PATH}" export PHP_CONF_PATH="${PHP_CONF_PATH:-${PHP_BASE_PATH}/etc}" export PHP_TMP_PATH="${PHP_TMP_PATH:-${PHP_BASE_PATH}/tmp}" export PHP_LOG_PATH="${PHP_LOG_PATH:-${PHP_BASE_PATH}/log}" export PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-750M}" export PHP_DATE_TIMEZONE="${PHP_DATE_TIMEZONE:-UTC}" export PHP_DISPLAY_ERRORS="${PHP_DISPLAY_ERRORS:-off}" export PHP_ERROR_REPORTING="${PHP_ERROR_REPORTING:-2147483647}" export PHP_ERROR_LOG="${PHP_ERROR_LOG:-/dev/stderr}" export PHP_OPCACHE_PRELOAD="${PHP_OPCACHE_PRELOAD:-}" export PHP_XDEBUG_ENABLE="${PHP_XDEBUG_ENABLE:-false}" export PHP_XDEBUG_MODE="${PHP_XDEBUG_MODE:-develop}" export PHP_XDEBUG_DISCOVER_CLIENT_HOST="${PHP_XDEBUG_DISCOVER_CLIENT_HOST:-false}" export PHP_XDEBUG_CLIENT_HOST="${PHP_XDEBUG_CLIENT_HOST:-}" export PHP_XDEBUG_CONFIG="${PHP_XDEBUG_CONFIG:-}" export XDEBUG_CONFIG="${XDEBUG_CONFIG:-${PHP_XDEBUG_CONFIG}}" export PHP_XDEBUG_MAX_NESTING_LEVEL="${PHP_XDEBUG_MAX_NESTING_LEVEL:-512}" export PHP_IGBINARY_ENABLE="${PHP_IGBINARY_ENABLE:-false}" export PHP_FPM_USER="1000" export PHP_FPM_GROUP="1000" export PHP_FPM_PORT="${PHP_FPM_PORT:-9000}" export PHP_FPM_PM_MODE="${PHP_FPM_PM_MODE:-ondemand}" export PHP_FPM_MAX_CHILDREN="${PHP_FPM_MAX_CHILDREN:-20}" export PHP_FPM_ERROR_LOG_PATH="${PHP_FPM_ERROR_LOG_PATH:-/opt/flownative/log/php-fpm-error.log}" export PHP_FPM_ACCESS_LOG_PATH="${PHP_FPM_ACCESS_LOG_PATH:-/opt/flownative/log/php-fpm-access.log}" EOF }
  • 50.
    [global] error_log = ${PHP_FPM_ERROR_LOG_PATH} pid= ${PHP_TMP_PATH}/php-fpm.pid ; don't daemonize, because we want to start PHP as a child process of ; the shell running php-fpm.sh, so we can wait for it with "wait": daemonize = no [www] access.log = ${PHP_FPM_ACCESS_LOG_PATH} listen = ["+]:${PHP_FPM_PORT} pm = ${PHP_FPM_PM_MODE} pm.max_children = ${PHP_FPM_MAX_CHILDREN} pm.status_path = /php-fpm-status clear_env = no
  • 51.
    # --------------------------------------------------------------------------------------- # php_fpm_initialize()- Initialize PHP configuration and check required files and dirs # # @global PHP_* The PHP_* environment variables # @return void # php_fpm_initialize() { if [[ $(id "$user) ", 0 ]]; then error "PHP-FPM: Container is running as root, but only unprivileged users are supported" exit 1 fi; info "PHP-FPM: Initializing configuration ""(" envsubst < "${PHP_CONF_PATH}/php-fpm.conf.template" > "${PHP_CONF_PATH}/php-fpm.conf" if is_boolean_yes "${PHP_XDEBUG_ENABLE}"; then info "PHP-FPM: Xdebug is enabled" mv "${PHP_CONF_PATH}/conf.d/php-ext-xdebug.ini.inactive" "${PHP_CONF_PATH}/conf.d/php-ext-xdebug.ini" else info "PHP-FPM: Xdebug is disabled" export PHP_XDEBUG_MODE="off" fi if is_boolean_yes "${PHP_IGBINARY_ENABLE}"; then # igbinary might have been enabled already by scripts in an Docker image which is based on this one if [ -f "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini.inactive" ]; then info "PHP-FPM: igbinary is enabled" mv -f "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini.inactive" "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini" fi else info "PHP-FPM: igbinary is disabled" fi # Create a file descriptor for the PHP-FPM log output and clean up the log lines a bit: exec 4> >(sed -e "s/^([0-9"--]* [0-9:,]*)".1 OUTPUT PHP-FPM:/") }
  • 52.
    # --------------------------------------------------------------------------------------- # php_fpm_initialize()- Initialize PHP configuration and check required files and dirs # # @global PHP_* The PHP_* environment variables # @return void # php_fpm_initialize() { if [[ $(id "$user) ", 0 ]]; then error "PHP-FPM: Container is running as root, but only unprivileged users are supported" exit 1 fi; info "PHP-FPM: Initializing configuration ""(" envsubst < "${PHP_CONF_PATH}/php-fpm.conf.template" > "${PHP_CONF_PATH}/php-fpm.conf" if is_boolean_yes "${PHP_XDEBUG_ENABLE}"; then info "PHP-FPM: Xdebug is enabled" mv "${PHP_CONF_PATH}/conf.d/php-ext-xdebug.ini.inactive" "${PHP_CONF_PATH}/conf.d/php-ext-xdebug.ini" else info "PHP-FPM: Xdebug is disabled" export PHP_XDEBUG_MODE="off" fi if is_boolean_yes "${PHP_IGBINARY_ENABLE}"; then # igbinary might have been enabled already by scripts in an Docker image which is based on this one if [ -f "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini.inactive" ]; then info "PHP-FPM: igbinary is enabled" mv -f "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini.inactive" "${PHP_CONF_PATH}/conf.d/php-ext-igbinary.ini" fi else info "PHP-FPM: igbinary is disabled" fi # Create a file descriptor for the PHP-FPM log output and clean up the log lines a bit: exec 4> >(sed -e "s/^([0-9"--]* [0-9:,]*)".1 OUTPUT PHP-FPM:/") }
  • 53.
  • 54.
  • 55.
    version: '3.7' services: webserver: image: flownative/nginx:latest container_name:ipc_muc_2023_webserver ports: - "8080" volumes: - ./:/application environment: - BEACH_PHP_FPM_HOST=ipc_muc_2023_php php: image: flownative/php:8.2 container_name: ipc_muc_2023_php ports: - "9000" - "9003" security_opt: - no-new-privileges volumes: - ./:/application environment: - PHP_DISPLAY_ERRORS=on
  • 56.
  • 58.
  • 59.
    FROM scratch MAINTAINER Flownative<support@flownative.com> VOLUME /build COPY DistributionPackages /build/DistributionPackages COPY Packages /build/Packages COPY bin /build/bin COPY Web /build/Web COPY flow /build/flow COPY composer.json /build/composer.json COPY composer.lock /build/composer.lock COPY Configuration /build/Configuration
  • 60.
    FROM scratch busybox:latest MAINTAINERFlownative <support@flownative.com> VOLUME /build COPY DistributionPackages /build/DistributionPackages COPY Packages /build/Packages COPY bin /build/bin COPY Web /build/Web COPY flow /build/flow COPY composer.json /build/composer.json COPY composer.lock /build/composer.lock COPY Configuration /build/Configuration
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
    announce: runs-on: ubuntu-latest needs: build steps: -name: Dispatch to beach-php uses: peter-evans/repository-dispatch@v1 with: token: ${{ secrets.FLOWNATIVE_BOT_TOKEN }} repository: flownative/docker-beach-php event-type: php-images-built client-payload: '{"image_name": "flownative/docker-php/php"}' Build Cascade
  • 68.
    name: Build Dockerimages on: repository_dispatch: types: [php-images-built] push: branches-ignore: - '**' tags: - 'v*.*.*' jobs: build: runs-on: ubuntu-latest strategy: matrix: Build Cascade
  • 69.
    Image Security - don'trun as root (use SecurityContext) - disable privilege escalation - automatically scan images - redeploy automatically, frequently - only include minimal amount of software – do you really need a shell?
  • 70.
  • 71.
  • 72.
  • 73.
    And what about…? ddev performance Mutagen SSH TLS Xdebug extensions Kubernetes Colima logging
  • 74.