diff --git a/.docker/.env b/.docker/.env new file mode 100644 index 0000000..6d09235 --- /dev/null +++ b/.docker/.env @@ -0,0 +1,35 @@ +# Default settings for docker-compose +# @see https://docs.docker.com/compose/reference/envvars/#compose_file +COMPOSE_PROJECT_NAME=standard-php-dev-env +COMPOSE_FILE=docker-compose.yml +COMPOSE_CONVERT_WINDOWS_PATHS=1 + +# build +PHP_VERSION=7.4 +TIMEZONE=UTC +NETWORKS_DRIVER=bridge + +# application +APP_USER=www-data +APP_GROUP=www-data +APP_USER_ID=1000 +APP_GROUP_ID=1000 +APP_CODE_PATH_HOST=../ +APP_CODE_PATH_CONTAINER=/var/www/current + +# required so we can reach the nginx server from other containers via that hostname +APP_HOST=standard-php-dev-env.local + +# nginx +NGINX_HOST_HTTP_PORT=8898 +NGINX_HOST_HTTPS_PORT=8899 + +# php-cli +WORKSPACE_HOST_SSH_PORT=2239 + +# mysql +MYSQL_ROOT_PASSWORD=S3cr3T +MYSQL_DATABASE=test +MYSQL_USER=dba +MYSQL_PASSWORD=S3cr3T +MYSQL_HOST_PORT=6089 diff --git a/.docker/.env.example b/.docker/.env.example new file mode 100644 index 0000000..d6b5526 --- /dev/null +++ b/.docker/.env.example @@ -0,0 +1,28 @@ +# Default settings for docker-compose +# @see https://docs.docker.com/compose/reference/envvars/#compose_file +COMPOSE_PROJECT_NAME=docker-php-tutorial +COMPOSE_FILE=docker-compose.yml +COMPOSE_CONVERT_WINDOWS_PATHS=1 + +# build +PHP_VERSION=7.3 +TIMEZONE=UTC +NETWORKS_DRIVER=bridge + +# application +APP_USER=www-data +APP_GROUP=www-data +APP_USER_ID=1000 +APP_GROUP_ID=1000 +APP_CODE_PATH_HOST=../ +APP_CODE_PATH_CONTAINER=/var/www/current + +# required so we can reach the nginx server from other containers via that hostname +APP_HOST=docker-php-tutorial.local + +# nginx +NGINX_HOST_HTTP_PORT=80 +NGINX_HOST_HTTPS_PORT=443 + +# workspace +WORKSPACE_HOST_SSH_PORT=2222 diff --git a/.docker/.shared/config/php/conf.d/zz-app.ini b/.docker/.shared/config/php/conf.d/zz-app.ini new file mode 100644 index 0000000..8b9b9a5 --- /dev/null +++ b/.docker/.shared/config/php/conf.d/zz-app.ini @@ -0,0 +1,14 @@ +; enable opcache +opcache.enable_cli = 1 +opcache.enable = 1 +opcache.fast_shutdown = 1 +; check with find . -type f -print | grep php | wc -l +opcache.max_accelerated_files = 100 +; revalidate everytime +opcache.revalidate_freq=0 +;opcache.validate_timestamps = 0 +opcache.memory_consumption=64 +opcache.interned_strings_buffer=12 +; enable xdebug +xdebug.remote_enable=1 +xdebug.remote_host=host.docker.internal diff --git a/.docker/.shared/scripts/cleanup.sh b/.docker/.shared/scripts/cleanup.sh new file mode 100644 index 0000000..eb2da12 --- /dev/null +++ b/.docker/.shared/scripts/cleanup.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +apt-get clean +rm -rf /var/lib/apt/lists/* \ + /tmp/* \ + /var/tmp/* \ + /var/log/lastlog \ + /var/log/faillog diff --git a/.docker/.shared/scripts/create_user.sh b/.docker/.shared/scripts/create_user.sh new file mode 100644 index 0000000..189f0c7 --- /dev/null +++ b/.docker/.shared/scripts/create_user.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +APP_USER=$1 +APP_GROUP=$2 +APP_USER_ID=$3 +APP_GROUP_ID=$4 + +new_user_id_exists=$(id ${APP_USER_ID} > /dev/null 2>&1; echo $?) +if [ "$new_user_id_exists" = "0" ]; then + (>&2 echo "ERROR: APP_USER_ID $APP_USER_ID already exists - Aborting!"); + exit 1; +fi + +new_group_id_exists=$(getent group ${APP_GROUP_ID} > /dev/null 2>&1; echo $?) +if [ "$new_group_id_exists" = "0" ]; then + (>&2 echo "ERROR: APP_GROUP_ID $APP_GROUP_ID already exists - Aborting!"); + exit 1; +fi + +old_user_id=$(id -u ${APP_USER}) +old_user_exists=$(id -u ${APP_USER} > /dev/null 2>&1; echo $?) +old_group_id=$(getent group ${APP_GROUP} | cut -d: -f3) +old_group_exists=$(getent group ${APP_GROUP} > /dev/null 2>&1; echo $?) + +if [ "$old_group_id" != "${APP_GROUP_ID}" ]; then + # create the group + groupadd -f ${APP_GROUP} + # and the correct id + groupmod -g ${APP_GROUP_ID} ${APP_GROUP} + if [ "$old_group_exists" = "0" ]; then + # set the permissions of all "old" files and folder to the new group + find / -group $old_group_id -exec chgrp -h ${APP_GROUP} {} \; || true + fi +fi + +if [ "$old_user_id" != "${APP_USER_ID}" ]; then + # create the user if it does not exist + if [ "$old_user_exists" != "0" ]; then + useradd ${APP_USER} -g ${APP_GROUP} + fi + + # make sure the home directory exists with the correct permissions + mkdir -p /home/${APP_USER} && chmod 755 /home/${APP_USER} && chown ${APP_USER}:${APP_GROUP} /home/${APP_USER} + + # change the user id, set the home directory and make sure the user has a login shell + usermod -u ${APP_USER_ID} -m -d /home/${APP_USER} ${APP_USER} -s $(which bash) + + if [ "$old_user_exists" = "0" ]; then + # set the permissions of all "old" files and folder to the new user + find / -user $old_user_id -exec chown -h ${APP_USER} {} \; || true + fi +fi \ No newline at end of file diff --git a/.docker/.shared/scripts/docker-entrypoint/resolve-docker-host-ip.sh b/.docker/.shared/scripts/docker-entrypoint/resolve-docker-host-ip.sh new file mode 100644 index 0000000..87e2410 --- /dev/null +++ b/.docker/.shared/scripts/docker-entrypoint/resolve-docker-host-ip.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -e + +# fix for host.docker.internal not existing on linux https://github.com/docker/for-linux/issues/264 +# see https://dev.to/bufferings/access-host-from-a-docker-container-4099 +HOST_DOMAIN="host.docker.internal" +# check if the host exists +# see https://stackoverflow.com/a/24049165/413531 +if dig ${HOST_DOMAIN} | grep -q 'NXDOMAIN' +then + # on linux, it will fail - so we'll "manually" add the hostname in the host file + HOST_IP=$(ip route | awk 'NR==1 {print $3}') + echo "$HOST_IP\t$HOST_DOMAIN" >> /etc/hosts +fi + +exec "$@" diff --git a/.docker/.shared/scripts/install_php_extensions.sh b/.docker/.shared/scripts/install_php_extensions.sh new file mode 100644 index 0000000..937caec --- /dev/null +++ b/.docker/.shared/scripts/install_php_extensions.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# add wget +apt-get update -yqq && apt-get -f install -yyq wget + +# download helper script +# @see https://github.com/mlocati/docker-php-extension-installer/ +wget -q -O /usr/local/bin/install-php-extensions https://raw.githubusercontent.com/mlocati/docker-php-extension-installer/master/install-php-extensions \ + || (echo "Failed while downloading php extension installer!"; exit 1) + +# install extensions +chmod uga+x /usr/local/bin/install-php-extensions && sync && install-php-extensions \ + opcache \ + xdebug \ + zip \ + pdo_mysql \ +; diff --git a/.docker/.shared/scripts/install_software.sh b/.docker/.shared/scripts/install_software.sh new file mode 100644 index 0000000..024530a --- /dev/null +++ b/.docker/.shared/scripts/install_software.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +apt-get update -yqq && apt-get install -yqq \ + curl \ + dnsutils \ + gdb \ + git \ + htop \ + iproute2 \ + iputils-ping \ + ltrace \ + make \ + procps \ + strace \ + sudo \ + sysstat \ + unzip \ + vim \ + wget \ +; diff --git a/.docker/.shared/scripts/modify_config.sh b/.docker/.shared/scripts/modify_config.sh new file mode 100644 index 0000000..67ed4fd --- /dev/null +++ b/.docker/.shared/scripts/modify_config.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +CONFIG_FILE=$1 +VAR_NAME=$2 +VAR_VALUE=$3 + +sed -i -e "s#${VAR_NAME}#${VAR_VALUE}#g" "${CONFIG_FILE}" + +# cat "${CONFIG_FILE}" \ No newline at end of file diff --git a/.docker/.shared/scripts/set_timezone.sh b/.docker/.shared/scripts/set_timezone.sh new file mode 100644 index 0000000..081e6fa --- /dev/null +++ b/.docker/.shared/scripts/set_timezone.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +TZ=$1 +ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml new file mode 100644 index 0000000..a8394e0 --- /dev/null +++ b/.docker/docker-compose.yml @@ -0,0 +1,95 @@ +version: '3.7' + +networks: + backend: + driver: ${NETWORKS_DRIVER} + ipam: + config: + - subnet: "192.168.230.0/24" + +volumes: + data-spde: + +services: + nginx: + image: standard-php-dev-env/nginx + build: + context: . + dockerfile: ./nginx/Dockerfile + args: + - APP_CODE_PATH=${APP_CODE_PATH_CONTAINER} + - APP_GROUP=${APP_GROUP} + - APP_GROUP_ID=${APP_GROUP_ID} + - APP_USER=${APP_USER} + - APP_USER_ID=${APP_USER_ID} + - TZ=${TIMEZONE} + volumes: + - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER} + ports: + - "${NGINX_HOST_HTTP_PORT}:80" + - "${NGINX_HOST_HTTPS_PORT}:443" + networks: + backend: + ipv4_address: 192.168.230.13 + aliases: + - ${APP_HOST} + + php-fpm: + image: standard-php-dev-env/php-fpm + build: + context: . + dockerfile: ./php-fpm/Dockerfile + args: + - APP_CODE_PATH=${APP_CODE_PATH_CONTAINER} + - APP_GROUP=${APP_GROUP} + - APP_GROUP_ID=${APP_GROUP_ID} + - APP_USER=${APP_USER} + - APP_USER_ID=${APP_USER_ID} + - TARGET_PHP_VERSION=${PHP_VERSION} + - TZ=${TIMEZONE} + volumes: + - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER} + networks: + backend: + ipv4_address: 192.168.230.12 + + php-cli: + image: standard-php-dev-env/php-cli + build: + context: . + dockerfile: ./php-cli/Dockerfile + args: + - APP_CODE_PATH=${APP_CODE_PATH_CONTAINER} + - APP_GROUP=${APP_GROUP} + - APP_GROUP_ID=${APP_GROUP_ID} + - APP_USER=${APP_USER} + - APP_USER_ID=${APP_USER_ID} + - TARGET_PHP_VERSION=${PHP_VERSION} + - TZ=${TIMEZONE} + volumes: + - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER} + ports: + - "${WORKSPACE_HOST_SSH_PORT}:22" + networks: + backend: + ipv4_address: 192.168.230.11 + + mysql: + image: standard-php-dev-env/mysql + build: + context: . + dockerfile: ./mysql/Dockerfile + restart: always + volumes: + - data-spde:/var/lib/mysql + - ./mysql/init_data:/docker-entrypoint-initdb.d + networks: + backend: + ipv4_address: 192.168.230.10 + ports: + - ${MYSQL_HOST_PORT}:3306 + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} diff --git a/.docker/docker-test.sh b/.docker/docker-test.sh new file mode 100644 index 0000000..7dd5108 --- /dev/null +++ b/.docker/docker-test.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +function service_info(){ + service=$1 + echo "" + echo -e "Testing service '\e[1m$service\e[0m'" + echo "=======" +} + +function assert_result(){ + if [[ "$1" == true ]]; + then + echo -e "\e[32;1mOK\e[0m" + else + echo -e "\e[31;1mERROR\e[0m" + fi; +} + +function docker_exec(){ + service=$1 + shift; + docker exec $(docker ps --filter name=${service} -q | head -1) "$@" +} + +function test_container_is_running(){ + service=$1 + result=false + echo "Checking if '${service}' has a running container" + echo "$(docker ps --filter name=${service})" | grep -q "${service}" && result=true + assert_result ${result} +} + +function test_host_docker_internal(){ + service=$1 + result=false + echo "Checking 'host.docker.internal' on '${service}'" + docker_exec ${service} dig host.docker.internal | grep -vq NXDOMAIN && result=true + assert_result ${result} +} + +function test_request_nginx(){ + url=$1 + expected=$2 + result=false + echo "Sending request to nginx via '$url' and expect to see '${expected}'" + curl -s ${url} | grep -q "${expected}" && result=true + assert_result ${result} +} + +function test_php_version(){ + service=$1 + php=$2 + version=$3 + expected="PHP $version" + result=false + echo "Testing PHP version '$version' on '$service' for '$php' and expect to see '${expected}'" + docker_exec ${service} php -v | grep -q "${expected}" && result=true + assert_result ${result} +} + +function test_php_modules(){ + service=$1 + php=$2 + shift; + shift; + for module in "$@"; do + test_php_module ${service} ${php} "${module}" + done + +} + +function test_php_module(){ + service=$1 + php=$2 + module=$3 + expected="$module" + result=false + echo "Testing PHP module '${expected}' on '$service' for '$php'" + docker_exec ${service} php -m | grep -q "${expected}" && result=true + assert_result $result +} + +service="php-cli" +service_info ${service} +test_container_is_running ${service} +test_php_version ${service} php 7.4 +test_php_modules ${service} php xdebug "Zend OPcache" +test_host_docker_internal ${service} + +service="php-fpm" +service_info ${service} +test_container_is_running ${service} +test_php_version ${service} php-fpm 7.4 +test_php_modules ${service} php-fpm xdebug "Zend OPcache" +test_host_docker_internal ${service} + +service="nginx" +service_info ${service} +test_container_is_running ${service} +test_request_nginx 127.0.0.1 "Hello world" diff --git a/.docker/mysql/Dockerfile b/.docker/mysql/Dockerfile new file mode 100755 index 0000000..57138cf --- /dev/null +++ b/.docker/mysql/Dockerfile @@ -0,0 +1 @@ +FROM mysql:8 diff --git a/.docker/nginx/Dockerfile b/.docker/nginx/Dockerfile new file mode 100644 index 0000000..d8bbf96 --- /dev/null +++ b/.docker/nginx/Dockerfile @@ -0,0 +1,48 @@ +FROM nginx:latest + +ARG SERVICE_DIR="./nginx" +COPY ./.shared/scripts/ /tmp/scripts/ +RUN chmod -R 777 /tmp/scripts/ + +# set timezone +ARG TZ=UTC +RUN /tmp/scripts/set_timezone.sh ${TZ} + +# add users +ARG APP_USER=www-data +ARG APP_GROUP=www-data +ARG APP_USER_ID=1000 +ARG APP_GROUP_ID=1000 + +RUN /tmp/scripts/create_user.sh ${APP_USER} ${APP_GROUP} ${APP_USER_ID} ${APP_GROUP_ID} + +RUN /tmp/scripts/install_software.sh + +# nginx config +COPY ${SERVICE_DIR}/nginx.conf /etc/nginx/ +ARG APP_USER +RUN /tmp/scripts/modify_config.sh /etc/nginx/nginx.conf \ + "__APP_USER" \ + "${APP_USER}" \ + && /tmp/scripts/modify_config.sh /etc/nginx/nginx.conf \ + "__APP_GROUP" \ + "${APP_GROUP}" \ +; + +# nginx app config +COPY ${SERVICE_DIR}/sites-available/* /etc/nginx/sites-available/ +ARG APP_CODE_PATH +RUN /tmp/scripts/modify_config.sh /etc/nginx/sites-available/default.conf \ + "__NGINX_ROOT" \ + "${APP_CODE_PATH}" \ +; + +# workdir +WORKDIR /etc/nginx/ + +# cleanup +RUN /tmp/scripts/cleanup.sh + +CMD ["nginx"] + +EXPOSE 8898 8899 diff --git a/.docker/nginx/nginx.conf b/.docker/nginx/nginx.conf new file mode 100644 index 0000000..0aa2bec --- /dev/null +++ b/.docker/nginx/nginx.conf @@ -0,0 +1,33 @@ +user __APP_USER __APP_GROUP; +worker_processes 4; +pid /run/nginx.pid; +daemon off; + +events { + worker_connections 2048; + multi_accept on; + use epoll; +} + +http { + server_tokens off; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 15; + types_hash_max_size 2048; + client_max_body_size 20M; + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log /dev/stdout; + error_log /dev/stderr; + gzip on; + gzip_disable "msie6"; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; + + include /etc/nginx/sites-available/*.conf; + open_file_cache off; # Disabled for issue 619 + charset UTF-8; +} \ No newline at end of file diff --git a/.docker/nginx/sites-available/default.conf b/.docker/nginx/sites-available/default.conf new file mode 100644 index 0000000..b27bed9 --- /dev/null +++ b/.docker/nginx/sites-available/default.conf @@ -0,0 +1,27 @@ +server { + listen 80 default_server; + listen [::]:80 default_server ipv6only=on; + + server_name localhost; + root __NGINX_ROOT; + index index.php index.html index.htm; + + location / { + try_files $uri $uri/ /index.php$is_args$args; + } + + location ~ \.php$ { + try_files $uri /index.php =404; + fastcgi_pass php-fpm:9000; + fastcgi_index index.php; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + #fixes timeouts + fastcgi_read_timeout 600; + include fastcgi_params; + } + + error_log /var/log/nginx/app_error.log; + access_log /var/log/nginx/app_access.log; +} diff --git a/.docker/php-cli/.ssh/insecure_id_rsa b/.docker/php-cli/.ssh/insecure_id_rsa new file mode 100644 index 0000000..b52645f --- /dev/null +++ b/.docker/php-cli/.ssh/insecure_id_rsa @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/.docker/php-cli/.ssh/insecure_id_rsa.pub b/.docker/php-cli/.ssh/insecure_id_rsa.pub new file mode 100644 index 0000000..606fbe9 --- /dev/null +++ b/.docker/php-cli/.ssh/insecure_id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC+V7bVLI0Q9BVRx40OTcaMKLeOrheCD8HvAHapo4ws+e7omkwiJSBtLg+jaZZiT6vecnBn5DCbsS1rgQ7Z/MJN78j/sfkdPSt038jd7ixuQFIWnjAVMzfwRB0z3NfYG1EKdhAG24YGszO6MWBAw6Aec5QGOHW1gccJ30TNICa6ZfHNWS6MNmmmUhNjK5XMvTyKgW5jOv6ccT0s3JYxuh54I5VKz6hfmk3UvYg77Ezbe26frmMqszzgN3q5cMKjLwMgC1eml4ETdZMpqVeFwDu+/V7Fx6KfhMBvQzKyICdPCkA6sTTFrJ9M2VnGiAXJk+17Q1o8EbtTo+SVIXC5deubG8Ww3xa+RWe0YlbGuu+rybTPNvWAn/VVY3tJ1DsdPJSSbPyvsA2MkeY2QLm+/Qz8F0WUx7mO2Ac18iIfaPmqJ2KVstB0XawIaaXdZoeaAUPiSpJWXXz36cOzlKQt1voYo4jUdM0SFNZmtBxA4xbA8VNd1bJvd8SO6PBDvHSYEAB19vogm1PUQl/X1GpRTDYuH88NHCkeUwmUi6w79F41vZQQcxJuetTSN/LwTSNduvDLAIQCPUq3MxE65jwniOdDGlKe5vYaq/w1+waO05SzmHRAztdGTHjzdOeTvEgYeJaBi0aOglLSm6ovH54PjU9+nzUz3H4XePFfohLEaTwAww== insecure@pascallandau.com diff --git a/.docker/php-cli/Dockerfile b/.docker/php-cli/Dockerfile new file mode 100644 index 0000000..3b043ae --- /dev/null +++ b/.docker/php-cli/Dockerfile @@ -0,0 +1,58 @@ +ARG TARGET_PHP_VERSION=7.4 +FROM php:${TARGET_PHP_VERSION}-cli + +ARG SERVICE_DIR="./php-cli" +COPY ./.shared/scripts/ /tmp/scripts/ +RUN chmod +x -R /tmp/scripts/ + +# set timezone +ARG TZ=UTC +RUN /tmp/scripts/set_timezone.sh ${TZ} + +# add users +ARG APP_USER=www-data +ARG APP_GROUP=www-data +ARG APP_USER_ID=1000 +ARG APP_GROUP_ID=1000 + +RUN /tmp/scripts/create_user.sh ${APP_USER} ${APP_GROUP} ${APP_USER_ID} ${APP_GROUP_ID} + +RUN /tmp/scripts/install_php_extensions.sh + +RUN /tmp/scripts/install_software.sh + +# set up ssh +RUN apt-get update -yqq && apt-get install -yqq openssh-server \ + && mkdir /var/run/sshd \ +; + +# add default public key to authorized_keys +USER ${APP_USER} +COPY ${SERVICE_DIR}/.ssh/insecure_id_rsa.pub /tmp/insecure_id_rsa.pub +RUN mkdir -p ~/.ssh \ + && cat /tmp/insecure_id_rsa.pub >> ~/.ssh/authorized_keys \ + && chown -R ${APP_USER}: ~/.ssh \ + && chmod 700 ~/.ssh \ + && chmod 600 ~/.ssh/authorized_keys \ +; +USER root + +# php config +COPY ./.shared/config/php/conf.d/* /usr/local/etc/php/conf.d/ + +# workdir +ARG APP_CODE_PATH="/var/www/current" +WORKDIR "$APP_CODE_PATH" + +# entrypoint +RUN mkdir -p /bin/docker-entrypoint/ \ + && cp /tmp/scripts/docker-entrypoint/* /bin/docker-entrypoint/ \ + && chmod +x -R /bin/docker-entrypoint/ \ +; + +RUN /tmp/scripts/cleanup.sh + +# @see https://docs.docker.com/engine/examples/running_ssh_service/ +CMD ["/usr/sbin/sshd", "-D"] +ENTRYPOINT ["/bin/docker-entrypoint/resolve-docker-host-ip.sh"] + diff --git a/.docker/php-fpm/Dockerfile b/.docker/php-fpm/Dockerfile new file mode 100644 index 0000000..9c3a9c9 --- /dev/null +++ b/.docker/php-fpm/Dockerfile @@ -0,0 +1,50 @@ +ARG TARGET_PHP_VERSION=7.4 +FROM php:${TARGET_PHP_VERSION}-fpm + +ARG SERVICE_DIR="./php-fpm" +COPY ./.shared/scripts/ /tmp/scripts/ +RUN chmod +x -R /tmp/scripts/ + +# set timezone +ARG TZ=UTC +RUN /tmp/scripts/set_timezone.sh ${TZ} + +# add users +ARG APP_USER=www-data +ARG APP_GROUP=www-data +ARG APP_USER_ID=1000 +ARG APP_GROUP_ID=1000 + +RUN /tmp/scripts/create_user.sh ${APP_USER} ${APP_GROUP} ${APP_USER_ID} ${APP_GROUP_ID} + +RUN /tmp/scripts/install_php_extensions.sh + +RUN /tmp/scripts/install_software.sh + +# php config +COPY ./.shared/config/php/conf.d/* /usr/local/etc/php/conf.d/ + +# php-fpm pool config +COPY ${SERVICE_DIR}/php-fpm.d/* /usr/local/etc/php-fpm.d +RUN /tmp/scripts/modify_config.sh /usr/local/etc/php-fpm.d/zz-app.conf \ + "__APP_USER" \ + "${APP_USER}" \ + && /tmp/scripts/modify_config.sh /usr/local/etc/php-fpm.d/zz-app.conf \ + "__APP_GROUP" \ + "${APP_GROUP}" \ +; + +# workdir +ARG APP_CODE_PATH="/var/www/current" +WORKDIR "$APP_CODE_PATH" + +# entrypoint +RUN mkdir -p /bin/docker-entrypoint/ \ + && cp /tmp/scripts/docker-entrypoint/* /bin/docker-entrypoint/ \ + && chmod +x -R /bin/docker-entrypoint/ \ +; + +RUN /tmp/scripts/cleanup.sh +ENTRYPOINT ["/bin/docker-entrypoint/resolve-docker-host-ip.sh","php-fpm"] +EXPOSE 9000 + diff --git a/.docker/php-fpm/php-fpm.d/zz-app.conf b/.docker/php-fpm/php-fpm.d/zz-app.conf new file mode 100644 index 0000000..710db6a --- /dev/null +++ b/.docker/php-fpm/php-fpm.d/zz-app.conf @@ -0,0 +1,2 @@ +user = __APP_USER +group = __APP_GROUP \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a0dc4ce --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +DOCKER_COMPOSE_DIR=./.docker +DOCKER_COMPOSE_FILE=$(DOCKER_COMPOSE_DIR)/docker-compose.yml +DEFAULT_CONTAINER=workspace +DOCKER_COMPOSE=docker-compose -f $(DOCKER_COMPOSE_FILE) --project-directory $(DOCKER_COMPOSE_DIR) + +DEFAULT_GOAL := help +help: + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-27s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ [Docker] Build / Infrastructure +.docker/.env: + cp $(DOCKER_COMPOSE_DIR)/.env.example $(DOCKER_COMPOSE_DIR)/.env + +.PHONY: docker-clean +docker-clean: ## Remove the .env file for docker + rm -f $(DOCKER_COMPOSE_DIR)/.env + +.PHONY: docker-init +docker-init: .docker/.env ## Make sure the .env file exists for docker + +.PHONY: docker-build-from-scratch +docker-build-from-scratch: docker-init ## Build all docker images from scratch, without cache etc. Build a specific image by providing the service name via: make docker-build CONTAINER= + $(DOCKER_COMPOSE) rm -fs $(CONTAINER) && \ + $(DOCKER_COMPOSE) build --pull --no-cache --parallel $(CONTAINER) && \ + $(DOCKER_COMPOSE) up -d --force-recreate $(CONTAINER) + +.PHONY: docker-test +docker-test: docker-init docker-up ## Run the infrastructure tests for the docker setup + sh $(DOCKER_COMPOSE_DIR)/docker-test.sh + +.PHONY: docker-build +docker-build: docker-init ## Build all docker images. Build a specific image by providing the service name via: make docker-build CONTAINER= + $(DOCKER_COMPOSE) build --parallel $(CONTAINER) && \ + $(DOCKER_COMPOSE) up -d --force-recreate $(CONTAINER) + +.PHONY: docker-prune +docker-prune: ## Remove unused docker resources via 'docker system prune -a -f --volumes' + docker system prune -a -f --volumes + +.PHONY: docker-up +docker-up: docker-init ## Start all docker containers. To only start one container, use CONTAINER= + $(DOCKER_COMPOSE) up -d $(CONTAINER) + +.PHONY: docker-down +docker-down: docker-init ## Stop all docker containers. To only stop one container, use CONTAINER= + $(DOCKER_COMPOSE) down $(CONTAINER) + +.PHONY: docker-ipaddress +docker-ipaddress: ## Get IP address of all containers. + docker network inspect -f '{{json .Containers}}' filipets-docker_backend | jq '.[] | .Name + ":" + .IPv4Address' diff --git a/README.md b/README.md index 1989a0c..6dabb49 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ # standard-php-dev-env -Standard PHP development environment. +Repository with basic environment for development with PHP language. + +Thanks for the great work provided in [docker-php-tutorial](https://github.com/paslandau/docker-php-tutorial/tree/part_3_structuring-the-docker-setup-for-php-projects), specifically the branch part 3, which served as the basis for this repository. + +## Environment +This repository contains four containers that make it possible to start coding after compiling the environment triggered by the make command. + +The four services available are: +| Service | Version | +| ------- | ------- | +| php-cli | 7.4 | +| php-fpm | 7.4 | +| mysql | 8 | +| nginx | latest | + +## Execution +To run the build and start the job, just clone the repository, access the created folder, and enter the following command lines: +```sh +make docker-clean +make docker-init +make docker-build-from-scratch +make docker-up +``` +And to stop the services: +```sh +make docker-down +``` + +## To do: +* Improve this documentation +* Include a service for email testing