diff --git a/.dockerignore b/.dockerignore index 3f43985..9eda228 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,9 +1,22 @@ **/*.log +**/*.md +**/*.php~ **/._* +**/.dockerignore **/.DS_Store -**/.gitignore +**/.git/ **/.gitattributes +**/.gitignore +**/.gitmodules +**/Dockerfile **/Thumbs.db -.dockerignore -Dockerfile -docker-compose.yaml +.editorconfig +.env* +.php_cs.cache +bin/* +!bin/console +docker/db/data/ +helm/ +public/bundles/ +var/ +vendor/ diff --git a/.travis.yml b/.travis.yml index 2a5d19a..875ea2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,9 +26,9 @@ script: - docker-compose build --pull - docker-compose up -d - sleep 30 - - docker-compose exec -T app composer req sensiolabs/security-checker - - docker-compose exec -T app bin/console security:check - - docker-compose run --no-deps -T app composer validate --no-check-publish + - docker-compose exec -T php composer req sensiolabs/security-checker + - docker-compose exec -T php bin/console security:check + - docker-compose run --no-deps -T php composer validate --no-check-publish - curl http://localhost # Client - curl -k https://localhost # Client (HTTP/2) - docker-compose logs # Display logs if anything wrong diff --git a/Dockerfile b/Dockerfile index 2db8134..78254bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,80 +1,81 @@ # 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 -ARG PHP_VERSION=7.2 -ARG NGINX_VERSION=1.15 -### NGINX -FROM nginx:${NGINX_VERSION}-alpine AS symfony_docker_nginx +# https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact +ARG PHP_VERSION=7.4 +ARG NGINX_VERSION=1.17 -COPY docker/nginx/conf.d /etc/nginx/conf.d/ -COPY public /srv/app/public/ - -### H2 PROXY -FROM alpine:latest AS symfony_docker_h2-proxy-cert - -RUN apk add --no-cache openssl - -# Use this self-generated certificate only in dev, IT IS NOT SECURE! -RUN openssl genrsa -des3 -passout pass:NotSecure -out server.pass.key 2048 -RUN openssl rsa -passin pass:NotSecure -in server.pass.key -out server.key -RUN rm server.pass.key -RUN openssl req -new -passout pass:NotSecure -key server.key -out server.csr \ - -subj '/C=SS/ST=SS/L=Gotham City/O=Symfony/CN=localhost' -RUN openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt - -FROM nginx:${NGINX_VERSION}-alpine AS symfony_docker_h2-proxy - -RUN mkdir -p /etc/nginx/ssl/ -COPY --from=symfony_docker_h2-proxy-cert server.key server.crt /etc/nginx/ssl/ -COPY ./docker/h2-proxy/default.conf /etc/nginx/conf.d/default.conf - -### PHP -FROM php:${PHP_VERSION}-fpm-alpine AS symfony_docker_php +# "php" stage +FROM php:${PHP_VERSION}-fpm-alpine AS symfony_php +# persistent / runtime deps RUN apk add --no-cache \ - git \ - icu-libs \ - zlib \ - jq + acl \ + fcgi \ + file \ + gettext \ + git \ + jq \ + ; -ENV APCU_VERSION 5.1.12 -RUN set -eux \ - && apk add --no-cache --virtual .build-deps \ - $PHPIZE_DEPS \ - icu-dev \ - zlib-dev \ - && docker-php-ext-install -j$(nproc) \ - intl \ - zip \ - && pecl install \ - apcu-${APCU_VERSION} \ - && docker-php-ext-enable --ini-name 20-apcu.ini apcu \ - && docker-php-ext-enable --ini-name 05-opcache.ini 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 .api-phpexts-rundeps $runDeps \ - && apk del .build-deps +ARG APCU_VERSION=5.1.18 +RUN set -eux; \ + apk add --no-cache --virtual .build-deps \ + $PHPIZE_DEPS \ + icu-dev \ + libzip-dev \ + zlib-dev \ + ; \ + \ + docker-php-ext-configure zip --with-libzip; \ + docker-php-ext-install -j$(nproc) \ + 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 --from=composer:latest /usr/bin/composer /usr/bin/composer RUN ln -s $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini -COPY docker/app/conf.d/symfony.ini $PHP_INI_DIR/conf.d/symfony.ini -COPY --from=composer:latest /usr/bin/composer /usr/bin/composer -COPY docker/app/docker-entrypoint.sh /usr/local/bin/docker-app-entrypoint -RUN chmod +x /usr/local/bin/docker-app-entrypoint +COPY docker/php/conf.d/symfony.ini $PHP_INI_DIR/conf.d/symfony.ini -WORKDIR /srv/app -ENTRYPOINT ["docker-app-entrypoint"] -CMD ["php-fpm"] +RUN set -eux; \ + { \ + echo '[www]'; \ + echo 'ping.path = /ping'; \ + } | tee /usr/local/etc/php-fpm.d/docker-healthcheck.conf # https://getcomposer.org/doc/03-cli.md#composer-allow-superuser -ENV COMPOSER_ALLOW_SUPERUSER 1 +ENV COMPOSER_ALLOW_SUPERUSER=1 +# install Symfony Flex globally to speed up download of Composer packages (parallelized prefetching) +RUN set -eux; \ + composer global require "symfony/flex" --prefer-dist --no-progress --no-suggest --classmap-authoritative; \ + composer clear-cache +ENV PATH="${PATH}:/root/.composer/vendor/bin" -# Use prestissimo to speed up builds -RUN composer global require "symfony/flex" --prefer-dist --no-progress --no-suggest --classmap-authoritative --no-interaction +WORKDIR /srv/app + +# build for production +ARG APP_ENV=prod # Allow to use development versions of Symfony ARG STABILITY="stable" @@ -84,14 +85,58 @@ ENV STABILITY ${STABILITY:-stable} ARG SYMFONY_VERSION="" # Download the Symfony skeleton and leverage Docker cache layers -RUN composer create-project "symfony/skeleton ${SYMFONY_VERSION}" . --stability=$STABILITY --prefer-dist --no-dev --no-progress --no-scripts --no-plugins --no-interaction +RUN composer create-project "symfony/skeleton ${SYMFONY_VERSION}" . --stability=$STABILITY --prefer-dist --no-dev --no-progress --no-scripts --no-interaction; \ + composer clear-cache ###> recipes ### ###< recipes ### COPY . . -RUN mkdir -p var/cache var/logs var/sessions \ - && composer install --prefer-dist --no-dev --no-scripts --no-progress --no-suggest --classmap-authoritative --no-interaction \ - && composer clear-cache \ - && chown -R www-data var +RUN set -eux; \ + mkdir -p var/cache var/log; \ + composer dump-autoload --classmap-authoritative --no-dev; \ + composer run-script --no-dev post-install-cmd; sync +VOLUME /srv/app/var + +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"] + +COPY docker/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint +RUN chmod +x /usr/local/bin/docker-entrypoint + +ENTRYPOINT ["docker-entrypoint"] +CMD ["php-fpm"] + + +# "nginx" stage +# depends on the "php" stage above +FROM nginx:${NGINX_VERSION}-alpine AS symfony_nginx + +COPY docker/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf + +WORKDIR /srv/app + +COPY --from=symfony_php /srv/app/public public/ + +# "h2-proxy-cert" stage +FROM alpine:latest AS symfony_h2-proxy-cert + +RUN apk add --no-cache openssl + +# Use this self-generated certificate only in dev, IT IS NOT SECURE! +RUN openssl genrsa -des3 -passout pass:NotSecure -out server.pass.key 2048 +RUN openssl rsa -passin pass:NotSecure -in server.pass.key -out server.key +RUN rm server.pass.key +RUN openssl req -new -passout pass:NotSecure -key server.key -out server.csr \ + -subj '/C=SS/ST=SS/L=Gotham City/O=Symfony/CN=localhost' +RUN openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt + +### "h2-proxy" stage +FROM nginx:${NGINX_VERSION}-alpine AS symfony_h2-proxy + +RUN mkdir -p /etc/nginx/ssl/ +COPY --from=symfony_h2-proxy-cert server.key server.crt /etc/nginx/ssl/ +COPY ./docker/h2-proxy/default.conf /etc/nginx/conf.d/default.conf diff --git a/README.md b/README.md index b41f930..92a6520 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,9 @@ it's recommended to add a custom stage to the end of the `Dockerfile`. ```Dockerfile # Dockerfile -FROM symfony_docker_php as symfony_docker_php_dev +FROM symfony_php as symfony_php_dev -ARG XDEBUG_VERSION=2.6.0 +ARG XDEBUG_VERSION=2.8.0 RUN set -eux; \ apk add --no-cache --virtual .build-deps $PHPIZE_DEPS; \ pecl install xdebug-$XDEBUG_VERSION; \ @@ -55,13 +55,13 @@ configuration remains untouched. As example, an override could look like this: ```yaml -version: '3.4' +version: "3.4" services: - app: + php: build: context: . - target: symfony_docker_php_dev + target: symfony_php_dev environment: # See https://docs.docker.com/docker-for-mac/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host # See https://github.com/docker/for-linux/issues/264 @@ -73,34 +73,31 @@ services: idekey=PHPSTORM # This should correspond to the server declared in PHPStorm `Preferences | Languages & Frameworks | PHP | Servers` # Then PHPStorm will use the corresponding path mappings - PHP_IDE_CONFIG: serverName=symfony-docker + PHP_IDE_CONFIG: serverName=symfony ``` Then run: -````bash +```bash docker-compose up -d -```` +``` -If `docker-compose.yaml` and a `docker-compose.override.yaml` are present on the same directory level, Docker Compose combines the two files into a single configuration, applying the configuration in the `docker-compose.override.yaml` file over and in addition to the values in the `docker-compose.yaml` file. +If `docker-compose.yml` and a `docker-compose.override.yml` are present on the same directory level, Docker Compose combines the two files into a single configuration, applying the configuration in the `docker-compose.override.yml` file over and in addition to the values in the `docker-compose.yml` file. ### Troubleshooting Inspect the installation with the following command. The requested Xdebug version should be displayed in the output. ```bash -$ docker-compose exec app php --version +$ docker-compose exec php php --version -PHP 7.2.8 (cli) (built: Jul 21 2018 08:09:37) ( NTS ) -Copyright (c) 1997-2018 The PHP Group -Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies - with Zend OPcache v7.2.8, Copyright (c) 1999-2018, by Zend Technologies - with Xdebug v2.6.0, Copyright (c) 2002-2018, by Derick Rethans +PHP ... + with Xdebug v2.8.0 ... ``` ### Editing Permissions on Linux -If you work on linux and cannot edit some of the project files right after the first installation, you can run `docker-compose run --rm app chown -R $(id -u):$(id -g) .` to set yourself as owner of the project files that were created by the docker container. +If you work on linux and cannot edit some of the project files right after the first installation, you can run `docker-compose run --rm php chown -R $(id -u):$(id -g) .` to set yourself as owner of the project files that were created by the docker container. ## Credits diff --git a/docker-compose.yaml b/docker-compose.yml similarity index 79% rename from docker-compose.yaml rename to docker-compose.yml index 36bdd30..a8abb03 100644 --- a/docker-compose.yaml +++ b/docker-compose.yml @@ -1,13 +1,18 @@ -version: '3.4' +version: "3.4" services: - app: + php: build: context: . - target: symfony_docker_php + target: symfony_php args: SYMFONY_VERSION: ${SYMFONY_VERSION:-} STABILITY: ${STABILITY:-stable} + healthcheck: + interval: 10s + timeout: 3s + retries: 3 + start_period: 30s volumes: # Comment out the next line in production - ./:/srv/app:rw,cached @@ -22,24 +27,24 @@ services: nginx: build: context: . - target: symfony_docker_nginx + target: symfony_nginx depends_on: - - app + - php volumes: # Comment out the next line in production - ./docker/nginx/conf.d:/etc/nginx/conf.d:ro - ./public:/srv/app/public:ro ports: - - '80:80' + - "80:80" # This HTTP/2 proxy is not secure: it should only be used in dev h2-proxy: build: context: . - target: symfony_docker_h2-proxy + target: symfony_h2-proxy depends_on: - nginx volumes: - ./docker/h2-proxy/default.conf:/etc/nginx/conf.d/default.conf:ro ports: - - '443:443' + - "443:443" diff --git a/docker/nginx/conf.d/default.conf b/docker/nginx/conf.d/default.conf index 9efd420..8474564 100644 --- a/docker/nginx/conf.d/default.conf +++ b/docker/nginx/conf.d/default.conf @@ -7,10 +7,10 @@ server { } location ~ ^/index\.php(/|$) { #resolver 127.0.0.11; - #set $upstream_host app; + #set $upstream_host php; #fastcgi_pass $upstream_host:9000; # Uncomment the previous lines and comment the next one to enable dynamic resolution (incompatible with Kubernetes) - fastcgi_pass app:9000; + fastcgi_pass php:9000; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; # When you are using symlinks to link the document root to the diff --git a/docker/app/conf.d/symfony.ini b/docker/php/conf.d/symfony.ini similarity index 100% rename from docker/app/conf.d/symfony.ini rename to docker/php/conf.d/symfony.ini diff --git a/docker/app/docker-entrypoint.sh b/docker/php/docker-entrypoint.sh similarity index 63% rename from docker/app/docker-entrypoint.sh rename to docker/php/docker-entrypoint.sh index fa8bbea..72c0fea 100755 --- a/docker/app/docker-entrypoint.sh +++ b/docker/php/docker-entrypoint.sh @@ -6,12 +6,14 @@ if [ "${1#-}" != "$1" ]; then set -- php-fpm "$@" fi -if [ "$1" = 'php-fpm' ] || [ "$1" = 'bin/console' ]; then - PHP_INI_RECOMMENDED="$PHP_INI_DIR/php.ini-production" - if [ "$APP_ENV" != 'prod' ]; then - PHP_INI_RECOMMENDED="$PHP_INI_DIR/php.ini-development" - fi - ln -sf "$PHP_INI_RECOMMENDED" "$PHP_INI_DIR/php.ini" +if [ "$1" = 'php-fpm' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then + PHP_INI_RECOMMENDED="$PHP_INI_DIR/php.ini-production" + if [ "$APP_ENV" != 'prod' ]; then + PHP_INI_RECOMMENDED="$PHP_INI_DIR/php.ini-development" + fi + ln -sf "$PHP_INI_RECOMMENDED" "$PHP_INI_DIR/php.ini" + + mkdir -p var/cache var/log # The first time volumes are mounted, the project needs to be recreated if [ ! -f composer.json ]; then @@ -23,12 +25,11 @@ if [ "$1" = 'php-fpm' ] || [ "$1" = 'bin/console' ]; then cp -Rp tmp/. . rm -Rf tmp/ elif [ "$APP_ENV" != 'prod' ]; then - # Always try to reinstall deps when not in prod composer install --prefer-dist --no-progress --no-suggest --no-interaction fi - # Permissions hack because setfacl does not work on Mac and Windows - chown -R www-data var + 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 "$@" diff --git a/docker/php/docker-healthcheck.sh b/docker/php/docker-healthcheck.sh new file mode 100644 index 0000000..cb566ee --- /dev/null +++ b/docker/php/docker-healthcheck.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -e + +export SCRIPT_NAME=/ping +export SCRIPT_FILENAME=/ping +export REQUEST_METHOD=GET + +if cgi-fcgi -bind -connect 127.0.0.1:9000; then + exit 0 +fi + +exit 1