From cba0a3ef9054c57f41683aa4c7e1ab765d5c9c66 Mon Sep 17 00:00:00 2001 From: Agnieszka Rudek <61757744+Blusia@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:09:38 +0100 Subject: [PATCH] #7 - add dev environment (#21) * -add dev environment * -corrections --- .env.example | 18 ++++--- Makefile | 47 ++++++++++++++++++ docker-compose.yml | 64 +++++++++++++++++++++++++ environment/dev/app/Dockerfile | 72 ++++++++++++++++++++++++++++ environment/dev/app/entrypoint.sh | 12 +++++ environment/dev/app/nginx.conf | 45 +++++++++++++++++ environment/dev/app/php-fpm.conf | 8 ++++ environment/dev/app/php.ini | 2 + environment/dev/app/supervisord.conf | 22 +++++++++ readme.md | 25 ++++++++-- 10 files changed, 304 insertions(+), 11 deletions(-) create mode 100644 Makefile create mode 100644 docker-compose.yml create mode 100644 environment/dev/app/Dockerfile create mode 100755 environment/dev/app/entrypoint.sh create mode 100644 environment/dev/app/nginx.conf create mode 100644 environment/dev/app/php-fpm.conf create mode 100644 environment/dev/app/php.ini create mode 100644 environment/dev/app/supervisord.conf diff --git a/.env.example b/.env.example index f69aeb7..6ee759d 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,15 @@ +APP_NAME=lmt ENVIRONMENT=dev -SMTP_HOST= -SMTP_PORT= -SMTP_USERNAME= -SMTP_PASSWORD= +EXTERNAL_WEBSERVER_PORT=8051 +EXTERNAL_MAILPIT_DASHBOARD_PORT=8052 -EMAIL_FROM= -REGISTRATION_NOTIFICATION_EMAIL= +SMTP_HOST=lmt-dev-mailpit-container +SMTP_PORT=1025 +SMTP_USERNAME=null +SMTP_PASSWORD=null + +EMAIL_FROM="sample@example.com" +REGISTRATION_NOTIFICATION_EMAIL="hello@example.com" + +SOPS_AGE_PROD_SECRET_KEY= diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f933b44 --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +include .env + +SHELL := /bin/bash + +DOCKER_COMPOSE_FILE = docker-compose.yml +DOCKER_COMPOSE_APP_CONTAINER = app + +CURRENT_USER_ID = $(shell id --user) +CURRENT_USER_GROUP_ID = $(shell id --group) +CURRENT_DIR = $(shell pwd) +PHP_SERVICE_NAME=app + +build: + docker compose build --pull + +run: + docker compose up -d + +stop: + docker compose stop + +restart: stop run + +shell: + @docker compose exec --user ${CURRENT_USER_ID} ${PHP_SERVICE_NAME} bash + +encrypt-prod-secrets: + @$(MAKE) encrypt-secrets SECRETS_ENV=prod + +decrypt-prod-secrets: + @$(MAKE) decrypt-secrets SECRETS_ENV=prod AGE_SECRET_KEY=${SOPS_AGE_PROD_SECRET_KEY} + +decrypt-secrets: + @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" --env SOPS_AGE_KEY=${AGE_SECRET_KEY} ${DOCKER_COMPOSE_APP_CONTAINER} \ + bash -c "echo 'Decrypting ${SECRETS_ENV} secrets' \ + && cd ./environment/prod/deployment/${SECRETS_ENV} \ + && sops --decrypt --input-type=dotenv --output-type=dotenv --output .env.${SECRETS_ENV}.secrets.decrypted .env.${SECRETS_ENV}.secrets \ + && echo 'Done'" + +encrypt-secrets: + @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} \ + bash -c "echo 'Encrypting ${SECRETS_ENV} secrets' \ + && cd ./environment/prod/deployment/${SECRETS_ENV} \ + && sops --encrypt --input-type=dotenv --output-type=dotenv --output .env.${SECRETS_ENV}.secrets .env.${SECRETS_ENV}.secrets.decrypted \ + && echo 'Done'" + +.PHONY: build run stop restart shell diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..464cc05 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,64 @@ +networks: + lmt-dev: + driver: bridge + traefik-proxy-blumilk-local: + external: true + +services: + app: + build: + context: ./environment/dev/app + dockerfile: Dockerfile + args: + - USER_ID=${DOCKER_HOST_USER_ID:-1000} + labels: + - "traefik.enable=true" + - "traefik.blumilk.environment=true" + # HTTP + - "traefik.http.routers.lmt-http-router.rule=Host(`lmt.blumilk.localhost`)" + - "traefik.http.routers.lmt-http-router.entrypoints=web" + # HTTP to HTTPS redirect + # - "traefik.http.routers.lmt-http-router.middlewares=https-redirect@file" + # HTTPS + - "traefik.http.routers.lmt-https-router.rule=Host(`lmt.blumilk.localhost`)" + - "traefik.http.routers.lmt-https-router.entrypoints=websecure" + - "traefik.http.routers.lmt-https-router.tls=true" + container_name: lmt-dev-app-container + working_dir: /application + volumes: + - ./environment/dev/app/nginx.conf:/etc/nginx/nginx.conf:ro + - ./environment/dev/app/php.ini:/usr/local/etc/php/conf.d/zzz-overrides.ini:ro + - ./environment/dev/app/php-fpm.conf:/usr/local/etc/php-fpm.d/zzz-overrides.conf:ro + - ./environment/dev/app/supervisord.conf:/etc/supervisor/custom-supervisord.conf:ro + - .:/application + ports: + - ${DOCKER_APP_HOST_PORT:-8051}:80 + networks: + - lmt-dev + - traefik-proxy-blumilk-local + restart: unless-stopped + + mailpit: + image: axllent/mailpit:v1.14.4 + container_name: lmt-dev-mailpit-container + ports: + - ${EXTERNAL_MAILPIT_DASHBOARD_PORT:-8052}:8025 + - ${EXTERNAL_MAILPIT_SMTP_PORT:-8053}:1025 + networks: + - lmt-dev + - traefik-proxy-blumilk-local + labels: + - "traefik.enable=true" + - "traefik.blumilk.environment=true" + # HTTP + - "traefik.http.routers.lmt-mailpit-http-router.rule=Host(`lmt-mailpit.blumilk.localhost`)" + - "traefik.http.routers.lmt-mailpit-http-router.entrypoints=web" + # HTTP to HTTPS redirect + # - "traefik.http.routers.lmt-mailpit-http-router.middlewares=https-redirect@file" + # HTTPS + - "traefik.http.routers.lmt-mailpit-https-router.rule=Host(`lmt-mailpit.blumilk.localhost`)" + - "traefik.http.routers.lmt-mailpit-https-router.entrypoints=websecure" + - "traefik.http.routers.lmt-mailpit-https-router.tls=true" + # LOADBALANCER MAILHOG PORT + - "traefik.http.services.lmt-mailpit.loadbalancer.server.port=8025" + restart: unless-stopped diff --git a/environment/dev/app/Dockerfile b/environment/dev/app/Dockerfile new file mode 100644 index 0000000..87486e9 --- /dev/null +++ b/environment/dev/app/Dockerfile @@ -0,0 +1,72 @@ +# https://hub.docker.com/_/alpine +FROM alpine:3.19.0 as secops-tools + +# https://github.com/FiloSottile/age/releases +ARG AGE_VERSION="1.1.1" + +# https://github.com/getsops/sops/releases +ARG SOPS_VERSION="3.8.1" + +RUN wget --output-document age.tar.gz "https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-linux-amd64.tar.gz" \ + && tar --extract --file age.tar.gz \ + && mv age/age /usr/local/bin \ + && mv age/age-keygen /usr/local/bin \ + && wget --output-document /usr/local/bin/sops "https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64" \ + && chmod +x /usr/local/bin/sops + +FROM composer/composer:2.7.2-bin as composer-bin + +FROM node:21.7.1-bookworm-slim as node + +FROM php:8.3.3-fpm-bookworm + +COPY --from=composer-bin ./composer /usr/bin/composer +COPY --from=secops-tools /usr/local/bin/age /usr/local/bin/age +COPY --from=secops-tools /usr/local/bin/age-keygen /usr/local/bin/age-keygen +COPY --from=secops-tools /usr/local/bin/sops /usr/local/bin/sops + + +ARG USER_NAME=host-user +ARG USER_ID=1000 +ARG PHP_FPM_GROUP=www-data + +RUN adduser \ + --disabled-password \ + --uid ${USER_ID} \ + ${USER_NAME} \ + && usermod \ + --append \ + --groups \ + ${PHP_FPM_GROUP} \ + ${USER_NAME} + +#Add node and npm +COPY --from=node --chown=${USER_NAME}:root /usr/local/lib/node_modules /usr/local/lib/node_modules +COPY --from=node --chown=${USER_NAME}:root /usr/local/bin/node /usr/local/bin/node +RUN ln --symbolic /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ + && chown --no-dereference ${USER_NAME}:root /usr/local/bin/npm \ + && ln --symbolic /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \ + && chown --no-dereference ${USER_NAME}:root /usr/local/bin/npx + +# Use the default development configuration +RUN mv "${PHP_INI_DIR}/php.ini-development" "${PHP_INI_DIR}/php.ini" + +# For other versions check: http://nginx.org/packages/mainline/debian/pool/nginx/n/nginx/ +ARG NGINX_VERSION="1.25.4-1~bookworm" + +RUN apt-get update \ + && apt-get install --assume-yes gpg \ + && curl https://nginx.org/keys/nginx_signing.key | gpg --dearmour --output /etc/apt/trusted.gpg.d/apt.nginx.org.gpg > /dev/null \ + && echo "deb https://nginx.org/packages/mainline/debian bookworm nginx" | tee /etc/apt/sources.list.d/nginx.list \ + && apt-get update && apt-get install --assume-yes \ + nginx=${NGINX_VERSION} \ + 7zip \ + libzip-dev \ + supervisor \ + cron + +COPY ./entrypoint.sh /entrypoint.sh + +WORKDIR /application + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/environment/dev/app/entrypoint.sh b/environment/dev/app/entrypoint.sh new file mode 100755 index 0000000..b951e25 --- /dev/null +++ b/environment/dev/app/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# -e is for "automatic error detection", tell shell to abort any time an error occurred +set -e + +# bash is not responding to the sigterm and container always have 10 second timeout (when stop/restart) +# exec is related with +# https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop +# https://github.com/moby/moby/issues/3766 +# https://unix.stackexchange.com/a/196053 + +exec supervisord --configuration /etc/supervisor/custom-supervisord.conf diff --git a/environment/dev/app/nginx.conf b/environment/dev/app/nginx.conf new file mode 100644 index 0000000..75b31bd --- /dev/null +++ b/environment/dev/app/nginx.conf @@ -0,0 +1,45 @@ +daemon off; + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + sendfile on; + keepalive_timeout 65; + +server { + listen 80 default; + server_name lmt-nginx; + + access_log /dev/stdout; + error_log /dev/stderr; + + root /application/public; + index index.html; + + location / { + try_files $uri $uri/ =404; + } + + location ~ \.php$ { + fastcgi_pass unix:/run/php-fpm.sock; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + } +} diff --git a/environment/dev/app/php-fpm.conf b/environment/dev/app/php-fpm.conf new file mode 100644 index 0000000..883caec --- /dev/null +++ b/environment/dev/app/php-fpm.conf @@ -0,0 +1,8 @@ +[global] +daemonize = no + +[www] +listen = /run/php-fpm.sock +listen.owner = nginx + +user = host-user diff --git a/environment/dev/app/php.ini b/environment/dev/app/php.ini new file mode 100644 index 0000000..39f373e --- /dev/null +++ b/environment/dev/app/php.ini @@ -0,0 +1,2 @@ +[PHP] +memory_limit = 256M diff --git a/environment/dev/app/supervisord.conf b/environment/dev/app/supervisord.conf new file mode 100644 index 0000000..2acd072 --- /dev/null +++ b/environment/dev/app/supervisord.conf @@ -0,0 +1,22 @@ +; For more information on the config file, please see: +; http://supervisord.org/configuration.html + +[supervisord] +logfile = /var/log/supervisor/supervisord.log +pidfile = /var/run/supervisord.pid +user = root +nodaemon = true + +[program:nginx] +command = nginx +stdout_logfile = /dev/stdout +stdout_logfile_maxbytes = 0 +stderr_logfile = /dev/stderr +stderr_logfile_maxbytes = 0 + +[program:php-fpm] +command = php-fpm +stdout_logfile = /dev/stdout +stdout_logfile_maxbytes = 0 +stderr_logfile = /dev/stderr +stderr_logfile_maxbytes = 0 diff --git a/readme.md b/readme.md index 652c777..c4daf07 100644 --- a/readme.md +++ b/readme.md @@ -3,12 +3,27 @@ ![./screenshot.png](./screenshot.png) -### Installation +### To run in docker environment: +Copy `.env.example` to `.env` and set your environment variables -Install PHP dependencies + cp .env.example .env - composer install +- build APP containers: -Copy `.env.example` to `.env` and set your environment variables + make build - cp .env.example .env +- run containers: + + make run + +- enter the APP container with `make shell`, then install php and node dependencies + + composer install + npm install + +- The website should be available at `localhost:8051` and `lmt.blumilk.localhost` if we use a Blumilk local traefik proxy + +| service | container name | default external port | +|-------------------|---------------------------|-----------------------| +| app | lmt-dev-app-container | 8051 | +| mailpit | lmt-dev-mailpit-container | 8052 |