diff --git a/.gitignore b/.gitignore index 6bed320..e0db0b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ config notes.txt +remote-temp/ diff --git a/production/Dockerfile b/production/Dockerfile new file mode 100644 index 0000000..13baf63 --- /dev/null +++ b/production/Dockerfile @@ -0,0 +1,49 @@ +FROM alpine:3.11 + +ENV WP_VERSION 5.3 + +RUN set -x \ + && addgroup -g 82 -S www-data \ + && adduser -u 82 -D -S -G www-data www-data + +RUN apk --no-cache add php7 php7-fpm php7-mysqli php7-json php7-openssl php7-curl \ + php7-simplexml php7-ctype php7-mbstring php7-gd php7-redis supervisor curl \ + php7-zlib php7-xml php7-phar php7-intl php7-dom php7-xmlreader php7-opcache less mariadb-client \ + libpng libjpeg-turbo bash \ + && rm -rf /var/www/localhost + +RUN { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + echo 'opcache.fast_shutdown=1'; \ + } > /etc/php7/conf.d/opcache-recommended.ini + +RUN mkdir -p /var/www && chown -R www-data:www-data /var/www + +WORKDIR /var/www +RUN sed -i s/'user = nobody'/'user = www-data'/g /etc/php7/php-fpm.d/www.conf \ + && sed -i s/'group = nobody'/'group = www-data'/g /etc/php7/php-fpm.d/www.conf \ + && sed -i s/'listen = 127.0.0.1:9000'/'listen = 9000'/g /etc/php7/php-fpm.d/www.conf + +VOLUME /var/www/wordpress + +COPY config/php.ini /etc/php7/conf.d/zzz_custom_php.ini +COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY config/cron.conf /etc/crontabs/www-data + +RUN rm -rf /tmp/* \ + && chmod 600 /etc/crontabs/www-data \ + && curl -sfo /usr/local/bin/wp -L https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ + && chmod +x /usr/local/bin/wp + +WORKDIR /var/www/wordpress + +COPY entrypoint.sh /entrypoint.sh +ENTRYPOINT [ "/entrypoint.sh" ] + +EXPOSE 80 + +CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] + diff --git a/production/config/cron.conf b/production/config/cron.conf new file mode 100644 index 0000000..6cb4fd3 --- /dev/null +++ b/production/config/cron.conf @@ -0,0 +1,2 @@ +# Cron configuration file, set to run WordPress cron once every minute +* * * * * php /usr/src/wordpress/wp-cron.php diff --git a/production/config/my.cnf b/production/config/my.cnf new file mode 100644 index 0000000..4066485 --- /dev/null +++ b/production/config/my.cnf @@ -0,0 +1,80 @@ +[mysqld] +max_allowed_packet = 16M + +# Sort buffer is used to perform sorts for some ORDER BY and GROUP BY +# queries. If sorted data does not fit into the sort buffer, a disk +# based merge sort is used instead - See the "Sort_merge_passes" +# status variable. Allocated per thread if sort is needed. +# Comment out for now, the default in MariaDB 10.2 is 2M +#sort_buffer_size = 1M + +# Size of the buffer used for doing full table scans. +# Allocated per thread, if a full scan is needed. +read_buffer_size = 1M + +# When reading rows in sorted order after a sort, the rows are read +# through this buffer to avoid disk seeks. You can improve ORDER BY +# performance a lot, if set this to a high value. +# Allocated per thread, when needed. +read_rnd_buffer_size = 8M + +join_buffer_size = 8M # should be equal to read_rnd_buffer_size? + +# Maximum allowed size for a single HEAP (in memory) table. This option +# is a protection against the accidential creation of a very large HEAP +# table which could otherwise use up all memory resources. +max_heap_table_size = 64M +tmp_table_size = 64M # Should be equal to max_heap_table_size + +## Generally, it is unwise to set the query cache to be larger than 64-128M +## as the costs associated with maintaining the cache outweigh the performance +## gains. +## The query cache is a well known bottleneck that can be seen even when +## concurrency is moderate. The best option is to disable it from day 1 +## by setting query_cache_size = 0 (now the default on MySQL 5.6) +## and to use other ways to speed up read queries: good indexing, adding +## replicas to spread the read load or using an external cache. +query_cache_type = on +query_cache_size = 32M +query_cache_strip_comments = on +query_cache_min_res_unit = 2K +# query_cache_limit = 256K # Default is 1M now + +thread_cache_size = 16 +table_open_cache = 4096 +table_definition_cache = 1024 + +# +# InnoDB +# +# The buffer pool is where data and indexes are cached: having it as large as possible +# will ensure you use memory and not disks for most read operations. +# Typical values are 50..75% of available RAM. +innodb_buffer_pool_size = 768M # 75% of 1GB RAM +innodb_log_file_size = 192M # 25% of innodb_buffer_pool_size +innodb_flush_method = O_DIRECT + +# This setting should be set to 0 (disabled) on SSDs which do not have +# any performance gains with sequential IO. +innodb_flush_neighbors = 0 + +# The default setting of 1 means that InnoDB is fully ACID compliant. +# It is the best value when your primary concern is data safety, for instance on a master. +# However it can have a significant overhead on systems with slow disks because of the +# extra fsyncs that are needed to flush each change to the redo logs. +# Setting it to 2 is a bit less reliable because committed transactions will be +# flushed to the redo logs only once a second, but that can be acceptable on some situations +# for a master and that is definitely a good value for a replica. 0 is even faster +# but you are more likely to lose some data in case of a crash: it is only a good value for a replica. +innodb_flush_log_at_trx_commit = 0 + +# Conquer an InnoDB crash with `InnoDB: A long semaphore wait` error +# See http://stackoverflow.com/questions/24860111/warning-a-long-semaphore-wait +# See http://www.markleith.co.uk/2009/05/13/innodb_stats_on_metadata-innodb_adaptive_hash_index/ +innodb_adaptive_hash_index = off + +# Kill iddle connections after 10min +wait_timeout = 600 + +[mysqldump] +max-allowed-packet = 16M diff --git a/production/config/nginx.conf b/production/config/nginx.conf new file mode 100644 index 0000000..2349ac7 --- /dev/null +++ b/production/config/nginx.conf @@ -0,0 +1,114 @@ +worker_processes 1; +pid /run/nginx.pid; +worker_rlimit_nofile 51200; + + +events { + use epoll; + worker_connections 1024; + multi_accept on; +} + +http { + include mime.types; + default_type application/octet-stream; + server_names_hash_bucket_size 128; + client_header_buffer_size 32k; + large_client_header_buffers 4 32k; + client_max_body_size 1024m; + client_body_buffer_size 10m; + sendfile on; + tcp_nopush on; + keepalive_timeout 120; + server_tokens off; + tcp_nodelay on; + + + log_format main_timed '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for" ' + '$request_time $upstream_response_time $pipe $upstream_cache_status' + '$document_root$fastcgi_script_name > $request'; + + + access_log /dev/stdout main_timed; + error_log /dev/stderr notice; + + gzip on; + gzip_buffers 16 8k; + gzip_comp_level 6; + gzip_http_version 1.1; + gzip_min_length 256; + gzip_proxied any; + gzip_vary on; + gzip_types + text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml + text/javascript application/javascript application/x-javascript + text/x-json application/json application/x-web-app-manifest+json + text/css text/plain text/x-component + font/opentype application/x-font-ttf application/vnd.ms-fontobject + image/x-icon; + gzip_disable "MSIE [1-6]\.(?!.*SV1)"; + + open_file_cache max=50000 inactive=60s; + open_file_cache_valid 120s; + open_file_cache_min_uses 2; + open_file_cache_errors off; + open_log_file_cache max=10000 inactive=30s min_uses=2; + + server { + listen [::]:80 default_server; + listen 80 default_server; + server_name _; + + sendfile off; + + proxy_buffer_size 128k; + proxy_buffers 4 256k; + proxy_busy_buffers_size 256k; + + client_max_body_size 50m; + client_body_buffer_size 128k; + + root /usr/share/nginx/html; + index index.php; + include /etc/nginx/includes/*.conf; + + location ~* ^/(wp-content)/(.*?)\.(zip|gz|tar|bzip2|7z)$ { deny all; } + location ~ /(\.DS_Store|wp-config.php|wp-config-sample.php|readme.html.gz|readme.txt.gz|readme.html|readme.txt|error_log|license.txt|changelog|changelog.txt) { access_log off; log_not_found off; deny all; } + location = /robots.txt { access_log off; log_not_found off; } + location = /favicon.ico { access_log off; log_not_found off; expires 30d; } + location ~ ~$ { access_log off; log_not_found off; deny all; } + + location /wp-content { + root /usr/share/nginx/html/wp-content; + expires 7d; + add_header Cache-Control "public"; + } + + location / { + try_files $uri $uri/ /index.php?$args; + } + + location ~ [^/]\.php(/|$) { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + fastcgi_buffer_size 64k; + fastcgi_buffers 4 64k; + fastcgi_busy_buffers_size 128k; + fastcgi_temp_file_write_size 128k; + fastcgi_intercept_errors on; + + fastcgi_index index.php; + fastcgi_pass wordpress:9000; + + # fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param SCRIPT_FILENAME /var/www/wordpress$fastcgi_script_name; + include fastcgi_params; + } + + } +} diff --git a/production/config/php.ini b/production/config/php.ini new file mode 100644 index 0000000..1516e52 --- /dev/null +++ b/production/config/php.ini @@ -0,0 +1,13 @@ +; Redirect errors to the container stderr +error_log = "/dev/stderr" + +max_input_time = 60 +max_input_vars = 1000 +variables_order = "EGPCS" +expose_php = Off +post_max_size = 50M +upload_max_filesize = 50M +disable_functions = passthru,system,chroot,chgrp,chown,shell_exec,ini_alter,ini_restore,readlink,symlink,stream_socket_server,fsocket + +[Date] +date.timezone = "Europe/London" diff --git a/production/config/supervisord.conf b/production/config/supervisord.conf new file mode 100644 index 0000000..95540a9 --- /dev/null +++ b/production/config/supervisord.conf @@ -0,0 +1,26 @@ +[supervisord] +nodaemon=true +logfile=/var/log/supervisord.log +pidfile=/run/supervisord.pid + +[program:php-fpm] +command=php-fpm7 -F +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +redirect_stderr=true +autorestart=false +startretries=0 + +; [program:nginx] +; command=nginx -g 'daemon off;' +; stdout_logfile=/dev/stdout +; stdout_logfile_maxbytes=0 +; redirect_stderr=true +; autorestart=false +; startretries=0 + +[program:cron] +command=/usr/sbin/crond -f +autostart=true +autorestart=true +redirect_stderr=true diff --git a/production/docker-compose.yml b/production/docker-compose.yml new file mode 100644 index 0000000..bfad53d --- /dev/null +++ b/production/docker-compose.yml @@ -0,0 +1,77 @@ +version: '3.7' + +networks: + web: + external: true + backend: + driver: bridge + +services: + + redis: + image: 'bitnami/redis:5.0' + networks: + - backend + container_name: redis + restart: unless-stopped + environment: + - ALLOW_EMPTY_PASSWORD=yes + ports: + - '127.0.0.1:6379:6379' +# volumes: +# - 'redis-data:/bitnami/redis/data' + labels: + - "traefik.enable=false" + + db: + image: mariadb:10.4 + container_name: mysql + command: --default-authentication-plugin=mysql_native_password + networks: + - backend + restart: unless-stopped +# env_file: +# - .env + volumes: +# - ./wp-db:/var/lib/mysql + - ./config/my.cnf:/etc/mysql/conf.d/zzz_my.cnf + ports: + - "127.0.0.1:3306:3306" + environment: + - MYSQL_ROOT_PASSWORD=db + labels: + - "traefik.enable=false" + + nginx: + image: nginx:1.17-alpine + depends_on: + - wordpress + container_name: nginx-wp-stack + networks: + - backend + - web + volumes: + - ./config/nginx.conf:/etc/nginx/nginx.conf:ro + - ./wordpress:/usr/share/nginx/html:ro + labels: + - "traefik.enable=true" + - "traefik.http.routers.nginx-wp-stack.entrypoints=http" + - "traefik.http.routers.nginx-wp-stack.rule=Host(`wp-stack.wptest.isnet.uk`)" + - "traefik.docker.network=web" + + wordpress: + depends_on: + - db + networks: + - backend + container_name: wordpress +# env_file: +# - .env + build: ./ + ports: + - "127.0.0.1:8081:80" + - "127.0.0.1:9000:9000" + volumes: + - ./wordpress:/var/www/wordpress + labels: + - "traefik.enable=false" diff --git a/production/entrypoint.sh b/production/entrypoint.sh new file mode 100755 index 0000000..91591de --- /dev/null +++ b/production/entrypoint.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +set -e +exec "$@"