diff --git a/Dockerfile b/Dockerfile index fa8c7f5..a10f538 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,11 +38,11 @@ COPY etc /etc # Add Containerpilot and its configuration # Releases at https://github.com/joyent/containerpilot/releases -ENV CONTAINERPILOT_VER 2.7.2 +ENV CONTAINERPILOT_VER 2.7.3 ENV CONTAINERPILOT file:///etc/containerpilot.json RUN set -ex \ - && export CONTAINERPILOT_CHECKSUM=e886899467ced6d7c76027d58c7f7554c2fb2bcc \ + && export CONTAINERPILOT_CHECKSUM=2511fdfed9c6826481a9048e8d34158e1d7728bf \ && curl --retry 7 --fail -Lso /tmp/containerpilot.tar.gz \ "https://github.com/joyent/containerpilot/releases/download/${CONTAINERPILOT_VER}/containerpilot-${CONTAINERPILOT_VER}.tar.gz" \ && echo "${CONTAINERPILOT_CHECKSUM} /tmp/containerpilot.tar.gz" | sha1sum -c \ @@ -63,8 +63,8 @@ RUN set -ex \ # Install Consul template # Releases at https://releases.hashicorp.com/consul-template/ RUN set -ex \ - && export CONSUL_TEMPLATE_VERSION=0.18.2 \ - && export CONSUL_TEMPLATE_CHECKSUM=6fee6ab68108298b5c10e01357ea2a8e4821302df1ff9dd70dd9896b5c37217c \ + && export CONSUL_TEMPLATE_VERSION=0.18.3 \ + && export CONSUL_TEMPLATE_CHECKSUM=caf6018d7489d97d6cc2a1ac5f1cbd574c6db4cd61ed04b22b8db7b4bde64542 \ && curl --retry 7 --fail -Lso /tmp/consul-template.zip "https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION}/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip" \ && echo "${CONSUL_TEMPLATE_CHECKSUM} /tmp/consul-template.zip" | sha256sum -c \ && unzip /tmp/consul-template.zip -d /usr/local/bin \ @@ -84,7 +84,8 @@ COPY /var/www/html /var/www/html RUN chown -R www-data:www-data /var/www/html/* # Install WordPress via wp-cli & move the default themes to our content dir -ENV WORDPRESS_VERSION 4.7.3 +# Releases at https://core.svn.wordpress.org/tags/ and https://wordpress.org/news/category/releases/ +ENV WORDPRESS_VERSION 4.7.5 RUN set -ex \ && wp --allow-root core download --version=${WORDPRESS_VERSION} \ && mv /var/www/html/wordpress/wp-content/themes/* /var/www/html/content/themes/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..36b3b13 --- /dev/null +++ b/Makefile @@ -0,0 +1,76 @@ +# Makefile for shipping and testing the container image. + +MAKEFLAGS += --warn-undefined-variables +.DEFAULT_GOAL := build +.PHONY: * + +# we get these from CI environment if available, otherwise from git +GIT_COMMIT ?= $(shell git rev-parse --short HEAD) +GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) +WORKSPACE ?= $(shell pwd) + +namespace ?= autopilotpattern +tag := branch-$(shell basename $(GIT_BRANCH)) +imageWordpress := $(namespace)/wordpress +imageNginx := $(namespace)/wordpress-nginx + +#dockerLocal := DOCKER_HOST= DOCKER_TLS_VERIFY= DOCKER_CERT_PATH= docker +dockerLocal := docker +#composeLocal := DOCKER_HOST= DOCKER_TLS_VERIFY= DOCKER_CERT_PATH= docker-compose +composeLocal := docker-compose + +## Display this help message +help: + @awk '/^##.*$$/,/[a-zA-Z_-]+:/' $(MAKEFILE_LIST) | awk '!(NR%2){print $$0p}{p=$$0}' | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' | sort + + +# ------------------------------------------------ +# Container builds + +## Builds the application container image locally +build: + $(dockerLocal) build -t=$(imageWordpress):$(tag) . + cd nginx && $(dockerLocal) build -t=$(imageNginx):$(tag) . + +## Push the current application container images to the Docker Hub +push: + $(dockerLocal) push $(imageWordpress):$(tag) + $(dockerLocal) push $(imageNginx):$(tag) + +## Tag the current images as 'latest' +tag: + $(dockerLocal) tag $(imageWordpress):$(tag) $(imageWordpress):latest + $(dockerLocal) tag $(imageNginx):$(tag) $(imageNginx):latest + +## Push latest tag(s) to the Docker Hub +ship: tag + $(dockerLocal) push $(imageWordpress):$(tag) + $(dockerLocal) push $(imageWordpress):latest + $(dockerLocal) push $(imageNginx):$(tag) + $(dockerLocal) push $(imageNginx):latest + + +# ------------------------------------------------ +# Test running + +## Pull the container images from the Docker Hub +pull: + $(dockerLocal) pull $(imageWordpress):$(tag) + $(dockerLocal) pull $(imageNginx):$(tag) + +## Print environment for build debugging +debug: + @echo WORKSPACE=$(WORKSPACE) + @echo GIT_COMMIT=$(GIT_COMMIT) + @echo GIT_BRANCH=$(GIT_BRANCH) + @echo namespace=$(namespace) + @echo tag=$(tag) + @echo imageWordpress=$(imageWordpress) + @echo imageNginx=$(imageNginx) + +# ------------------------------------------------------- +# helper functions for testing if variables are defined +# +check_var = $(foreach 1,$1,$(__check_var)) +__check_var = $(if $(value $1),,\ + $(error Missing $1 $(if $(value 2),$(strip $2)))) diff --git a/docker-compose.yml b/docker-compose.yml index 657e3b4..aab07a4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ # An demo version of WordPress for easy scaling wordpress: - image: autopilotpattern/wordpress:4.7.3-r4.0.0 + image: autopilotpattern/wordpress:latest restart: always env_file: _env environment: @@ -86,7 +86,7 @@ memcached: # Nginx as a load-balancing tier and reverse proxy nginx: - image: autopilotpattern/wordpress-nginx:4.7.3-r4.0.0 + image: autopilotpattern/wordpress-nginx:latest restart: always ports: - 80 @@ -103,7 +103,7 @@ nginx: # Prometheus is an open source performance monitoring tool # it is included here for demo purposes and is not required prometheus: - image: autopilotpattern/prometheus:latest + image: autopilotpattern/prometheus:branch-triton-support restart: always env_file: _env ports: diff --git a/nginx/.dockerignore b/nginx/.dockerignore new file mode 100644 index 0000000..8365fc4 --- /dev/null +++ b/nginx/.dockerignore @@ -0,0 +1,3 @@ +.DS_Store +**/.DS_Store +_env* diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 638d04b..d9756a9 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,5 +1,5 @@ # a minimal Nginx container including containerbuddy and a simple virtulhost config -FROM autopilotpattern/nginx:1-r6.1.0 +FROM autopilotpattern/nginx:1.13-r7.0.1 # Add our configuration files COPY etc /etc diff --git a/nginx/etc/containerpilot.json b/nginx/etc/containerpilot.json index 6b67942..9ac9789 100644 --- a/nginx/etc/containerpilot.json +++ b/nginx/etc/containerpilot.json @@ -1,6 +1,6 @@ { "consul": "{{ if .CONSUL_AGENT }}localhost{{ else }}{{ .CONSUL }}{{ end }}:8500", - "preStart": "/usr/local/bin/reload.sh preStart", + "preStart": "generate-config", "logging": {"level": "DEBUG"}, "services": [ { @@ -32,7 +32,7 @@ { "name": "wordpress", "poll": 7, - "onChange": "/usr/local/bin/reload.sh" + "onChange": "reload" } ], "coprocesses": [{{ if .CONSUL_AGENT }} @@ -62,14 +62,14 @@ "help": "Number of accepted connnections that were not handled", "type": "gauge", "poll": 5, - "check": ["/usr/local/bin/sensor.sh", "unhandled"] + "check": ["sensor", "unhandled"] }, { "name": "nginx_connections_load", "help": "Ratio of active connections (less waiting) to the maximum worker connections", "type": "gauge", "poll": 5, - "check": ["/usr/local/bin/sensor.sh", "connections_load"] + "check": ["sensor", "connections_load"] } ] }, diff --git a/nginx/etc/nginx/nginx.conf.ctmpl b/nginx/etc/nginx/nginx.conf.ctmpl deleted file mode 100644 index 0d98620..0000000 --- a/nginx/etc/nginx/nginx.conf.ctmpl +++ /dev/null @@ -1,107 +0,0 @@ -user nginx; -worker_processes 1; - -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; -} - - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - map $status $isError { - ~^2 0; - default 1; - } - - 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; - - # Environment variables that are provided to us at the time our Nginx - # config is generated from this template - {{ $acme_domain := env "ACME_DOMAIN" }} # Domain that we're requesting certs for if set - {{ $ssl_ready := env "SSL_READY" }} # true if certs have been written to disk - - {{ if service "wordpress" }} - upstream wordpress { - # write the address:port pairs for each healthy wordpress node - {{range service "wordpress"}} - server {{.Address}}:{{.Port}}; - {{end}} - least_conn; - }{{ end }} - - # If we're listening on https, define an http listener that redirects everything to https - {{ if eq $ssl_ready "true" }} - server { - server_name _; - listen 80; - - # Respond to health requests defined in containerpilot.json - location /nginx-health { - stub_status; - allow 127.0.0.1; - deny all; - # Don't log these requests unless they fail - access_log /var/log/nginx/access.log main if=$isError; - } - - location / { - return 301 https://$host$request_uri; - } - } - {{ end }} - - server { - server_name _; - # Listen on port 80 unless we have certificates installed, then listen on 443 - listen {{ if ne $ssl_ready "true" }}80{{ else }}443 ssl{{ end }}; - {{ if eq $ssl_ready "true" }} - ssl_certificate /var/www/ssl/fullchain.pem; - ssl_certificate_key /var/www/ssl/privkey.pem; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; - ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; - ssl_session_timeout 1d; - ssl_session_cache shared:SSL:50m; - ssl_stapling on; - ssl_stapling_verify on; - add_header Strict-Transport-Security max-age=15768000; - {{ end }} - - {{ if service "wordpress" }} - rewrite ^/wp-admin/?(.*) /wordpress/wp-admin/$1; - - location ^~ / { - proxy_pass http://wordpress; - proxy_set_header Host $http_host; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_redirect off; - }{{end}} - - # Respond to health requests defined in containerpilot.json - location /nginx-health { - stub_status; - allow 127.0.0.1; - deny all; - # Don't log these requests unless they fail - access_log /var/log/nginx/access.log main if=$isError; - } - - # Respond to ACME certificate request challenges - location /.well-known/acme-challenge { - alias /var/www/acme/challenge; - } - } -} diff --git a/nginx/etc/nginx/templates/conf.d/site.conf b/nginx/etc/nginx/templates/conf.d/site.conf new file mode 100644 index 0000000..f4b14fa --- /dev/null +++ b/nginx/etc/nginx/templates/conf.d/site.conf @@ -0,0 +1,45 @@ +{{ $acme_domain := env "ACME_DOMAIN" }} +{{ $ssl_ready := env "SSL_READY" }} + +# If we're listening on https, define an http listener that redirects everything to https +{{ if eq $ssl_ready "true" }} +server { + server_name _; + listen 80; + + include /etc/nginx/health.conf; + + location / { + return 301 https://$host$request_uri; + } +} +{{ end }} + +# The main server block +server { + server_name _; + # Listen on port 80 unless we have certificates installed, then listen on 443 + listen {{ if ne $ssl_ready "true" }}80{{ else }}443 ssl{{ end }}; + + include /etc/nginx/health.conf; + + location /.well-known/acme-challenge { + alias /var/www/acme/challenge; + } + + {{ if service "wordpress" }} + rewrite ^/wp-admin/?(.*) /wordpress/wp-admin/$1; + + location ^~ / { + proxy_pass http://wordpress; + proxy_redirect off; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + }{{ else }} + # return 503 errors if we don't have our expected back-end + location / { + return 503; + }{{ end }} +} diff --git a/nginx/etc/nginx/templates/nginx.conf b/nginx/etc/nginx/templates/nginx.conf new file mode 100644 index 0000000..95bd7a5 --- /dev/null +++ b/nginx/etc/nginx/templates/nginx.conf @@ -0,0 +1,48 @@ +# This is an example Nginx configuration template file. +# Adjust the values below as required for your application. + +user nginx; +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + map $status $isNot2xx { + ~^2 0; + default 1; + } + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for" ' + '$upstream_addr'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + keepalive_timeout 65; + + {{ $ssl_ready := env "SSL_READY" }} + + {{ if eq $ssl_ready "true" }} + include ssl.conf; + {{ end }} + + {{ if service "wordpress" }} + upstream wordpress { + # write the address:port pairs for each healthy wordpress node + {{range service "wordpress"}} + server {{.Address}}:{{.Port}}; + {{end}} + }{{ end }} + + include conf.d/*.conf; +}