Ever wanted to deploy a LAMP Stack on Kubernetes?
$ helm repo add lamp https://lead4good.github.io/lamp-helm-repository
$ helm install my-lamp lamp/lamp
This chart bootstraps a LAMP Stack deployment on a Kubernetes cluster using the Helm package manager. It was designed in a very modular and transparent way. Instead of using a custom built docker container running multiple services like apache and php-fpm inside with no control or overwatch of these processes from within kubernetes, this chart takes the approach of using one service per container.
The charts default configurations were made with performance in mind. By default PHP-FPM is enabled and communication between php and mysql as well as apache and php is realized over unix sockets.
By default the chart is exposed to the public via LoadBalancer IP but exposing the chart via an ingress controller is also supported. If a working lego container is configured the chart supports creating lets encrypt certificates.
Setting up your website is easy, you can either use git or svn to copy your repo into the pod or use sftp or webdav and simply transfer your files into the container. If you have a different method of setting up your website, you can manually prepare it inside of an init container before the services start.
Once you've set up your website, you'd like to have separate development environments for testing? Don't worry, with one additional setting you can clone an existing release without downtime using the xtrabackup init container.
Official containers are used wherever possible ( namingly php, apache, mysql, mariadb and percona ) while the use of well established containers was anticipated otherwise ( phpmyadmin/phpmyadmin, atmoz/sftp, openweb/git-sync ) . To provide some of its unique features such as chart cloning and wordpress support some containers had to be newly created. All of those are hosted as automated builds on docker hub - with their respective sources on GitHub (lead4good/init-wp, lead4good/svn-sync, lead4good/webdav, lead4good/xtrabackup).
- Kubernetes 1.7+
- LoadBalancer support or Ingress Controller
To install the chart with the release name my-release
:
$ helm install --name my-release stable/lamp
The command deploys the LAMP chart on the Kubernetes cluster in the default configuration. The configuration section lists the parameters that can be configured during installation.
Tip: List all releases using
helm list
To uninstall/delete the my-release
deployment:
$ helm delete my-release
The command removes all the Kubernetes components associated with the chart and deletes the release.
To try out one of the examples. you can run, e.g. for wordpress:
$ helm install -f examples/wordpress.yaml --name wordpress stable/lamp
Currently you can try the following examples:
- examples/wordpress.yaml
- examples/wordpress-ingress-ssl.yaml
- examples/wordpress-php-ini.yaml
- examples/drupal.yaml
- examples/joomla.yaml
- examples/joomla.yaml
- examples/owncloud.yaml
- examples/nextcloud.yaml
The following tables list the configurable parameters of the LAMP chart and their default values.
You can specify each of the parameters using the --set key=value[,key=value]
argument to helm install
. For example,
$ helm install --name my-release \
--set init.clone.release=my-first-release-lamp,php.sockets=false,php.oldHTTPRoot=/var/www/my-website.com \
stable/lamp
The above command sets up the chart to create its persistent contents by cloning its content from my-first-release-lamp
, sets PHP socket communication to false
and sets an old http root to compensate for absolute path file links
Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
$ helm install --name my-release -f values.yaml stable/lamp
Tip: You can use the default values.yaml file as a template
The manual init container enables you to manually pull a websites backup from somewhere and set it up inside the container before the chart is deployed. Set init.manually.enabled
to true
and connect to the container by replacing example-com-lamp
and executing
$ kubectl exec -it \
$(kubectl get pods -l app=example-com-lamp --output=jsonpath={.items..metadata.name}) \
-c init-manually /bin/bash
The container has the document root mounted at /var/www/html
and the database directory mounted at /var/lib/mysql
. The default manual init container is derived from the official mysql container and can create and startup a mysql db by setting the necessary environment variables and then executing
$ /entrypoint.sh mysqld &
If another flavor of DB is used (mariadb or percona) then the image needs to be pointing to the right container.
After setting up your DB backup you can stop the database by executing
$ mysqladmin -uroot -p$MYSQL_ROOT_PASSWORD shutdown
Now copy all necessary files into the web directory, but do not forget to recursively chown the webroot to the www-data
user ( id 33 ) by executing
$ chown -R 33:33 /var/www/html
Once everything is setup stopping the init container is done by executing
$ im-done
Parameter | Description | Default |
---|---|---|
init.manually.enabled |
Enables container for manual initialization | false |
init.manually.repository |
Containers image | lead4good/init-wp |
init.manually.tag |
Containers image tag | latest |
init.manually.pullPolicy |
Image pull policy | Always |
If init.clone.release
is set to the fullname of an existing, already running LAMP chart (e.g. example-com-lamp
), the persistent storage of that chart (web files and db) will be copied to this charts persistent storage. It is mandatory that both database containers are of the same type (mysql, mariadb or percona). Mixing them will not work.
Parameter | Description | Default |
---|---|---|
init.clone.release |
Fullname of the release to clone | empty |
init.clone.hostPath |
If the release to clone uses hostPath instead of PVC, set it here. This will only work if both releases are deployed on the same node | empty |
| init.resources
| init containers resource requests/limits | resources
|
The PHP container is at the heart of the LAMP chart. By default, the LAMP chart uses the official PHP container from docker hub. You can also use your own PHP container, which needs to have the official PHP container at its base.
FPM is enabled by default, this creates an additional HTTPD container which routes PHP request via FCGI to the PHP container. Set php.fpmEnabled
to false to work with the official php:apache
image.
Note: If you are using a custom container, be sure to use the official
php:apache
orphp:fpm
containers at its base and setphp.fpmEnabled
accordingly
Parameter | Description | Default |
---|---|---|
php.repository |
default php image | php |
php.tag |
default php image tag | 7-fpm-alpine |
php.pullPolicy |
Image pull policy | Always |
php.fpmEnabled |
Enables FPM functionality, be sure to disable if working with a custom repository based on the apache tag | true |
php.sockets |
If FPM is enabled, enables communication between HTTPD and PHP via sockets instead of TCP | true |
php.oldHTTPRoot |
Additionally mounts the webroot at php.oldHTTPRoot to compensate for absolute path file links |
empty |
php.ini |
additional PHP config values, see examples on how to use | empty |
php.fpm |
addditonal PHP FPM config values | empty |
php.copyRoot |
if true, copies the containers web root /var/www/html into persistent storage. This must be enabled, if the container already comes with files installed to /var/www/html |
false |
php.persistentSubpaths |
instead of enabling persistence for the whole webroot, only subpaths of webroot can be enabled for persistence. Have a look at the nextcloud example to see how it works | empty |
php.resources |
PHP container resource requests/limits | resources |
httpd.repository |
default httpd image if fpm is enabled | httpd |
httpd.tag |
default httpd image tag | 2.4-alpine |
httpd.resources |
HTTPD container resource requests/limits | resources |
The MySQL container is disabled by default, any container with the base image of the official mysql, mariadb or percona should work.
Parameter | Description | Default |
---|---|---|
mysql.rootPassword |
Sets the MySQL root password, enables MySQL service if not empty | empty |
mysql.user |
MySQL user | empty |
mysql.password |
MySQL user password | empty |
mysql.database |
MySQL user database | empty |
mysql.repository |
MySQL image - choose one of the official mysql, mariadb or percona images | mysql |
mysql.tag |
MySQL image tag | 5.7 |
mysql.imagePullPolicy |
Image pull policy | Always |
mysql.sockets |
Enables communication between MySQL and PHP via sockets instead of TCP | true |
mysql.resources |
Resource requests/limits | resources |
SFTP is an instance of the atmoz/sftp container, through which you can access the webroot.
Note: The webroot is located in the subfolder of the sftp users home directory so putting files into the webroot via sftp have to be put to the web subfolder and the put command will fail if you upload to the root directory since writing permissions are disabled there.
Note: using a different image than the default atmoz/sftp will most probably not work since the containers startup command is overwritten to be able to configure the sftp user, you may however change the tag to a different version without any problems
Parameter | Description | Default |
---|---|---|
sftp.repository |
default sftp image | atmoz/sftp |
sftp.tag |
default sftp image tag | alpine |
sftp.enabled |
Enables sftp service | false |
sftp.serviceType |
Type of sftp service in Ingress mode | NodePort |
sftp.port |
Port to advertise service in LoadBalancer mode | 22 |
sftp.nodePort |
Port to advertise service in Ingress mode | empty |
sftp.user |
SFTP User | empty |
sftp.password |
SFTP Password | empty |
sftp.resources |
resource requests/limits | resources |
An instance of WebDAV, through which you can access the webroot.
Parameter | Description | Default |
---|---|---|
webdav.enabled |
Enables webdav service | false |
webdav.port |
Port to advertise service in LoadBalancer mode | 8001 |
webdav.subdomain |
Subdomain to advertise service on if ingress is enabled | webdav |
webdav.user |
WebDAV User | empty |
webdav.password |
WebDAV Password | empty |
webdav.resources |
resource requests/limits | resources |
If Git is enabled, the contents of the specified repository will be synchronized every git.wait
seconds to the web root. The web root needs to be empty otherwise the container will fail exit.
Note: You should not combine SFTP or WebDAV with the Git container since this might cause confusion if someone edits a file via SFTP just to find out that its changes got reverted by the Git sync process
Parameter | Description | Default |
---|---|---|
git.enabled |
Enables Git service | false |
git.repoURL |
Git Repository URL | empty |
git.branch |
Repository branch to sync | master |
git.revision |
Revision to sync | FETCH_HEAD |
git.wait |
Time between Git syncs | 30 |
git.resources |
resource requests/limits | resources |
If SVN is enabled, the contents of the specified repository will be synchronized every 30 seconds to the web root. If allowOverwrite is disabled and files already exist in the web folder then it will not create a working clone or sync files.
Note: You should not combine SFTP or WebDAV with the SVN container since this might cause confusion if someone edits a file via SFTP just to find out that its changes got reverted by the SVN sync process
Parameter | Description | Default |
---|---|---|
svn.enabled |
Enables svn service | false |
svn.user |
SVN User | empty |
svn.password |
SVN Password | empty |
svn.repoURL |
SVN Repository URL | empty |
svn.allowOverwrite |
if disabled and files already exist in the web folder will not create working clone or sync files | true |
svn.resources |
resource requests/limits | resources |
An instance of PHPMyAdmin through which you can access the database.
Note: using a different image than the default phpmyadmin/phpmyadmin image might not work since the containers startup command is overwritten to be able to advertise the http services on
phpmyadmin.port
, you may however change the tag to a different version without any problems
Parameter | Description | Default |
---|---|---|
phpmyadmin.repository |
default phpmyadmin image | phpmyadmin |
phpmyadmin.tag |
default phpmyadmin image tag | phpmyadmin |
phpmyadmin.enabled |
Enables phpmyadmin service | false |
phpmyadmin.port |
Port to advertise service in LoadBalancer mode | 8080 |
phpmyadmin.subdomain |
Subdomain to advertise service on if ingress is enabled | phpmyadmin |
phpmyadmin.resources |
resource requests/limits | resources |
Default resources are used by all containers which have no custom resources configured.
Parameter | Description | Default |
---|---|---|
resources.requests.cpu |
CPU resource requests | 1 |
resources.requests.memory |
Memory resource requests | 1Mi |
resources.limits.cpu |
CPU resource limits | empty |
resources.limits.memory |
Memory resource limits | empty |
If persistence
is enabled, PVC's will be used to store the web root and the db root. If a pod then is redeployed to another node, it will restart within seconds with the old state prevailing. If it is disabled, EmptyDir
is used, which would lead to deletion of the persistent storage once the pod is moved. Also cloning a chart with persistence
disabled will not work. Therefor persistence is enabled by default and should only be disabled in a testing environment. In environments where no PVCs are available you can use persistence.hostPath
instead. This will store the charts persistent data on the node it is running on.
Parameter | Description | Default |
---|---|---|
persistence.enabled |
Enables persistent volume - PV provisioner support necessary | true |
persistence.keep |
Keep persistent volume after helm delete | false |
persistence.accessMode |
PVC Access Mode | ReadWriteOnce |
persistence.size |
PVC Size | 5Gi |
persistence.storageClass |
PVC Storage Class | empty |
persistence.hostPath |
if specified, used as persistent storage instead of PVC | empty |
To be able to connect to the services provided by the LAMP chart, a Kubernetes cluster with working LoadBalancer or Ingress Controller support is necessary.
By default the chart will create a LoadBalancer Service, all services will be available via LoadBalancer IP through different ports. You can set service.type
to ClusterIP if you do not want your chart to be exposed at all.
If ingress.enabled
is set to true, the LAMP charts services are made accessible via ingress rules. Those services which are not provided by HTTP protocol via nodePorts
. In ingress mode the LAMP chart also supports ssl with certificates signed by lets encrypt. This requires a working lego container running on the cluster.
Note: In ingress mode it is mandatory to set
ingress.domain
, otherwise the ingress rules won't know how to route the traffic to the services.
Parameter | Description | Default |
---|---|---|
service.type |
Changes to ClusterIP automatically if ingress enabled | LoadBalancer |
service.HTTPPort |
Port to advertise the main web service in LoadBalancer mode | 80 |
ingress.enabled |
Enables ingress support - working ingress controller necessary | false |
ingress.domain |
domain to advertise the services - A records need to point to ingress controllers IP | empty |
ingress.subdomainWWW |
enables www subdomain and 301 redirect from domain. Requires nginx ingress controller. | false |
ingress.ssl |
Enables lego letsencrypt ssl support - working nginx controller and lego container necessary | false |
ingress.htpasswdString |
if specified main web service requires authentication. Requires nginx ingress controller. Format: user:$apr1$F... | empty |
ingress.annotations |
specify custom ingress annotations such as e.g. ingress.kubernetes.io/proxy-body-size |
The LAMP chart offers additional wordpress features during the init stage. It supports two modes, normal mode sets up the chart completely automatic by downloading an InfiniteWP backup from google drive, while the other mode gets executed when in manual mode (see: init.manually
). While in manual mode, the web files and db backup need to be manually downloaded and stashed in the appropriate folders (/var/www/html
<-- web root, /var/www/mysql
<-- sql backup). The automatic mode does this automatically. Both modes then import the backup and do some necesssary config file changes. So even in manual mode it is not necessary to import the db backup.
In development mode everything that gets executed in normal mode will also get executed. Additionally the wordpress domain is automatically search replaced inside the database. Also the wp_content/uploads
and wp_content/cache
directories are deleted. The .htaccess
file is modified to redirect requests to the uploads directory to the uploads directory of wordpress.domain
.
Parameter | Description | Default |
---|---|---|
wordpress.enabled |
Enables wordpress normal mode | false |
wordpress.gdriveRToken |
gdrive rtoken for authentication used for downloading InfiniteWP backup from gdrive | empty |
wordpress.gdriveFolder |
gdrive backup folder - the latest backup inside of the folder where the name includes the string _full will be downloaded |
wordpress.domain |
wordpress.domain |
wordpress domain used in dev mode to be search replaced | empty |
wordpress.develop.enabled |
enables develop mode | false |
wordpress.develop.deleteUploads |
deletes wp_content/uploads folder and links to live site within htaccess |
false |
wordpress.develop.devDomain |
used to search replace wordpress.domain to fullname of template .develop.devDomain e.g mysite-com-lamp.dev.example.com |
empty |
Parameter | Description | Default |
---|---|---|
keepSecrets |
Keep secrets after helm delete | false |
replicaCount |
> 1 will corrupt your database if one is used. Future releases might enable elastic scaling via galeradb | 1 |