Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement docker #931

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/public/build/fonts/glyphicons-*
/public/build/images/glyphicons-*

###> docker ###
docker-compose.override.yml
###< docker ###

###> symfony/framework-bundle ###
/.env.local
/.env.*.local
Expand Down
1 change: 1 addition & 0 deletions .php_cs.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ COMMENT;
$finder = PhpCsFixer\Finder::create()
->in(__DIR__)
->exclude('config')
->exclude('src/Migrations')
->exclude('var')
->exclude('public/bundles')
->exclude('public/build')
Expand Down
179 changes: 179 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
ARG NODE_VERSION=11.6.0
ARG COMPOSER_VERSION=1.8.0
ARG PHP_VERSION=7.2.13
ARG ICU_VERSION=63.1
ARG APCU_VERSION=5.1.16
ARG XDEBUG_VERSION=2.6.1


#####################################
## APP ##
#####################################
FROM php:${PHP_VERSION}-fpm as app

ARG ICU_VERSION
ARG APCU_VERSION

WORKDIR /app

EXPOSE 80

# Install paquet requirements
RUN export PHP_CPPFLAGS="${PHP_CPPFLAGS} -std=c++11"; \
set -ex; \
# Install required system packages
apt-get update; \
apt-get install -qy --no-install-recommends \
libzip-dev \
; \
# Compile ICU (required by intl php extension)
curl -L -o /tmp/icu.tar.gz http://download.icu-project.org/files/icu4c/${ICU_VERSION}/icu4c-$(echo ${ICU_VERSION} | sed s/\\./_/g)-src.tgz; \
tar -zxf /tmp/icu.tar.gz -C /tmp; \
cd /tmp/icu/source; \
./configure --prefix=/usr/local; \
make clean; \
make; \
make install; \
#Install the PHP extensions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd split it as separate RUN step, any mistake in app-specific set of docker-php-ext requires full rebuild of this later which takes ages.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've add all of it in a single layout to follow Dockerfile best practices that recommend to do the less number of layer as possible. Then, when you want to add something in your image, you can split it to test before, then regroup it after ?

I think, it's not totaly usefull to split this part, because we just need the split sometimes when you want to change something, but otherwise we never change anything.

That's my opinion. But I understand what you say, because when I edit this part I split it for try, because this part (with ICU) is very long.

Wait coments, but if more people prefer, we can split after the ICU install.

docker-php-ext-configure intl --with-icu-dir=/usr/local; \
docker-php-ext-install -j "$(nproc)" \
intl \
pdo \
zip \
bcmath \
; \
pecl install \
apcu-${APCU_VERSION} \
; \
docker-php-ext-enable \
opcache \
apcu \
; \
docker-php-source delete; \
# Clean aptitude cache and tmp directory
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*;

## set recommended PHP.ini settings
RUN { \
echo 'date.timezone = UTC'; \
echo 'short_open_tag = off'; \
echo 'expose_php = off'; \
echo 'error_log = /proc/self/fd/2'; \
echo 'memory_limit = 128m'; \
echo 'post_max_size = 110m'; \
echo 'upload_max_filesize = 100m'; \
echo 'opcache.enable = 1'; \
echo 'opcache.enable_cli = 1'; \
echo 'opcache.memory_consumption = 256'; \
echo 'opcache.interned_strings_buffer = 16'; \
echo 'opcache.max_accelerated_files = 20011'; \
echo 'opcache.fast_shutdown = 1'; \
echo 'realpath_cache_size = 4096K'; \
echo 'realpath_cache_ttl = 600'; \
} > /usr/local/etc/php/php.ini

RUN { \
echo 'date.timezone = UTC'; \
echo 'short_open_tag = off'; \
echo 'memory_limit = -1'; \
} > /usr/local/etc/php/php-cli.ini

CMD ["php-fpm"]


#####################################
## APP DEV ##
#####################################
FROM app as app-dev

ARG NODE_VERSION
ARG COMPOSER_VERSION
ARG XDEBUG_VERSION

ENV COMPOSER_ALLOW_SUPERUSER=1
ENV APP_ENV=dev

# Install paquet requirements
RUN set -ex; \
# Install required system packages
apt-get update; \
apt-get install -qy --no-install-recommends \
unzip \
git \
; \
# Clean aptitude cache and tmp directory
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*;

# Install Node
RUN set -ex; \
curl -L -o /tmp/nodejs.tar.gz https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz; \
tar xfvz /tmp/nodejs.tar.gz -C /usr/local --strip-components=1; \
rm -f /tmp/nodejs.tar.gz; \
npm install yarn -g

# Install Composer
RUN set -ex; \
EXPECTED_SIGNATURE="$(curl -L https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar.sha256sum)"; \
curl -L -o composer.phar https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar; \
ACTUAL_SIGNATURE="$(sha256sum composer.phar)"; \
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]; then >&2 echo 'ERROR: Invalid installer signature' && rm /usr/local/bin/composer && exit 1 ; fi; \
chmod +x composer.phar && mv composer.phar /usr/local/bin/composer; \
RESULT=$?; \
exit $RESULT;

# Edit OPCache configuration
RUN set -ex; \
{ \
echo 'opcache.validate_timestamps = 1'; \
echo 'opcache.revalidate_freq = 0'; \
} >> /usr/local/etc/php/php.ini

# Install Xdebug
RUN set -ex; \
if [ "${XDEBUG_VERSION}" != 0 ]; \
then \
pecl install xdebug-${XDEBUG_VERSION}; \
docker-php-ext-enable xdebug; \
{ \
echo 'xdebug.remote_enable = on'; \
echo 'xdebug.remote_connect_back = on'; \
} >> /usr/local/etc/php/php.ini \
; fi


#####################################
## PROD ASSETS BUILDER ##
#####################################
FROM node:${NODE_VERSION} as assets-builder

COPY . /app
WORKDIR /app

RUN yarn install && yarn build && rm -R node_modules

#####################################
## PROD VENDOR BUILDER ##
#####################################
FROM composer:${COMPOSER_VERSION} as vendor-builder
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'd inherit from app step, because if application relies on some extension installed before (in app step) (example bcmath) here this dependency will be missing. Proposal:

FROM app as vendor-builder

ARG COMPOSER_VERSION

# Install Composer
RUN set -ex; \
    EXPECTED_SIGNATURE="$(curl -L https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar.sha256sum)"; \
    curl -L -o composer.phar https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar; \
    ACTUAL_SIGNATURE="$(sha256sum composer.phar)"; \
    if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]; then >&2 echo 'ERROR: Invalid installer signature' && rm /usr/local/bin/composer && exit 1 ; fi; \
    chmod +x composer.phar && mv composer.phar /usr/local/bin/composer; \
    RESULT=$?; \
    exit $RESULT;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, I was doing like it, but I've change to avoid have composer in the final image.

At the moment on all my projects, I've never had this problem (but, sure, I've not test all dependencies). The goal of this part is to only install vendors and avoid to have composer in the final image.

Copy link

@athlan athlan Aug 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good point 👍 . Build mechanisms should't be on final image.

However, final image won't have this dependency, because app-prod inherits from app, not vendor-builder. You wisely used only copy of /app files from vendor-builder while composer is located under /usr/local/bin/composer.

(hope it will be finally merged - good contribution I think)

Copy link
Author

@mpiot mpiot Aug 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but if we install composer on app then, you have composer on app-prod and finaly on the image.

Do you have a case were it fails ? At the moment on all projects it work fine like it (use the composer image to install vendors).

For the last point, I'm not sure, this PR is open since a long time... :(

Copy link

@athlan athlan Aug 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes yes, that's why my proposal is to have it only on vendor-builder (not app):

FROM app as vendor-builder

I do have a case for amqp not-native library, which requires bcmath. Otherwise I'd need to install amqp extension, so, both cases would require that chage.


COPY --chown=www-data --from=assets-builder /app /app
WORKDIR /app

RUN APP_ENV=prod composer install -o -n --no-ansi --no-dev


#####################################
## APP PROD ##
#####################################
FROM app as app-prod

ENV APP_ENV=prod

COPY --from=vendor-builder /app /app
WORKDIR /app

# Edit OPCache configuration
RUN set -ex; \
{ \
echo 'opcache.validate_timestamps = 0'; \
} >> /usr/local/etc/php/php.ini
166 changes: 166 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
DOCKER_COMPOSE?=docker-compose
EXEC?=$(DOCKER_COMPOSE) exec app
CONSOLE=php bin/console
PHPCSFIXER?=$(EXEC) php -d memory_limit=1024m vendor/bin/php-cs-fixer

.DEFAULT_GOAL := help
.PHONY: help start stop restart install uninstall reset clear-cache shell clear clean
.PHONY: db-diff db-migrate db-rollback db-fixtures db-validate
.PHONY: watch assets assets-build
.PHONY: tests lint lint-symfony lint-yaml lint-twig lint-xliff php-cs php-cs-fix security-check test-schema test-all
.PHONY: deps
.PHONY: build up perm docker-compose.override.yml

help:
@grep -E '(^[a-zA-Z_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/'


##
## Project setup
##---------------------------------------------------------------------------

start: ## Start docker containers
$(DOCKER_COMPOSE) start

stop: ## Stop docker containers
$(DOCKER_COMPOSE) stop

restart: ## Restart docker containers
$(DOCKER_COMPOSE) restart

install: docker-compose.override.yml build up deps perm ## Create and start docker containers

uninstall: stop ## Remove docker containers
$(DOCKER_COMPOSE) rm -vf

reset: uninstall install ## Remove and re-create docker containers

clear-cache: perm
$(EXEC) $(CONSOLE) cache:clear --no-warmup
$(EXEC) $(CONSOLE) cache:warmup

shell: ## Run app container in interactive mode
$(EXEC) /bin/bash

clear: perm ## Remove all the cache, the logs, the sessions and the built assets
$(EXEC) rm -rf var/cache/*
rm -rf var/log/*
rm -rf public/build
rm -f var/.php_cs.cache

clean: clear ## Clear and remove dependencies
rm -rf vendor node_modules


##
## Database
##---------------------------------------------------------------------------

db-diff: vendor ## Generate a migration by comparing your current database to your mapping information
$(EXEC) $(CONSOLE) doctrine:migration:diff

db-migrate: vendor ## Migrate database schema to the latest available version
$(EXEC) $(CONSOLE) doctrine:migration:migrate -n

db-rollback: vendor ## Rollback the latest executed migration
$(EXEC) $(CONSOLE) doctrine:migration:migrate prev -n

db-fixtures: vendor ## Apply doctrine fixtures
$(EXEC) $(CONSOLE) doctrine:fixtures:load -n

db-validate: vendor ## Check the ORM mapping
$(EXEC) $(CONSOLE) doctrine:schema:validate


##
## Assets
##---------------------------------------------------------------------------

watch: node_modules ## Watch the assets and build their development version on change
$(EXEC) yarn watch

assets: node_modules ## Build the development version of the assets
$(EXEC) yarn dev

assets-build: node_modules ## Build the production version of the assets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assets and assets-build both building assets
maybe use assets-prod instead of assets-build?

Suggested change
assets-build: node_modules ## Build the production version of the assets
assets-prod: node_modules ## Build the production version of the assets

$(EXEC) yarn build


##
## Tests
##---------------------------------------------------------------------------

tests: ## Run all the PHP tests
$(EXEC) bin/phpunit

lint: lint-symfony php-cs ## Run lint on Twig, YAML, XLIFF, and PHP files

lint-symfony: lint-yaml lint-twig lint-xliff ## Lint Symfony (Twig and YAML) files

lint-yaml: ## Lint YAML files
$(EXEC) $(CONSOLE) lint:yaml config

lint-twig: ## Lint Twig files
$(EXEC) $(CONSOLE) lint:twig templates

lint-xliff: ## Lint Translation files
$(EXEC) $(CONSOLE) lint:xliff translations

php-cs: vendor ## Lint PHP code
$(PHPCSFIXER) fix --diff --dry-run --no-interaction -v

php-cs-fix: vendor ## Fix PHP code to follow the convention
$(PHPCSFIXER) fix

security-check: vendor ## Check for vulnerable dependencies
$(EXEC) vendor/bin/security-checker security:check

test-schema: vendor ## Test the doctrine Schema
$(EXEC) $(CONSOLE) doctrine:schema:validate --skip-sync -vvv --no-interaction

test-all: lint test-schema security-check tests ## Lint all, run schema and security check, then unit and functionnal tests


##
## Dependencies
##---------------------------------------------------------------------------

deps: vendor assets ## Install the project dependencies


Copy link
Contributor

@mstrom mstrom Sep 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think do we need to add command to update dependencies?

Suggested change
deps-update:
## Update the project dependencies
$(EXEC) composer update -n
$(EXEC) yarn upgrade

##


# Internal rules

build:
$(DOCKER_COMPOSE) pull --ignore-pull-failures
$(DOCKER_COMPOSE) build --force-rm

up:
$(DOCKER_COMPOSE) up -d --remove-orphans

perm:
$(EXEC) chmod -R 777 var public/build node_modules vendor
$(EXEC) chown -R www-data:root var public/build node_modules vendor

docker-compose.override.yml:
ifneq ($(wildcard docker-compose.override.yml),docker-compose.override.yml)
@echo docker-compose.override.yml do not exists, copy docker-compose.override.yml.dist to create it, and fill it.
exit 1
endif


# Rules from files

vendor: composer.lock
$(EXEC) composer install -n

composer.lock: composer.json
@echo composer.lock is not up to date.

node_modules: yarn.lock
$(EXEC) yarn install

yarn.lock: package.json
@echo yarn.lock is not up to date.
6 changes: 6 additions & 0 deletions docker-compose.override.yml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: '3.4'

services:
nginx:
ports:
- 127.0.0.1:8080:80
18 changes: 18 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: '3.4'

services:
nginx:
build:
context: docker
dockerfile: NginxDockerfile
depends_on:
- app
volumes:
- .:/app

app:
build:
context: .
target: app-dev
volumes:
- .:/app
4 changes: 4 additions & 0 deletions docker/NginxDockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM nginx:1.15.8

# set nginx config
ADD nginx-default.conf /etc/nginx/conf.d/default.conf