Repository: autopilotpattern/wordpress
Branch: master
Commit: 164a3d32a721
Files: 34
Total size: 82.2 KB
Directory structure:
gitextract_1bdlvoha/
├── .dockerignore
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── bin/
│ ├── health.sh
│ ├── onchange-db.sh
│ ├── onchange-memcached.sh
│ ├── onchange-nfs.sh
│ ├── onchange-wp-config.sh
│ ├── prestart.sh
│ └── sensor.sh
├── docker-compose.yml
├── etc/
│ └── containerpilot.json
├── local-compose.yml
├── nginx/
│ ├── .dockerignore
│ ├── Dockerfile
│ └── etc/
│ ├── containerpilot.json
│ └── nginx/
│ └── templates/
│ ├── conf.d/
│ │ └── site.conf
│ └── nginx.conf
├── setup.sh
└── var/
└── www/
└── html/
├── .htaccess
├── content/
│ ├── mu-plugins/
│ │ ├── index.php
│ │ └── register-theme-directory.php
│ ├── plugins/
│ │ └── index.php
│ ├── themes/
│ │ └── index.php
│ └── uploads/
│ └── .empty
├── db-config.php.ctmpl
├── inactive-plugins/
│ └── no-uploads.php
├── index.php
├── memcached-config.php.ctmpl
├── wp-cli.yml
└── wp-config.php.ctmpl
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
.DS_Store
**/.DS_Store
_env*
================================================
FILE: .gitignore
================================================
# credentials
_env*
manta*
# macos frustration
.DS_Store
================================================
FILE: Dockerfile
================================================
FROM php:5.6-apache
RUN a2enmod rewrite
# Install the PHP extensions we need, and other packages
RUN set -ex \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
curl \
jq \
less \
libjpeg-dev \
libmemcached-dev \
libpng12-dev \
nfs-common \
unzip \
&& rm -rf /var/lib/apt/lists/* \
# Memcached 2.2.0 is the latest for PHP < 7
# see https://pecl.php.net/package/memcached
&& pecl install memcached-2.2.0 \
&& docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \
&& docker-php-ext-install gd mysqli opcache \
&& docker-php-ext-enable memcached \
# Set recommended PHP.ini settings
# See https://secure.php.net/manual/en/opcache.installation.php
&& { \
echo 'opcache.memory_consumption=128'; \
echo 'opcache.interned_strings_buffer=8'; \
echo 'opcache.max_accelerated_files=4000'; \
echo 'opcache.revalidate_freq=60'; \
echo 'opcache.fast_shutdown=1'; \
echo 'opcache.enable_cli=1'; \
} > /usr/local/etc/php/conf.d/opcache-recommended.ini
# The our helper/glue scripts and configuration for this specific app
COPY bin /usr/local/bin
COPY etc /etc
# Add Containerpilot and its configuration
# Releases at https://github.com/joyent/containerpilot/releases
ENV CONTAINERPILOT_VER 2.7.3
ENV CONTAINERPILOT file:///etc/containerpilot.json
RUN set -ex \
&& 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 \
&& tar zxf /tmp/containerpilot.tar.gz -C /usr/local/bin \
&& rm /tmp/containerpilot.tar.gz
# Install Consul
# Releases at https://releases.hashicorp.com/consul
RUN set -ex \
&& export CONSUL_VERSION=0.7.5 \
&& export CONSUL_CHECKSUM=40ce7175535551882ecdff21fdd276cef6eaab96be8a8260e0599fadb6f1f5b8 \
&& curl --retry 7 --fail -vo /tmp/consul.zip "https://releases.hashicorp.com/consul/${CONSUL_VERSION}/consul_${CONSUL_VERSION}_linux_amd64.zip" \
&& echo "${CONSUL_CHECKSUM} /tmp/consul.zip" | sha256sum -c \
&& unzip /tmp/consul -d /usr/local/bin \
&& rm /tmp/consul.zip \
&& mkdir /config
# Install Consul template
# Releases at https://releases.hashicorp.com/consul-template/
RUN set -ex \
&& 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 \
&& rm /tmp/consul-template.zip
# Install wp-cli, http://wp-cli.org
ENV WP_CLI_CONFIG_PATH /var/www/html/wp-cli.yml
RUN set -ex \
&& curl --retry 7 --fail -Ls -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \
&& chmod +x wp-cli.phar \
&& mv wp-cli.phar /usr/local/bin/wp \
&& wp --info --allow-root
# Copy the WordPress skeleton from this repo into the container
# This includes any themes and/or plugins we've added to the content/themes and content/plugins directories.
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
# 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/
# Install HyperDB, https://wordpress.org/plugins/hyperdb
# Releases at https://wordpress.org/plugins/hyperdb/developers/ , though no SHA1 fingerprints are published
RUN set -ex \
&& export HYPERDB_VERSION=1.1 \
&& curl --retry 7 --fail -Ls -o /var/www/html/hyperdb.zip https://downloads.wordpress.org/plugin/hyperdb.${HYPERDB_VERSION}.zip \
&& unzip hyperdb.zip \
&& chown -R www-data:www-data /var/www/html/hyperdb \
&& mv hyperdb/db.php /var/www/html/content/. \
&& rm -rf /var/www/html/hyperdb.zip /var/www/html/hyperdb \
&& touch /var/www/html/content/db-config.php
# Install ztollman's object-cache.php or object caching to memcached
RUN set -ex \
&& curl --retry 7 --fail -Ls -o /var/www/html/content/object-cache.php https://raw.githubusercontent.com/tollmanz/wordpress-pecl-memcached-object-cache/master/object-cache.php
# The volume is defined after we install everything
VOLUME /var/www/html
CMD ["/usr/local/bin/containerpilot", \
"apache2-foreground"]
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
================================================
FILE: Makefile
================================================
# 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))))
================================================
FILE: README.md
================================================
# Autopilot Pattern WordPress
*A robust and highly-scalable implementation of WordPress in Docker using the Autopilot Pattern*
[](https://registry.hub.docker.com/u/autopilotpattern/wordpress/)
[](https://registry.hub.docker.com/u/autopilotpattern/wordpress/)
---
### Containerized and easily scalable
This project uses the Autopilot Pattern to automate operations, including discovery and configuration, for easy scaling to any size. All component containers use [ContainerPilot](https://www.joyent.com/containerpilot) and [Consul](https://consul.io/) to configure themselves. This also allows each service to be scaled independently to handle incoming traffic and as more services are added, the containers that consume these services will reconfigure themselves accordingly.
### Project architecture
A running cluster includes the following components:
- [ContainerPilot](https://www.joyent.com/containerpilot): included in our MySQL containers to orchestrate bootstrap behavior and coordinate replication using keys and checks stored in Consul in the `preStart`, `health`, and `onChange` handlers.
- [MySQL](https://github.com/autopilotpattern/mysql/): we're using the [Autopilot Pattern implementation of MySQL](https://www.joyent.com/blog/dbaas-simplicity-no-lock-in) for automatic backups and self-clustering so that we can deploy and scale easily
- [HyperDB](https://wordpress.org/plugins/hyperdb/): an "advanced database class that replaces a few of the WordPress built-in database functions" to support the MySQL cluster that's necessary for scaling WordPress; everything is automatically configured so running a scalable WordPress site is no more complex than running without the scaling features
- [Memcached](https://github.com/autpilotpattern/memcached/): improves performance by keeping frequently accessed data in memory so WordPress doesn't have to query the database for every request; the images include [tollmanz's Memcached plugin](https://github.com/tollmanz/wordpress-pecl-memcached-object-cache) pre-installed, and ContainerPilot automatically configures it as we scale
- [Nginx](https://github.com/autopilotpattern/nginx): the front-end load balancer for the WordPress environment; passes traffic from users to the WordPress containers on the back-end
- [NFS](https://github.com/autopilotpattern/nfsserver/): stores user uploaded files so these files can be shared between many WordPress containers
- [Consul](https://www.consul.io/): used to coordinate replication and failover
- [Manta](https://www.joyent.com/object-storage): the Joyent object store, for securely and durably storing our MySQL snapshots
- [Prometheus](https://github.com/autopilotpattern/prometheus): an optional, [open source monitoring tool](https://prometheus.io) that tracks the performance of each component and demonstrates [ContainerPilot telemetry](https://www.joyent.com/blog/containerpilot-telemetry)
- [WP-CLI](http://wp-cli.org/): to make managing WordPress easier
### How do I use this thing?
Pick the answer that fits:
1. For the hello world experience: follow the directions below for configuration, then `docker-compose up -d` and you're done.
1. For building your own WordPress in Docker: clone this repository and place the WordPress theme you want to use into the `var/www/html/content/themes` directory. Develop locally using the `local-compose.yml` file, then build your Docker image and run those in the cloud with your own `docker-compose.yml` file that specifies your custom image.
The instructions below will get you set up to run containers on [Triton](https://www.joyent.com/), or anywhere that supports the Autopilot Pattern.
### Getting started on Triton
1. [Get a Joyent account](https://my.joyent.com/landing/signup/) and [add your SSH key](https://docs.joyent.com/public-cloud/getting-started).
1. Install the [Docker Toolbox](http://docker.com/toolbox) (including `docker` and `docker-compose`) on your laptop or other environment, as well as the [Joyent Triton CLI](https://www.joyent.com/blog/introducing-the-triton-command-line-tool)
1. [Configure Docker and Docker Compose for use with Joyent](https://docs.joyent.com/public-cloud/api-access/docker):
```bash
curl -O https://raw.githubusercontent.com/joyent/sdc-docker/master/tools/sdc-docker-setup.sh && chmod +x sdc-docker-setup.sh
./sdc-docker-setup.sh -k us-east-1.api.joyent.com <ACCOUNT> ~/.ssh/<PRIVATE_KEY_FILE>
```
### Configure your environment
Check that everything is configured correctly by running `./setup.sh`. You'll need an SSH key that has access to Manta, the object store where the MySQL backups are stored. Pass the path of that SSH key as `./setup.sh ~/path/to/MANTA_SSH_KEY`. The script will create an `_env` file that names the variables that you will need to run your WordPress environment.
#### Manta settings
The script will set defaults for almost every config variable, but the Manta config is required and must be set manually. The two most important variables there are:
```
MANTA_BUCKET=/<username>/stor/<bucketname> # an existing Manta bucket
MANTA_USER=<username> # a user with access to that bucket
```
The MySQL container will take a backup during its `preStart` handler and periodically while running. Configure these Manta settings to specify how and where this backup is stored. Here you need to specify the `MANTA_USER`, and also the `MANTA_BUCKET` where the backups will be stored.
#### WordPress configuration
The setup script will set working defaults for the entire WordPress configuration. The defaults will work for a quick "hello world" experience, but you'll probably want to set your own values for many fields.
```
# Environment variables for for WordPress site
WORDPRESS_URL=http://my-site.example.org/
WORDPRESS_SITE_TITLE=My Blog
WORDPRESS_ADMIN_EMAIL=user@example.net
WORDPRESS_ADMIN_USER=admin
WORDPRESS_ADMIN_PASSWORD=<random string>
WORDPRESS_ACTIVE_THEME=twentysixteen
WORDPRESS_CACHE_KEY_SALT=<random string>
#WORDPRESS_TEST_DATA=true # uncomment to import a collection of test content on start
```
This block is the typical information you must provide when installing WordPress. The URL of the site, the site title and admin user information are all straightforward. `WORDPRESS_ACTIVE_THEME` is the theme that will be activated automatically when the container starts. This will typically be theme that you are developing in this repo, or one of the default themes. `WORDPRESS_CACHE_KEY_SALT` should be set to a unique string, the object caching in WordPress will use this salt to determine the cache keys for information it sets on the Memcached container.
If you are not bringing your own theme in this repo, you can choose from these default themes for the `WORDPRESS_ACTIVE_THEME` variable:
- `twentyfifteen`
- `twentyfourteen`
- `twentysixteen`
The script will set a `WORDPRESS_URL` value for Triton users using [Container Name Service](https://www.joyent.com/blog/introducing-triton-container-name-service) that will make it easy to test the containers without setting any DNS information. You can [`CNAME` your site DNS](https://www.joyent.com/blog/introducing-triton-container-name-service#example-global-dns) to that to make it easy to scale and replace the Nginx containers at the front of your site without ever needing to update the DNS configuration.
Setting `WORDPRESS_TEST_DATA` will download the [manovotny/wptest](https://github.com/manovotny/wptest) content library when the WordPress container starts.
#### MySQL settings
The setup script will set default values for the MySQL configuration, including randomly generated passwords.
```
# Environment variables for MySQL service
# WordPress database/WPDB information
MYSQL_USER=wpdbuser
MYSQL_PASSWORD=<random string>
MYSQL_DATABASE=wp
# MySQL replication user, should be different from above
MYSQL_REPL_USER=repluser
MYSQL_REPL_PASSWORD=<random string>
```
These values will be automatically set in the `wp-config.php`. The last two options are used by the Autopilot Pattern MySQL container to set up its replication when scaled up to more than a single container. You can keep `repluser`, but set a unique password for your environment.
#### WordPress unique salts
As with most of the other configuration blocks, the setup script will set reasonable defaults for these values.
```
# Wordpress security salts
# These must be unique for your install to ensure the security of the site
WORDPRESS_AUTH_KEY=<random string>
WORDPRESS_SECURE_AUTH_KEY=<random string>
WORDPRESS_LOGGED_IN_KEY=<random string>
WORDPRESS_NONCE_KEY=<random string>
WORDPRESS_AUTH_SALT=<random string>
WORDPRESS_SECURE_AUTH_SALT=<random string>
WORDPRESS_LOGGED_IN_SALT=<random string>
WORDPRESS_NONCE_SALT=<random string>
```
These variables are how WordPress secures your logins and other secret info. These should be unique for your site. You can set your own values, or use [this WordPress tool](https://api.wordpress.org/secret-key/1.1/salt/) to generate a new set of random values.
#### Consul
Finally we need to configure an environment variable with the location of our Consul service. The setup script will pre-set this for Triton users.
```
CONSUL=<IP or DNS to Consul>
```
For local development, we use Docker links and simply set this to `CONSUL=consul`, but on Triton we use [Container Name Service](https://www.joyent.com/blog/introducing-triton-container-name-service) so that we can have a raft of Consul instances operating as a highly available service ([see example](https://www.joyent.com/blog/introducing-triton-container-name-service#example-consul-bootstrapping)).
### A note on Nginx
This project also builds it's own Nginx container that is based on the [AutoPilot Pattern Nginx](https://github.com/autopilotpattern/nginx). We build a custom Nginx container to more easily inject our custom configurations. The configs located in the `/nginx` directory should work well for most use cases of this project, but they can be customized and baked into the Nginx image if the need arises.
### Start the containers!
After configuring everything, we are now ready to start the containers. To do that simply execute `docker-compose up -d` to spin everything up on Triton. Open your browser to the `WORDPRESS_URL` and enjoy your new site!
For local development, use `docker-compose -f local-compose.yml up -d`.
### Going big
To scale, use `docker-compose scale...`. For example, the following will set the scale of the WordPress, Memcached, Nginx, and MySQL services to three instances each:
```bash
docker-compose scale wordpress=3 memcached=3 nginx=3 mysql=3
```
If there are few instances running for any of those services, more will be added to meet the specified count. As you scale, the application will automatically reconfigure itself so that everything is connected. All the Nginx instances will connect to all the WordPress instances, and those will connect to all the Memcached and MySQL instances. If an instance should unexpectedly crash, the other instances will automatically reconfigure to re-route requests around the failed instance.
To scale back down, simply run `docker-compose scale...` and specify a smaller number of instances.
### Compatibility
This project has been fully tested and documented to run in Docker in local development environments and on [Joyent Triton](https://www.joyent.com), however it has been demonstrated on, or is believe compatible with container environments including:
- [Mantl](http://mantl.io)
- [DC/OS](https://dcos.io)
- [Docker Swarm](https://www.docker.com/products/docker-swarm)
- [Kubernetes](http://kubernetes.io)
- Others
### Contributing
- Please [report bugs in Github](https://github.com/autopilotpattern/wordpress/issues) (and check the bug list for known bugs)
- It's open source, [pull requests welcome](https://github.com/autopilotpattern/wordpress/pulls)!
### Sponsors
Initial development of this project was sponsored by [Joyent](https://www.joyent.com) and [10up](http://10up.com).
================================================
FILE: bin/health.sh
================================================
#!/bin/bash
/usr/local/bin/wp --allow-root core is-installed \
&& /usr/local/bin/wp --allow-root option get siteurl \
&& /usr/bin/curl --fail -s -o /dev/null http://localhost/
================================================
FILE: bin/onchange-db.sh
================================================
#!/bin/bash
/usr/local/bin/onchange-wp-config.sh
consul-template \
-once \
-dedup \
-consul-addr ${CONSUL}:8500 \
-template "/var/www/html/db-config.php.ctmpl:/var/www/html/content/db-config.php"
================================================
FILE: bin/onchange-memcached.sh
================================================
#!/bin/bash
consul-template \
-once \
-dedup \
-consul-addr ${CONSUL}:8500 \
-template "/var/www/html/memcached-config.php.ctmpl:/var/www/html/memcached-config.php"
================================================
FILE: bin/onchange-nfs.sh
================================================
#!/bin/bash
if [[ `curl -s ${CONSUL}:8500/v1/health/state/passing | grep nfs` ]]
then
echo "nfs is healthy, mounting uploads directory...."
NFS=$(curl -s http://${CONSUL}:8500/v1/catalog/service/nfs?passing | jq -r '.[0] | .ServiceAddress')
mount -t nfs -v -o nolock,vers=3 ${NFS}:/exports /var/www/html/content/uploads
echo "removing no-uploads.php mu-plugin"
rm /var/www/html/content/mu-plugins/no-uploads.php
# check 'wp core is-installed' here to prevent errors in the log on first run
# before WP gets installed into the database
if $(wp --allow-root core is-installed)
then
echo "adding 'upload_files' capability back to default roles"
# only these roles have 'upload_files' cap by default
for role in administrator editor author
do
if [ "$role" != 'role' ]
then
wp --allow-root cap add ${role} upload_files
fi
done
fi
else
echo "nfs is not healthly, umounting uploads directory..."
umount -f -l /var/www/html/content/uploads
echo "creating mu-plugin for NFS error in wp-admin"
cp /var/www/html/inactive-plugins/no-uploads.php /var/www/html/content/mu-plugins/
echo "removing 'upload_files' capability from all roles..."
for role in $(wp --allow-root role list --fields=role --format=csv)
do
if [ "$role" != 'role' ]
then
wp --allow-root cap remove ${role} upload_files
fi
done
fi
================================================
FILE: bin/onchange-wp-config.sh
================================================
#!/bin/bash
# The WordPress config file
consul-template \
-once \
-dedup \
-consul-addr ${CONSUL}:8500 \
-template "/var/www/html/wp-config.php.ctmpl:/var/www/html/wp-config.php"
================================================
FILE: bin/prestart.sh
================================================
#!/bin/bash
# Refresh all config files in order
#
# This script is typically called once at the container start, but
# it can be called manually if WP config details must be changed
# The database and memcached config files are separate to avoid collisions
# if their backends' onChange handlers are triggered simultaneously
echo "******running preStart script*********"
until [[ `curl -s ${CONSUL}:8500/v1/health/state/passing | grep mysql-primary` ]]
do
echo "mysql-primary not healthly...."
sleep 5
done
until [[ `curl -s ${CONSUL}:8500/v1/health/state/passing | grep nfs` ]]
do
echo "no healthly nfs server avaliable yet...."
sleep 5
done
echo "mysql-primary and nfs are now healthly, moving on..."
/usr/local/bin/onchange-db.sh
/usr/local/bin/onchange-memcached.sh
/usr/local/bin/onchange-nfs.sh
# The WordPress config file
/usr/local/bin/onchange-wp-config.sh
if $(wp --allow-root core is-installed)
then
echo "WP is installed"
# run update-db to ensure the database schema is up to date in case the WP version was upgraded in the Docker image
wp --allow-root core update-db
else
echo "WP is NOT installed"
echo "installing now...."
# TODO: check WORDPRESS_URL to ensure it has http:// or https:// at the beginning, if not put it in
wp --allow-root core install --url=$WORDPRESS_URL --title="$WORDPRESS_SITE_TITLE" --admin_user=$WORDPRESS_ADMIN_USER --admin_password=$WORDPRESS_ADMIN_PASSWORD --admin_email=$WORDPRESS_ADMIN_EMAIL --skip-email
# update siteurl to work with our directory structure
# wp option update for siteurl REQUIRES http://, need to determine will we handle that here
# or ask for it in the _env file or test for it above
wp --allow-root option update siteurl `wp --allow-root option get siteurl`/wordpress
# set a nice default permalink structure
wp --allow-root option update permalink_structure '/%year%/%monthnum%/%postname%/'
# set theme
if [ -n "$WORDPRESS_ACTIVE_THEME" ]
then
wp --allow-root theme activate $WORDPRESS_ACTIVE_THEME
fi
if [ -n "$WORDPRESS_TEST_DATA" ]
then
echo "installing WP test content"
wp --allow-root plugin install wordpress-importer --activate
curl -OL https://raw.githubusercontent.com/manovotny/wptest/master/wptest.xml
wp --allow-root import wptest.xml --authors=create
wp --allow-root plugin uninstall wordpress-importer --deactivate
chown -R www-data:www-data /var/www/html/content/uploads/*
rm wptest.xml
fi
fi
================================================
FILE: bin/sensor.sh
================================================
#!/bin/bash
set -e
help() {
echo 'Uses cli tools free and top to determine current CPU and memory usage'
echo 'for the telemetry service.'
}
# memory usage in percent
memory() {
# awk oneliner to get memory usage
# free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'
# output:
# Memory Usage: 15804/15959MB (99.03%)
(>&2 echo "memory check fired")
local memory=$(free -m | awk 'NR==2{printf "%.2f", $3*100/$2 }')
echo ${memory}
}
# cpu load
cpu() {
# oneliner to display cpu load
# top -bn1 | grep load | awk '{printf "CPU Load: %.2f\n", $(NF-2)}'
(>&2 echo "cpu check fired")
local cpuload=$(top -bn1 | grep load | awk '{printf "%.2f", $(NF-2)}')
echo ${cpuload}
}
cmd=$1
if [ ! -z "$cmd" ]; then
shift 1
$cmd "$@"
exit
fi
help
================================================
FILE: docker-compose.yml
================================================
# An demo version of WordPress for easy scaling
wordpress:
image: autopilotpattern/wordpress:latest
restart: always
env_file: _env
environment:
- CONSUL_AGENT=1
ports:
- 80
- 9090
labels:
# Setting the CNS service name
- triton.cns.services=wp-wordpress
# Soft anti-affinity to avoid other WordPress instances
- com.docker.swarm.affinities=["container!=~*wordpress*"]
# Set the package
- com.joyent.package=g4-highcpu-1G
# Consul is the service catalog that helps discovery between the components
# Change "-bootstrap" to "-bootstrap-expect 3", then scale to 3 or more to
# turn this into an HA Consul raft.
consul:
image: autopilotpattern/consul:latest
command: >
/usr/local/bin/containerpilot
/bin/consul agent -server
-bootstrap
-config-dir=/etc/consul
-ui-dir /ui
restart: always
env_file: _env
ports:
- 8500
dns:
- 127.0.0.1
labels:
- triton.cns.services=wp-consul
# Soft anti-affinity to avoid all other containers
- com.docker.swarm.affinities=["container!=~*"]
- com.joyent.package=g4-highcpu-128M
# NFS is used to store user uploads
nfs:
image: autopilotpattern/nfsserver:latest
restart: always
env_file: _env
environment:
- CONSUL_AGENT=1
expose:
- 111
- 1892
- 2049
labels:
- triton.cns.services=wp-nfs
- com.docker.swarm.affinities=["container!=~*"]
- com.joyent.package=g4-highcpu-256M
# The MySQL database will automatically cluster and scale
# see https://www.joyent.com/blog/dbaas-simplicity-no-lock-in
mysql:
image: autopilotpattern/mysql:latest
restart: always
env_file: _env
environment:
- CONSUL_AGENT=1
expose:
- 3306
labels:
- triton.cns.services=wp-mysql
- com.docker.swarm.affinities=["container!=~*mysql*"]
- com.joyent.package=g4-highcpu-4G
# Memcached is a high performance object cache
memcached:
image: autopilotpattern/memcached:latest
restart: always
env_file: _env
environment:
- CONSUL_AGENT=1
ports:
- 11211
labels:
- triton.cns.services=wp-memcached
- com.docker.swarm.affinities=["container!=~*memcached*"]
- com.joyent.package=g4-highcpu-512M
# Nginx as a load-balancing tier and reverse proxy
nginx:
image: autopilotpattern/wordpress-nginx:latest
restart: always
ports:
- 80
- 443
- 9090
env_file: _env
environment:
- CONSUL_AGENT=1
labels:
- triton.cns.services=wp-nginx,nginx
- com.docker.swarm.affinities=["container!=~*nginx*"]
- com.joyent.package=g4-highcpu-512M
# Prometheus is an open source performance monitoring tool
# it is included here for demo purposes and is not required
prometheus:
image: autopilotpattern/prometheus:branch-triton-support
restart: always
env_file: _env
ports:
- 9090
labels:
- triton.cns.services=wp-prometheus
- com.docker.swarm.affinities=["container!=~*prometheus*"]
- com.joyent.package=g4-highcpu-1G
================================================
FILE: etc/containerpilot.json
================================================
{
"consul": "{{ if .CONSUL_AGENT }}localhost{{ else }}{{ .CONSUL }}{{ end }}:8500",
"preStart": "/usr/local/bin/prestart.sh",
"services": [
{
"name": "wordpress",
"port": 80,
"health": "/usr/local/bin/health.sh",
"poll": 10,
"ttl": 25
}
],
"backends": [
{
"name": "mysql",
"poll": 7,
"onChange": "/usr/local/bin/onchange-db.sh"
},
{
"name": "mysql-primary",
"poll": 7,
"onChange": "/usr/local/bin/onchange-db.sh"
},
{
"name": "nfs",
"poll": 11,
"onChange": "/usr/local/bin/onchange-nfs.sh"
},
{
"name": "memcached",
"poll": 5,
"onChange": "/usr/local/bin/onchange-memcached.sh"
}
],
"coprocesses": [{{ if .CONSUL_AGENT }}
{
"command": ["/usr/local/bin/consul", "agent",
"-data-dir=/data",
"-config-dir=/config",
"-rejoin",
"-retry-join", "{{ .CONSUL }}",
"-retry-max", "10",
"-retry-interval", "10s"],
"restarts": "unlimited"
}{{ end }}],
"telemetry": {
"port": 9090,
"sensors": [
{
"name": "wp_memory_percent",
"help": "percentage of memory used",
"type": "gauge",
"poll": 5,
"check": ["/usr/local/bin/sensor.sh", "memory"]
},
{
"name": "wp_cpu_load",
"help": "cpu load",
"type": "gauge",
"poll": 5,
"check": ["/usr/local/bin/sensor.sh", "cpu"]
}
]
}
}
================================================
FILE: local-compose.yml
================================================
# See docker-compose.yml for further descriptions and configuration details of each service
wordpress:
extends:
file: docker-compose.yml
service: wordpress
build: .
privileged: true
restart: "no"
environment:
- CONSUL=consul
links:
- consul:consul
ports:
- "80"
consul:
extends:
file: docker-compose.yml
service: consul
restart: "no"
ports:
- 8500:8500
nfs:
extends:
file: docker-compose.yml
service: nfs
privileged: true
restart: "no"
environment:
- CONSUL=consul
links:
- consul:consul
ports:
- 111
- 1892:1892
- 2049
mysql:
extends:
file: docker-compose.yml
service: mysql
restart: "no"
environment:
- CONSUL=consul
links:
- consul:consul
ports:
- 3306
memcached:
extends:
file: docker-compose.yml
service: memcached
restart: "no"
environment:
- CONSUL=consul
links:
- consul:consul
nginx:
extends:
file: docker-compose.yml
service: nginx
build: nginx/
restart: "no"
environment:
- CONSUL=consul
links:
- consul:consul
ports:
- 80:80
prometheus:
extends:
file: docker-compose.yml
service: prometheus
restart: "no"
environment:
- CONSUL=consul
links:
- consul:consul
ports:
- 9090:9090
================================================
FILE: nginx/.dockerignore
================================================
.DS_Store
**/.DS_Store
_env*
================================================
FILE: nginx/Dockerfile
================================================
# a minimal Nginx container including containerbuddy and a simple virtulhost config
FROM autopilotpattern/nginx:1.13-r7.0.1
# Add our configuration files
COPY etc /etc
================================================
FILE: nginx/etc/containerpilot.json
================================================
{
"consul": "{{ if .CONSUL_AGENT }}localhost{{ else }}{{ .CONSUL }}{{ end }}:8500",
"preStart": "generate-config",
"logging": {"level": "DEBUG"},
"services": [
{
"name": "nginx",
"port": 80,
"health": "/usr/bin/curl --fail --silent --show-error --output /dev/null http://localhost/nginx-health",
"poll": 10,
"ttl": 25,
"interfaces": ["eth0"]
},
{
"name": "nginx-public",
"port": 80,
"health": "/usr/bin/curl --fail --silent --show-error --output /dev/null http://localhost/nginx-health",
"poll": 10,
"ttl": 25,
"interfaces": ["eth1", "eth0"]
}{{ if .ACME_DOMAIN }},
{
"name": "nginx-public-ssl",
"port": 443,
"health": "/usr/local/bin/acme init && /usr/bin/curl --insecure --fail --silent --show-error --output /dev/null --header \"HOST: {{ .ACME_DOMAIN }}\" https://localhost/nginx-health",
"poll": 10,
"ttl": 25,
"interfaces": ["eth1", "eth0"]
}{{ end }}
],
"backends": [
{
"name": "wordpress",
"poll": 7,
"onChange": "reload"
}
],
"coprocesses": [{{ if .CONSUL_AGENT }}
{
"command": ["/usr/local/bin/consul", "agent",
"-data-dir=/data",
"-config-dir=/config",
"-rejoin",
"-retry-join", "{{ .CONSUL }}",
"-retry-max", "10",
"-retry-interval", "10s"],
"restarts": "unlimited"
}{{ end }}
{{ if and .CONSUL_AGENT .ACME_DOMAIN }},{{ end }}
{{ if .ACME_DOMAIN }}
{
"command": ["/usr/local/bin/consul-template",
"-config", "/etc/acme/watch.hcl",
"-consul", "{{ if .CONSUL_AGENT }}localhost{{ else }}{{ .CONSUL }}{{ end }}:8500"],
"restarts": "unlimited"
}{{ end }}],
"telemetry": {
"port": 9090,
"sensors": [
{
"name": "nginx_connections_unhandled_total",
"help": "Number of accepted connnections that were not handled",
"type": "gauge",
"poll": 5,
"check": ["sensor", "unhandled"]
},
{
"name": "nginx_connections_load",
"help": "Ratio of active connections (less waiting) to the maximum worker connections",
"type": "gauge",
"poll": 5,
"check": ["sensor", "connections_load"]
}
]
},
"tasks": [{{ if .ACME_DOMAIN }}
{
"name": "acme-checkin",
"command": [ "/usr/local/bin/acme", "checkin" ],
"frequency": "5m",
"timeout": "10s"
},
{
"name": "acme-renew-certs",
"command": [ "/usr/local/bin/acme", "renew-certs" ],
"frequency": "12h",
"timeout": "10m"
},
{
"name": "clean-unused-certs",
"command": ["/usr/local/bin/acme", "clean-certs" ],
"frequency": "24h",
"timeout": "10m"
}{{ end }}
]
}
================================================
FILE: nginx/etc/nginx/templates/conf.d/site.conf
================================================
{{ $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 }}
}
================================================
FILE: nginx/etc/nginx/templates/nginx.conf
================================================
# 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;
}
================================================
FILE: setup.sh
================================================
#!/bin/bash
set -e -o pipefail
help() {
echo
echo 'Usage ./setup.sh ~/path/to/MANTA_PRIVATE_KEY'
echo
echo 'Checks that your Triton and Docker environment is sane and configures'
echo 'an environment file to use.'
echo
echo 'MANTA_PRIVATE_KEY is the filesystem path to an SSH private key'
echo 'used to connect to Manta for the database backups.'
echo
echo 'Additional details must be configured in the _env file, but this script will properly'
echo 'encode the SSH key details for use with this this project.'
echo
echo '-'
echo
echo 'Usage ./setup.sh get-cns-hostname'
echo
echo 'Output the CNS hostname suitable for aliasing in DNS for custom domain names.'
echo
}
# populated by `check` function whenever we're using Triton
TRITON_USER=
TRITON_DC=
TRITON_ACCOUNT=
# ---------------------------------------------------
# Top-level commands
# Output aliasable CNS hostname
get-cns-hostname() {
TRITON_DC=$(triton profile get | awk -F"/" '/url:/{print $3}' | awk -F'.' '{print $1}')
TRITON_ACCOUNT=$(triton account get | awk -F": " '/id:/{print $2}')
echo "nginx.svc.${TRITON_ACCOUNT}.${TRITON_DC}.cns.joyent.com"
}
# Check for correct configuration and setup _env file
envcheck() {
if [ -z "$1" ]; then
tput rev # reverse
tput bold # bold
echo 'Please provide a path to a SSH private key to access Manta.'
tput sgr0 # clear
help
exit 1
fi
if [ ! -f "$1" ]; then
tput rev # reverse
tput bold # bold
echo 'SSH private key for Manta is unreadable.'
tput sgr0 # clear
help
exit 1
fi
# Assign args to named vars
MANTA_PRIVATE_KEY_PATH=$1
command -v docker >/dev/null 2>&1 || {
echo
tput rev # reverse
tput bold # bold
echo 'Docker is required, but does not appear to be installed.'
tput sgr0 # clear
echo 'See https://docs.joyent.com/public-cloud/api-access/docker'
exit 1
}
command -v json >/dev/null 2>&1 || {
echo
tput rev # reverse
tput bold # bold
echo 'Error! JSON CLI tool is required, but does not appear to be installed.'
tput sgr0 # clear
echo 'See https://apidocs.joyent.com/cloudapi/#getting-started'
exit 1
}
command -v triton >/dev/null 2>&1 || {
echo
tput rev # reverse
tput bold # bold
echo 'Error! Joyent Triton CLI is required, but does not appear to be installed.'
tput sgr0 # clear
echo 'See https://www.joyent.com/blog/introducing-the-triton-command-line-tool'
exit 1
}
# make sure Docker client is pointed to the same place as the Triton client
local docker_user=$(docker info 2>&1 | awk -F": " '/SDCAccount:/{print $2}')
local docker_dc=$(echo $DOCKER_HOST | awk -F"/" '{print $3}' | awk -F'.' '{print $1}')
TRITON_USER=$(triton profile get | awk -F": " '/account:/{print $2}')
TRITON_DC=$(triton profile get | awk -F"/" '/url:/{print $3}' | awk -F'.' '{print $1}')
TRITON_ACCOUNT=$(triton account get | awk -F": " '/id:/{print $2}')
if [ ! "$docker_user" = "$TRITON_USER" ] || [ ! "$docker_dc" = "$TRITON_DC" ]; then
echo
tput rev # reverse
tput bold # bold
echo 'Error! The Triton CLI configuration does not match the Docker CLI configuration.'
tput sgr0 # clear
echo
echo "Docker user: ${docker_user}"
echo "Triton user: ${TRITON_USER}"
echo "Docker data center: ${docker_dc}"
echo "Triton data center: ${TRITON_DC}"
exit 1
fi
local triton_cns_enabled=$(triton account get | awk -F": " '/cns/{print $2}')
if [ ! "true" == "$triton_cns_enabled" ]; then
echo
tput rev # reverse
tput bold # bold
echo 'Error! Triton CNS is required and not enabled.'
tput sgr0 # clear
echo
echo 'Consider running:'
echo ' triton account update triton_cns_enabled=true'
echo
exit 1
fi
# setup environment file
if [ ! -f "_env" ]; then
echo '# Environment variables for for WordPress site' > _env
echo '# please include the scheme http:// or https:// in the URL variable' >> _env
echo 'WORDPRESS_URL=http://'nginx.svc.${TRITON_ACCOUNT}.${TRITON_DC}.triton.zone >> _env
echo 'WORDPRESS_SITE_TITLE=Autopilot Pattern WordPress test site' >> _env
echo 'WORDPRESS_ADMIN_EMAIL=user@example.net' >> _env
echo 'WORDPRESS_ADMIN_USER=admin-'$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 3) >> _env
echo 'WORDPRESS_ADMIN_PASSWORD='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 11) >> _env
echo 'WORDPRESS_ACTIVE_THEME=twentysixteen' >> _env
echo 'WORDPRESS_CACHE_KEY_SALT='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 53) >> _env
echo '#WORDPRESS_TEST_DATA=true # uncomment to import a collection of test content on start' >> _env
echo >> _env
echo '# Wordpress security salts' >> _env
echo '# These must be unique for your install to ensure the security of the site' >> _env
echo 'WORDPRESS_AUTH_KEY='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 53) >> _env
echo 'WORDPRESS_SECURE_AUTH_KEY='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 53) >> _env
echo 'WORDPRESS_LOGGED_IN_KEY='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 53) >> _env
echo 'WORDPRESS_NONCE_KEY='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 53) >> _env
echo 'WORDPRESS_AUTH_SALT='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 53) >> _env
echo 'WORDPRESS_SECURE_AUTH_SALT='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 53) >> _env
echo 'WORDPRESS_LOGGED_IN_SALT='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 53) >> _env
echo 'WORDPRESS_NONCE_SALT='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 53) >> _env
echo >> _env
echo '# Nginx LetsEncrypt (ACME) config' >> _env
echo '# be sure ACME_DOMAIN host and WORDPRESS_URL host are the same, if using automated SSL via LetsEncrypt' >> _env
echo '# ACME_ENV defaults to "staging", uncomment following ACME_ENV line to switch to LetsEncrypt production endpoint' >> _env
echo '#ACME_DOMAIN='nginx.svc.${TRITON_ACCOUNT}.${TRITON_DC}.triton.zone >> _env
echo '#ACME_ENV=production' >> _env
echo >> _env
echo '# Environment variables for MySQL service' >> _env
echo '# WordPress database/WPDB information' >> _env
echo 'MYSQL_USER=wpdbuser' >> _env
echo 'MYSQL_PASSWORD='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 7) >> _env
echo 'MYSQL_DATABASE=wp' >> _env
echo '# MySQL replication user, should be different from above' >> _env
echo 'MYSQL_REPL_USER=repluser' >> _env
echo 'MYSQL_REPL_PASSWORD='$(cat /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 7) >> _env
echo >> _env
echo '# Environment variables for backups to Manta' >> _env
echo 'MANTA_URL=https://us-east.manta.joyent.com' >> _env
echo 'MANTA_BUCKET=/<username>/stor/<bucketname> # an existing Manta bucket' >> _env
echo 'MANTA_USER=<username> # a user with access to that bucket' >> _env
echo 'MANTA_SUBUSER=' >> _env
echo 'MANTA_ROLE=' >> _env
# MANTA_KEY_ID must be the md5 formatted key fingerprint. A SHA256 will result in errors.
set +o pipefail
# The -E option was added to ssh-keygen recently; if it doesn't work, then
# assume we're using an older version of ssh-keygen that only outputs MD5 fingerprints
ssh-keygen -yl -E md5 -f ${MANTA_PRIVATE_KEY_PATH} > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo MANTA_KEY_ID=$(ssh-keygen -yl -E md5 -f ${MANTA_PRIVATE_KEY_PATH} | awk '{print substr($2,5)}') >> _env
else
echo MANTA_KEY_ID=$(ssh-keygen -yl -f ${MANTA_PRIVATE_KEY_PATH} | awk '{print $2}') >> _env
fi
set -o pipefail
# munge the private key so that we can pass it into an env var sanely
# and then unmunge it in our startup script
echo MANTA_PRIVATE_KEY=$(cat ${MANTA_PRIVATE_KEY_PATH} | tr '\n' '#') >> _env
echo >> _env
echo '# Consul discovery via Triton CNS' >> _env
echo CONSUL=wp-consul.svc.${TRITON_ACCOUNT}.${TRITON_DC}.cns.joyent.com >> _env
echo >> _env
echo 'Edit the _env file to confirm and set your desired configuration details'
else
echo 'Existing _env file found, exiting'
exit
fi
}
# ---------------------------------------------------
# parse arguments
# Get function list
funcs=($(declare -F -p | cut -d " " -f 3))
until
if [ ! -z "$1" ]; then
# check if the first arg is a function in this file, or use a default
if [[ " ${funcs[@]} " =~ " $1 " ]]; then
cmd=$1
shift 1
else
cmd="envcheck"
fi
$cmd "$@"
if [ $? == 127 ]; then
help
fi
exit
else
help
fi
do
echo
done
================================================
FILE: var/www/html/.htaccess
================================================
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
================================================
FILE: var/www/html/content/mu-plugins/index.php
================================================
================================================
FILE: var/www/html/content/mu-plugins/register-theme-directory.php
================================================
<?php
register_theme_directory( ABSPATH . 'wp-content/themes/' );
================================================
FILE: var/www/html/content/plugins/index.php
================================================
================================================
FILE: var/www/html/content/themes/index.php
================================================
================================================
FILE: var/www/html/content/uploads/.empty
================================================
================================================
FILE: var/www/html/db-config.php.ctmpl
================================================
<?php
/**
* HyperDB configuration file
*
* This file is automatically generated via a template
* parsed by consul-template, https://github.com/hashicorp/consul-template .
*
* Last update {{timestamp}}
*
* This file should be installed at ABSPATH/db-config.php
*
* $wpdb is an instance of the hyperdb class which extends the wpdb class.
*
* See readme.txt for documentation.
*/
/** DB hosts **/
/**
* See usage details below in the section on "Configuration Functions"
*/
/**
* The DB master
*
* This has reads turned off, though any reads following a write _will_ be against the master.
*/
{{if service "mysql-primary"}}
{{with index (service "mysql-primary") 0}}
$wpdb->add_database(array(
'host' => '{{.Address}}:{{.Port}}',
'user' => DB_USER,
'password' => DB_PASSWORD,
'name' => DB_NAME,
'write' => 1,
'read' => 0,
'dataset' => 'global',
));{{end}}
{{end}}
{{if service "mysql"}}
/**
* The DB replicas
*
* These are read-only.
*/
{{range service "mysql"}}
$wpdb->add_database(array(
'host' => '{{.Address}}:{{.Port}}',
'user' => DB_USER,
'password' => DB_PASSWORD,
'name' => DB_NAME,
'write' => 0,
'read' => 1,
'dataset' => 'global',
'timeout' => 0.2,
));{{end}}
{{else}}
/**
* No DB replicas are healthy, so we're adding the master in as a read-host.
*/
{{range service "mysql-primary"}}
$wpdb->add_database(array(
'host' => '{{.Address}}:{{.Port}}',
'user' => DB_USER,
'password' => DB_PASSWORD,
'name' => DB_NAME,
'write' => 0,
'read' => 1,
'dataset' => 'global',
));{{end}}
{{end}}
/** HyperDB configuration documentation and examples **/
/**
* Introduction to HyperDB configuration
*
* HyperDB can manage connections to a large number of databases. Queries are
* distributed to appropriate servers by mapping table names to datasets.
*
* A dataset is defined as a group of tables that are located in the same
* database. There may be similarly-named databases containing different
* tables on different servers. There may also be many replicas of a database
* on different servers. The term "dataset" removes any ambiguity. Consider a
* dataset as a group of tables that can be mirrored on many servers.
*
* Configuring HyperDB involves defining databases and datasets. Defining a
* database involves specifying the server connection details, the dataset it
* contains, and its capabilities and priorities for reading and writing.
* Defining a dataset involves specifying its exact table names or registering
* one or more callback functions that translate table names to datasets.
*/
/** Variable settings **/
/**
* save_queries (bool)
* This is useful for debugging. Queries are saved in $wpdb->queries. It is not
* a constant because you might want to use it momentarily.
* Default: false
*/
$wpdb->save_queries = false;
/**
* persistent (bool)
* This determines whether to use mysql_connect or mysql_pconnect. The effects
* of this setting may vary and should be carefully tested.
* Default: false
*/
$wpdb->persistent = false;
/**
* max_connections (int)
* This is the number of mysql connections to keep open. Increase if you expect
* to reuse a lot of connections to different servers. This is ignored if you
* enable persistent connections.
* Default: 10
*/
$wpdb->max_connections = 10;
/**
* check_tcp_responsiveness
* Enables checking TCP responsiveness by fsockopen prior to mysql_connect or
* mysql_pconnect. This was added because PHP's mysql functions do not provide
* a variable timeout setting. Disabling it may improve average performance by
* a very tiny margin but lose protection against connections failing slowly.
* Default: true
*/
$wpdb->check_tcp_responsiveness = true;
/** Configuration Functions **/
/**
* $wpdb->add_database( $database );
*
* $database is an associative array with these parameters:
* host (required) Hostname with optional :port. Default port is 3306.
* user (required) MySQL user name.
* password (required) MySQL user password.
* name (required) MySQL database name.
* read (optional) Whether server is readable. Default is 1 (readable).
* Also used to assign preference. See "Network topology".
* write (optional) Whether server is writable. Default is 1 (writable).
* Also used to assign preference in multi-master mode.
* dataset (optional) Name of dataset. Default is 'global'.
* timeout (optional) Seconds to wait for TCP responsiveness. Default is 0.2
* lag_threshold (optional) The minimum lag on a slave in seconds before we consider it lagged.
* Set null to disable. When not set, the value of $wpdb->default_lag_threshold is used.
*/
/**
* $wpdb->add_table( $dataset, $table );
*
* $dataset and $table are strings.
*/
/**
* $wpdb->add_callback( $callback, $callback_group = 'dataset' );
*
* $callback is a callable function or method. $callback_group is the
* group of callbacks, this $callback belongs to.
*
* Callbacks are executed in the order in which they are registered until one
* of them returns something other than null.
*
* The default $callback_group is 'dataset'. Callback in this group
* will be called with two arguments and expected to compute a dataset or return null.
* $dataset = $callback($table, &$wpdb);
*
* Anything evaluating to false will cause the query to be aborted.
*
* For more complex setups, the callback may be used to overwrite properties of
* $wpdb or variables within hyperdb::connect_db(). If a callback returns an
* array, HyperDB will extract the array. It should be an associative array and
* it should include a $dataset value corresponding to a database added with
* $wpdb->add_database(). It may also include $server, which will be extracted
* to overwrite the parameters of each randomly selected database server prior
* to connection. This allows you to dynamically vary parameters such as the
* host, user, password, database name, lag_threshold and TCP check timeout.
*/
/** Masters and slaves
*
* A database definition can include 'read' and 'write' parameters. These
* operate as boolean switches but they are typically specified as integers.
* They allow or disallow use of the database for reading or writing.
*
* A master database might be configured to allow reading and writing:
* 'write' => 1,
* 'read' => 1,
* while a slave would be allowed only to read:
* 'write' => 0,
* 'read' => 1,
*
* It might be advantageous to disallow reading from the master, such as when
* there are many slaves available and the master is very busy with writes.
* 'write' => 1,
* 'read' => 0,
* HyperDB tracks the tables that it has written since instantiation and sending
* subsequent read queries to the same server that received the write query.
* Thus a master set up this way will still receive read queries, but only
* subsequent to writes.
*/
/**
* Network topology / Datacenter awareness
*
* When your databases are located in separate physical locations there is
* typically an advantage to connecting to a nearby server instead of a more
* distant one. The read and write parameters can be used to place servers into
* logical groups of more or less preferred connections. Lower numbers indicate
* greater preference.
*
* This configuration instructs HyperDB to try reading from one of the local
* slaves at random. If that slave is unreachable or refuses the connection,
* the other slave will be tried, followed by the master, and finally the
* remote slaves in random order.
* Local slave 1: 'write' => 0, 'read' => 1,
* Local slave 2: 'write' => 0, 'read' => 1,
* Local master: 'write' => 1, 'read' => 2,
* Remote slave 1: 'write' => 0, 'read' => 3,
* Remote slave 2: 'write' => 0, 'read' => 3,
*
* In the other datacenter, the master would be remote. We would take that into
* account while deciding where to send reads. Writes would always be sent to
* the master, regardless of proximity.
* Local slave 1: 'write' => 0, 'read' => 1,
* Local slave 2: 'write' => 0, 'read' => 1,
* Remote slave 1: 'write' => 0, 'read' => 2,
* Remote slave 2: 'write' => 0, 'read' => 2,
* Remote master: 'write' => 1, 'read' => 3,
*
* There are many ways to achieve different configurations in different
* locations. You can deploy different config files. You can write code to
* discover the web server's location, such as by inspecting $_SERVER or
* php_uname(), and compute the read/write parameters accordingly. An example
* appears later in this file using the legacy function add_db_server().
*/
/**
* Slaves lag awareness
*
* HyperDB accommodates slave lag by making decisions, based on the defined lag
* threshold. If the lag threshold is not set, it will ignore the slave lag.
* Otherwise, it will try to find a non-lagged slave, before connecting to a lagged one.
*
* A slave is considered lagged, if it's replication lag is bigger than the lag threshold
* you have defined in $wpdb->$default_lag_threshold or in the per-database settings, using
* add_database(). You can also rewrite the lag threshold, by returning
* $server['lag_threshold'] variable with the 'dataset' group callbacks.
*
* HyperDB does not check the lag on the slaves. You have to define two callbacks
* callbacks to do that:
*
* $wpdb->add_callback( $callback, 'get_lag_cache' );
*
* and
*
* $wpdb->add_callback( $callback, 'get_lag' );
*
* The first one is called, before connecting to a slave and should return
* the replication lag in seconds or false, if unknown, based on $wpdb->lag_cache_key.
*
* The second callback is called after a connection to a slave is established.
* It should return it's replication lag or false, if unknown,
* based on the connection in $wpdb->dbhs[ $wpdb->dbhname ].
*/
/** Sample Configuration 1: Using the Default Server **/
/**
* This is the most basic way to add a server to HyperDB using only the
* required parameters: host, user, password, name.
* This adds the DB defined in wp-config.php as a read/write server for
* the 'global' dataset. (Every table is in 'global' by default.)
*/
/*
$wpdb->add_database(array(
'host' => DB_HOST, // If port is other than 3306, use host:port.
'user' => DB_USER,
'password' => DB_PASSWORD,
'name' => DB_NAME,
));
*/
/**
* This adds the same server again, only this time it is configured as a slave.
* The last three parameters are set to the defaults but are shown for clarity.
*/
/*
$wpdb->add_database(array(
'host' => DB_HOST, // If port is other than 3306, use host:port.
'user' => DB_USER,
'password' => DB_PASSWORD,
'name' => DB_NAME,
'write' => 0,
'read' => 1,
'dataset' => 'global',
'timeout' => 0.2,
));
*/
/** Sample Configuration 2: Partitioning **/
/**
* This example shows a setup where the multisite blog tables have been
* separated from the global dataset.
*/
/*
$wpdb->add_database(array(
'host' => 'global.db.example.com',
'user' => 'globaluser',
'password' => 'globalpassword',
'name' => 'globaldb',
));
$wpdb->add_database(array(
'host' => 'blog.db.example.com',
'user' => 'bloguser',
'password' => 'blogpassword',
'name' => 'blogdb',
'dataset' => 'blog',
));
$wpdb->add_callback('my_db_callback');
function my_db_callback($query, $wpdb) {
// Multisite blog tables are "{$base_prefix}{$blog_id}_*"
if ( preg_match("/^{$wpdb->base_prefix}\d+_/i", $wpdb->table) )
return 'blog';
}
*/
/** Sample helper functions from WordPress.com **/
/**
* This is back-compatible with an older config style. It is for convenience.
* lhost, part, and dc were removed from hyperdb because the read and write
* parameters provide enough power to achieve the desired effects via config.
*
* @param string $dataset Datset: the name of the dataset. Just use "global" if you don't need horizontal partitioning.
* @param int $part Partition: the vertical partition number (1, 2, 3, etc.). Use "0" if you don't need vertical partitioning.
* @param string $dc Datacenter: where the database server is located. Airport codes are convenient. Use whatever.
* @param int $read Read group: tries all servers in lowest number group before trying higher number group. Typical: 1 for slaves, 2 for master. This will cause reads to go to slaves unless all slaves are unreachable. Zero for no reads.
* @param bool $write Write flag: is this server writable? Works the same as $read. Typical: 1 for master, 0 for slaves.
* @param string $host Internet address: host:port of server on internet.
* @param string $lhost Local address: host:port of server for use when in same datacenter. Leave empty if no local address exists.
* @param string $name Database name.
* @param string $user Database user.
* @param string $password Database password.
*/
/*
function add_db_server($dataset, $part, $dc, $read, $write, $host, $lhost, $name, $user, $password, $timeout = 0.2 ) {
global $wpdb;
// dc is not used in hyperdb. This produces the desired effect of
// trying to connect to local servers before remote servers. Also
// increases time allowed for TCP responsiveness check.
if ( !empty($dc) && defined(DATACENTER) && $dc != DATACENTER ) {
if ( $read )
$read += 10000;
if ( $write )
$write += 10000;
$timeout = 0.7;
}
// You'll need a hyperdb::add_callback() callback function to use partitioning.
// $wpdb->add_callback( 'my_func' );
if ( $part )
$dataset = $dataset . '_' . $part;
$database = compact('dataset', 'read', 'write', 'host', 'name', 'user', 'password', 'timeout');
$wpdb->add_database($database);
// lhost is not used in hyperdb. This configures hyperdb with an
// additional server to represent the local hostname so it tries to
// connect over the private interface before the public one.
if ( !empty( $lhost ) ) {
if ( $read )
$database['read'] = $read - 0.5;
if ( $write )
$database['write'] = $write - 0.5;
$wpdb->add_database( $database );
}
}
*/
/**
* Sample replication lag detection configuration.
*
* We use mk-heartbeat (http://www.maatkit.org/doc/mk-heartbeat.html)
* to detect replication lag.
*
* This implementation requires the database user
* to have read access to the heartbeat table.
*
* The cache uses shared memory for portability.
* Can be modified to work with Memcached, APC and etc.
*/
/*
$wpdb->lag_cache_ttl = 30;
$wpdb->shmem_key = ftok( __FILE__, "Y" );
$wpdb->shmem_size = 128 * 1024;
$wpdb->add_callback( 'get_lag_cache', 'get_lag_cache' );
$wpdb->add_callback( 'get_lag', 'get_lag' );
function get_lag_cache( $wpdb ) {
$segment = shm_attach( $wpdb->shmem_key, $wpdb->shmem_size, 0600 );
$lag_data = @shm_get_var( $segment, 0 );
shm_detach( $segment );
if ( !is_array( $lag_data ) || !is_array( $lag_data[ $wpdb->lag_cache_key ] ) )
return false;
if ( $wpdb->lag_cache_ttl < time() - $lag_data[ $wpdb->lag_cache_key ][ 'timestamp' ] )
return false;
return $lag_data[ $wpdb->lag_cache_key ][ 'lag' ];
}
function get_lag( $wpdb ) {
$dbh = $wpdb->dbhs[ $wpdb->dbhname ];
if ( !mysql_select_db( 'heartbeat', $dbh ) )
return false;
$result = mysql_query( "SELECT UNIX_TIMESTAMP() - UNIX_TIMESTAMP(ts) AS lag FROM heartbeat LIMIT 1", $dbh );
if ( !$result || false === $row = mysql_fetch_assoc( $result ) )
return false;
// Cache the result in shared memory with timestamp
$sem_id = sem_get( $wpdb->shmem_key, 1, 0600, 1 ) ;
sem_acquire( $sem_id );
$segment = shm_attach( $wpdb->shmem_key, $wpdb->shmem_size, 0600 );
$lag_data = @shm_get_var( $segment, 0 );
if ( !is_array( $lag_data ) )
$lag_data = array();
$lag_data[ $wpdb->lag_cache_key ] = array( 'timestamp' => time(), 'lag' => $row[ 'lag' ] );
shm_put_var( $segment, 0, $lag_data );
shm_detach( $segment );
sem_release( $sem_id );
return $row[ 'lag' ];
}
*/
// The ending PHP tag is omitted. This is actually safer than including it.
================================================
FILE: var/www/html/inactive-plugins/no-uploads.php
================================================
<?php
/**
* Will be installed into mu-plugins when consul reports NFS container is not healthy
* Display an error when the NFS container is unavailable.
*/
function nfs_error_notice() {
?>
<div class="error notice">
<p><?php esc_html_e( 'The NFS container is not present, media uploads have been disabled'); ?></p>
</div>
<?php
}
add_action( 'admin_notices', 'nfs_error_notice' );
================================================
FILE: var/www/html/index.php
================================================
<?php
// WordPress view bootstrapper
define( 'WP_USE_THEMES', true );
require( './wordpress/wp-blog-header.php' );
================================================
FILE: var/www/html/memcached-config.php.ctmpl
================================================
<?php
/**
* Memcached configuration file
*
* This file is automatically generated via a template
* parsed by consul-template, https://github.com/hashicorp/consul-template .
*
* Last update {{timestamp}}
*/
{{ if service "memcached" }}
# turn on WP caching
define('WP_CACHE', true);
define( 'WP_APC_KEY_SALT', '{{env "WORDPRESS_CACHE_KEY_SALT"}}' );
define( 'WP_CACHE_KEY_SALT', '{{env "WORDPRESS_CACHE_KEY_SALT"}}' );
global $memcached_servers;
# write the address:port pairs for each healthy memcached node
$memcached_servers = array(
{{range service "memcached"}}
array(
'{{.Address}}',
{{.Port}}
),
{{end}}
);
{{ end }}
// The ending PHP tag is omitted. This is actually safer than including it.
================================================
FILE: var/www/html/wp-cli.yml
================================================
path: wordpress/
apache_modules:
- mod_rewrite
================================================
FILE: var/www/html/wp-config.php.ctmpl
================================================
<?php
/**
* WordPress configuration file
*
* This file is automatically generated via a template
* parsed by consul-template, https://github.com/hashicorp/consul-template .
*
* Last update {{timestamp}}
*/
/**
* The following database info is needed here, but is ignored after we load HyperDB.
* See db-config.php.ctmpl for details.
*/
## this is a test
define( 'DB_NAME', '{{env "MYSQL_DATABASE"}}' );
define( 'DB_USER', '{{env "MYSQL_USER"}}' );
define( 'DB_PASSWORD', '{{env "MYSQL_PASSWORD"}}' );
define( 'DB_HOST', '{{if service "mysql-primary"}}{{with index (service "mysql-primary") 0}}{{.Address}}:{{.Port}}{{end}}{{end}}' );
/**
* Handle SSL reverse proxy
*/
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
{
$_SERVER['HTTPS']='on';
}
/**
* Custom Content Directory
*/
define( 'WP_CONTENT_DIR', dirname( __FILE__ ) . '/content' );
define( 'WP_CONTENT_URL', '{{env "WORDPRESS_URL"}}/content' );
/**
* You almost certainly do not want to change these
*/
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
/**
* Salts, for security
*
* Generate random strings in your shell with:
* tr -dc 'a-zA-Z0-9!@#$%^&*()-_ []{}<>~`+=,.;:/?|' < /dev/urandom | head -c 64
*
* On a Mac you'll need to use:
* LC_ALL=C tr -dc 'a-zA-Z0-9!@#$%^&*()-_ []{}<>~`+=,.;:/?|' < /dev/urandom | head -c 64
*
* Props to https://gist.github.com/earthgecko/3089509 for some hints there
*/
define( 'AUTH_KEY', '{{env "WORDPRESS_AUTH_KEY"}}' );
define( 'SECURE_AUTH_KEY', '{{env "WORDPRESS_SECURE_AUTH_KEY"}}' );
define( 'LOGGED_IN_KEY', '{{env "WORDPRESS_LOGGED_IN_KEY"}}' );
define( 'NONCE_KEY', '{{env "WORDPRESS_NONCE_KEY"}}' );
define( 'AUTH_SALT', '{{env "WORDPRESS_AUTH_SALT"}}' );
define( 'SECURE_AUTH_SALT', '{{env "WORDPRESS_SECURE_AUTH_SALT"}}' );
define( 'LOGGED_IN_SALT', '{{env "WORDPRESS_LOGGED_IN_SALT"}}' );
define( 'NONCE_SALT', '{{env "WORDPRESS_NONCE_SALT"}}' );
/**
* Table prefix
* Change this if you have multiple installs in the same database
*/
$table_prefix = 'wp_';
/**
* Language
* Leave blank for American English
*/
define( 'WPLANG', '' );
/**
* Hide errors
*/
ini_set( 'display_errors', {{key_or_default "wordpress/config/WP_DEBUG_DISPLAY" "0" | parseInt}} );
define( 'WP_DEBUG_DISPLAY', {{key_or_default "wordpress/config/WP_DEBUG_DISPLAY" "0" | parseBool}} );
/**
* Debug mode
* Debugging? Enable these.
*/
/**
* Load the Memcached config
*/
include( dirname( __FILE__ ) . '/memcached-config.php' );
/**
* Bootstrap WordPress
*/
if ( ! defined( 'ABSPATH' ) )
{
define( 'ABSPATH', dirname( __FILE__ ) . '/wordpress/' );
}
require_once( ABSPATH . 'wp-settings.php' );
// The ending PHP tag is omitted. This is actually safer than including it.
gitextract_1bdlvoha/
├── .dockerignore
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── bin/
│ ├── health.sh
│ ├── onchange-db.sh
│ ├── onchange-memcached.sh
│ ├── onchange-nfs.sh
│ ├── onchange-wp-config.sh
│ ├── prestart.sh
│ └── sensor.sh
├── docker-compose.yml
├── etc/
│ └── containerpilot.json
├── local-compose.yml
├── nginx/
│ ├── .dockerignore
│ ├── Dockerfile
│ └── etc/
│ ├── containerpilot.json
│ └── nginx/
│ └── templates/
│ ├── conf.d/
│ │ └── site.conf
│ └── nginx.conf
├── setup.sh
└── var/
└── www/
└── html/
├── .htaccess
├── content/
│ ├── mu-plugins/
│ │ ├── index.php
│ │ └── register-theme-directory.php
│ ├── plugins/
│ │ └── index.php
│ ├── themes/
│ │ └── index.php
│ └── uploads/
│ └── .empty
├── db-config.php.ctmpl
├── inactive-plugins/
│ └── no-uploads.php
├── index.php
├── memcached-config.php.ctmpl
├── wp-cli.yml
└── wp-config.php.ctmpl
SYMBOL INDEX (1 symbols across 1 files)
FILE: var/www/html/inactive-plugins/no-uploads.php
function nfs_error_notice (line 6) | function nfs_error_notice() {
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (89K chars).
[
{
"path": ".dockerignore",
"chars": 29,
"preview": ".DS_Store\n**/.DS_Store\n_env*\n"
},
{
"path": ".gitignore",
"chars": 58,
"preview": "# credentials\n_env*\nmanta*\n\n# macos frustration\n.DS_Store\n"
},
{
"path": "Dockerfile",
"chars": 5019,
"preview": "FROM php:5.6-apache\n\nRUN a2enmod rewrite\n\n# Install the PHP extensions we need, and other packages\nRUN set -ex \\\n && "
},
{
"path": "LICENSE",
"chars": 18047,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
},
{
"path": "Makefile",
"chars": 2435,
"preview": "# Makefile for shipping and testing the container image.\n\nMAKEFLAGS += --warn-undefined-variables\n.DEFAULT_GOAL := build"
},
{
"path": "README.md",
"chars": 12174,
"preview": "# Autopilot Pattern WordPress\n\n*A robust and highly-scalable implementation of WordPress in Docker using the Autopilot P"
},
{
"path": "bin/health.sh",
"chars": 179,
"preview": "#!/bin/bash\n\n/usr/local/bin/wp --allow-root core is-installed \\\n\t&& /usr/local/bin/wp --allow-root option get siteurl \\\n"
},
{
"path": "bin/onchange-db.sh",
"chars": 214,
"preview": "#!/bin/bash\n\n/usr/local/bin/onchange-wp-config.sh\n\nconsul-template \\\n -once \\\n -dedup \\\n -consul-addr ${CONSUL}"
},
{
"path": "bin/onchange-memcached.sh",
"chars": 182,
"preview": "#!/bin/bash\n\nconsul-template \\\n -once \\\n -dedup \\\n -consul-addr ${CONSUL}:8500 \\\n -template \"/var/www/html/m"
},
{
"path": "bin/onchange-nfs.sh",
"chars": 1390,
"preview": "#!/bin/bash\n\nif [[ `curl -s ${CONSUL}:8500/v1/health/state/passing | grep nfs` ]]\nthen\n echo \"nfs is healthy, mounting"
},
{
"path": "bin/onchange-wp-config.sh",
"chars": 195,
"preview": "#!/bin/bash\n# The WordPress config file\nconsul-template \\\n -once \\\n -dedup \\\n -consul-addr ${CONSUL}:8500 \\\n "
},
{
"path": "bin/prestart.sh",
"chars": 2466,
"preview": "#!/bin/bash\n\n# Refresh all config files in order\n#\n# This script is typically called once at the container start, but\n# "
},
{
"path": "bin/sensor.sh",
"chars": 833,
"preview": "#!/bin/bash\nset -e\n\nhelp() {\n echo 'Uses cli tools free and top to determine current CPU and memory usage'\n echo '"
},
{
"path": "docker-compose.yml",
"chars": 2960,
"preview": "# An demo version of WordPress for easy scaling\nwordpress:\n image: autopilotpattern/wordpress:latest\n restart: always\n"
},
{
"path": "etc/containerpilot.json",
"chars": 1558,
"preview": "{\n \"consul\": \"{{ if .CONSUL_AGENT }}localhost{{ else }}{{ .CONSUL }}{{ end }}:8500\",\n \"preStart\": \"/usr/local/bin/pres"
},
{
"path": "local-compose.yml",
"chars": 1303,
"preview": "# See docker-compose.yml for further descriptions and configuration details of each service\n\nwordpress:\n extends:\n f"
},
{
"path": "nginx/.dockerignore",
"chars": 29,
"preview": ".DS_Store\n**/.DS_Store\n_env*\n"
},
{
"path": "nginx/Dockerfile",
"chars": 169,
"preview": "# a minimal Nginx container including containerbuddy and a simple virtulhost config\nFROM autopilotpattern/nginx:1.13-r7."
},
{
"path": "nginx/etc/containerpilot.json",
"chars": 2871,
"preview": "{\n \"consul\": \"{{ if .CONSUL_AGENT }}localhost{{ else }}{{ .CONSUL }}{{ end }}:8500\",\n \"preStart\": \"generate-config\",\n "
},
{
"path": "nginx/etc/nginx/templates/conf.d/site.conf",
"chars": 1218,
"preview": "{{ $acme_domain := env \"ACME_DOMAIN\" }}\n{{ $ssl_ready := env \"SSL_READY\" }}\n\n# If we're listening on https, define an ht"
},
{
"path": "nginx/etc/nginx/templates/nginx.conf",
"chars": 1171,
"preview": "# This is an example Nginx configuration template file.\n# Adjust the values below as required for your application.\n\nuse"
},
{
"path": "setup.sh",
"chars": 9344,
"preview": "#!/bin/bash\nset -e -o pipefail\n\nhelp() {\n echo\n echo 'Usage ./setup.sh ~/path/to/MANTA_PRIVATE_KEY'\n echo\n e"
},
{
"path": "var/www/html/.htaccess",
"chars": 200,
"preview": "<IfModule mod_rewrite.c>\nRewriteEngine On\nRewriteBase /\nRewriteRule ^index\\.php$ - [L]\nRewriteCond %{REQUEST_FILENAME} !"
},
{
"path": "var/www/html/content/mu-plugins/index.php",
"chars": 0,
"preview": ""
},
{
"path": "var/www/html/content/mu-plugins/register-theme-directory.php",
"chars": 66,
"preview": "<?php\n\nregister_theme_directory( ABSPATH . 'wp-content/themes/' );"
},
{
"path": "var/www/html/content/plugins/index.php",
"chars": 0,
"preview": ""
},
{
"path": "var/www/html/content/themes/index.php",
"chars": 0,
"preview": ""
},
{
"path": "var/www/html/content/uploads/.empty",
"chars": 0,
"preview": ""
},
{
"path": "var/www/html/db-config.php.ctmpl",
"chars": 16028,
"preview": "<?php\n/**\n * HyperDB configuration file\n *\n * This file is automatically generated via a template\n * parsed by consul-te"
},
{
"path": "var/www/html/inactive-plugins/no-uploads.php",
"chars": 393,
"preview": "<?php\n/**\n* Will be installed into mu-plugins when consul reports NFS container is not healthy\n* Display an error when t"
},
{
"path": "var/www/html/index.php",
"chars": 115,
"preview": "<?php\n// WordPress view bootstrapper\ndefine( 'WP_USE_THEMES', true );\nrequire( './wordpress/wp-blog-header.php' );\n"
},
{
"path": "var/www/html/memcached-config.php.ctmpl",
"chars": 746,
"preview": "<?php\n/**\n * Memcached configuration file\n *\n * This file is automatically generated via a template\n * parsed by consul-"
},
{
"path": "var/www/html/wp-cli.yml",
"chars": 49,
"preview": "path: wordpress/\napache_modules:\n - mod_rewrite\n"
},
{
"path": "var/www/html/wp-config.php.ctmpl",
"chars": 2773,
"preview": "<?php\n/**\n * WordPress configuration file\n *\n * This file is automatically generated via a template\n * parsed by consul-"
}
]
About this extraction
This page contains the full source code of the autopilotpattern/wordpress GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (82.2 KB), approximately 22.5k tokens, and a symbol index with 1 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.