symfony-docker icon indicating copy to clipboard operation
symfony-docker copied to clipboard

Combined PHP + Caddy Docker image

Open back-2-95 opened this issue 3 years ago • 5 comments

For environment like Digital Ocean App Platform you need to setup your app as a one container.

To easily use of such service would need to combine PHP-FPM and Caddy created with the current Dockerfile.

Could it be done with feasible effort?

back-2-95 avatar Mar 14 '22 10:03 back-2-95

@back-2-95 this would require to write a complete new Dockerfile which uses for example the caddy base image and then adds all the PHP stuff.

But it's not possible to simply merge two images into one with docker (for some good reasons I guess :thinking: )

michael-schaefer-eu avatar Mar 23 '22 11:03 michael-schaefer-eu

I actually did after posting this issue. Not sure how to deliver this for anyone who wants to use it, but here is it for anyone stumbling to this issue:

ARG PHP_VERSION=8.1
ARG CADDY_VERSION=2
ARG NODE_VERSION=16

FROM php:${PHP_VERSION}-fpm-alpine AS symfony_php

# persistent / runtime deps
RUN apk add --no-cache acl fcgi file gettext git gnu-libiconv

# install gnu-libiconv and set LD_PRELOAD env to make iconv work fully on Alpine image.
# see https://github.com/docker-library/php/issues/240#issuecomment-763112749
ENV LD_PRELOAD /usr/lib/preloadable_libiconv.so

ARG APCU_VERSION=5.1.21
RUN set -eux; \
	apk add --no-cache --virtual .build-deps \
		$PHPIZE_DEPS \
		icu-dev \
		libzip-dev \
		zlib-dev \
	; \
	\
	docker-php-ext-configure zip; \
	docker-php-ext-install -j$(nproc) \
		pdo_mysql \
		intl \
		zip \
	; \
	pecl install \
		apcu-${APCU_VERSION} \
	; \
	pecl clear-cache; \
	docker-php-ext-enable \
		apcu \
		opcache \
	; \
	\
	runDeps="$( \
		scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \
			| tr ',' '\n' \
			| sort -u \
			| awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
	)"; \
	apk add --no-cache --virtual .phpexts-rundeps $runDeps; \
	\
	apk del .build-deps

COPY docker/php/docker-healthcheck.sh /usr/local/bin/docker-healthcheck
RUN chmod +x /usr/local/bin/docker-healthcheck

#HEALTHCHECK --interval=10s --timeout=3s --retries=3 CMD ["docker-healthcheck"]

RUN ln -s $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
COPY docker/php/conf.d/symfony.prod.ini $PHP_INI_DIR/conf.d/symfony.ini

COPY docker/php/php-fpm.d/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf

COPY docker/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
RUN chmod +x /usr/local/bin/docker-entrypoint

VOLUME /var/run/php

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

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

ENV PATH="${PATH}:/root/.composer/vendor/bin"

WORKDIR /srv/app

RUN apk add --no-cache --virtual .pgsql-deps postgresql-dev; \
	docker-php-ext-install -j$(nproc) pdo_pgsql; \
	apk add --no-cache --virtual .pgsql-rundeps so:libpq.so.5; \
	apk del .pgsql-deps

COPY . .

RUN set -eux; \
	mkdir -p var/cache var/log; \
	composer install --prefer-dist --no-dev --no-progress --no-scripts --no-interaction; \
	composer dump-autoload --classmap-authoritative --no-dev; \
	composer symfony:dump-env prod; \
	composer run-script --no-dev post-install-cmd; \
	chmod +x bin/console; sync

VOLUME /srv/app/var

ARG CADDY_VERSION
COPY --from=caddy:${CADDY_VERSION} /usr/bin/caddy /usr/bin/caddy
COPY docker/caddy/Caddyfile /etc/caddy/Caddyfile

EXPOSE 80
EXPOSE 443
EXPOSE 2019

ENTRYPOINT ["docker-entrypoint"]

CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]

FROM node:${NODE_VERSION}-alpine as asset-builder

COPY --from=symfony_php /srv/app /srv/app

WORKDIR /srv/app
RUN yarn install --frozen-lockfile
RUN yarn build

FROM symfony_php as prod-build

COPY --from=asset-builder /srv/app/public/build/ /srv/app/public/build/

back-2-95 avatar Mar 24 '22 06:03 back-2-95

Great :muscle:

So you have three steps here:

  1. Image symfony_php with PHP and Caddy
  2. Image asset-builder to build the Frontend (js,css)
  3. Image prod-build with PHP and Caddy which then gets the assets - that image you deploy on your server

Also for my understanding, this Dockerfile does not build Caddy with mercure and vulcain add-ons, right?

And the docker-compose also would not work with this Dockerfile anymore (or if someone would like to use docker-compose with this file, then it needs to be adjusted as well) ?

Maybe it's worth to add this to the docs for other users as well ?

michael-schaefer-eu avatar Mar 24 '22 10:03 michael-schaefer-eu

Somewhat related to this I created bake file which creates php-fpm and caddy "base images" as amd64 and arm64 variants:

PHP81_MINOR=8.1.4 docker buildx bake -f symfony/docker-bake.hcl --pull --no-cache --push

Images are here:

  • https://hub.docker.com/repository/docker/druidfi/symfony-php
  • https://hub.docker.com/repository/docker/druidfi/symfony-caddy

They are now just with PHP 8.1 and my project's dockerfile uses them like this:

#
# Asset builder
#
FROM node:16-alpine as asset-builder

WORKDIR /srv/app

COPY package.json postcss.config.js tailwind.config.js webpack.config.js yarn.lock ./
COPY assets ./assets
COPY templates ./templates

RUN yarn install --frozen-lockfile
RUN yarn build-prod

#
# PHP-FPM
#
FROM druidfi/symfony-php:fpm-8.1 AS symfony_php

# Only customization to base image
RUN echo "date.timezone=Europe/Helsinki" > /usr/local/etc/php/conf.d/timezone.ini

COPY . .
COPY --from=asset-builder /srv/app/public/build/ public/build/

RUN set -eux; \
	mkdir -p var/cache var/log; \
	composer install --prefer-dist --no-dev --no-progress --no-scripts --no-interaction; \
	composer dump-autoload --classmap-authoritative --no-dev; \
	composer symfony:dump-env prod; \
	composer run-script --no-dev post-install-cmd; \
	chmod +x bin/console; sync

#
# Caddy
#
FROM druidfi/symfony-caddy:2 AS symfony_caddy

COPY --from=symfony_php /srv/app/public public/
COPY --from=asset-builder /srv/app/public/build/ public/build/

back-2-95 avatar Apr 05 '22 12:04 back-2-95

If someone stubles on this issue, maybe ppl can test those images and can give feedback 🙏

I'm also planning to create that image including caddy+php-fpm in the same.

back-2-95 avatar Apr 05 '22 13:04 back-2-95

I'd like to add my two cents of Docker experience to this subject :wink:

If you want to run caddy and php-fpm on the same image, you will also need to run inside the image/container a process like supervisor that launches caddy and php-fpm, since you can't invoke two process on the CMD directive of Docker. Or you could create a script that launches the two process in the background.

Anyway, I understand that certain services ask to run your app in a single image/container, but in general terms, the best Docker practices says that you should avoid running more than one different process in the same image/container.

Greets. Ernesto

edomato avatar Sep 01 '22 02:09 edomato

If you want to run caddy and php-fpm on the same image, you will also need to run inside the image/container a process like supervisor that launches caddy and php-fpm, since you can't invoke two process on the CMD directive of Docker. Or you could create a script that launches the two process in the background.

This could easily be achieved with https://github.com/Baldinof/caddy-supervisor

stefanpoensgen avatar Sep 07 '22 06:09 stefanpoensgen