diff --git a/.drone.yml b/.drone.yml index b8eeba2..4f800ae 100644 --- a/.drone.yml +++ b/.drone.yml @@ -6,14 +6,14 @@ steps: image: decentral1se/stack-ssh-deploy:latest settings: host: swarm-test.autonomic.zone - stack: wordpress + stack: lemp generate_secrets: true purge: true deploy_key: from_secret: drone_ssh_swarm_test environment: - DOMAIN: wordpress.swarm-test.autonomic.zone - STACK_NAME: wordpress + DOMAIN: lemp.swarm-test.autonomic.zone + STACK_NAME: lemp LETS_ENCRYPT_ENV: production SECRET_DB_PASSWORD_VERSION: v1 SECRET_DB_ROOT_PASSWORD_VERSION: v1 diff --git a/.env.sample b/.env.sample index 94ce6c2..bf165df 100644 --- a/.env.sample +++ b/.env.sample @@ -1,43 +1,26 @@ -TYPE=wordpress +TYPE=lemp -DOMAIN=wordpress.example.com +DOMAIN=example.com ## Domain aliases -#EXTRA_DOMAINS=', `www.wordpress.example.com`' +#EXTRA_DOMAINS=', `www.example.com`' LETS_ENCRYPT_ENV=production -## Additional extensions -#PHP_EXTENSIONS="calendar" +## Extensions you do NOT need, out of this list of installed extensions: https://github.com/adhocore/docker-phpfpm#php80 +#PHP_EXTENSIONS_TO_DISABLE="calendar" SECRET_DB_ROOT_PASSWORD_VERSION=v1 SECRET_DB_PASSWORD_VERSION=v1 -# Multisite -#WORDPRESS_CONFIG_EXTRA="\ -# define('WP_CACHE', false);\ -# define('WP_ALLOW_MULTISITE', true );" - -# Multisite phase 2 (see README) -#WORDPRESS_CONFIG_EXTRA="\ -# define('WP_CACHE', false);\ -# define('WP_ALLOW_MULTISITE', true );\ -# define('MULTISITE', true);\ -# define('SUBDOMAIN_INSTALL', true);\ -# define('DOMAIN_CURRENT_SITE', '${DOMAIN}');\ -# define('PATH_CURRENT_SITE', '/');\ -# define('SITE_ID_CURRENT_SITE', 1);\ -# define('BLOG_ID_CURRENT_SITE', 1);\ -# define('FORCE_SSL_ADMIN', true );\ -# define('COOKIE_DOMAIN', \$_SERVER['HTTP_HOST']);" # Local SMTP relay #COMPOSE_FILE="compose.yml:compose.mailrelay.yml" #SMTP_HOST="postfix_relay_app" -#MAIL_FROM="wordpress@example.com" +#MAIL_FROM="site@example.com" # Remote SMTP relay #COMPOSE_FILE="compose.yml:compose.mailrelay.yml:compose.smtp.yml" #SMTP_HOST="mail.example.com" -#MAIL_FROM="wordpress@example.com" +#MAIL_FROM="site@example.com" #SMTP_PORT=587 #SMTP_AUTH=on #SMTP_TLS=on diff --git a/README.md b/README.md index d59ef9c..df0d77f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ -# Wordpress +# PHP / LEMP -[![Build Status](https://drone.autonomic.zone/api/badges/coop-cloud/wordpress/status.svg)](https://drone.autonomic.zone/coop-cloud/wordpress) -Coöp Cloud + [Wordpress](https://wordpress.org) = 🥳 +Coöp Cloud + [PHP](https://php.org) + MariaDB + Nginx = 🥳 - **Category**: Apps - **Status**: ❶💚 -- **Image**: [`wordpress`](https://hub.docker.com/_/wordpress), ❶💚, upstream +- **Image**: [`adhocore/phpfpm`](https://github.com/adhocore/docker-phpfpm), ❶💚, upstream - **Healthcheck**: Yes - **Backups**: Yes - **Email**: ❶💚 @@ -21,39 +20,15 @@ Coöp Cloud + [Wordpress](https://wordpress.org) = 🥳 1. Set up Docker Swarm and [`abra`][abra] 2. Deploy [`coop-cloud/traefik`][cc-traefik] -3. `abra app new wordpress --secrets` (optionally with `--pass` if you'd like +3. `abra app new lemp --secrets` (optionally with `--pass` if you'd like to save secrets in `pass`) 4. `abra app YOURAPPDOMAIN config` - be sure to change `$DOMAIN` to something that resolves to your Docker swarm box 5. `abra app YOURAPPDOMAIN deploy` -6. Open the configured domain in your browser to finish set-up -7. `abra app YOURAPPDOMAIN run app chown www-data:www-data /var/www/html/wp-content` to fix - file permissions (see #3) +6. Copy your site files using something like: `abra app YOURAPPDOMAIN cp index.html app:/var/www/html/` +6. Use [restore functionality](https://docs.coopcloud.tech/backup-restore/) to import a SQL file into the db +6. Open the configured domain in your browser to check all is good -## Running WP-CLI - -`abra app YOURAPPDOMAIN wp 'core check-update --major'` - -(the WP-CLI arguments need to be quoted, because of how `abra` handles -command-line arguments) - -## Network (Multi-site) - -_(Only tested using subdomains)_ - -1. Set up as above -2. `abra app YOURAPPDOMAIN config`, and uncomment the first `# Multisite` section -3. `abra app YOURAPPDOMAIN deploy` -4. Log into the Wordpress admin dashboard, go to Tools » Network Setup -5. Don't worry about the suggested file changes -6. `abra app YOURAPPDOMAIN config` again - comment out the first `# Multisite` - section in `.envrc`, uncomment the `# Multisite phase 2` section, and add - your multisite subdomain(s) to `EXTRA_DOMAINS` (beware the weird syntax..) -7. `abra app YOURAPPDOMAIN deploy` - -## Installing a custom theme - -`abra app YOURAPPDOMAIN cp ~/path/to/local/theme wordpress:/var/www/html/wp-content/themes/` ## Email diff --git a/abra.sh b/abra.sh index ac50858..4c985ff 100644 --- a/abra.sh +++ b/abra.sh @@ -2,49 +2,14 @@ export PHP_UPLOADS_CONF_VERSION=v3 export ENTRYPOINT_CONF_VERSION=v2 export ENTRYPOINT_MAILRELAY_CONF_VERSION=v1 export MSMTP_CONF_VERSION=v3 - -sub_wp() { - CONTAINER=$(docker container ls -f "Name=${STACK_NAME}_app" --format '{{ .ID }}') - if [ -z "$CONTAINER" ]; then - error "Can't find a container for ${STACK_NAME}_app" - exit - fi - debug "Using Container ID ${CONTAINER}" - - # FIXME 3wc: we're fighting the Wordpress image, which recommends a named - # volume for /var/www/html -- this used to work fine using --volumes-from - # because the actual MySQL password was inserted into the generated - # wp-config.php -- but as of Wordpress 5.7.0, wp-config loads data straight - # from the environment, which requires Docker secrets to work, which only work - # in swarm services (not one-off `docker run` commands). Defining a `cli` - # service in compose.yml almost works, but there's no volumes_from: in Compose - # V3, and without it then the `cli` service can't access Wordpress core. - # See https://git.autonomic.zone/coop-cloud/wordpress/issues/21 - warning "Slowly looking up MySQL password..." - silence - abra__service_="app" - DB_PASSWORD="$(sub_app_run cat "/run/secrets/db_password")" - unsilence - - # shellcheck disable=SC2154,SC2086 - docker run -it \ - --volumes-from "$CONTAINER" \ - --network "container:$CONTAINER" \ - -u xfs:xfs \ - -e WORDPRESS_DB_HOST=db \ - -e WORDPRESS_DB_USER=wordpress \ - -e WORDPRESS_DB_PASSWORD="${DB_PASSWORD}" \ - -e WORDPRESS_DB_NAME=wordpress \ - -e WORDPRESS_CONFIG_EXTRA="${WORDPRESS_CONFIG_EXTRA}" \ - wordpress:cli wp ${abra__args_[*]} -} +export NGINX_DEFAULT_CONF_VERSION=v1 abra_backup_app() { - _abra_backup_dir "app:/var/www/html/wp-content" + _abra_backup_dir "app:/var/www/html/" } abra_backup_db() { - _abra_backup_mysql "db" "wordpress" + _abra_backup_mysql "db" "site" } abra_backup() { @@ -72,7 +37,7 @@ abra_restore_db() { DB_ROOT_PASSWORD=$(sub_app_run cat /run/secrets/db_root_password) - zcat "$@" | sub_app_run mysql -u root -p"$DB_ROOT_PASSWORD" wordpress + zcat "$@" | sub_app_run mysql -u root -p"$DB_ROOT_PASSWORD" site success "Restored 'db'" } diff --git a/compose.yml b/compose.yml index 7b25de6..8130dff 100644 --- a/compose.yml +++ b/compose.yml @@ -1,19 +1,18 @@ version: "3.8" services: - app: - image: "wordpress:5.7.2" + backend: + image: adhocore/phpfpm:8.0 volumes: - - "wordpress_content:/var/www/html/wp-content/" + - "site_content:/var/www/html/" networks: - backend - proxy environment: - - WORDPRESS_DB_HOST=db - - WORDPRESS_DB_USER=wordpress - - WORDPRESS_DB_PASSWORD_FILE=/run/secrets/db_password - - WORDPRESS_DB_NAME=wordpress - - WORDPRESS_CONFIG_EXTRA=${WORDPRESS_CONFIG_EXTRA} - - PHP_EXTENSIONS + - DB_HOST=db + - DB_USER=site + - DB_PASSWORD_FILE=/run/secrets/db_password + - DB_NAME=site + - PHP_EXTENSIONS_TO_DISABLE secrets: - db_password configs: @@ -25,28 +24,41 @@ services: entrypoint: /docker-entrypoint.sh depends_on: - db + deploy: + update_config: + failure_action: rollback + order: start-first + labels: + - coop-cloud.${STACK_NAME}.backend.version=8.0-92fac7ac + frontend: + image: nginx:1.19.2 + networks: + - proxy + deploy: + restart_policy: + condition: on-failure + labels: + - "traefik.enable=true" + - "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=80" + - "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})" + - "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure" + - "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}" + # Redirect from EXTRA_DOMAINS to DOMAIN + - "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect" + - "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLForceHost=true" + - "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLHost=${DOMAIN}" + - coop-cloud.${STACK_NAME}.frontend.version=1.19.2-c628b67d + volumes: + - site_content:/var/www/html/ + configs: + - source: nginx_default_conf + target: /etc/nginx/conf.d/default.conf healthcheck: test: ["CMD", "curl", "-f", "http://localhost"] interval: 30s timeout: 10s retries: 10 start_period: 1m - deploy: - update_config: - failure_action: rollback - order: start-first - labels: - - "traefik.enable=true" - - "traefik.docker.network=proxy" - - "traefik.http.routers.${STACK_NAME}.tls=true" - - "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=80" - - "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})" - # 3wc: this rule works for routing, but not for generating certificates - # see https://git.autonomic.zone/coop-cloud/planning/issues/14 - #- "traefik.http.routers.${STACK_NAME}.rule=HostRegexp(`{subdomain:.+}.${DOMAIN}`, `${DOMAIN}`)" - - "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}" - - "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure" - - coop-cloud.${STACK_NAME}.app.version=5.7.2-92fac7ac db: image: "mariadb:10.6" volumes: @@ -55,8 +67,8 @@ services: - backend environment: - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_root_password - - MYSQL_DATABASE=wordpress - - MYSQL_USER=wordpress + - MYSQL_DATABASE=site + - MYSQL_USER=site - MYSQL_PASSWORD_FILE=/run/secrets/db_password secrets: - db_password @@ -69,7 +81,7 @@ networks: external: true volumes: mariadb: - wordpress_content: + site_content: secrets: db_root_password: external: true @@ -82,6 +94,9 @@ configs: name: ${STACK_NAME}_entrypoint_conf_${ENTRYPOINT_CONF_VERSION} file: entrypoint.sh.tmpl template_driver: golang + nginx_default_conf: + name: ${STACK_NAME}_nginx_default_conf_${NGINX_DEFAULT_CONF_VERSION} + file: default.conf php_uploads_conf: name: ${STACK_NAME}_php_uploads_conf_${PHP_UPLOADS_CONF_VERSION} file: uploads.ini diff --git a/default.conf b/default.conf new file mode 100644 index 0000000..3e0a265 --- /dev/null +++ b/default.conf @@ -0,0 +1,51 @@ +# edited copy of upstream default config +server { + listen 80; + listen [::]:80; + server_name localhost; + + #charset koi8-r; + #access_log /var/log/nginx/host.access.log main; + + location / { + root /var/www/html; + index index.html index.htm; + try_files $uri $uri/ $uri.html; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + # error_page 500 502 503 504 /50x.html; + # location = /50x.html { + # root /var/www/html; + # } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + location ~ \.php$ { + try_files $uri = 404; + #root html; + fastcgi_pass backend:9000; + fastcgi_index index.php; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + include fastcgi_params; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + } + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + location ~ /\.ht { + deny all; + } +} \ No newline at end of file diff --git a/entrypoint.sh.tmpl b/entrypoint.sh.tmpl index 01800ba..2aa409a 100644 --- a/entrypoint.sh.tmpl +++ b/entrypoint.sh.tmpl @@ -1,13 +1,9 @@ #!/bin/bash -{{ if (env "PHP_EXTENSIONS") }} -docker-php-ext-install {{ env "PHP_EXTENSIONS" }} +{{ if (env "PHP_EXTENSIONS_TO_DISABLE") }} +docker-php-ext-disable {{ env "PHP_EXTENSIONS_TO_DISABLE" }} {{ end }} if [ -n "$@" ]; then "$@" fi - -# Upstream ENTRYPOINT -# https://github.com/docker-library/wordpress/blob/master/php7.4/apache/Dockerfile#L120 -/usr/local/bin/docker-entrypoint.sh apache2-foreground