├── code ├── composer.lock ├── drush-version.txt ├── composer.json ├── drupal-version.txt ├── drush │ ├── composer.json │ ├── send-emails │ │ └── send-emails.php │ ├── update-stats │ │ └── update-stats.php │ ├── drush-status.sh │ ├── transfer-files.sh │ ├── transfer-data.sh │ └── helloworld.script ├── config │ └── README.md ├── README.md ├── sites │ ├── development.services.yml │ ├── README.txt │ ├── example.sites.php │ ├── example.settings.local.php │ └── default │ │ └── default.services.yml ├── profiles │ └── README.txt ├── themes │ └── README.txt └── modules │ └── README.txt ├── k8-minikube ├── build │ ├── php-cli │ │ ├── composer.lock │ │ ├── composer.json │ │ ├── noop.php │ │ ├── Dockerfile │ │ └── php-cli.conf │ ├── php-fpm │ │ ├── config │ │ │ └── etc │ │ │ │ ├── php │ │ │ │ └── conf.d │ │ │ │ │ ├── docker-php-ext-gd.ini │ │ │ │ │ ├── docker-php-ext-zip.ini │ │ │ │ │ ├── docker-php-ext-redis.ini │ │ │ │ │ ├── docker-php-ext-memcached.ini │ │ │ │ │ ├── docker-php-ext-pdo_mysql.ini │ │ │ │ │ ├── docker-php-ext-opcache.ini │ │ │ │ │ └── opcache-recommended.ini │ │ │ │ ├── README.txt │ │ │ │ ├── php-fpm.d │ │ │ │ ├── zz-docker.conf │ │ │ │ └── docker.conf │ │ │ │ ├── pear.conf │ │ │ │ ├── php-fpm.conf │ │ │ │ └── php-fpm.conf.default │ │ ├── Dockerfile │ │ └── php-fpm.conf │ ├── app-image │ │ ├── app-sync.sh │ │ ├── Dockerfile │ │ └── start.sh │ ├── build-base-images.sh │ ├── nginx │ │ ├── ping.conf │ │ ├── Dockerfile │ │ ├── nginx.conf │ │ ├── fastcgi_params │ │ └── fastcgi.conf │ └── build-code-images.sh ├── eval.sh ├── bash-nginx.sh ├── volumes │ ├── volume-claims.yaml │ └── volumes.yaml ├── restart-nginx.sh ├── bash-php-fpm.sh ├── bash-container.sh ├── start.sh ├── recreate.sh ├── jobs │ └── app-image-rsync.yaml ├── pods-services │ ├── nginx.yaml │ ├── php-fpm.yaml │ └── php-cli.yaml ├── TODO.md └── POC-MODIFICATIONS.md ├── config ├── nginx-version.txt ├── php-cli-version.txt ├── php-fpm-version.txt └── README.md ├── .gitignore ├── scripts ├── pipeline │ ├── kube-exec.sh │ ├── README.md │ ├── deployScript.sh │ ├── pipelineDeployScripts │ │ ├── php-cli-deploy.sh │ │ ├── nginx-deploy.sh │ │ └── php-fpm-deploy.sh │ ├── pipeline-build-on-config-change.sh │ ├── rolling-code-deploy.sh │ ├── buildImage.sh │ ├── build-on-config-change.sh │ ├── build-on-code-change.sh │ └── pipeline-build-on-code-change.sh ├── docker │ ├── config-php-cli │ │ ├── noop.php │ │ ├── php-cli.conf │ │ └── Dockerfile │ ├── code-nginx │ │ ├── start.sh │ │ └── Dockerfile │ ├── README.md │ ├── code-php-fpm │ │ ├── start.sh │ │ └── Dockerfile │ ├── config-nginx │ │ ├── Dockerfile │ │ └── fastcgi.conf │ ├── code-php-cli │ │ └── Dockerfile │ └── config-php-fpm │ │ ├── Dockerfile │ │ └── php-fpm.conf ├── local │ ├── destroy-containers.sh │ ├── README.md │ ├── destroy-infrastructure.sh │ ├── deploy-containers.sh │ ├── build-containers.sh │ └── setup-infrastructure.sh └── kubernetes │ ├── ingress │ └── ingress.yaml │ ├── persistent-volumes.yaml │ ├── README.md │ ├── secrets │ └── service-credentials.txt.tpl │ ├── nginx-prd.yaml │ ├── nginx-stg.yaml │ ├── php-fpm-prd.yaml │ ├── php-fpm-stg.yaml │ └── php-cli.yaml ├── docs ├── img │ ├── Thumbs.db │ ├── links.png │ ├── 6-access.png │ ├── EnvVar.PNG │ ├── addStage.PNG │ ├── addTool.PNG │ ├── buildJob.PNG │ ├── gitTool.PNG │ ├── 4-overview.png │ ├── buildInput.PNG │ ├── deployJob.PNG │ ├── testStage.PNG │ ├── 1-hamburger.png │ ├── architecture.png │ ├── bluemixMenu.PNG │ ├── deployInput.PNG │ ├── gitSettings.PNG │ ├── toolchainPage.PNG │ ├── 5-worker-nodes.png │ ├── customTemplate.PNG │ ├── threePipelines.PNG │ ├── 2-create-cluster.png │ ├── 3-standard-cluster.png │ ├── completedToolchain.PNG │ ├── deliveryPipeline.PNG │ ├── finishedPipeline.PNG │ ├── 14-redis-credentials.png │ ├── 10-create-redis-service.png │ ├── 12-compose-credentials.png │ ├── 13-cleardb-credentials.png │ ├── 8-create-data-analytics.png │ ├── 9-create-mysql-service.png │ ├── persistentVolumeDeploy.PNG │ ├── 11-create-memcached-service.png │ └── 7-hamburger-data-analytics.png ├── SYNCHRONIZING-DATA.md ├── PHP-CLI-DRUSH.md ├── ONGOING-DEVELOPMENT.md ├── FEATURE-LIST.md ├── LOCAL-KUBERNETES.md ├── INITIAL-SETUP.md ├── DEMO.md ├── DEPLOY-CONTAINERS.md └── PIPELINE-SETUP.md ├── CONTRIBUTING.md ├── MAINTAINERS.md └── README.md /code/composer.lock: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /k8-minikube/build/php-cli/composer.lock: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /code/drush-version.txt: -------------------------------------------------------------------------------- 1 | DRUSH_VERSION=8.1.15 2 | -------------------------------------------------------------------------------- /config/nginx-version.txt: -------------------------------------------------------------------------------- 1 | NGINX_VERSION=1.13.5 2 | -------------------------------------------------------------------------------- /config/php-cli-version.txt: -------------------------------------------------------------------------------- 1 | PHP_CLI_VERSION=7.1-cli 2 | -------------------------------------------------------------------------------- /config/php-fpm-version.txt: -------------------------------------------------------------------------------- 1 | PHP_FPM_VERSION=7.1-fpm 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | service-credentials.txt 3 | Untitled 4 | build.log 5 | tmp 6 | -------------------------------------------------------------------------------- /scripts/pipeline/kube-exec.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | kubectl exec -it $1 -- /bin/bash 4 | -------------------------------------------------------------------------------- /code/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "psr/log": "1.0.*" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/config/etc/php/conf.d/docker-php-ext-gd.ini: -------------------------------------------------------------------------------- 1 | extension=gd.so 2 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/config/etc/php/conf.d/docker-php-ext-zip.ini: -------------------------------------------------------------------------------- 1 | extension=zip.so 2 | -------------------------------------------------------------------------------- /code/drupal-version.txt: -------------------------------------------------------------------------------- 1 | DRUPAL_VERSION=8.3.7 2 | DRUPAL_MD5=e7b1f382d6bd2b18d4b4aca01d335bc0 3 | -------------------------------------------------------------------------------- /code/drush/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "psr/log": "1.0.*" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/config/etc/php/conf.d/docker-php-ext-redis.ini: -------------------------------------------------------------------------------- 1 | extension=redis.so 2 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/config/etc/php/conf.d/docker-php-ext-memcached.ini: -------------------------------------------------------------------------------- 1 | extension=memcached.so 2 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/config/etc/php/conf.d/docker-php-ext-pdo_mysql.ini: -------------------------------------------------------------------------------- 1 | extension=pdo_mysql.so 2 | -------------------------------------------------------------------------------- /docs/img/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badtuxx/drupal-nginx-php-kubernetes/HEAD/docs/img/Thumbs.db -------------------------------------------------------------------------------- /docs/img/links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badtuxx/drupal-nginx-php-kubernetes/HEAD/docs/img/links.png -------------------------------------------------------------------------------- /docs/img/6-access.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badtuxx/drupal-nginx-php-kubernetes/HEAD/docs/img/6-access.png -------------------------------------------------------------------------------- /docs/img/EnvVar.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badtuxx/drupal-nginx-php-kubernetes/HEAD/docs/img/EnvVar.PNG -------------------------------------------------------------------------------- /docs/img/addStage.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badtuxx/drupal-nginx-php-kubernetes/HEAD/docs/img/addStage.PNG -------------------------------------------------------------------------------- /docs/img/addTool.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badtuxx/drupal-nginx-php-kubernetes/HEAD/docs/img/addTool.PNG -------------------------------------------------------------------------------- /docs/img/buildJob.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badtuxx/drupal-nginx-php-kubernetes/HEAD/docs/img/buildJob.PNG -------------------------------------------------------------------------------- /docs/img/gitTool.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badtuxx/drupal-nginx-php-kubernetes/HEAD/docs/img/gitTool.PNG -------------------------------------------------------------------------------- /k8-minikube/build/php-cli/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "psr/log": "1.0.*" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /k8-minikube/build/php-cli/noop.php: -------------------------------------------------------------------------------- 1 | #find by running bx cs cluster-get 8 | http: 9 | paths: 10 | - path: / 11 | backend: 12 | serviceName: nginx 13 | servicePort: 80 14 | -------------------------------------------------------------------------------- /code/sites/development.services.yml: -------------------------------------------------------------------------------- 1 | # Local development services. 2 | # 3 | # To activate this feature, follow the instructions at the top of the 4 | # 'example.settings.local.php' file, which sits next to this file. 5 | parameters: 6 | http.response.debug_cacheability_headers: true 7 | services: 8 | cache.backend.null: 9 | class: Drupal\Core\Cache\NullBackendFactory 10 | -------------------------------------------------------------------------------- /scripts/local/destroy-infrastructure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | # If needed. Otherwise tear down the cluster and the services in the UI. 5 | 6 | # Tear up cluster 7 | # TODO: 8 | 9 | # Deprovision MySQL from Compose or ClearDB 10 | # TODO: 11 | 12 | # Deprovision Redis from Compose or Redis Labs 13 | # TODO: 14 | 15 | # Deprovision memcached from Redis Labs 16 | # TODO: 17 | -------------------------------------------------------------------------------- /code/drush/transfer-files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PRD_PERSISTENT_VOLUME=/var/www/drupal/web/sites/default/files-prd 4 | STG_PERSISTENT_VOLUME=/var/www/drupal/web/sites/default/files-stg 5 | 6 | # Copy production user generated files back to staging 7 | echo "Synchronizing data from production ${PRD_PERSISTENT_VOLUME} to ${STG_PERSISTENT_VOLUME}." 8 | rsync -av ${PRD_PERSISTENT_VOLUME}/ ${STG_PERSISTENT_VOLUME} 9 | -------------------------------------------------------------------------------- /k8-minikube/restart-nginx.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This is a little extreme: the pod name changes: 4 | # kubectl delete pod $(kubectl get pod -l "app=nginx" -o jsonpath='{.items[0].metadata.name}') 5 | 6 | # Recreate the pod without changing it's name. 7 | kubectl replace --force -f pods-services/nginx.yaml 8 | 9 | # Show nginx endpoint, automatically shows up when ready. 10 | minikube service nginx --url 11 | -------------------------------------------------------------------------------- /scripts/docker/README.md: -------------------------------------------------------------------------------- 1 | # Docker manifests 2 | This directory contains the Dockerfiles to build base PHP and NGINX (`config`) images as well as the Drupal and custom site (`code`) images that are based on those. 3 | 4 | If anything changes in the top level `config` directory, that should trigger both a `config` and `code` image rebuild and redeploy. 5 | 6 | If anything changes in the top level `code` directory, that should trigger only a `code` image rebuild and redeploy. 7 | -------------------------------------------------------------------------------- /scripts/kubernetes/persistent-volumes.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: sites-stg-lv-claim 6 | spec: 7 | accessModes: 8 | - ReadWriteMany 9 | resources: 10 | requests: 11 | storage: 10Gi 12 | --- 13 | apiVersion: v1 14 | kind: PersistentVolumeClaim 15 | metadata: 16 | name: sites-prd-lv-claim 17 | spec: 18 | accessModes: 19 | - ReadWriteMany 20 | resources: 21 | requests: 22 | storage: 10Gi 23 | -------------------------------------------------------------------------------- /scripts/kubernetes/README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes deployment files 2 | This directory contains the manifests to create persistent volumes, persistent volume claims, pod deployments, and service definitions. 3 | 4 | It also includes a properties file template for IBM Cloud services. By copying the file to `service-credentials.txt` Kubernetes can then create secrets from each value in that file as a key value dictionary. 5 | 6 | These in turn are configured in the pod/service manifests as environment variables. 7 | -------------------------------------------------------------------------------- /k8-minikube/build/app-image/app-sync.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "App Image Sidecar Container" 4 | echo "" 5 | echo "Copying application into shared volume." 6 | echo "" 7 | 8 | echo "Rsync source is: ${RSYNC_SOURCE}/" 9 | echo "Rsync destination is: ${RSYNC_DEST}/" 10 | 11 | # Rsync to destination and preserve symlinks and file perms. 12 | rsync -vah "${RSYNC_SOURCE}/" "${RSYNC_DEST}" 13 | 14 | # Keep the container alive. 15 | echo "" 16 | echo "Finished sync job, keeping container alive..." 17 | echo "" 18 | php /root/noop.php 19 | -------------------------------------------------------------------------------- /code/sites/README.txt: -------------------------------------------------------------------------------- 1 | This directory structure contains the settings and configuration files specific 2 | to your site or sites and is an integral part of multisite configurations. 3 | 4 | It is now recommended to place your custom and downloaded extensions in the 5 | /modules, /themes, and /profiles directories located in the Drupal root. The 6 | sites/all/ subdirectory structure, which was recommended in previous versions 7 | of Drupal, is still supported. 8 | 9 | See core/INSTALL.txt for information about single-site installation or 10 | multisite configuration. 11 | -------------------------------------------------------------------------------- /scripts/docker/code-php-fpm/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Now that the volume is mounted, change owner and make it read/write 5 | chown www-data:www-data /var/www/drupal/web/sites/default/files 6 | chmod -R 777 /var/www/drupal/web/sites/default/files 7 | 8 | # Do this one too while we're at it (could be done in Dockerfile) 9 | mkdir -p /var/www/drupal/config/sync 10 | chown www-data:www-data /var/www/drupal/config/sync 11 | chmod -R 777 /var/www/drupal/config/sync 12 | 13 | # Now that volume is usable by non-root user, start up PHP on port 9000 14 | php-fpm 15 | -------------------------------------------------------------------------------- /k8-minikube/build/build-base-images.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -x 3 | 4 | ROOT_DIR=`pwd` 5 | 6 | echo "Building base images... " 7 | 8 | cd nginx 9 | docker build --tag registry.ng.bluemix.net/alexanderallen/nginx:latest . 10 | 11 | cd $ROOT_DIR/php-fpm 12 | docker build --tag registry.ng.bluemix.net/alexanderallen/php-fpm:latest . 13 | 14 | cd $ROOT_DIR/php-cli 15 | docker build --tag registry.ng.bluemix.net/alexanderallen/php-cli:latest . 16 | 17 | cd $ROOT_DIR/app-image 18 | docker build --tag registry.ng.bluemix.net/alexanderallen/app-image:latest . 19 | 20 | echo "Build complete." 21 | -------------------------------------------------------------------------------- /scripts/docker/config-nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile provides a base level of NGINX configured to delegate PHP requests to PHP-FPM 2 | 3 | ARG NGINX_VERSION=latest 4 | 5 | FROM nginx:${NGINX_VERSION} 6 | 7 | # Set consistent timezone 8 | ENV CONTAINER_TIMEZONE="UTC" 9 | RUN rm -f /etc/localtime \ 10 | && ln -s /usr/share/zoneinfo/${CONTAINER_TIMEZONE} /etc/localtime 11 | 12 | # Install prerequisite OS packages 13 | RUN apt-get update -y && apt-get upgrade -y && apt-get install -y curl 14 | 15 | RUN rm /etc/nginx/conf.d/default.conf 16 | 17 | COPY fastcgi.conf /etc/nginx/conf.d/ 18 | 19 | EXPOSE 80 20 | 21 | CMD ["nginx", "-g", "daemon off;"] 22 | -------------------------------------------------------------------------------- /scripts/pipeline/README.md: -------------------------------------------------------------------------------- 1 | # Pipeline scripts 2 | This directory contains the legacy scripts for building and deploying from IBM DevOps Services to the IBM Container Service. 3 | 4 | The goal is to have these replace the files in the `local` directory so they can be invoked by a pipeline or by the developer on his or her workstation. 5 | 6 | ### Note About the scripts 7 | 8 | The buildImage.sh, deployScript.sh, and the scripts prefixed with "pipeline-" are used by the pipeline in IBM Cloud Platform to build and deploy to a cluster running in the cloud. These scripts require environment variables that are set as part of the pipeline process and will not work locally. 9 | -------------------------------------------------------------------------------- /scripts/kubernetes/secrets/service-credentials.txt.tpl: -------------------------------------------------------------------------------- 1 | HASH_SALT= 2 | 3 | # Staging cluster 4 | 5 | MYSQL_USER_STG= 6 | MYSQL_PASS_STG= 7 | MYSQL_NAME_STG= 8 | MYSQL_HOST_STG= 9 | MYSQL_PORT_STG= 10 | 11 | REDIS_USER_STG= 12 | REDIS_PASS_STG= 13 | REDIS_HOST_STG= 14 | REDIS_PORT_STG= 15 | 16 | MEMCACHED_USER_STG= 17 | MEMCACHED_PASS_STG= 18 | MEMCACHED_HOST_STG= 19 | MEMCACHED_PORT_STG= 20 | 21 | 22 | # Production cluster 23 | 24 | MYSQL_USER_PRD= 25 | MYSQL_PASS_PRD= 26 | MYSQL_NAME_PRD= 27 | MYSQL_HOST_PRD= 28 | MYSQL_PORT_PRD= 29 | 30 | REDIS_USER_PRD= 31 | REDIS_PASS_PRD= 32 | REDIS_HOST_PRD= 33 | REDIS_PORT_PRD= 34 | 35 | MEMCACHED_USER_PRD= 36 | MEMCACHED_PASS_PRD= 37 | MEMCACHED_HOST_PRD= 38 | MEMCACHED_PORT_PRD= 39 | -------------------------------------------------------------------------------- /scripts/local/deploy-containers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | # Create service credentials as a secret 5 | kubectl delete secret service-credentials 6 | kubectl create secret generic service-credentials --from-env-file=../kubernetes/secrets/service-credentials.txt 7 | 8 | # Processes everything in the kubernetes folder: 9 | # - Create the shared persistent volume 10 | # - Create the deployment replication controllers for NGINX and PHP-FPM 11 | # - Create the services for the NGINX and PHP-FPM deployment 12 | kubectl apply -f ../kubernetes 13 | 14 | # Confirm everything looks good 15 | kubectl describe deployment php-fpm 16 | kubectl describe service php-fpm 17 | kubectl describe deployment nginx 18 | kubectl describe service nginx 19 | -------------------------------------------------------------------------------- /code/drush/transfer-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURRENT_DATE=`/bin/date "+%m-%d-%y"` 4 | 5 | # Dump production data 6 | echo "Extracting production data from the ${MYSQL_NAME_PRD} database on ${MYSQL_HOST_PRD}." 7 | mysqldump --verbose --add-drop-table --quote-names -u${MYSQL_USER_PRD} -p${MYSQL_PASS_PRD} -h${MYSQL_HOST_PRD} ${MYSQL_NAME_PRD} > /root/backups/production-backup.sql 8 | tar -zcvf /root/backups/backup-${CURRENT_DATE}.tar.gz /root/backups/production-backup.sql 9 | 10 | # Restore data into staging 11 | echo "Restoring production data to staging database ${MYSQL_NAME_STG} on ${MYSQL_HOST_STG}." 12 | mysql --verbose -u${MYSQL_USER_STG} -p${MYSQL_PASS_STG} -h${MYSQL_HOST_STG} ${MYSQL_NAME_STG} < /root/backups/production-backup.sql 13 | -------------------------------------------------------------------------------- /k8-minikube/bash-php-fpm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | kubectl exec -i -t $(kubectl get pod -l "app=php-fpm" -o jsonpath='{.items[0].metadata.name}') -- bash 4 | 5 | # Dump the Json so that you can narrow it down using Json Path: 6 | # kubectl get pod -l "app=php-fpm" -o=json 7 | 8 | # Example: the json path 9 | # .items[0].spec.containers[?(@.name=="php-cli-sidecar-phpfpm")].name 10 | 11 | # Example: get container name. 12 | # └─[$]> kubectl get pod -l "app=php-fpm" -o jsonpath='{.items[0].spec.containers[?(@.name=="php-cli-sidecar-phpfpm")].name}' 13 | # php-cli-sidecar-phpfpm 14 | 15 | # Connect to a particular container. 16 | # kubectl exec -i -t $(kubectl get pod -l "app=php-fpm" -o jsonpath='{.items[0].metadata.name}') -c php-cli-sidecar-phpfpm -- bash 17 | -------------------------------------------------------------------------------- /k8-minikube/bash-container.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | kubectl exec -i -t $(kubectl get pod -l "app=$1" -o jsonpath='{.items[0].metadata.name}') -c "${2}" -- bash 4 | 5 | # Dump the Json so that you can narrow it down using Json Path: 6 | # kubectl get pod -l "app=php-fpm" -o=json 7 | 8 | # Example: the json path 9 | # .items[0].spec.containers[?(@.name=="php-cli-sidecar-phpfpm")].name 10 | 11 | # Example: get container name. 12 | # └─[$]> kubectl get pod -l "app=php-fpm" -o jsonpath='{.items[0].spec.containers[?(@.name=="php-cli-sidecar-phpfpm")].name}' 13 | # php-cli-sidecar-phpfpm 14 | 15 | # Connect to a particular container. 16 | # kubectl exec -i -t $(kubectl get pod -l "app=php-fpm" -o jsonpath='{.items[0].metadata.name}') -c php-cli-sidecar-phpfpm -- bash 17 | -------------------------------------------------------------------------------- /k8-minikube/build/nginx/ping.conf: -------------------------------------------------------------------------------- 1 | upstream php_fpm_service { 2 | server php-fpm:9000; 3 | } 4 | 5 | server { 6 | # server_name example.com; 7 | 8 | listen 80 default; 9 | 10 | root /www; ## <-- Your only path reference. 11 | 12 | location ~ \.php$ { 13 | try_files $uri =404; 14 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 15 | fastcgi_pass php_fpm_service; 16 | fastcgi_index index.php; 17 | include fastcgi_params; 18 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 19 | fastcgi_param PATH_INFO $fastcgi_path_info; 20 | } 21 | 22 | location ~ ^/(status|ping)$ { 23 | allow all; 24 | include fastcgi_params; 25 | fastcgi_pass php_fpm_service; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /scripts/pipeline/deployScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Starting deploy stage" 4 | 5 | cd scripts 6 | 7 | #Pulling image details 8 | IMAGE=$(grep "IMAGE=" pipeline/build.properties|cut -d'=' -f2) 9 | REGISTRY_URL=$(grep "REGISTRY_URL" pipeline/build.properties|cut -d'=' -f2) 10 | REGISTRY_NAMESPACE=$(grep "REGISTRY_NAMESPACE" pipeline/build.properties|cut -d'=' -f2) 11 | 12 | if kubectl get deployments | grep "${IMAGE}" ; then 13 | 14 | #Applying configs 15 | 16 | kubectl set image deployment/${IMAGE} ${IMAGE}="${REGISTRY_URL}/${REGISTRY_NAMESPACE}/code-${IMAGE}:latest" 17 | echo $? 18 | 19 | kubectl rollout status deployment/${IMAGE} 20 | echo $? 21 | 22 | kubectl get pods 23 | 24 | else 25 | 26 | kubectl apply -f kubernetes/${IMAGE}.yaml 27 | 28 | echo $? 29 | 30 | kubectl get pods 31 | 32 | fi 33 | -------------------------------------------------------------------------------- /k8-minikube/build/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile provides a base level of NGINX configured to delegate PHP requests to PHP-FPM 2 | 3 | FROM nginx:latest 4 | 5 | #FROM nginx:${NGINX_VERSION} 6 | ARG NGINX_VERSION=latest 7 | 8 | # Set consistent timezone 9 | ENV CONTAINER_TIMEZONE="UTC" 10 | RUN rm -f /etc/localtime \ 11 | && ln -s /usr/share/zoneinfo/${CONTAINER_TIMEZONE} /etc/localtime 12 | 13 | # Install prerequisite OS packages 14 | RUN apt-get update && apt-get install -y curl iputils-ping libfcgi0ldbl vim 15 | 16 | RUN mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.bak 17 | 18 | # Copy Nginx configs. 19 | COPY nginx.conf /etc/nginx/nginx.conf 20 | COPY fastcgi_params /etc/nginx/fastcgi_params 21 | COPY fastcgi.conf /etc/nginx/conf.d/fastcgi.conf 22 | 23 | EXPOSE 80 24 | 25 | CMD ["nginx", "-g", "daemon off;"] 26 | -------------------------------------------------------------------------------- /code/drush/helloworld.script: -------------------------------------------------------------------------------- 1 | output()->writeln("Hello world!"); 10 | $this->output()->writeln("The extra options/arguments to this command were:"); 11 | $this->output()->writeln(print_r($extra, true)); 12 | 13 | // 14 | // We can check which site was bootstrapped via 15 | // the '@self' alias, which is defined only if 16 | // there is a bootstrapped site. 17 | // 18 | $self = Drush::aliasManager()->getSelf();; 19 | if (empty($self->root())) { 20 | $this->output()->writeln('No bootstrapped site.'); 21 | } 22 | else { 23 | $this->output()->writeln('The following site is bootstrapped:'); 24 | $this->output()->writeln(print_r($self->legacyRecord(), true)); 25 | } 26 | -------------------------------------------------------------------------------- /k8-minikube/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set +x 4 | 5 | # Start minikube. 6 | minikube start 7 | 8 | # Connect Docker and kubectl to Minikube context. 9 | eval $(minikube docker-env) 10 | 11 | # Delete everything. 12 | kubectl delete deployment,service,rs --all 13 | 14 | # Recreate all services. 15 | kubectl apply -f volumes -f pods-services 16 | 17 | # Show system status. 18 | kubectl get deployment,service,rs,pods 19 | 20 | # Show nginx endpoint, automatically shows up when ready. 21 | minikube service nginx --url 22 | 23 | # Open default browser to Nginx 24 | minikube service nginx 25 | 26 | # Pop open the local dashboard. 27 | minikube dashboard 28 | 29 | # Double check docker status, should show minikube containers. 30 | # docker ps 31 | 32 | # Nginx is now ready, start tailing it. 33 | # kubectl logs -f nginx 34 | # kubectl logs -f nginx-3049359248-5hvt9 35 | -------------------------------------------------------------------------------- /k8-minikube/recreate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set +x 4 | 5 | # Start minikube. 6 | # minikube start 7 | 8 | # Connect Docker and kubectl to Minikube context. 9 | eval $(minikube docker-env) 10 | 11 | # Delete everything. 12 | echo "Deleting all the things!" 13 | kubectl delete deployment,service,rs --all 14 | 15 | # Recreate all services. 16 | echo "Re-creating services" 17 | kubectl apply -f volumes -f pods-services 18 | 19 | # Show system status. 20 | kubectl get deployment,service,rs,pods 21 | 22 | # Show nginx endpoint, automatically shows up when ready. 23 | minikube service nginx --url 24 | 25 | # Open default browser to Nginx 26 | minikube service nginx 27 | 28 | # Pop open the local dashboard. 29 | minikube dashboard 30 | 31 | # Double check docker status, should show minikube containers. 32 | # docker ps 33 | 34 | # Nginx is now ready, start tailing it. 35 | # kubectl logs -f nginx 36 | # kubectl logs -f nginx-3049359248-5hvt9 37 | -------------------------------------------------------------------------------- /scripts/pipeline/pipelineDeployScripts/php-cli-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Starting deploy stage" 4 | 5 | cd scripts 6 | 7 | #Pulling image details 8 | IMAGE=$(grep "IMAGE=" pipeline/build.properties|cut -d'=' -f2) 9 | REGISTRY_URL=$(grep "REGISTRY_URL" pipeline/build.properties|cut -d'=' -f2) 10 | REGISTRY_NAMESPACE=$(grep "REGISTRY_NAMESPACE" pipeline/build.properties|cut -d'=' -f2) 11 | 12 | 13 | if kubectl get deployments | grep "${IMAGE}" ; then 14 | 15 | #Applying configs 16 | 17 | echo "Updating image in the deployment and rehashing deployment..." 18 | sed -ie "s/REPLACE_AT_BUILD_TIME/$(date)/g" kubernetes/${IMAGE}.yaml 19 | cat kubernetes/${IMAGE}.yaml 20 | 21 | echo "Starting rolling update..." 22 | kubectl apply -f kubernetes/${IMAGE}.yaml 23 | 24 | kubectl get pods 25 | 26 | else 27 | 28 | echo "No current deployment found. Creating a new deployment" 29 | kubectl apply -f kubernetes/${IMAGE}.yaml 30 | 31 | echo $? 32 | 33 | kubectl get pods 34 | 35 | fi 36 | -------------------------------------------------------------------------------- /docs/SYNCHRONIZING-DATA.md: -------------------------------------------------------------------------------- 1 | ## Synchronizing data and drush scripts 2 | From time to time you may want to bring production data and files into the staging environment so that you have comparable environments. 3 | 4 | ## Synchronizing files 5 | Execute the [`transfer-files.sh`](../code/drush/transfer-files.sh) script. 6 | 7 | This can be done by connecting to the PHP-CLI container. 8 | 9 | `kubectl exec $CONTAINER_NAME /root/drush/transfer-files.sh` 10 | 11 | 12 | ## Synchronizing data 13 | Execute the [`transfer-data.sh`](../code/drush/transfer-data.sh) script. 14 | 15 | `kubectl exec $CONTAINER_NAME /root/drush/transfer-data.sh` 16 | 17 | ## Executing drush scripts 18 | Execute the [`drush-status.sh`](../code/drush/drush-status.sh) script. 19 | 20 | `kubectl exec $CONTAINER_NAME /var/www/drupal/drush/drush-status.sh` 21 | 22 | ## Running arbitrary commands 23 | You can enter a running container and execute commands by passing the container name to the [`kube-exec.sh`](../scripts/pipeline/kube-exec.sh) script. 24 | -------------------------------------------------------------------------------- /k8-minikube/build/app-image/Dockerfile: -------------------------------------------------------------------------------- 1 | # Example application container. 2 | # Extends from the PHP-CLI image so that it can use tools such as Composer. 3 | # Otherwise Busybox would be a candidate source image. 4 | 5 | # Candidate pattern: sidecar container. 6 | # https://github.com/kubernetes/git-sync 7 | 8 | # Reference: 9 | # - https://getcomposer.org/doc/03-cli.md#create-project 10 | 11 | FROM registry.ng.bluemix.net/alexanderallen/php-cli:latest 12 | 13 | # todo: find and check in composer.lock for application. 14 | 15 | # Install Drupal app. 16 | RUN \ 17 | # Create app directory. 18 | mkdir /app \ 19 | # Install Drupal to app directory. 20 | && composer create-project \ 21 | drupal-composer/drupal-project:8.x-dev \ 22 | /app/ \ 23 | --stability dev \ 24 | --no-interaction \ 25 | --no-progress 26 | 27 | # RUN composer dump-autoload --optimize && composer run-script post-install-cmd 28 | 29 | COPY start.sh /root/ 30 | COPY app-sync.sh /root/app-sync.sh 31 | 32 | # Assuming this is using the PV/PVC. 33 | CMD /root/app-sync.sh 34 | -------------------------------------------------------------------------------- /k8-minikube/build/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | user nginx; 3 | worker_processes 1; 4 | 5 | error_log /var/log/nginx/error.log warn; 6 | pid /var/run/nginx.pid; 7 | 8 | 9 | events { 10 | worker_connections 1024; 11 | } 12 | 13 | 14 | http { 15 | include /etc/nginx/mime.types; 16 | default_type application/octet-stream; 17 | 18 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 19 | '$status $body_bytes_sent "$http_referer" ' 20 | '"$http_user_agent" "$http_x_forwarded_for"'; 21 | 22 | access_log /var/log/nginx/access.log main; 23 | 24 | sendfile on; 25 | #tcp_nopush on; 26 | 27 | keepalive_timeout 65; 28 | 29 | #gzip on; 30 | 31 | include /etc/nginx/conf.d/*.conf; 32 | 33 | # Help resolve HTTP 504 timeout errors. 34 | # http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout 35 | proxy_read_timeout 600; 36 | 37 | # https://easyengine.io/tutorials/php/increase-script-execution-time/ 38 | fastcgi_read_timeout 600; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /scripts/pipeline/pipelineDeployScripts/nginx-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Starting deploy stage" 4 | 5 | cd scripts 6 | 7 | #Pulling image details 8 | IMAGE=$(grep "IMAGE=" pipeline/build.properties|cut -d'=' -f2) 9 | REGISTRY_URL=$(grep "REGISTRY_URL" pipeline/build.properties|cut -d'=' -f2) 10 | REGISTRY_NAMESPACE=$(grep "REGISTRY_NAMESPACE" pipeline/build.properties|cut -d'=' -f2) 11 | 12 | echo "Current environment is: ${ENVIRONMENT}" 13 | 14 | if kubectl get deployments | grep "${IMAGE}" ; then 15 | 16 | #Applying configs 17 | 18 | echo "Updating image in the deployment and rehashing deployment..." 19 | sed -ie "s/REPLACE_AT_BUILD_TIME/$(date)/g" kubernetes/${IMAGE}-${ENVIRONMENT}.yaml 20 | cat kubernetes/${IMAGE}-${ENVIRONMENT}.yaml 21 | 22 | echo "Starting rolling update..." 23 | kubectl apply -f kubernetes/${IMAGE}-${ENVIRONMENT}.yaml 24 | 25 | kubectl get pods 26 | 27 | else 28 | 29 | echo "No current deployment found. Creating a new deployment" 30 | kubectl apply -f kubernetes/${IMAGE}-${ENVIRONMENT}.yaml 31 | 32 | echo $? 33 | 34 | kubectl get pods 35 | 36 | fi 37 | -------------------------------------------------------------------------------- /code/profiles/README.txt: -------------------------------------------------------------------------------- 1 | Installation profiles define additional steps that run after the base 2 | installation of Drupal is completed. They may also offer additional 3 | functionality and change the behavior of the site. 4 | 5 | WHAT TO PLACE IN THIS DIRECTORY? 6 | -------------------------------- 7 | 8 | Place downloaded and custom installation profiles in this directory. 9 | Note that installation profiles are generally provided as part of a Drupal 10 | distribution. 11 | 12 | DOWNLOAD ADDITIONAL DISTRIBUTIONS 13 | --------------------------------- 14 | 15 | Contributed distributions from the Drupal community may be downloaded at 16 | https://www.drupal.org/project/project_distribution. 17 | 18 | MULTISITE CONFIGURATION 19 | ----------------------- 20 | 21 | In multisite configurations, installation profiles found in this directory are 22 | available to all sites during their initial site installation. 23 | 24 | MORE INFORMATION 25 | ---------------- 26 | 27 | Refer to the "Installation profiles" section of the README.txt in the Drupal 28 | root directory for further information on extending Drupal with custom profiles. 29 | -------------------------------------------------------------------------------- /k8-minikube/build/php-cli/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile provides a base level of OS packages with PHP-CLI and extensions 2 | 3 | FROM registry.ng.bluemix.net/alexanderallen/php-fpm:latest 4 | 5 | # Specify $COMPOSER_HOME. 6 | ENV COMPOSER_HOME /.root 7 | 8 | # Add global binary directory to PATH. 9 | ENV PATH $COMPOSER_HOME/vendor/bin:$PATH 10 | 11 | # Allow Composer to be run as root. 12 | ENV COMPOSER_ALLOW_SUPERUSER 1 13 | 14 | RUN curl -sS https://getcomposer.org/installer | \ 15 | php -- --install-dir=/usr/bin/ --filename=composer 16 | 17 | WORKDIR $COMPOSER_HOME 18 | 19 | #COPY composer.json /root/composer.json 20 | #COPY composer.lock /root/composer.json 21 | # 22 | ## RUN composer install --no-scripts --no-autoloader 23 | #RUN composer install 24 | 25 | # Dump autoloader, without optimizations. 26 | # Optimizations remove debugging capabilities. 27 | #RUN composer dump-autoload 28 | 29 | # RUN composer dump-autoload --optimize && composer run-script post-install-cmd 30 | 31 | RUN composer require drush/drush:8.x 32 | 33 | # Copy start script 34 | COPY noop.php /root/ 35 | 36 | CMD [ "php", "/root/noop.php" ] 37 | -------------------------------------------------------------------------------- /scripts/pipeline/pipelineDeployScripts/php-fpm-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Starting deploy stage" 4 | 5 | cd scripts 6 | 7 | #Pulling image details 8 | IMAGE=$(grep "IMAGE=" pipeline/build.properties|cut -d'=' -f2) 9 | REGISTRY_URL=$(grep "REGISTRY_URL" pipeline/build.properties|cut -d'=' -f2) 10 | REGISTRY_NAMESPACE=$(grep "REGISTRY_NAMESPACE" pipeline/build.properties|cut -d'=' -f2) 11 | 12 | 13 | echo "Current environment is: ${ENVIRONMENT}" 14 | 15 | 16 | if kubectl get deployments | grep "${IMAGE}" ; then 17 | 18 | #Applying configs 19 | 20 | echo "Updating image in the deployment and rehashing deployment..." 21 | sed -ie "s/REPLACE_AT_BUILD_TIME/$(date)/g" kubernetes/${IMAGE}-${ENVIRONMENT}.yaml 22 | cat kubernetes/${IMAGE}-${ENVIRONMENT}.yaml 23 | 24 | echo "Starting rolling update..." 25 | kubectl apply -f kubernetes/${IMAGE}-${ENVIRONMENT}.yaml 26 | 27 | kubectl get pods 28 | 29 | else 30 | 31 | echo "No current deployment found. Creating a new deployment" 32 | kubectl apply -f kubernetes/${IMAGE}-${ENVIRONMENT}.yaml 33 | 34 | echo $? 35 | 36 | kubectl get pods 37 | 38 | fi 39 | -------------------------------------------------------------------------------- /k8-minikube/build/nginx/fastcgi_params: -------------------------------------------------------------------------------- 1 | 2 | fastcgi_param QUERY_STRING $query_string; 3 | fastcgi_param REQUEST_METHOD $request_method; 4 | fastcgi_param CONTENT_TYPE $content_type; 5 | fastcgi_param CONTENT_LENGTH $content_length; 6 | 7 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 8 | fastcgi_param REQUEST_URI $request_uri; 9 | fastcgi_param DOCUMENT_URI $document_uri; 10 | fastcgi_param DOCUMENT_ROOT $document_root; 11 | fastcgi_param SERVER_PROTOCOL $server_protocol; 12 | fastcgi_param REQUEST_SCHEME $scheme; 13 | fastcgi_param HTTPS $https if_not_empty; 14 | 15 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 16 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 17 | 18 | fastcgi_param REMOTE_ADDR $remote_addr; 19 | fastcgi_param REMOTE_PORT $remote_port; 20 | fastcgi_param SERVER_ADDR $server_addr; 21 | fastcgi_param SERVER_PORT $server_port; 22 | fastcgi_param SERVER_NAME $server_name; 23 | 24 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 25 | fastcgi_param REDIRECT_STATUS 200; 26 | -------------------------------------------------------------------------------- /scripts/pipeline/pipeline-build-on-config-change.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Inspect all the files in config and extract into env variables 5 | set -o allexport 6 | source ../../config/$IMAGE-version.txt 7 | set +o allexport 8 | 9 | echo "IMAGE = ${IMAGE}" 10 | 11 | case "$IMAGE" in 12 | "nginx") 13 | VERSION=$NGINX_VERSION 14 | ;; 15 | "php-cli") 16 | VERSION=$PHP_CLI_VERSION 17 | ;; 18 | "php-fpm") 19 | VERSION=$PHP_FPM_VERSION 20 | ;; 21 | esac 22 | 23 | echo "${IMAGE} version = ${VERSION}" 24 | 25 | # Build each image and push 26 | ROOT_DIR=`pwd` 27 | 28 | UPCASE_IMAGE=${IMAGE^^} 29 | 30 | 31 | # Build the NGINX image (configure Fast CGI) 32 | cd ../docker/config-$IMAGE 33 | bx cr build \ 34 | --tag registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/config-${IMAGE}:${VERSION} \ 35 | --tag registry.ng.bluemix.net/orod/config-${IMAGE}:latest \ 36 | --build-arg ${UPCASE_IMAGE//-/_}_VERSION=${VERSION} \ 37 | . 38 | # --no-cache \ 39 | #docker push registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/config-${IMAGE}:latest 40 | 41 | echo "Done with config build" 42 | 43 | # Move back to ROOT_DIR 44 | cd $ROOT_DIR 45 | -------------------------------------------------------------------------------- /docs/PHP-CLI-DRUSH.md: -------------------------------------------------------------------------------- 1 | ## Managing Drupal clusters 2 | You can use the PHP-CLI container to execute regular `bash` commands or `drush` commands against the deployed staging and production systems. 3 | 4 | ## Execute arbitrary commands 5 | - Use `kubectl get pods` to find the name of the PHP-CLI container 6 | - Use `scripts/pipeline/kube-exec.sh` and pass the name of the container instance 7 | - You will be in the `/root/drush/` folder where you can run ad hoc commands or scripts added to the image from the `code/drush` directory. 8 | - For example `./transfer-data.sh` or `./transfer-files.sh` 9 | - You can also invoke those as a one liner with 10 | - `kubectl exec $PHP_CLI_CONTAINER_NAME /root/drush/transfer-data.sh` 11 | - `kubectl exec $PHP_CLI_CONTAINER_NAME /root/drush/transfer-files.sh` 12 | 13 | ## Execute Drush commands 14 | - As above, you can exec into the PHP-CLI container and run `drush` commands as needed. 15 | - For example 16 | - `drush sql-cli --db-url="mysql://${MYSQL_USER_STG}:${MYSQL_PASS_STG}@${MYSQL_HOST_STG}:${MYSQL_PORT_STG}/${MYSQL_NAME_STG}"` 17 | - `drush sql-cli --db-url="mysql://${MYSQL_USER_PRD}:${MYSQL_PASS_PRD}@${MYSQL_HOST_PRD}:${MYSQL_PORT_PRD}/${MYSQL_NAME_PRD}"` 18 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/config/etc/pear.conf: -------------------------------------------------------------------------------- 1 | #PEAR_Config 0.9 2 | a:33:{s:9:"cache_dir";s:15:"/tmp/pear/cache";s:15:"default_channel";s:12:"pear.php.net";s:16:"preferred_mirror";s:12:"pear.php.net";s:13:"remote_config";s:0:"";s:13:"auto_discover";i:0;s:13:"master_server";s:12:"pear.php.net";s:10:"http_proxy";s:0:"";s:7:"php_dir";s:18:"/usr/local/lib/php";s:7:"ext_dir";s:55:"/usr/local/lib/php/extensions/no-debug-non-zts-20160303";s:7:"doc_dir";s:22:"/usr/local/lib/php/doc";s:7:"bin_dir";s:14:"/usr/local/bin";s:8:"data_dir";s:23:"/usr/local/lib/php/data";s:7:"cfg_dir";s:22:"/usr/local/lib/php/cfg";s:7:"www_dir";s:25:"/usr/local/lib/php/htdocs";s:7:"man_dir";s:28:"/usr/local/lib/php/local/man";s:8:"test_dir";s:23:"/usr/local/lib/php/test";s:8:"temp_dir";s:14:"/tmp/pear/temp";s:12:"download_dir";s:18:"/tmp/pear/download";s:7:"php_bin";s:18:"/usr/local/bin/php";s:10:"php_prefix";s:0:"";s:10:"php_suffix";s:0:"";s:7:"php_ini";s:0:"";s:12:"metadata_dir";s:0:"";s:8:"username";s:0:"";s:8:"password";s:0:"";s:7:"verbose";i:1;s:15:"preferred_state";s:6:"stable";s:5:"umask";i:18;s:9:"cache_ttl";i:3600;s:8:"sig_type";s:3:"gpg";s:7:"sig_bin";s:12:"/usr/bin/gpg";s:9:"sig_keyid";s:0:"";s:10:"sig_keydir";s:23:"/usr/local/etc/pearkeys";} -------------------------------------------------------------------------------- /scripts/local/build-containers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | ROOT_DIR=`pwd` 5 | 6 | # Log into the Bluemix Container Registry 7 | bx cr login 8 | 9 | # Build the NGINX image (configure Fast CGI) 10 | cd ../docker/nginx 11 | # docker build --tag registry.ng.bluemix.net/jjdojo/nginx:latest --no-cache . 12 | docker build --tag registry.ng.bluemix.net/jjdojo/nginx:latest . 13 | docker push registry.ng.bluemix.net/jjdojo/nginx:latest 14 | 15 | # Move back to ROOT_DIR 16 | cd $ROOT_DIR 17 | 18 | # Build the PHP-FPM image (base image, inject code, run composer) 19 | cd ../docker/php-fpm 20 | # docker build --tag registry.ng.bluemix.net/jjdojo/php-fpm:latest --no-cache . 21 | docker build --tag registry.ng.bluemix.net/jjdojo/php-fpm:latest . 22 | docker push registry.ng.bluemix.net/jjdojo/php-fpm:latest 23 | 24 | # Move back to ROOT_DIR 25 | cd $ROOT_DIR 26 | 27 | # Build the PHP-FPM image (base image, inject code, run composer) 28 | cd ../docker/php-cli 29 | # docker build --tag registry.ng.bluemix.net/jjdojo/php-cli:latest --no-cache . 30 | docker build --tag registry.ng.bluemix.net/jjdojo/php-cli:latest . 31 | docker push registry.ng.bluemix.net/jjdojo/php-cli:latest 32 | 33 | # Move back to ROOT_DIR 34 | cd $ROOT_DIR 35 | -------------------------------------------------------------------------------- /scripts/local/setup-infrastructure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | # If needed. Otherwise set up the cluster and the services in the UI. 5 | 6 | # Based on https://ibm-blockchain.github.io/setup/ 7 | 8 | # 1. Prepare required CLIs and plugins 9 | # 1.1. Download and install kubectl CLI 10 | # https://kubernetes.io/docs/tasks/kubectl/install/ 11 | # 1.2. Download and install the IBM Cloud CLI 12 | # http://clis.ng.bluemix.net/ui/home.html 13 | # 14 | # 1.3. Add the IBM Cloud plugins repo 15 | # bx plugin repo-add bluemix https://plugins.ng.bluemix.net 16 | # 17 | # 1.4. Add the container service plugin 18 | # bx plugin install container-service -r bluemix 19 | 20 | # 2. Setup a cluster 21 | # 2.1. Point IBM Cloud CLI to production API 22 | # bx api api.ng.bluemix.net 23 | # 24 | # 2.2. Login to IBM Cloud (use --sso if federated) 25 | # bx login 26 | # 27 | # 2.3. Wait for the cluster to be ready 28 | # $ bx cs clusters 29 | # 30 | # 2.4. Configure kubectl to use the cluster 31 | # $ bx cs cluster-config blockchain 32 | # export KUBECONFIG=... 33 | 34 | # 3. Provision MySQL from Compose or ClearDB 35 | # 3.1. TODO: 36 | 37 | # 4. Provision Redis from Compose or Redis Labs 38 | # 4.1. TODO: 39 | 40 | # 5. Provision memcached from Redis Labs 41 | # 5.1. TODO: 42 | -------------------------------------------------------------------------------- /docs/ONGOING-DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | ## Ongoing development 2 | Now that the Kubernetes cluster is provisioned and you have a set of containers running, the ongoing DevOps workflow will be to write code locally, push that to GitHub into the `config` or `code` directories, and that in turn will start a job to rebuild the container images with the versioned code. These images are in turn pushed to the private Docker registry, and pulled to run on the pods. 3 | 4 | ## Updating the base image configuration 5 | Push updates to the `config` directory. The pipeline will detect changes and initiate a base image rebuild, and then another build on top for the `code` files. 6 | 7 | ## Updating the Drupal and code version 8 | Push updates to the `code` directory. The pipeline will detect changes and initiate a custom image rebuild. 9 | 10 | ## Addressing security issues with Vulnerability Advisor 11 | As container images are built and pushed to the IBM Cloud Container Registry, they are automatically scanned by the Vulnerability Advisor. 12 | 13 | You can see whether there are any vulnerabilities in your images by listing the images: 14 | ```bash 15 | bx cr images 16 | ``` 17 | 18 | If any of them are listed as `Vulnerable` you can then see the specific issues with: 19 | 20 | ```bash 21 | bx cr va $IMAGE_NAME 22 | ``` 23 | -------------------------------------------------------------------------------- /code/themes/README.txt: -------------------------------------------------------------------------------- 1 | Themes allow you to change the look and feel of your Drupal site. You can use 2 | themes contributed by others or create your own. 3 | 4 | WHAT TO PLACE IN THIS DIRECTORY? 5 | -------------------------------- 6 | 7 | Placing downloaded and custom themes in this directory separates downloaded and 8 | custom themes from Drupal core's themes. This allows Drupal core to be updated 9 | without overwriting these files. 10 | 11 | DOWNLOAD ADDITIONAL THEMES 12 | -------------------------- 13 | 14 | Contributed themes from the Drupal community may be downloaded at 15 | https://www.drupal.org/project/project_theme. 16 | 17 | MULTISITE CONFIGURATION 18 | ----------------------- 19 | 20 | In multisite configurations, themes found in this directory are available to 21 | all sites. You may also put themes in the sites/all/themes directory, and the 22 | versions in sites/all/themes will take precedence over versions of the same 23 | themes that are here. Alternatively, the sites/your_site_name/themes directory 24 | pattern may be used to restrict themes to a specific site instance. 25 | 26 | MORE INFORMATION 27 | ----------------- 28 | 29 | Refer to the "Appearance" section of the README.txt in the Drupal root directory 30 | for further information on customizing the appearance of Drupal with custom 31 | themes. 32 | -------------------------------------------------------------------------------- /k8-minikube/build/build-code-images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Inspect all the files in config and extract into env variables 5 | 6 | BUILD_NUMBER=$( date +%s ) 7 | 8 | # Build each image and push 9 | ROOT_DIR=`pwd` 10 | 11 | # Build the NGINX image (configure Fast CGI) 12 | 13 | # rallen: The nginx image should have no code whatsoever in it. 14 | # Whatever user code nginx sees should be on a volume, not on the docker image. 15 | 16 | cd code-nginx 17 | docker build \ 18 | --tag registry.ng.bluemix.net/jjdojo/code-nginx:${BUILD_NUMBER} \ 19 | --tag registry.ng.bluemix.net/jjdojo/code-nginx:latest \ 20 | --build-arg NGINX_VERSION=1.13.5 \ 21 | . 22 | 23 | # Build the PHP-FPM image (base image, inject code, run composer) 24 | cd $ROOT_DIR/code-php-fpm 25 | 26 | # TODO: There's really nothing to build on the code php image. 27 | #docker build \ 28 | # --tag registry.ng.bluemix.net/jjdojo/code-php-fpm:${BUILD_NUMBER} \ 29 | # --tag registry.ng.bluemix.net/jjdojo/code-php-fpm:latest \ 30 | # . 31 | 32 | # Build the PHP-CLI image (base image, inject code, run composer) 33 | cd $ROOT_DIR/code-php-cli 34 | docker build \ 35 | --tag registry.ng.bluemix.net/jjdojo/code-php-cli:${BUILD_NUMBER} \ 36 | --tag registry.ng.bluemix.net/jjdojo/code-php-cli:latest \ 37 | . 38 | 39 | # Move back to ROOT_DIR 40 | cd $ROOT_DIR 41 | 42 | echo "Build completed." 43 | -------------------------------------------------------------------------------- /scripts/pipeline/rolling-code-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # set -e 3 | 4 | # If needed first: 5 | # kubectl delete deployment,service,rs,pvc --all 6 | kubectl delete deployment,service,rs --all 7 | sleep 5 8 | 9 | # Create service credentials as a secret 10 | kubectl delete secret service-credentials 11 | kubectl create secret generic service-credentials --from-env-file=../kubernetes/secrets/service-credentials.txt 12 | 13 | # Processes everything in the kubernetes folder: 14 | # - Create the shared persistent volume 15 | # - Create the deployment replication controllers for NGINX and PHP-FPM 16 | # - Create the services for the NGINX and PHP-FPM deployment 17 | # kubectl apply -f ../kubernetes 18 | 19 | kubectl apply -f ../kubernetes/persistent-volumes.yaml 20 | kubectl apply -f ../kubernetes/php-cli.yaml 21 | 22 | kubectl apply -f ../kubernetes/php-fpm-stg.yaml 23 | kubectl apply -f ../kubernetes/nginx-stg.yaml 24 | 25 | kubectl apply -f ../kubernetes/php-fpm-prd.yaml 26 | kubectl apply -f ../kubernetes/nginx-prd.yaml 27 | 28 | 29 | # Confirm everything looks good 30 | 31 | kubectl describe deployment php-fpm-stg 32 | kubectl describe service php-fpm-stg 33 | kubectl describe deployment nginx-stg 34 | kubectl describe service nginx-stg 35 | 36 | kubectl describe deployment php-fpm-prd 37 | kubectl describe service php-fpm-prd 38 | kubectl describe deployment nginx-prd 39 | kubectl describe service nginx-prd 40 | -------------------------------------------------------------------------------- /scripts/docker/code-nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile builds on the base image and adds static code 2 | 3 | ARG NGINX_VERSION=latest 4 | ARG REGISTRY_NAMESPACE=orod 5 | 6 | FROM registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/config-nginx:${NGINX_VERSION} 7 | 8 | # Allow Composer to be run as root. 9 | ENV COMPOSER_ALLOW_SUPERUSER 1 10 | 11 | # Install prerequisite OS packages 12 | RUN apt-get update -y && apt-get upgrade -y && apt-get install -y php7.0-cli php7.0-xml php-mbstring zip git 13 | 14 | # --- We do this to add static files, but we should look at an alternative approach. 15 | 16 | RUN curl -sS https://getcomposer.org/installer | \ 17 | php -- --install-dir=/usr/bin/ --filename=composer 18 | 19 | COPY tmp/composer.json ./ 20 | 21 | # COPY tmp/composer.lock ./ 22 | 23 | RUN composer install --no-scripts --no-autoloader 24 | 25 | COPY . ./ 26 | 27 | RUN composer create-project drupal-composer/drupal-project:8.x-dev /var/www/drupal/ --stability dev --no-interaction 28 | 29 | ADD tmp/modules/ /var/www/drupal/web/modules/ 30 | ADD tmp/profiles/ /var/www/drupal/web/profiles/ 31 | ADD tmp/sites/ /var/www/drupal/web/sites/ 32 | ADD tmp/themes/ /var/www/drupal/web/themes/ 33 | ADD tmp/config/ /var/www/drupal/config/ 34 | 35 | # --- We do the above to add static files, but we should look at an alternative approach. 36 | 37 | WORKDIR /var/www/drupal/web 38 | 39 | COPY start.sh /root/ 40 | RUN chmod +x /root/start.sh 41 | 42 | EXPOSE 80 43 | 44 | ENTRYPOINT ["/root/start.sh"] 45 | -------------------------------------------------------------------------------- /k8-minikube/jobs/app-image-rsync.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: app-image-rsync 5 | spec: 6 | template: 7 | metadata: 8 | name: app-image-rsync 9 | spec: 10 | containers: 11 | - image: "registry.ng.bluemix.net/alexanderallen/app-image:latest" 12 | name: app-sidecar 13 | imagePullPolicy: IfNotPresent 14 | env: 15 | # Location inside the application image containing desired code. 16 | - name: RSYNC_SOURCE 17 | value: /app 18 | # Shared volume where to copy the data to (could be emptyDir, PV/NFS, etc.). 19 | - name: RSYNC_DEST 20 | value: /www/app 21 | volumeMounts: 22 | 23 | - mountPath: /www 24 | name: sites-local-storage 25 | readOnly: false 26 | 27 | - mountPath: /www-data 28 | name: shared-volume 29 | readOnly: false 30 | 31 | # Development. 32 | - mountPath: /root/app-sync.sh 33 | name: sites-local-storage 34 | subPath: nginx-php-container-cluster/k8-minikube/build/app-image/app-sync.sh 35 | 36 | restartPolicy: Never 37 | 38 | volumes: 39 | - 40 | name: sites-local-storage 41 | persistentVolumeClaim: 42 | claimName: local-sites-claim 43 | 44 | # Ephemeral tmpfs (ramdisk) volume for sharing code. 45 | - name: shared-volume 46 | emptyDir: {} 47 | # backoffLimit: 1 48 | -------------------------------------------------------------------------------- /k8-minikube/build/php-cli/php-cli.conf: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;; 2 | ; Global Options ; 3 | ;;;;;;;;;;;;;;;;;; 4 | 5 | [global] 6 | ; Pid file 7 | ; Default Value: none 8 | pid = /var/run/php-worker/php-worker.pid 9 | 10 | ; Error log file 11 | error_log = /var/log/php-worker/error.log 12 | 13 | ; Log level 14 | ; Possible Values: alert, error, warning, notice, debug 15 | ; Default Value: notice 16 | ;log_level = notice 17 | 18 | ; If this number of child processes exit with SIGSEGV or SIGBUS within the time 19 | ; interval set by emergency_restart_interval then FPM will restart. A value 20 | ; of '0' means 'Off'. 21 | ; Default Value: 0 22 | ;emergency_restart_threshold = 0 23 | 24 | ; Interval of time used by emergency_restart_interval to determine when 25 | ; a graceful restart will be initiated. This can be useful to work around 26 | ; accidental corruptions in an accelerator's shared memory. 27 | ; Available Units: s(econds), m(inutes), h(ours), or d(ays) 28 | ; Default Unit: seconds 29 | ; Default Value: 0 30 | ;emergency_restart_interval = 0 31 | 32 | ; Time limit for child processes to wait for a reaction on signals from master. 33 | ; Available units: s(econds), m(inutes), h(ours), or d(ays) 34 | ; Default Unit: seconds 35 | ; Default Value: 0 36 | ;process_control_timeout = 0 37 | 38 | ; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging. 39 | ; Default Value: yes 40 | ;daemonize = yes 41 | ; STM - set deamonize to no for Docker 42 | daemonize = no 43 | 44 | ;;;;;;;;;;;;;;;;;;;; 45 | ; Pool Definitions ; 46 | ;;;;;;;;;;;;;;;;;;;; 47 | -------------------------------------------------------------------------------- /scripts/docker/config-php-cli/php-cli.conf: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;; 2 | ; Global Options ; 3 | ;;;;;;;;;;;;;;;;;; 4 | 5 | [global] 6 | ; Pid file 7 | ; Default Value: none 8 | pid = /var/run/php-worker/php-worker.pid 9 | 10 | ; Error log file 11 | error_log = /var/log/php-worker/error.log 12 | 13 | ; Log level 14 | ; Possible Values: alert, error, warning, notice, debug 15 | ; Default Value: notice 16 | ;log_level = notice 17 | 18 | ; If this number of child processes exit with SIGSEGV or SIGBUS within the time 19 | ; interval set by emergency_restart_interval then FPM will restart. A value 20 | ; of '0' means 'Off'. 21 | ; Default Value: 0 22 | ;emergency_restart_threshold = 0 23 | 24 | ; Interval of time used by emergency_restart_interval to determine when 25 | ; a graceful restart will be initiated. This can be useful to work around 26 | ; accidental corruptions in an accelerator's shared memory. 27 | ; Available Units: s(econds), m(inutes), h(ours), or d(ays) 28 | ; Default Unit: seconds 29 | ; Default Value: 0 30 | ;emergency_restart_interval = 0 31 | 32 | ; Time limit for child processes to wait for a reaction on signals from master. 33 | ; Available units: s(econds), m(inutes), h(ours), or d(ays) 34 | ; Default Unit: seconds 35 | ; Default Value: 0 36 | ;process_control_timeout = 0 37 | 38 | ; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging. 39 | ; Default Value: yes 40 | ;daemonize = yes 41 | ; STM - set deamonize to no for Docker 42 | daemonize = no 43 | 44 | ;;;;;;;;;;;;;;;;;;;; 45 | ; Pool Definitions ; 46 | ;;;;;;;;;;;;;;;;;;;; 47 | -------------------------------------------------------------------------------- /scripts/docker/code-php-cli/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PHP_CLI_VERSION=latest 2 | ARG REGISTRY_NAMESPACE=orod 3 | 4 | FROM registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/config-php-cli:${PHP_CLI_VERSION} 5 | 6 | ARG DRUSH_VERSION 7 | 8 | RUN apt-get update -y && apt-get upgrade -y 9 | 10 | # Worker name and index (TODO: where to set this?) 11 | ENV WORKER_NAME="send-emails" \ 12 | WORKER_INDEX="1" 13 | 14 | # Register the COMPOSER_HOME environment variable. 15 | ENV COMPOSER_HOME=/root/.composer 16 | 17 | # Add global binary directory to PATH. 18 | ENV PATH /root/.composer/vendor/bin:$PATH 19 | 20 | # Allow Composer to be run as root. 21 | ENV COMPOSER_ALLOW_SUPERUSER=1 22 | 23 | RUN curl -sS https://getcomposer.org/installer | \ 24 | php -- --install-dir=/usr/bin/ --filename=composer 25 | 26 | COPY tmp/composer.json ./ 27 | 28 | # COPY tmp/code/composer.lock ./ 29 | 30 | RUN composer install --no-scripts --no-autoloader 31 | 32 | COPY . ./ 33 | 34 | RUN composer global require drush/drush:8.x 35 | 36 | ADD tmp/composer.json /root/drush/ 37 | ADD tmp/composer.lock /root/drush/ 38 | 39 | # Add Drush specific scripts 40 | ADD tmp/drush/ /root/drush/ 41 | RUN chmod +x /root/drush/*.sh 42 | 43 | # Add database connection info 44 | ADD tmp/sites/default/ /root/drush/sites/default/ 45 | 46 | # Prep a backup directory 47 | RUN mkdir /root/backups/ 48 | 49 | WORKDIR /root/drush/ 50 | 51 | # Change this to be a Drush command? 52 | # This container is more here to just exec into rather than run something headless 53 | CMD php $WORKER_NAME/$WORKER_NAME.php WORKER_INDEX=$WORKER_INDEX 54 | -------------------------------------------------------------------------------- /scripts/pipeline/buildImage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -e "Build environment variables:" 4 | echo "REGISTRY_URL=${REGISTRY_URL}" 5 | echo "REGISTRY_NAMESPACE=${REGISTRY_NAMESPACE}" 6 | echo "IMAGE_NAME=${IMAGE_NAME}" 7 | 8 | IMAGE=$IMAGE_NAME 9 | 10 | echo $IMAGE 11 | 12 | echo "Files changed in previous commit :" 13 | echo `git log -m -1 --name-only` 14 | 15 | # Check for mentions of the config and docker directories in the last commit. 16 | if [ `git log -m -1 --name-only | grep "config/"` ] || [ `git log -m -1 --name-only | grep "docker/"` ] ; then 17 | echo "Changes found in config/ or docker/ directory" 18 | # Call the build-on-config-change.sh script 19 | . ./pipeline-build-on-config-change.sh 20 | . ./pipeline-build-on-code-change.sh 21 | # Check for changes to the /code directory in the last commit. 22 | elif git log -m -1 --name-only | grep "code/" ; then 23 | echo "Changes found in code/ directory" 24 | # Call the build-on-code-change.sh script 25 | . ./pipeline-build-on-code-change.sh 26 | fi 27 | 28 | # Pass kubernetes files along with build artifacts 29 | cp -r ../kubernetes/ $ARCHIVE_DIR 30 | 31 | echo -e "Copying artifacts needed for deployment and testing" 32 | 33 | # Save the registry url and namespace in the build artifacts to be used in deploy stage. 34 | echo "REGISTRY_URL=${REGISTRY_URL}" >> $ARCHIVE_DIR/build.properties 35 | echo "REGISTRY_NAMESPACE=${REGISTRY_NAMESPACE}" >> $ARCHIVE_DIR/build.properties 36 | echo "IMAGE=${IMAGE}" >> $ARCHIVE_DIR/build.properties 37 | 38 | # IMAGE_NAME from build.properties is used by Vulnerability Advisor job to reference the image qualified location in registry 39 | echo "IMAGE_NAME=${REGISTRY_URL}/${REGISTRY_NAMESPACE}/${IMAGE}:latest" >> $ARCHIVE_DIR/build.properties 40 | -------------------------------------------------------------------------------- /code/modules/README.txt: -------------------------------------------------------------------------------- 1 | Modules extend your site functionality beyond Drupal core. 2 | 3 | WHAT TO PLACE IN THIS DIRECTORY? 4 | -------------------------------- 5 | 6 | Placing downloaded and custom modules in this directory separates downloaded and 7 | custom modules from Drupal core's modules. This allows Drupal core to be updated 8 | without overwriting these files. 9 | 10 | DOWNLOAD ADDITIONAL MODULES 11 | --------------------------- 12 | 13 | Contributed modules from the Drupal community may be downloaded at 14 | https://www.drupal.org/project/project_module. 15 | 16 | ORGANIZING MODULES IN THIS DIRECTORY 17 | ------------------------------------ 18 | 19 | You may create subdirectories in this directory, to organize your added modules, 20 | without breaking the site. Some common subdirectories include "contrib" for 21 | contributed modules, and "custom" for custom modules. Note that if you move a 22 | module to a subdirectory after it has been enabled, you may need to clear the 23 | Drupal cache so it can be found. 24 | 25 | There are number of directories that are ignored when looking for modules. These 26 | are 'src', 'lib', 'vendor', 'assets', 'css', 'files', 'images', 'js', 'misc', 27 | 'templates', 'includes', 'fixtures' and 'Drupal'. 28 | 29 | MULTISITE CONFIGURATION 30 | ----------------------- 31 | 32 | In multisite configurations, modules found in this directory are available to 33 | all sites. You may also put modules in the sites/all/modules directory, and the 34 | versions in sites/all/modules will take precedence over versions of the same 35 | module that are here. Alternatively, the sites/your_site_name/modules directory 36 | pattern may be used to restrict modules to a specific site instance. 37 | 38 | MORE INFORMATION 39 | ---------------- 40 | 41 | Refer to the “Developing for Drupal” section of the README.txt in the Drupal 42 | root directory for further information on extending Drupal with custom modules. 43 | -------------------------------------------------------------------------------- /scripts/pipeline/build-on-config-change.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Inspect all the files in config and extract into env variables 5 | set -o allexport 6 | for file in ../../config/*.txt 7 | do 8 | source $file 9 | done 10 | set +o allexport 11 | 12 | echo $NGINX_VERSION 13 | echo $PHP_FPM_VERSION 14 | echo $PHP_CLI_VERSION 15 | 16 | # Build each image and push 17 | ROOT_DIR=`pwd` 18 | 19 | # Log into the IBM Cloud Container Registry 20 | bx cr login 21 | 22 | # Purge all existing images 23 | # bx cr image-rm $(bx cr images -q) 24 | 25 | # Build the NGINX image (configure Fast CGI) 26 | cd ../docker/config-nginx 27 | docker build \ 28 | --tag registry.ng.bluemix.net/orod/config-nginx:${NGINX_VERSION} \ 29 | --tag registry.ng.bluemix.net/orod/config-nginx:latest \ 30 | --build-arg NGINX_VERSION=${NGINX_VERSION} \ 31 | . 32 | # --no-cache \ 33 | docker push registry.ng.bluemix.net/orod/config-nginx:latest 34 | 35 | # Move back to ROOT_DIR 36 | cd $ROOT_DIR 37 | 38 | # Build the PHP-FPM image (base image, inject code, run composer) 39 | cd ../docker/config-php-fpm 40 | docker build \ 41 | --tag registry.ng.bluemix.net/orod/config-php-fpm:${PHP_FPM_VERSION} \ 42 | --tag registry.ng.bluemix.net/orod/config-php-fpm:latest \ 43 | --build-arg PHP_FPM_VERSION=${PHP_FPM_VERSION} \ 44 | . 45 | docker push registry.ng.bluemix.net/orod/config-php-fpm:latest 46 | 47 | # Move back to ROOT_DIR 48 | cd $ROOT_DIR 49 | 50 | # Build the PHP-FPM image (base image, inject code, run composer) 51 | cd ../docker/config-php-cli 52 | docker build \ 53 | --tag registry.ng.bluemix.net/orod/config-php-cli:${PHP_CLI_VERSION} \ 54 | --tag registry.ng.bluemix.net/orod/config-php-cli:latest \ 55 | --build-arg PHP_CLI_VERSION=${PHP_CLI_VERSION} \ 56 | . 57 | docker push registry.ng.bluemix.net/orod/config-php-cli:latest 58 | # Move back to ROOT_DIR 59 | cd $ROOT_DIR 60 | 61 | # TODO: invoke ./build-on-code-change.sh with env params 62 | ./build-on-code-change.sh 63 | -------------------------------------------------------------------------------- /scripts/kubernetes/nginx-prd.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: nginx-prd 6 | spec: 7 | loadBalancerIP: xxx.xxx.xxx.xxx # Change to public from kubectl get cm ibm-cloud-provider-vlan-ip-config -n kube-system -o yaml 8 | ports: 9 | - 10 | port: 80 11 | protocol: TCP 12 | targetPort: 80 13 | selector: 14 | app: nginx-prd 15 | tier: frontend 16 | type: LoadBalancer 17 | --- 18 | apiVersion: extensions/v1beta1 19 | kind: Deployment 20 | metadata: 21 | name: nginx-prd 22 | spec: 23 | replicas: 1 24 | strategy: 25 | rollingUpdate: 26 | maxSurge: 1 27 | maxUnavailable: 1 28 | type: RollingUpdate 29 | template: 30 | metadata: 31 | labels: 32 | app: nginx-prd 33 | tier: frontend 34 | track: stable 35 | spec: 36 | containers: 37 | - 38 | env: 39 | - 40 | name: BUILD_TIME 41 | value: 'REPLACE_AT_BUILD_TIME' 42 | - 43 | name: ENV 44 | value: "prd" 45 | image: "registry.ng.bluemix.net/orod/code-nginx:latest" 46 | lifecycle: 47 | preStop: 48 | exec: 49 | command: 50 | - /usr/sbin/nginx 51 | - "-s" 52 | - quit 53 | name: nginx-prd 54 | imagePullPolicy: Always # Set to IfNotPresent to improve performance at the expense of caching. 55 | ports: 56 | - 57 | containerPort: 80 58 | name: http 59 | volumeMounts: 60 | - 61 | mountPath: /var/www/drupal/web/sites/default/files 62 | name: sites-local-storage 63 | imagePullSecrets: 64 | - 65 | name: image-pull 66 | volumes: 67 | - 68 | name: sites-local-storage 69 | persistentVolumeClaim: 70 | claimName: sites-prd-lv-claim 71 | -------------------------------------------------------------------------------- /scripts/kubernetes/nginx-stg.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: nginx-stg 6 | spec: 7 | loadBalancerIP: xxx.xxx.xxx.xxx # Change to public from kubectl get cm ibm-cloud-provider-vlan-ip-config -n kube-system -o yaml 8 | ports: 9 | - 10 | port: 80 11 | protocol: TCP 12 | targetPort: 80 13 | selector: 14 | app: nginx-stg 15 | tier: frontend 16 | type: LoadBalancer 17 | --- 18 | apiVersion: extensions/v1beta1 19 | kind: Deployment 20 | metadata: 21 | name: nginx-stg 22 | spec: 23 | replicas: 1 24 | strategy: 25 | rollingUpdate: 26 | maxSurge: 1 27 | maxUnavailable: 1 28 | type: RollingUpdate 29 | template: 30 | metadata: 31 | labels: 32 | app: nginx-stg 33 | tier: frontend 34 | track: stable 35 | spec: 36 | containers: 37 | - 38 | env: 39 | - 40 | name: BUILD_TIME 41 | value: 'REPLACE_AT_BUILD_TIME' 42 | - 43 | name: ENV 44 | value: "stg" 45 | image: "registry.ng.bluemix.net/orod/code-nginx:latest" 46 | lifecycle: 47 | preStop: 48 | exec: 49 | command: 50 | - /usr/sbin/nginx 51 | - "-s" 52 | - quit 53 | name: nginx-stg 54 | imagePullPolicy: Always # Set to IfNotPresent to improve performance at the expense of caching. 55 | ports: 56 | - 57 | containerPort: 80 58 | name: http 59 | volumeMounts: 60 | - 61 | mountPath: /var/www/drupal/web/sites/default/files 62 | name: sites-local-storage 63 | imagePullSecrets: 64 | - 65 | name: image-pull 66 | volumes: 67 | - 68 | name: sites-local-storage 69 | persistentVolumeClaim: 70 | claimName: sites-stg-lv-claim 71 | -------------------------------------------------------------------------------- /scripts/docker/config-php-fpm/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile provides a base level of OS packages with PHP-FPM and extensions 2 | 3 | ARG PHP_FPM_VERSION=7.1-fpm 4 | 5 | FROM php:${PHP_FPM_VERSION} 6 | 7 | # Set consistent timezone 8 | ENV CONTAINER_TIMEZONE="UTC" 9 | RUN rm -f /etc/localtime \ 10 | && ln -s /usr/share/zoneinfo/${CONTAINER_TIMEZONE} /etc/localtime 11 | 12 | # Install prerequisite OS packages 13 | RUN apt-get update -y && apt-get upgrade -y && apt-get install -y curl git mysql-client 14 | 15 | # Install the PHP extensions we need 16 | RUN set -ex \ 17 | && buildDeps=' \ 18 | libjpeg62-turbo-dev \ 19 | libpng12-dev \ 20 | libpq-dev \ 21 | zlib1g-dev \ 22 | libicu-dev \ 23 | libmemcached-dev \ 24 | ' \ 25 | && apt-get install -y --no-install-recommends $buildDeps \ 26 | && pecl install -o -f redis \ 27 | && docker-php-ext-enable redis \ 28 | && ls -la /usr/local/etc/php/conf.d/ \ 29 | && docker-php-ext-configure gd \ 30 | --with-jpeg-dir=/usr \ 31 | --with-png-dir=/usr \ 32 | && docker-php-ext-install -j "$(nproc)" gd mbstring opcache pdo pdo_mysql zip 33 | 34 | RUN cd /tmp \ 35 | && git clone -b php7 https://github.com/php-memcached-dev/php-memcached \ 36 | && cd php-memcached \ 37 | && phpize \ 38 | && ./configure \ 39 | && make \ 40 | && cp /tmp/php-memcached/modules/memcached.so /usr/local/lib/php/extensions/no-debug-non-zts-20160303/memcached.so \ 41 | && docker-php-ext-enable memcached \ 42 | && ls -la /usr/local/etc/php/conf.d/ 43 | 44 | # See https://secure.php.net/manual/en/opcache.installation.php 45 | RUN { \ 46 | echo 'opcache.memory_consumption=128'; \ 47 | echo 'opcache.interned_strings_buffer=8'; \ 48 | echo 'opcache.max_accelerated_files=4000'; \ 49 | echo 'opcache.revalidate_freq=60'; \ 50 | echo 'opcache.fast_shutdown=1'; \ 51 | echo 'opcache.enable_cli=1'; \ 52 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini 53 | 54 | EXPOSE 9000 55 | 56 | CMD ["php-fpm"] 57 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile provides a base level of OS packages with PHP-FPM and extensions 2 | 3 | FROM php:7.1-fpm 4 | 5 | ARG PHP_FPM_VERSION=7.1-fpm 6 | 7 | # Set consistent timezone 8 | ENV CONTAINER_TIMEZONE="UTC" 9 | RUN rm -f /etc/localtime \ 10 | && ln -s /usr/share/zoneinfo/${CONTAINER_TIMEZONE} /etc/localtime 11 | 12 | # Install prerequisite OS packages 13 | RUN apt-get update && apt-get install -y curl git vim libfcgi0ldbl 14 | 15 | # Install the PHP extensions we need 16 | RUN set -ex \ 17 | && buildDeps=' \ 18 | libjpeg62-turbo-dev \ 19 | libpng12-dev \ 20 | libpq-dev \ 21 | zlib1g-dev \ 22 | libicu-dev \ 23 | libmemcached-dev \ 24 | ' \ 25 | && apt-get install -y --no-install-recommends $buildDeps \ 26 | && pecl install -o -f redis \ 27 | && docker-php-ext-enable redis \ 28 | && ls -la /usr/local/etc/php/conf.d/ \ 29 | && docker-php-ext-configure gd \ 30 | --with-jpeg-dir=/usr \ 31 | --with-png-dir=/usr \ 32 | && docker-php-ext-install -j "$(nproc)" gd mbstring opcache pdo pdo_mysql zip 33 | 34 | RUN cd /tmp \ 35 | && git clone -b php7 https://github.com/php-memcached-dev/php-memcached \ 36 | && cd php-memcached \ 37 | && phpize \ 38 | && ./configure \ 39 | && make \ 40 | && cp /tmp/php-memcached/modules/memcached.so /usr/local/lib/php/extensions/no-debug-non-zts-20160303/memcached.so \ 41 | && docker-php-ext-enable memcached \ 42 | && ls -la /usr/local/etc/php/conf.d/ 43 | 44 | # See https://secure.php.net/manual/en/opcache.installation.php 45 | RUN { \ 46 | echo 'opcache.memory_consumption=128'; \ 47 | echo 'opcache.interned_strings_buffer=8'; \ 48 | echo 'opcache.max_accelerated_files=4000'; \ 49 | echo 'opcache.revalidate_freq=60'; \ 50 | echo 'opcache.fast_shutdown=1'; \ 51 | echo 'opcache.enable_cli=1'; \ 52 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini 53 | 54 | # Copy customized community configuration back into the image. 55 | COPY config/etc /usr/local/etc 56 | 57 | WORKDIR /www 58 | 59 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing In General 2 | Our project welcomes external contributions! If you have an itch, please feel free to scratch it. 3 | 4 | To contribute code or documentation, please submit a pull request to the [GitHub repository](https://github.com/IBM/drupal-nginx-php-kubernetes). 5 | 6 | A good way to familiarize yourself with the codebase and contribution process is to look for and tackle low-hanging fruit in the [issue tracker](https://github.com/IBM/drupal-nginx-php-kubernetes/issues). Before embarking on a more ambitious contribution, please quickly [get in touch](#communication) with us. 7 | 8 | **We appreciate your effort, and want to avoid a situation where a contribution requires extensive rework (by you or by us), sits in the queue for a long time, or cannot be accepted at all!** 9 | 10 | ### Proposing new features 11 | 12 | If you would like to implement a new feature, please [raise an issue](https://github.com/IBM/drupal-nginx-php-kubernetes/issues) before sending a pull request so the feature can be discussed. 13 | This is to avoid you spending your valuable time working on a feature that the project developers are not willing to accept into the code base. 14 | 15 | ### Fixing bugs 16 | 17 | If you would like to fix a bug, please [raise an issue](https://github.com/IBM/drupal-nginx-php-kubernetes/issues) before sending a pull request so it can be discussed. 18 | If the fix is trivial or non controversial then this is not usually necessary. 19 | 20 | ### Merge approval 21 | 22 | The project maintainers use LGTM (Looks Good To Me) in comments on the code review to 23 | indicate acceptance. A change requires a LGTMs from one of the maintainers. 24 | 25 | For more details, see the [MAINTAINERS](MAINTAINERS.md) page. 26 | 27 | ## Setup 28 | Please add any special setup instructions for your project to help the developer become productive quickly. 29 | 30 | ## Testing 31 | Please provide information that helps the developer test any changes they make before submitting. 32 | 33 | ## Coding style guidelines 34 | Beautiful code rocks! Please share any specific style guidelines you might have for your project. 35 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/php-fpm.conf: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;; 2 | ; FPM Configuration ; 3 | ;;;;;;;;;;;;;;;;;;;;; 4 | 5 | ; All relative paths in this configuration file are relative to PHP's install 6 | ; prefix. 7 | 8 | ; Include one or more files. If glob(3) exists, it is used to include a bunch of 9 | ; files from a glob(3) pattern. This directive can be used everywhere in the 10 | ; file. 11 | ;include=/etc/php-fpm.d/*.conf 12 | ; STM - modify include path for php-fpm docker image 13 | include=/usr/local/etc/php-fpm.d/*.conf 14 | 15 | ;;;;;;;;;;;;;;;;;; 16 | ; Global Options ; 17 | ;;;;;;;;;;;;;;;;;; 18 | 19 | [global] 20 | ; Pid file 21 | ; Default Value: none 22 | pid = /var/run/php-fpm/php-fpm.pid 23 | 24 | ; Error log file 25 | ; Default Value: /var/log/php-fpm.log 26 | error_log = /var/log/php-fpm/error.log 27 | 28 | ; Log level 29 | ; Possible Values: alert, error, warning, notice, debug 30 | ; Default Value: notice 31 | ;log_level = notice 32 | 33 | ; If this number of child processes exit with SIGSEGV or SIGBUS within the time 34 | ; interval set by emergency_restart_interval then FPM will restart. A value 35 | ; of '0' means 'Off'. 36 | ; Default Value: 0 37 | ;emergency_restart_threshold = 0 38 | 39 | ; Interval of time used by emergency_restart_interval to determine when 40 | ; a graceful restart will be initiated. This can be useful to work around 41 | ; accidental corruptions in an accelerator's shared memory. 42 | ; Available Units: s(econds), m(inutes), h(ours), or d(ays) 43 | ; Default Unit: seconds 44 | ; Default Value: 0 45 | ;emergency_restart_interval = 0 46 | 47 | ; Time limit for child processes to wait for a reaction on signals from master. 48 | ; Available units: s(econds), m(inutes), h(ours), or d(ays) 49 | ; Default Unit: seconds 50 | ; Default Value: 0 51 | ;process_control_timeout = 0 52 | 53 | ; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging. 54 | ; Default Value: yes 55 | ;daemonize = yes 56 | ; STM - set daemonize to no for Docker 57 | daemonize = no 58 | 59 | ;;;;;;;;;;;;;;;;;;;; 60 | ; Pool Definitions ; 61 | ;;;;;;;;;;;;;;;;;;;; 62 | 63 | ; See /etc/php-fpm.d/*.conf 64 | -------------------------------------------------------------------------------- /scripts/docker/config-php-fpm/php-fpm.conf: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;; 2 | ; FPM Configuration ; 3 | ;;;;;;;;;;;;;;;;;;;;; 4 | 5 | ; All relative paths in this configuration file are relative to PHP's install 6 | ; prefix. 7 | 8 | ; Include one or more files. If glob(3) exists, it is used to include a bunch of 9 | ; files from a glob(3) pattern. This directive can be used everywhere in the 10 | ; file. 11 | ;include=/etc/php-fpm.d/*.conf 12 | ; STM - modify include path for php-fpm docker image 13 | include=/usr/local/etc/php-fpm.d/*.conf 14 | 15 | ;;;;;;;;;;;;;;;;;; 16 | ; Global Options ; 17 | ;;;;;;;;;;;;;;;;;; 18 | 19 | [global] 20 | ; Pid file 21 | ; Default Value: none 22 | pid = /var/run/php-fpm/php-fpm.pid 23 | 24 | ; Error log file 25 | ; Default Value: /var/log/php-fpm.log 26 | error_log = /var/log/php-fpm/error.log 27 | 28 | ; Log level 29 | ; Possible Values: alert, error, warning, notice, debug 30 | ; Default Value: notice 31 | ;log_level = notice 32 | 33 | ; If this number of child processes exit with SIGSEGV or SIGBUS within the time 34 | ; interval set by emergency_restart_interval then FPM will restart. A value 35 | ; of '0' means 'Off'. 36 | ; Default Value: 0 37 | ;emergency_restart_threshold = 0 38 | 39 | ; Interval of time used by emergency_restart_interval to determine when 40 | ; a graceful restart will be initiated. This can be useful to work around 41 | ; accidental corruptions in an accelerator's shared memory. 42 | ; Available Units: s(econds), m(inutes), h(ours), or d(ays) 43 | ; Default Unit: seconds 44 | ; Default Value: 0 45 | ;emergency_restart_interval = 0 46 | 47 | ; Time limit for child processes to wait for a reaction on signals from master. 48 | ; Available units: s(econds), m(inutes), h(ours), or d(ays) 49 | ; Default Unit: seconds 50 | ; Default Value: 0 51 | ;process_control_timeout = 0 52 | 53 | ; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging. 54 | ; Default Value: yes 55 | ;daemonize = yes 56 | ; STM - set daemonize to no for Docker 57 | daemonize = no 58 | 59 | ;;;;;;;;;;;;;;;;;;;; 60 | ; Pool Definitions ; 61 | ;;;;;;;;;;;;;;;;;;;; 62 | 63 | ; See /etc/php-fpm.d/*.conf 64 | -------------------------------------------------------------------------------- /scripts/docker/config-php-cli/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile provides a base level of OS packages with PHP-CLI and extensions 2 | 3 | ARG PHP_CLI_VERSION=7.1-cli 4 | 5 | FROM php:${PHP_CLI_VERSION} 6 | 7 | # Set consistent timezone 8 | ENV CONTAINER_TIMEZONE="UTC" 9 | RUN rm -f /etc/localtime \ 10 | && ln -s /usr/share/zoneinfo/${CONTAINER_TIMEZONE} /etc/localtime 11 | 12 | # Worker name and index (TODO: where to set this?) 13 | ENV WORKER_NAME="send-emails" \ 14 | WORKER_INDEX="1" 15 | 16 | # Install prerequisite OS packages 17 | RUN apt-get update -y && apt-get upgrade -y && apt-get install -y curl git mysql-client 18 | 19 | # install the PHP extensions we need 20 | RUN set -ex \ 21 | && buildDeps=' \ 22 | libjpeg62-turbo-dev \ 23 | libpng12-dev \ 24 | libpq-dev \ 25 | zlib1g-dev \ 26 | libicu-dev \ 27 | libmemcached-dev \ 28 | ' \ 29 | && apt-get install -y --no-install-recommends $buildDeps \ 30 | && pecl install -o -f redis \ 31 | && docker-php-ext-enable redis \ 32 | && ls -la /usr/local/etc/php/conf.d/ \ 33 | && docker-php-ext-configure gd \ 34 | --with-jpeg-dir=/usr \ 35 | --with-png-dir=/usr \ 36 | && docker-php-ext-install -j "$(nproc)" gd mbstring opcache pdo pdo_mysql zip 37 | 38 | RUN cd /tmp \ 39 | && git clone -b php7 https://github.com/php-memcached-dev/php-memcached \ 40 | && cd php-memcached \ 41 | && phpize \ 42 | && ./configure \ 43 | && make \ 44 | && cp /tmp/php-memcached/modules/memcached.so /usr/local/lib/php/extensions/no-debug-non-zts-20160303/memcached.so \ 45 | && docker-php-ext-enable memcached \ 46 | && ls -la /usr/local/etc/php/conf.d/ 47 | 48 | # See https://secure.php.net/manual/en/opcache.installation.php 49 | RUN { \ 50 | echo 'opcache.memory_consumption=128'; \ 51 | echo 'opcache.interned_strings_buffer=8'; \ 52 | echo 'opcache.max_accelerated_files=4000'; \ 53 | echo 'opcache.revalidate_freq=60'; \ 54 | echo 'opcache.fast_shutdown=1'; \ 55 | echo 'opcache.enable_cli=1'; \ 56 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini 57 | 58 | WORKDIR /root 59 | 60 | # Copy start script 61 | COPY noop.php /root/ 62 | 63 | CMD [ "php", "/root/noop.php" ] 64 | -------------------------------------------------------------------------------- /k8-minikube/build/app-image/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Configure the read/write volume for a non-root user 5 | groupadd --gid 1010 temp_user 6 | useradd --uid 1010 --gid 1010 -m --shell /bin/bash temp_user 7 | TEMP_USER=temp_user 8 | 9 | # This is the mount point for the shared volume. 10 | # By default the mount point is owned by the root user. 11 | MOUNT_PATH="/var/www/html/sites/default/files" 12 | TEMP_USER=${TEMP_USER:-"temp_user"} 13 | 14 | # Debug 15 | echo "MOUNT_PATH is ${MOUNT_PATH}" 16 | echo "TEMP_USER is ${TEMP_USER}" 17 | 18 | echo "DRUPAL_VERSION is ${DRUPAL_VERSION}" 19 | echo "DRUPAL_MD5 is ${DRUPAL_MD5}" 20 | 21 | # This function creates a subdirectory that is owned by 22 | # the non-root user under the shared volume mount path. 23 | create_data_dir() { 24 | # Add the non-root user to primary group of root user. 25 | usermod -aG root $TEMP_USER 26 | 27 | # Provide read-write-execute permission to the group for the shared volume mount path. 28 | chmod 777 $MOUNT_PATH 29 | 30 | cd $MOUNT_PATH 31 | pwd 32 | 33 | echo "ls -al before" 34 | ls -al ${MOUNT_PATH} 35 | 36 | # Change permissions on the folders for the non-root user. 37 | echo "Changing directory permissions" 38 | chmod -R 777 ${MOUNT_PATH} 39 | 40 | echo "Changing directory owners" 41 | echo $TEMP_USER 42 | echo ${MOUNT_PATH} 43 | chown -R $TEMP_USER ${MOUNT_PATH} 44 | 45 | echo "ls -al after" 46 | ls -al /var/www/html/sites/default/ 47 | 48 | # For security, remove the non-root user from root user group. 49 | echo "Removing user from group" 50 | deluser $TEMP_USER root 51 | 52 | # Change the shared volume mount path back to its original read-write-execute permission. 53 | echo "Resetting parent directory permissions" 54 | chmod 755 $MOUNT_PATH 55 | 56 | echo "Created Data directory..." 57 | } 58 | 59 | create_data_dir 60 | 61 | # Set up Composer (Shouldn't be necessary when using the tarball) 62 | # echo "Install and run Composer" 63 | # curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer 64 | # cd ${MOUNT_PATH} && composer install --no-interaction 65 | 66 | # Now that volume is usable by non-root user, start up PHP on port 9000 67 | php-fpm 68 | -------------------------------------------------------------------------------- /scripts/pipeline/build-on-code-change.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Inspect all the files in config and extract into env variables 5 | set -o allexport 6 | for file in ../../code/*.txt 7 | do 8 | source $file 9 | done 10 | for file in ../../config/*.txt 11 | do 12 | source $file 13 | done 14 | set +o allexport 15 | 16 | BUILD_NUMBER=$( date +%s ) 17 | 18 | echo $DRUPAL_VERSION 19 | echo $DRUPAL_MD5 20 | echo $DRUSH_VERSION 21 | echo $NGINX_VERSION # Override from args 22 | echo $PHP_FPM_VERSION # Override from args 23 | echo $PHP_CLI_VERSION # Override from args 24 | 25 | # Build each image and push 26 | ROOT_DIR=`pwd` 27 | 28 | # Log into the IBM Cloud Container Registry 29 | bx cr login 30 | 31 | # Build the NGINX image (configure Fast CGI) 32 | cd ../docker/code-nginx 33 | if [ -d tmp ]; then 34 | rm -fr tmp 35 | fi 36 | mkdir tmp 37 | cp -R ../../../code/* tmp/ 38 | docker build \ 39 | --tag registry.ng.bluemix.net/orod/code-nginx:${BUILD_NUMBER} \ 40 | --tag registry.ng.bluemix.net/orod/code-nginx:latest \ 41 | . 42 | # --no-cache \ 43 | docker push registry.ng.bluemix.net/orod/code-nginx:latest 44 | rm -fr tmp 45 | 46 | # Move back to ROOT_DIR 47 | cd $ROOT_DIR 48 | 49 | # Build the PHP-FPM image (base image, inject code, run composer) 50 | cd ../docker/code-php-fpm 51 | if [ -d tmp ]; then 52 | rm -fr tmp 53 | fi 54 | mkdir tmp 55 | cp -R ../../../code/* tmp/ 56 | docker build \ 57 | --tag registry.ng.bluemix.net/orod/code-php-fpm:${BUILD_NUMBER} \ 58 | --tag registry.ng.bluemix.net/orod/code-php-fpm:latest \ 59 | --build-arg DRUPAL_MD5=${DRUPAL_MD5} \ 60 | --build-arg DRUPAL_VERSION=${DRUPAL_VERSION} \ 61 | --no-cache \ 62 | . 63 | docker push registry.ng.bluemix.net/orod/code-php-fpm:latest 64 | rm -fr tmp 65 | 66 | # Move back to ROOT_DIR 67 | cd $ROOT_DIR 68 | 69 | # Build the PHP-CLI image (base image, inject code, run composer) 70 | cd ../docker/code-php-cli 71 | if [ -d tmp ]; then 72 | rm -fr tmp 73 | fi 74 | mkdir tmp 75 | cp -R ../../../code/* tmp/ 76 | docker build \ 77 | --tag registry.ng.bluemix.net/orod/code-php-cli:${BUILD_NUMBER} \ 78 | --tag registry.ng.bluemix.net/orod/code-php-cli:latest \ 79 | --build-arg DRUSH_VERSION=${DRUSH_VERSION} \ 80 | . 81 | docker push registry.ng.bluemix.net/orod/code-php-cli:latest 82 | rm -fr tmp 83 | 84 | # Move back to ROOT_DIR 85 | cd $ROOT_DIR 86 | -------------------------------------------------------------------------------- /scripts/docker/code-php-fpm/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PHP_FPM_VERSION=latest 2 | ARG REGISTRY_NAMESPACE=orod 3 | 4 | FROM registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/config-php-fpm:${PHP_FPM_VERSION} 5 | 6 | ARG DRUPAL_VERSION 7 | ARG DRUPAL_MD5 8 | 9 | RUN apt-get update -y && apt-get upgrade -y 10 | 11 | # Register the COMPOSER_HOME environment variable. 12 | ENV COMPOSER_HOME=/root/.composer 13 | 14 | # Add global binary directory to PATH. 15 | ENV PATH /root/.composer/vendor/bin:$PATH 16 | 17 | # Allow Composer to be run as root. 18 | ENV COMPOSER_ALLOW_SUPERUSER=1 19 | 20 | RUN curl -sS https://getcomposer.org/installer | \ 21 | php -- --install-dir=/usr/bin/ --filename=composer 22 | 23 | COPY tmp/composer.json ./ 24 | 25 | # COPY tmp/composer.lock ./ 26 | 27 | RUN composer install --no-scripts --no-autoloader 28 | 29 | COPY . ./ 30 | 31 | RUN composer create-project drupal-composer/drupal-project:${DRUPAL_VERSION} /var/www/drupal/ --stability dev --no-interaction 32 | 33 | # The above uses the web subdirectory and default composer.json. 34 | # To customize before install, you can take the following approach instead: 35 | # git clone https://github.com/drupal-composer/drupal-project.git my_site_name_dir 36 | # cd my_site_name_dir 37 | # vi composer.json to customize 38 | # composer install 39 | 40 | ADD tmp/composer.json /var/www/drupal/ 41 | ADD tmp/composer.lock /var/www/drupal/ 42 | 43 | ADD tmp/drush/ /var/www/drupal/drush/ 44 | RUN chmod +x /var/www/drupal/drush/*.sh 45 | 46 | ADD tmp/modules/ /var/www/drupal/web/modules/ 47 | ADD tmp/profiles/ /var/www/drupal/web/profiles/ 48 | ADD tmp/sites/ /var/www/drupal/web/sites/ 49 | ADD tmp/themes/ /var/www/drupal/web/themes/ 50 | ADD tmp/config/ /var/www/drupal/config/ 51 | 52 | # One way to do the initial setup. 53 | # cd /var/www/drupal/web 54 | # ../vendor/drush/drush/drush site-install standard \ 55 | # --db-url=mysql://DBUSERNAME:DBPASSWORD@localhost/some_db \ 56 | # --account-mail="admin@example.com" \ 57 | # --account-name=admin \ 58 | # --account-pass=some_admin_password \ 59 | # --site-mail="admin@example.com" \ 60 | # --site-name="Site-Install" 61 | 62 | # Install drush 63 | RUN composer global require drush/drush:8.x 64 | 65 | WORKDIR /var/www/drupal/web 66 | 67 | COPY start.sh /root/ 68 | RUN chmod +x /root/start.sh 69 | 70 | EXPOSE 9000 71 | 72 | ENTRYPOINT ["/root/start.sh"] 73 | -------------------------------------------------------------------------------- /code/sites/example.sites.php: -------------------------------------------------------------------------------- 1 | ..' => 'directory'. As an 24 | * example, to map https://www.drupal.org:8080/mysite/test to the configuration 25 | * directory sites/example.com, the array should be defined as: 26 | * @code 27 | * $sites = array( 28 | * '8080.www.drupal.org.mysite.test' => 'example.com', 29 | * ); 30 | * @endcode 31 | * The URL, https://www.drupal.org:8080/mysite/test/, could be a symbolic link 32 | * or an Apache Alias directive that points to the Drupal root containing 33 | * index.php. An alias could also be created for a subdomain. See the 34 | * @link https://www.drupal.org/documentation/install online Drupal installation guide @endlink 35 | * for more information on setting up domains, subdomains, and subdirectories. 36 | * 37 | * The following examples look for a site configuration in sites/example.com: 38 | * @code 39 | * URL: http://dev.drupal.org 40 | * $sites['dev.drupal.org'] = 'example.com'; 41 | * 42 | * URL: http://localhost/example 43 | * $sites['localhost.example'] = 'example.com'; 44 | * 45 | * URL: http://localhost:8080/example 46 | * $sites['8080.localhost.example'] = 'example.com'; 47 | * 48 | * URL: https://www.drupal.org:8080/mysite/test/ 49 | * $sites['8080.www.drupal.org.mysite.test'] = 'example.com'; 50 | * @endcode 51 | * 52 | * @see default.settings.php 53 | * @see \Drupal\Core\DrupalKernel::getSitePath() 54 | * @see https://www.drupal.org/documentation/install/multi-site 55 | */ 56 | -------------------------------------------------------------------------------- /docs/FEATURE-LIST.md: -------------------------------------------------------------------------------- 1 | ## Feature list 2 | 3 | It covers these baseline features and scenarios: 4 | - [ ] Provides a script [`scripts/setup-infrastructure.sh`](scripts/setup-infrastructure.sh) that is a placeholder to deploy a Kubernetes cluster and provision the MySQL, Redis, and Memcached services from the IBM Cloud. As an alternative, see the [configuration page](docs/INITIAL-SETUP.md) for the UI instructions. 5 | - [x] Provides a script [`scripts/build-containers.sh`](scripts/build-containers.sh) that starts with a supported base PHP 5.6 image, injects custom code, runs Composer, tags and pushes the image to an IBM Cloud Container Registry. 6 | - [x] Provides a script [`scripts/deploy-containers.sh`](scripts/deploy-containers.sh) to deploy a set of 4 containers (1 NGINX container, 2 PHP-FPM containers, 1 PHP-CLI) from those images and mounts a shared volume to the 3 PHP containers. 7 | - [x] Connects the 3 PHP containers to a MySQL-as-a-Service on startup. 8 | - [x] Connects the 3 PHP containers to a Redis-as-a-Service on startup. 9 | - [x] Connects the 3 PHP containers to a Memcached-as-a-Service on startup. 10 | - [x] Exposes a load balanced endpoint that takes an HTTP POST request and routes it through NGINX to the PHP containers, which saves data in the MySQL and Redis databases, stores it in Memcached, and writes a file to the shared file system. 11 | - [x] Exposes a load balanced endpoint [`app/read.php`](scripts/docker/php-fpm/app/read.php) that takes an HTTP GET request and routes it through NGINX to the PHP containers, which retrieves data in the MySQL and Redis databases, retrieves data from Memcached, and reads a file from the shared file system. 12 | - [x] Exposes a load balanced endpoint [`app/create.php`](scripts/docker/php-fpm/app/create.php) that takes an HTTP POST request and routes it through NGINX to the PHP containers, which creates data in the MySQL and Redis databases, caches data from Memcached, and creates a file on the shared file system. 13 | - [x] Exposes a load balanced endpoint [`app/delete.php`](scripts/docker/php-fpm/app/create.php) that takes an HTTP DELETE request and routes it through NGINX to the PHP containers, which deletes data in the MySQL and Redis databases, clears data from Memcached, and deletes a file from the shared file system. 14 | - [ ] Builds and redeploys new containers with zero downtime on GitHub push. 15 | - [x] Provides a script [`scripts/destroy-containers.sh`](scripts/destroy-containers.sh) to stop and remove the containers (but not the storage volume). 16 | - [ ] Provides a script [`scripts/destroy-infrastructure.sh`](scripts/setup-infrastructure.sh) that is a placeholder to destroy a Kubernetes cluster and deprovision the MySQL, Redis, and Memcached services from the IBM Cloud. 17 | -------------------------------------------------------------------------------- /k8-minikube/pods-services/nginx.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: nginx 6 | spec: 7 | ports: 8 | - 9 | port: 80 10 | protocol: TCP 11 | targetPort: 80 12 | selector: 13 | app: nginx 14 | tier: frontend 15 | type: NodePort 16 | --- 17 | apiVersion: extensions/v1beta1 18 | kind: Deployment 19 | metadata: 20 | name: nginx 21 | spec: 22 | replicas: 1 23 | template: 24 | metadata: 25 | labels: 26 | app: nginx 27 | tier: frontend 28 | track: stable 29 | spec: 30 | containers: 31 | - 32 | image: "registry.ng.bluemix.net/alexanderallen/nginx:latest" 33 | lifecycle: 34 | preStop: 35 | exec: 36 | command: 37 | - /usr/sbin/nginx 38 | - "-s" 39 | - quit 40 | name: nginx 41 | imagePullPolicy: IfNotPresent 42 | ports: 43 | - 44 | containerPort: 80 45 | name: http 46 | volumeMounts: 47 | - 48 | mountPath: /www 49 | name: sites-local-storage 50 | 51 | # Mount "hot" file from host for development. 52 | # The "mountPath" is the target location. 53 | - mountPath: /etc/nginx/conf.d/fastcgi.conf 54 | name: sites-local-storage 55 | # The "sub-path" param is the source location. 56 | # The source sub path is located in the /www volume (~/Sites on the host). 57 | subPath: nginx-php-container-cluster/k8-minikube/build/nginx/fastcgi.conf 58 | 59 | # Development. 60 | - mountPath: /etc/nginx/nginx.conf 61 | name: sites-local-storage 62 | subPath: nginx-php-container-cluster/k8-minikube/build/nginx/nginx.conf 63 | 64 | # Development. 65 | - mountPath: /etc/nginx/fastcgi_params 66 | name: sites-local-storage 67 | subPath: nginx-php-container-cluster/k8-minikube/build/nginx/fastcgi_params 68 | 69 | - mountPath: /www-data 70 | name: shared-volume 71 | readOnly: false 72 | 73 | env: 74 | - 75 | name: WEB_ROOT 76 | value: /www 77 | 78 | # PHP-CLI sidecar container. 79 | - image: "registry.ng.bluemix.net/alexanderallen/php-cli:latest" 80 | name: php-cli-sidecar 81 | imagePullPolicy: IfNotPresent 82 | volumeMounts: 83 | - mountPath: /www-data 84 | name: shared-volume 85 | readOnly: false 86 | 87 | imagePullSecrets: 88 | - 89 | name: image-pull 90 | 91 | volumes: 92 | - 93 | name: sites-local-storage 94 | persistentVolumeClaim: 95 | claimName: local-sites-claim 96 | 97 | # Ephemeral tmpfs (ramdisk) volume for sharing code. 98 | - name: shared-volume 99 | emptyDir: {} 100 | -------------------------------------------------------------------------------- /scripts/pipeline/pipeline-build-on-code-change.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "Starting custom code container build" 5 | 6 | # Inspect all the files in config and extract into env variables 7 | set -o allexport 8 | for file in ../../code/*.txt 9 | do 10 | source $file 11 | done 12 | 13 | for file in ../../config/*.txt 14 | do 15 | source $file 16 | done 17 | set +o allexport 18 | 19 | BUILD_NUMBER=$( date +%s ) 20 | 21 | echo $DRUPAL_VERSION 22 | echo $DRUPAL_MD5 23 | echo $DRUSH_VERSION 24 | echo $NGINX_VERSION # Override from args 25 | echo $PHP_FPM_VERSION # Override from args 26 | echo $PHP_CLI_VERSION # Override from args 27 | 28 | # Build each image and push 29 | ROOT_DIR=`pwd` 30 | 31 | case "$IMAGE" in 32 | 33 | "nginx") 34 | echo "nginx" 35 | # Build the NGINX image (configure Fast CGI) 36 | cd ../docker/code-nginx 37 | if [ -d tmp ]; then 38 | rm -fr tmp 39 | fi 40 | mkdir tmp 41 | cp -R ../../../code/* tmp/ 42 | bx cr build \ 43 | --tag registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/code-nginx:${BUILD_NUMBER} \ 44 | --tag registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/code-nginx:latest \ 45 | --build-arg REGISTRY_NAMESPACE=${REGISTRY_NAMESPACE} \ 46 | . 47 | # --no-cache \ 48 | #docker push registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/code-nginx:latest 49 | rm -fr tmp 50 | 51 | # Move back to ROOT_DIR 52 | cd $ROOT_DIR 53 | ;; 54 | 55 | "php-fpm") 56 | echo "php-fpm" 57 | # Build the PHP-FPM image (base image, inject code, run composer) 58 | cd ../docker/code-php-fpm 59 | if [ -d tmp ]; then 60 | rm -fr tmp 61 | fi 62 | mkdir tmp 63 | cp -R ../../../code/* tmp/ 64 | 65 | bx cr build \ 66 | --tag registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/code-php-fpm:${BUILD_NUMBER} \ 67 | --tag registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/code-php-fpm:latest \ 68 | --build-arg DRUPAL_MD5=${DRUPAL_MD5} \ 69 | --build-arg DRUPAL_VERSION=${DRUPAL_VERSION} \ 70 | --build-arg REGISTRY_NAMESPACE=${REGISTRY_NAMESPACE} \ 71 | . 72 | #docker push registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/code-php-fpm:latest 73 | rm -fr tmp 74 | 75 | # Move back to ROOT_DIR 76 | cd $ROOT_DIR 77 | ;; 78 | 79 | "php-cli") 80 | echo "php-cli" 81 | # Build the PHP-CLI image (base image, inject code, run composer) 82 | cd ../docker/code-php-cli 83 | if [ -d tmp ]; then 84 | rm -fr tmp 85 | fi 86 | mkdir tmp 87 | cp -R ../../../code/* tmp/ 88 | 89 | bx cr build \ 90 | --tag registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/code-php-cli:${BUILD_NUMBER} \ 91 | --tag registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/code-php-cli:latest \ 92 | --build-arg DRUSH_VERSION=${DRUSH_VERSION} \ 93 | --build-arg REGISTRY_NAMESPACE=${REGISTRY_NAMESPACE} \ 94 | . 95 | #docker push registry.ng.bluemix.net/${REGISTRY_NAMESPACE}/code-php-cli:latest 96 | rm -fr tmp 97 | 98 | # Move back to ROOT_DIR 99 | cd $ROOT_DIR 100 | ;; 101 | esac 102 | -------------------------------------------------------------------------------- /docs/LOCAL-KUBERNETES.md: -------------------------------------------------------------------------------- 1 | 2 | ### Installing MiniKube 3 | 4 | TODO 5 | 6 | ### Using MiniKube 7 | 8 | Start Minikube 9 | 10 | minikube start 11 | Starting local Kubernetes v1.7.5 cluster... 12 | Starting VM... 13 | Getting VM IP address... 14 | Moving files into cluster... 15 | Setting up certs... 16 | Connecting to cluster... 17 | Setting up kubeconfig... 18 | Starting cluster components... 19 | Kubectl is now configured to use the cluster. 20 | 21 | Check status 22 | 23 | kubectl get nodes 24 | NAME STATUS ROLES AGE VERSION 25 | minikube Ready 6m v1.7.5 26 | 27 | If opening another terminal windows, point Docker and Kubectl to 28 | Minikube 29 | 30 | eval $(minikube docker-env) 31 | 32 | ### Accessing services in Minikube 33 | 34 | Switching the nginx service from `LoadBalancer` to `NodePort`: 35 | 36 | └─[$]> kubectl get services 37 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 38 | kubernetes ClusterIP 10.0.0.1 443/TCP 1d 39 | nginx LoadBalancer 10.0.0.34 80:32352/TCP 1d 40 | php-fpm ClusterIP 10.0.0.93 9000/TCP 1d 41 | 42 | # Change spec.type to NodePort 43 | └─[$]> kubectl edit service nginx 44 | service "nginx" edited 45 | 46 | └─[$]> kubectl get services 47 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 48 | kubernetes ClusterIP 10.0.0.1 443/TCP 1d 49 | nginx NodePort 10.0.0.34 80:32352/TCP 1d 50 | php-fpm ClusterIP 10.0.0.93 9000/TCP 1d 51 | 52 | └─[$]> minikube service nginx 53 | Opening kubernetes service default/nginx in default browser... 54 | 55 | └─[$]> minikube service nginx --url 56 | http://192.168.99.100:32352 57 | 58 | ### MiniKube load balancer and nodeports 59 | 60 | 61 | https://github.com/kubernetes/minikube/issues/950 62 | 63 | > Currently minikube doesn't support LoadBalancer, it doesn't assign to 64 | it external IP. And services are supposed to access using minikube 65 | service service-name... is uses port mapping and it is quite cumbersome 66 | (esp if service exposes more then one port) 67 | 68 | https://github.com/kubernetes/minikube/issues/38 69 | 70 | Referenced by #950, #950 marked as a dupe of #38. 71 | 72 | https://medium.com/@claudiopro/getting-started-with-kubernetes-via-minikube-ada8c7a29620 73 | 74 | > Note we must use the type=NodePort because minikube doesn’t support the LoadBalancer service. 75 | 76 | $ kubectl expose deployment hello-node --type=NodePort 77 | $ kubectl get services 78 | $ curl $(minikube service hello-node --url) 79 | 80 | https://github.com/kubernetes/minikube/issues/384 81 | 82 | > LoadBalancer services run fine on minikube, just with no real external 83 | load balancer created. LoadBalancer services get a node port assigned 84 | too so you can access services via `minikube service ` to open 85 | browser or add `--url` flag to output service URL to terminal. 86 | Would that cover what you need or is there something more that you'd 87 | like to see? -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers Guide 2 | 3 | This guide is intended for maintainers - anybody with commit access to one or 4 | more Code Pattern repositories. 5 | 6 | ## Methodology 7 | 8 | This repository does not have a traditional release management cycle, but 9 | should instead be maintained as as a useful, working, and polished reference at 10 | all times. While all work can therefore be focused on the master branch, the 11 | quality of this branch should never be compromised. 12 | 13 | The remainder of this document details how to merge pull requests to the 14 | repositories. 15 | 16 | ## Merge approval 17 | 18 | The project maintainers use LGTM (Looks Good To Me) in comments on the pull 19 | request to indicate acceptance prior to merging. A change requires a LGTM from 20 | one project maintainer. If the code is written by a maintainer, the change 21 | does not require an additional LGTM. 22 | 23 | ## Reviewing Pull Requests 24 | 25 | We recommend reviewing pull requests directly within GitHub. This allows a 26 | public commentary on changes, providing transparency for all users. When 27 | providing feedback be civil, courteous, and kind. Disagreement is fine, so long 28 | as the discourse is carried out politely. If we see a record of uncivil or 29 | abusive comments, we will revoke your commit privileges and invite you to leave 30 | the project. 31 | 32 | During your review, consider the following points: 33 | 34 | ### Does the change have positive impact? 35 | 36 | Some proposed changes may not represent a positive impact to the project. Ask 37 | whether or not the change will make understanding the code easier, or if it 38 | could simply be a personal preference on the part of the author (see 39 | [bikeshedding](https://en.wiktionary.org/wiki/bikeshedding)). 40 | 41 | Pull requests that do not have a clear positive impact should be closed without 42 | merging. 43 | 44 | ### Do the changes make sense? 45 | 46 | If you do not understand what the changes are or what they accomplish, ask the 47 | author for clarification. Ask the author to add comments and/or clarify test 48 | case names to make the intentions clear. 49 | 50 | At times, such clarification will reveal that the author may not be using the 51 | code correctly, or is unaware of features that accommodate their needs. If you 52 | feel this is the case, work up a code sample that would address the pull 53 | request for them, and feel free to close the pull request once they confirm. 54 | 55 | ### Does the change introduce a new feature? 56 | 57 | For any given pull request, ask yourself "is this a new feature?" If so, does 58 | the pull request (or associated issue) contain narrative indicating the need 59 | for the feature? If not, ask them to provide that information. 60 | 61 | Are new unit tests in place that test all new behaviors introduced? If not, do 62 | not merge the feature until they are! Is documentation in place for the new 63 | feature? (See the documentation guidelines). If not do not merge the feature 64 | until it is! Is the feature necessary for general use cases? Try and keep the 65 | scope of any given component narrow. If a proposed feature does not fit that 66 | scope, recommend to the user that they maintain the feature on their own, and 67 | close the request. You may also recommend that they see if the feature gains 68 | traction among other users, and suggest they re-submit when they can show such 69 | support. 70 | -------------------------------------------------------------------------------- /k8-minikube/build/nginx/fastcgi.conf: -------------------------------------------------------------------------------- 1 | upstream php_fpm_service { 2 | server php-fpm:9000; 3 | } 4 | 5 | server { 6 | # server_name example.com; 7 | 8 | listen 80 default; 9 | 10 | root /www; ## <-- Your only path reference. 11 | 12 | location = /favicon.ico { 13 | log_not_found off; 14 | access_log off; 15 | } 16 | 17 | location = /robots.txt { 18 | allow all; 19 | log_not_found off; 20 | access_log off; 21 | } 22 | 23 | # Very rarely should these ever be accessed outside of your lan 24 | location ~* \.(txt|log)$ { 25 | allow 192.168.0.0/16; 26 | deny all; 27 | } 28 | 29 | location ~ \..*/.*\.php$ { 30 | return 403; 31 | } 32 | 33 | location ~ ^/sites/.*/private/ { 34 | return 403; 35 | } 36 | 37 | # Allow "Well-Known URIs" as per RFC 5785 38 | location ~* ^/.well-known/ { 39 | allow all; 40 | } 41 | 42 | # Block access to "hidden" files and directories whose names begin with a 43 | # period. This includes directories used by version control systems such 44 | # as Subversion or Git to store control files. 45 | location ~ (^|/)\. { 46 | return 403; 47 | } 48 | 49 | location / { 50 | # try_files $uri @rewrite; # For Drupal <= 6 51 | try_files $uri /index.php?$query_string; # For Drupal >= 7 52 | } 53 | 54 | location @rewrite { 55 | rewrite ^/(.*)$ /index.php?q=$1; 56 | } 57 | 58 | # Don't allow direct access to PHP files in the vendor directory. 59 | location ~ /vendor/.*\.php$ { 60 | deny all; 61 | return 404; 62 | } 63 | 64 | # In Drupal 8, we must also match new paths where the '.php' appears in 65 | # the middle, such as update.php/selection. The rule we use is strict, 66 | # and only allows this pattern with the update.php front controller. 67 | # This allows legacy path aliases in the form of 68 | # blog/index.php/legacy-path to continue to route to Drupal nodes. If 69 | # you do not have any paths like that, then you might prefer to use a 70 | # laxer rule, such as: 71 | # location ~ \.php(/|$) { 72 | # The laxer rule will continue to work if Drupal uses this new URL 73 | # pattern with front controllers other than update.php in a future 74 | # release. 75 | location ~ '\.php$|^/update.php' { 76 | fastcgi_split_path_info ^(.+?\.php)(|/.*)$; 77 | # Security note: If you're running a version of PHP older than the 78 | # latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini. 79 | # See http://serverfault.com/q/627903/94922 for details. 80 | include fastcgi_params; 81 | # Block httpoxy attacks. See https://httpoxy.org/. 82 | fastcgi_param HTTP_PROXY ""; 83 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 84 | fastcgi_param PATH_INFO $fastcgi_path_info; 85 | fastcgi_param QUERY_STRING $query_string; 86 | fastcgi_intercept_errors on; 87 | # PHP 5 socket location. 88 | #fastcgi_pass unix:/var/run/php5-fpm.sock; 89 | # PHP 7 socket location. 90 | fastcgi_pass php_fpm_service; 91 | } 92 | 93 | # Fighting with Styles? This little gem is amazing. 94 | # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6 95 | location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7 96 | try_files $uri @rewrite; 97 | } 98 | 99 | # Handle private files through Drupal. Private file's path can come 100 | # with a language prefix. 101 | location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7 102 | try_files $uri /index.php?$query_string; 103 | } 104 | 105 | location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { 106 | try_files $uri @rewrite; 107 | expires max; 108 | log_not_found off; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /scripts/docker/config-nginx/fastcgi.conf: -------------------------------------------------------------------------------- 1 | upstream php_fpm_service { 2 | server php-fpm:9000; 3 | } 4 | 5 | server { 6 | # server_name example.com; 7 | 8 | listen 80 default; 9 | 10 | root /var/www/drupal/web; ## <-- Your only path reference. 11 | 12 | location = /favicon.ico { 13 | log_not_found off; 14 | access_log off; 15 | } 16 | 17 | location = /robots.txt { 18 | allow all; 19 | log_not_found off; 20 | access_log off; 21 | } 22 | 23 | # Very rarely should these ever be accessed outside of your lan 24 | location ~* \.(txt|log)$ { 25 | allow 192.168.0.0/16; 26 | deny all; 27 | } 28 | 29 | location ~ \..*/.*\.php$ { 30 | return 403; 31 | } 32 | 33 | location ~ ^/sites/.*/private/ { 34 | return 403; 35 | } 36 | 37 | # Allow "Well-Known URIs" as per RFC 5785 38 | location ~* ^/.well-known/ { 39 | allow all; 40 | } 41 | 42 | # Block access to "hidden" files and directories whose names begin with a 43 | # period. This includes directories used by version control systems such 44 | # as Subversion or Git to store control files. 45 | location ~ (^|/)\. { 46 | return 403; 47 | } 48 | 49 | location / { 50 | # try_files $uri @rewrite; # For Drupal <= 6 51 | try_files $uri /index.php?$query_string; # For Drupal >= 7 52 | } 53 | 54 | location @rewrite { 55 | rewrite ^/(.*)$ /index.php?q=$1; 56 | } 57 | 58 | # Don't allow direct access to PHP files in the vendor directory. 59 | location ~ /vendor/.*\.php$ { 60 | deny all; 61 | return 404; 62 | } 63 | 64 | # In Drupal 8, we must also match new paths where the '.php' appears in 65 | # the middle, such as update.php/selection. The rule we use is strict, 66 | # and only allows this pattern with the update.php front controller. 67 | # This allows legacy path aliases in the form of 68 | # blog/index.php/legacy-path to continue to route to Drupal nodes. If 69 | # you do not have any paths like that, then you might prefer to use a 70 | # laxer rule, such as: 71 | # location ~ \.php(/|$) { 72 | # The laxer rule will continue to work if Drupal uses this new URL 73 | # pattern with front controllers other than update.php in a future 74 | # release. 75 | location ~ '\.php$|^/update.php' { 76 | fastcgi_split_path_info ^(.+?\.php)(|/.*)$; 77 | # Security note: If you're running a version of PHP older than the 78 | # latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini. 79 | # See http://serverfault.com/q/627903/94922 for details. 80 | include fastcgi_params; 81 | # Block httpoxy attacks. See https://httpoxy.org/. 82 | fastcgi_param HTTP_PROXY ""; 83 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 84 | fastcgi_param PATH_INFO $fastcgi_path_info; 85 | fastcgi_param QUERY_STRING $query_string; 86 | fastcgi_intercept_errors on; 87 | # PHP 5 socket location. 88 | #fastcgi_pass unix:/var/run/php5-fpm.sock; 89 | # PHP 7 socket location. 90 | fastcgi_pass php_fpm_service; 91 | } 92 | 93 | # Fighting with Styles? This little gem is amazing. 94 | # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6 95 | location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7 96 | try_files $uri @rewrite; 97 | } 98 | 99 | # Handle private files through Drupal. Private file's path can come 100 | # with a language prefix. 101 | location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7 102 | try_files $uri /index.php?$query_string; 103 | } 104 | 105 | location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { 106 | try_files $uri @rewrite; 107 | expires max; 108 | log_not_found off; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Drupal 8 on the IBM Cloud (based on NGINX, PHP-FPM) 2 | This project shows how to deploy a [Drupal 8](https://www.drupal.org/docs/8/) environment on a cluster of NGINX and PHP containers using the [IBM Container Service](https://www.ibm.com/cloud-computing/bluemix/containers) and several IBM Cloud catalog data services. 3 | 4 | These containers mount a persistent volume for sites (which change after build and deployment time with user generated content) and connect to [MySQL, Redis, and Memcached services from the IBM Cloud catalog](https://console.bluemix.net/catalog/?env_id=ibm%3Ayp%3Aus-south&category=data) (not self-hosted containers inside the same cluster). 5 | 6 | After deployment, Drupal developers (who are the end users of the cluster) can manage site lifecycle by delivering configuration or code changes to specific folders ([config](/tree/master/config), [code](/tree/master/code)) in this repository. Commits trigger fresh rebuild and deploys in an [IBM Continuous Delivery](https://console.bluemix.net/catalog/services/continuous-delivery/?taxonomyNavigation=services&cm_mc_uid=36647820015315082453960&cm_mc_sid_50200000=1509381998&env_id=ibm%3Ayp%3Aus-south) pipeline. Production data can also be synchronized back to the staging environment using file and data migration scripts. 7 | 8 | ## What makes the IBM Cloud different from other Kubernetes providers 9 | - A secure, high-performance, [IBM Container Service cluster](https://console.bluemix.net/docs/containers/cs_planning.html#cs_planning) (based on Kubernetes) with advanced network and storage configuration options. 10 | - Integration with managed MySQL, Redis, and Memcached Databases-as-a-service provided through the IBM Cloud service catalog. 11 | - [Multiple levels of security](https://console.bluemix.net/docs/containers/cs_security.html#cs_security) for Docker images stored in the IBM Container Registry, including automatic scanning by the [IBM Vulnerability Advisor](https://www.ibm.com/blogs/bluemix/2017/03/assessing-security-risk-containers-vulnerability-advisor/). 12 | - Automatic build and deploy workflows with the [IBM Continuous Delivery Service](https://console.bluemix.net/catalog/services/continuous-delivery/?taxonomyNavigation=services&cm_mc_uid=36647820015315082453960&cm_mc_sid_50200000=1509381998&env_id=ibm%3Ayp%3Aus-south) (formerly IBM DevOps Services). 13 | 14 | ## Logical overview diagram 15 | There are two separate Drupal installations that are deployed onto the container cluster. One to represent a "staging" environment and one to represent a "production" environment. Each has its own dedicated services and volume mounts. A CLI container can run `drush` or scripts such as `transfer-files.sh` and `transfer-data.sh` on those environments to synchronize them. 16 | 17 | ![](docs/img/architecture.png) 18 | 19 | ## Landscape diagram of the services used 20 | When custom code is checked into this repository, it triggers a Docker image build which stores the images into a private container registry that analyzes the security of the images. These images are then rolled out across the Kubernetes cluster through staging and production gates. Data from production can be synchronized back to staging to ensure the environments are as close as possible. 21 | 22 | ![](docs/img/links.png) 23 | 24 | ## Setup the environment and deploy the cluster 25 | 26 | ### One time Container Service and Cloud data services setup 27 | See the Container Service Kubernetes and IBM Cloud services (MySQL, Redis, Memcached) [configuration instructions](docs/INITIAL-SETUP.md). 28 | 29 | ### Building and deploying the first set of containers 30 | See the Docker container build and Kubernetes deployment [instructions](docs/DEPLOY-CONTAINERS.md). 31 | 32 | ### Ongoing development and operations with GitHub commits 33 | See the ongoing development [instructions](docs/ONGOING-DEVELOPMENT.md). And the work in progress DevOps [pipeline docs](docs/PIPELINE-SETUP.md). This shows how container images are rebuilt and how to address security issues detected by the IBM Vulnerability Advisor. 34 | 35 | ### Synchronizing data from production back to staging 36 | There are two [synchronization scripts](docs/SYNCHRONIZING-DATA.md) that can be invoked to bring user generated changes to files or data from production back into the staging environment. You can also execute other scripts inside the containers as well. 37 | 38 | # License 39 | 40 | [Apache 2.0](LICENSE.txt) 41 | -------------------------------------------------------------------------------- /k8-minikube/TODO.md: -------------------------------------------------------------------------------- 1 | TODO 2 | ==== 3 | 4 | - [ ] The PHP-CLI image should be able to run drush commands against any site. 5 | - [ ] NGINX image should have a sane default configuration (currently 504 6 | Gateway Time-out). 7 | - [ ] Have a application side-cart pod/container or volume accessible by all 8 | base containers (PHP-FPM, Nginx, and PHP-CLI/Drush). 9 | - [ ] Drush SQL-SYNC working on cli container 10 | 11 | --- 12 | 13 | ## Brainstorming 14 | 15 | Reference: 16 | 17 | - Pod patterns: http://blog.kubernetes.io/2015/06/the-distributed-system-toolkit-patterns.html 18 | 19 | ### thinking right now 20 | 21 | a) shared volume 22 | 23 | - run composer install on cli container 24 | - save that code to a target volume shared by all containers 25 | - the volume does not have inherent versioning 26 | 27 | Empty dir pattern: 28 | https://kubernetes.io/docs/tasks/access-application-cluster/communicate-containers-same-pod-shared-volume/ 29 | side car pattern: https://kubernetes.io/docs/user-guide/walkthrough/ 30 | 31 | Empty dir drawbacks: 32 | 33 | - Good for sharing code between containers. 34 | - The tmpfs/ramdisk is endemic to the pod, and it's contents cannot be seen by 35 | different pods. 36 | - So the contents of the tmpdir on the PHP-FPM are not the same as the NGINX pod 37 | (tested, verified). 38 | 39 | What good does it do ? 40 | 41 | - Running a sidecar container with php-cli that does the app update ceremonies 42 | in each pod. 43 | - Each pod would have to run the sidecar container to pull code via php-cli. 44 | 45 | b) app image 46 | 47 | - the code lives an image, that runs composer install drupal 48 | - the image has versioning and tracking capabilities 49 | - the app image can be deployed to a pod 50 | - how does the nginx and php-fpm pods see the app image 51 | - how do they access the app image's contents 52 | 53 | --- 54 | 55 | 4:30-5:30pm Wed, October 25 @rallen, @coden 56 | 57 | Using app image conversation: 58 | 59 | > * 10/25/17, 5:18:40 PM] Christopher Oden: 1. build the application code into a databox. 60 | > * 10/25/17, 5:18:49 PM] Christopher Oden: 2. add that databox to the pod 61 | > * 10/25/17, 5:19:10 PM] Christopher Oden: 3. copy the data to a shared 62 | > ephemeral volume mounted into the pod 63 | > * 10/25/17, 5:19:22 PM] Christopher Oden: and building the app code - we can 64 | use a multi stage docker build 65 | > * 10/25/17, 5:19:29 PM] Christopher Oden: to avoid having composer in teh 66 | databox 67 | > * 10/25/17, 5:18:26 PM] R. Allen: so I'd do the sidecar first, since that the simplest (without worrying about fate/resources) 68 | > * 10/25/17, 5:18:27 PM] R. Allen: prove it 69 | > * 10/25/17, 5:18:33 PM] R. Allen: if it's good (to you) 70 | > * 10/25/17, 5:18:37 PM] R. Allen: then move it to an init 71 | > * 10/25/17, 5:18:44 PM] R. Allen: the init config is a bit more complex ; ) 72 | > * 10/25/17, 5:18:18 PM] Christopher Oden: sure 73 | > * 10/25/17, 5:18:20 PM] Christopher Oden: that’s fine 74 | 75 | Reference: 76 | 77 | - http://container42.com/2014/11/18/data-only-container-madness/ 78 | - https://stackoverflow.com/questions/30538210/how-to-mimic-volumes-from-in-kubernetes 79 | - https://dev-stash.jjconsumer.com/projects/CTECH/repos/rancher-services/browse/templates/ctech-application/5/docker-compose.yml#34 80 | 81 | 82 | ### Job controller to share and update code 83 | 84 | - Mount emptydir? Shared across all containers. 85 | - Whenever there is a git push 86 | - Run job 87 | - Job runs cli container, 88 | - CLI container runs required composer install or update commands. 89 | - empty dir volume mounted to job spec with drush container 90 | - New code is placed by drush container into empty dir volume 91 | - empty dir volume is shared across all pod/containers 92 | - nginx/php-fpm/php-cli are pointing to empty dir as web root 93 | - they do not need to restart to see the new code, they can just serve it immediatly 94 | 95 | ### emptydir pull 96 | 97 | - The main containers don't have any code when staring 98 | - the empty dir is populated with code once the cluster is running 99 | 100 | ### Daemonset 101 | 102 | - Ensures all nodes are running a particular pod. 103 | - This runs at a higher level than we want probably, we probably want to run 104 | something at the pod level. 105 | 106 | ### Cron job 107 | 108 | - Could be used to pull from git repos 109 | - But I'd rather see automatic push than pull 110 | 111 | ### Init containers 112 | 113 | - I don't need the code for the base image containers to be running, so this 114 | one's probably out. 115 | -------------------------------------------------------------------------------- /code/sites/example.settings.local.php: -------------------------------------------------------------------------------- 1 | =1.7.6 is required. 29 | 30 | ![](img/6-access.png) 31 | 32 | - Run the configuration commands, making sure that the `$KUBECONFIG` variable path is indeed correct (it may not reflect your actual home directory). 33 | - You can then use `kubectl` or the `kubectl proxy` dashboard web UI that starts on localhost to inspect your Kubernetes environment. 34 | 35 | ## Provision and bind two MySQL-as-a-Service instances 36 | - You can do this with the `bx` command too, but I prefer to work with the dashboard as it's just a one time setup operation with several options to browse through. 37 | - Go to the hamburger navigation again and choose Data & Analytics. 38 | 39 | ![](img/7-hamburger-data-analytics.png) 40 | 41 | - Click "Create" 42 | 43 | ![](img/8-create-data-analytics.png) 44 | 45 | - You have a choice of two MySQL-as-a-Service providers, Compose and ClearDB. ClearDB is quicker to get running with for a PoC. You can use the free plan, but if you need better performance, choose a paid plan. 46 | 47 | ![](img/9-create-mysql-service.png) 48 | 49 | - Take note of the credentials, and save them in a `scripts/kubernetes/secrets/service-credentials.txt` file you copy from [`scripts/kubernetes/secrets/service-credentials.txt.tpl`](../scripts/kubernetes/secrets/service-credentials.txt.tpl). There are variables for both a "Staging" database instance and a "Production" instance. They will be separate databases with separate credentials. 50 | 51 | For ClearDB, the credentials can be found in the ClearDB Dashboard. Select your database and click the "System Information" tab 52 | ![](img/13-cleardb-credentials.png) 53 | 54 | For Compose, the credentials will be shown right before you create the service. 55 | ![](img/12-compose-credentials.png) 56 | 57 | 58 | - The IBM Cloud Container Service offers a way to autobind credentials, but using a secret from this credentials file gives us the option to use services in another organization and space and/or start it up later as its own pod. 59 | 60 | - Repeat these steps to create a second database and make sure you have entries for both in the `scripts/kubernetes/secrets/service-credentials.txt` file. 61 | 62 | ## Provision and bind a Redis-as-a-Service 63 | - You can do this with the `bx` command too, but I prefer to work with the dashboard as it's a one time setup operation with several options. 64 | - Go to the hamburger navigation again and choose Data & Analytics. 65 | 66 | ![](img/7-hamburger-data-analytics.png) 67 | 68 | - Click "Create" 69 | 70 | ![](img/8-create-data-analytics.png) 71 | 72 | - You have a choice of two Redis-as-a-Service providers, Compose and Redis Cloud. Redis Cloud has a free tier, so you can just use that. 73 | 74 | ![](img/10-create-redis-service.png) 75 | 76 | - Take note of the credentials, and save them in `scripts/kubernetes/secrets/service-credentials.txt`. 77 | 78 | 79 | - For Compose, the credentials can be found by selecting the service from the dasboard, and clicking "Service Credentials" 80 | 81 | ![](img/14-redis-credentials.png) 82 | 83 | ## Provision and bind a Memcached-as-a-Service 84 | - You can do this with the `bx` command too, but I prefer to work with the dashboard as it's a one time setup operation with several options. 85 | - Go to the hamburger navigation again and choose Data & Analytics. 86 | 87 | ![](img/7-hamburger-data-analytics.png) 88 | 89 | - Click "Create" 90 | 91 | ![](img/8-create-data-analytics.png) 92 | 93 | - Choose Memcached from Redis Cloud. 94 | 95 | ![](img/11-create-memcached-service.png) 96 | 97 | - Take note of the credentials, and save them in `scripts/kubernetes/secrets/service-credentials.txt`. 98 | -------------------------------------------------------------------------------- /k8-minikube/pods-services/php-fpm.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: php-fpm 6 | spec: 7 | ports: 8 | - 9 | port: 9000 10 | protocol: TCP 11 | targetPort: 9000 12 | selector: 13 | app: php-fpm 14 | tier: backend 15 | --- 16 | apiVersion: extensions/v1beta1 17 | kind: Deployment 18 | metadata: 19 | name: php-fpm 20 | spec: 21 | replicas: 1 22 | template: 23 | metadata: 24 | labels: 25 | app: php-fpm 26 | tier: backend 27 | track: stable 28 | spec: 29 | containers: 30 | - 31 | env: 32 | - 33 | name: WEB_ROOT 34 | value: /www 35 | - 36 | name: MYSQL_USER 37 | valueFrom: 38 | secretKeyRef: 39 | key: MYSQL_USER 40 | name: service-credentials 41 | - 42 | name: MYSQL_PASS 43 | valueFrom: 44 | secretKeyRef: 45 | key: MYSQL_PASS 46 | name: service-credentials 47 | - 48 | name: MYSQL_HOST 49 | valueFrom: 50 | secretKeyRef: 51 | key: MYSQL_HOST 52 | name: service-credentials 53 | - 54 | name: MYSQL_PORT 55 | valueFrom: 56 | secretKeyRef: 57 | key: MYSQL_PORT 58 | name: service-credentials 59 | - 60 | name: MYSQL_NAME 61 | valueFrom: 62 | secretKeyRef: 63 | key: MYSQL_NAME 64 | name: service-credentials 65 | - 66 | name: REDIS_USER 67 | valueFrom: 68 | secretKeyRef: 69 | key: REDIS_USER 70 | name: service-credentials 71 | - 72 | name: REDIS_PASS 73 | valueFrom: 74 | secretKeyRef: 75 | key: REDIS_PASS 76 | name: service-credentials 77 | - 78 | name: REDIS_HOST 79 | valueFrom: 80 | secretKeyRef: 81 | key: REDIS_HOST 82 | name: service-credentials 83 | - 84 | name: REDIS_PORT 85 | valueFrom: 86 | secretKeyRef: 87 | key: REDIS_PORT 88 | name: service-credentials 89 | - 90 | name: MEMCACHED_USER 91 | valueFrom: 92 | secretKeyRef: 93 | key: MEMCACHED_USER 94 | name: service-credentials 95 | - 96 | name: MEMCACHED_PASS 97 | valueFrom: 98 | secretKeyRef: 99 | key: MEMCACHED_PASS 100 | name: service-credentials 101 | - 102 | name: MEMCACHED_HOST 103 | valueFrom: 104 | secretKeyRef: 105 | key: MEMCACHED_HOST 106 | name: service-credentials 107 | - 108 | name: MEMCACHED_PORT 109 | valueFrom: 110 | secretKeyRef: 111 | key: MEMCACHED_PORT 112 | name: service-credentials 113 | - 114 | name: HASH_SALT 115 | valueFrom: 116 | secretKeyRef: 117 | key: HASH_SALT 118 | name: service-credentials 119 | image: "registry.ng.bluemix.net/alexanderallen/php-fpm:latest" 120 | name: php-fpm 121 | imagePullPolicy: IfNotPresent 122 | ports: 123 | - 124 | containerPort: 9000 125 | name: fastcgi 126 | 127 | volumeMounts: 128 | - 129 | mountPath: /www 130 | name: sites-local-storage 131 | readOnly: false 132 | 133 | - 134 | mountPath: /www-data 135 | name: shared-volume 136 | readOnly: false 137 | 138 | # PHP-CLI sidecar container. 139 | - image: "registry.ng.bluemix.net/alexanderallen/php-cli:latest" 140 | name: php-cli-sidecar 141 | imagePullPolicy: IfNotPresent 142 | volumeMounts: 143 | - mountPath: /www-data 144 | name: shared-volume 145 | readOnly: false 146 | 147 | imagePullSecrets: 148 | - 149 | name: image-pull 150 | 151 | volumes: 152 | - 153 | name: sites-local-storage 154 | persistentVolumeClaim: 155 | claimName: local-sites-claim 156 | 157 | # Ephemeral tmpfs (ramdisk) volume for sharing code. 158 | - name: shared-volume 159 | emptyDir: {} 160 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/config/etc/php-fpm.conf: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;; 2 | ; FPM Configuration ; 3 | ;;;;;;;;;;;;;;;;;;;;; 4 | 5 | ; All relative paths in this configuration file are relative to PHP's install 6 | ; prefix (/usr/local). This prefix can be dynamically changed by using the 7 | ; '-p' argument from the command line. 8 | 9 | ;;;;;;;;;;;;;;;;;; 10 | ; Global Options ; 11 | ;;;;;;;;;;;;;;;;;; 12 | 13 | [global] 14 | ; Pid file 15 | ; Note: the default prefix is /usr/local/var 16 | ; Default Value: none 17 | ;pid = run/php-fpm.pid 18 | 19 | ; Error log file 20 | ; If it's set to "syslog", log is sent to syslogd instead of being written 21 | ; into a local file. 22 | ; Note: the default prefix is /usr/local/var 23 | ; Default Value: log/php-fpm.log 24 | ;error_log = log/php-fpm.log 25 | 26 | ; syslog_facility is used to specify what type of program is logging the 27 | ; message. This lets syslogd specify that messages from different facilities 28 | ; will be handled differently. 29 | ; See syslog(3) for possible values (ex daemon equiv LOG_DAEMON) 30 | ; Default Value: daemon 31 | ;syslog.facility = daemon 32 | 33 | ; syslog_ident is prepended to every message. If you have multiple FPM 34 | ; instances running on the same server, you can change the default value 35 | ; which must suit common needs. 36 | ; Default Value: php-fpm 37 | ;syslog.ident = php-fpm 38 | 39 | ; Log level 40 | ; Possible Values: alert, error, warning, notice, debug 41 | ; Default Value: notice 42 | ;log_level = notice 43 | 44 | ; If this number of child processes exit with SIGSEGV or SIGBUS within the time 45 | ; interval set by emergency_restart_interval then FPM will restart. A value 46 | ; of '0' means 'Off'. 47 | ; Default Value: 0 48 | ;emergency_restart_threshold = 0 49 | 50 | ; Interval of time used by emergency_restart_interval to determine when 51 | ; a graceful restart will be initiated. This can be useful to work around 52 | ; accidental corruptions in an accelerator's shared memory. 53 | ; Available Units: s(econds), m(inutes), h(ours), or d(ays) 54 | ; Default Unit: seconds 55 | ; Default Value: 0 56 | ;emergency_restart_interval = 0 57 | 58 | ; Time limit for child processes to wait for a reaction on signals from master. 59 | ; Available units: s(econds), m(inutes), h(ours), or d(ays) 60 | ; Default Unit: seconds 61 | ; Default Value: 0 62 | ;process_control_timeout = 0 63 | 64 | ; The maximum number of processes FPM will fork. This has been designed to control 65 | ; the global number of processes when using dynamic PM within a lot of pools. 66 | ; Use it with caution. 67 | ; Note: A value of 0 indicates no limit 68 | ; Default Value: 0 69 | ; process.max = 128 70 | 71 | ; Specify the nice(2) priority to apply to the master process (only if set) 72 | ; The value can vary from -19 (highest priority) to 20 (lowest priority) 73 | ; Note: - It will only work if the FPM master process is launched as root 74 | ; - The pool process will inherit the master process priority 75 | ; unless specified otherwise 76 | ; Default Value: no set 77 | ; process.priority = -19 78 | 79 | ; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging. 80 | ; Default Value: yes 81 | ;daemonize = yes 82 | 83 | ; Set open file descriptor rlimit for the master process. 84 | ; Default Value: system defined value 85 | ;rlimit_files = 1024 86 | 87 | ; Set max core size rlimit for the master process. 88 | ; Possible Values: 'unlimited' or an integer greater or equal to 0 89 | ; Default Value: system defined value 90 | ;rlimit_core = 0 91 | 92 | ; Specify the event mechanism FPM will use. The following is available: 93 | ; - select (any POSIX os) 94 | ; - poll (any POSIX os) 95 | ; - epoll (linux >= 2.5.44) 96 | ; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0) 97 | ; - /dev/poll (Solaris >= 7) 98 | ; - port (Solaris >= 10) 99 | ; Default Value: not set (auto detection) 100 | ;events.mechanism = epoll 101 | 102 | ; When FPM is built with systemd integration, specify the interval, 103 | ; in seconds, between health report notification to systemd. 104 | ; Set to 0 to disable. 105 | ; Available Units: s(econds), m(inutes), h(ours) 106 | ; Default Unit: seconds 107 | ; Default value: 10 108 | ;systemd_interval = 10 109 | 110 | ;;;;;;;;;;;;;;;;;;;; 111 | ; Pool Definitions ; 112 | ;;;;;;;;;;;;;;;;;;;; 113 | 114 | ; Multiple pools of child processes may be started with different listening 115 | ; ports and different management options. The name of the pool will be 116 | ; used in logs and stats. There is no limitation on the number of pools which 117 | ; FPM can handle. Your system will tell you anyway :) 118 | 119 | ; Include one or more files. If glob(3) exists, it is used to include a bunch of 120 | ; files from a glob(3) pattern. This directive can be used everywhere in the 121 | ; file. 122 | ; Relative path can also be used. They will be prefixed by: 123 | ; - the global prefix if it's been set (-p argument) 124 | ; - /usr/local otherwise 125 | include=etc/php-fpm.d/*.conf 126 | -------------------------------------------------------------------------------- /k8-minikube/build/php-fpm/config/etc/php-fpm.conf.default: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;; 2 | ; FPM Configuration ; 3 | ;;;;;;;;;;;;;;;;;;;;; 4 | 5 | ; All relative paths in this configuration file are relative to PHP's install 6 | ; prefix (/usr/local). This prefix can be dynamically changed by using the 7 | ; '-p' argument from the command line. 8 | 9 | ;;;;;;;;;;;;;;;;;; 10 | ; Global Options ; 11 | ;;;;;;;;;;;;;;;;;; 12 | 13 | [global] 14 | ; Pid file 15 | ; Note: the default prefix is /usr/local/var 16 | ; Default Value: none 17 | ;pid = run/php-fpm.pid 18 | 19 | ; Error log file 20 | ; If it's set to "syslog", log is sent to syslogd instead of being written 21 | ; into a local file. 22 | ; Note: the default prefix is /usr/local/var 23 | ; Default Value: log/php-fpm.log 24 | ;error_log = log/php-fpm.log 25 | 26 | ; syslog_facility is used to specify what type of program is logging the 27 | ; message. This lets syslogd specify that messages from different facilities 28 | ; will be handled differently. 29 | ; See syslog(3) for possible values (ex daemon equiv LOG_DAEMON) 30 | ; Default Value: daemon 31 | ;syslog.facility = daemon 32 | 33 | ; syslog_ident is prepended to every message. If you have multiple FPM 34 | ; instances running on the same server, you can change the default value 35 | ; which must suit common needs. 36 | ; Default Value: php-fpm 37 | ;syslog.ident = php-fpm 38 | 39 | ; Log level 40 | ; Possible Values: alert, error, warning, notice, debug 41 | ; Default Value: notice 42 | ;log_level = notice 43 | 44 | ; If this number of child processes exit with SIGSEGV or SIGBUS within the time 45 | ; interval set by emergency_restart_interval then FPM will restart. A value 46 | ; of '0' means 'Off'. 47 | ; Default Value: 0 48 | ;emergency_restart_threshold = 0 49 | 50 | ; Interval of time used by emergency_restart_interval to determine when 51 | ; a graceful restart will be initiated. This can be useful to work around 52 | ; accidental corruptions in an accelerator's shared memory. 53 | ; Available Units: s(econds), m(inutes), h(ours), or d(ays) 54 | ; Default Unit: seconds 55 | ; Default Value: 0 56 | ;emergency_restart_interval = 0 57 | 58 | ; Time limit for child processes to wait for a reaction on signals from master. 59 | ; Available units: s(econds), m(inutes), h(ours), or d(ays) 60 | ; Default Unit: seconds 61 | ; Default Value: 0 62 | ;process_control_timeout = 0 63 | 64 | ; The maximum number of processes FPM will fork. This has been designed to control 65 | ; the global number of processes when using dynamic PM within a lot of pools. 66 | ; Use it with caution. 67 | ; Note: A value of 0 indicates no limit 68 | ; Default Value: 0 69 | ; process.max = 128 70 | 71 | ; Specify the nice(2) priority to apply to the master process (only if set) 72 | ; The value can vary from -19 (highest priority) to 20 (lowest priority) 73 | ; Note: - It will only work if the FPM master process is launched as root 74 | ; - The pool process will inherit the master process priority 75 | ; unless specified otherwise 76 | ; Default Value: no set 77 | ; process.priority = -19 78 | 79 | ; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging. 80 | ; Default Value: yes 81 | ;daemonize = yes 82 | 83 | ; Set open file descriptor rlimit for the master process. 84 | ; Default Value: system defined value 85 | ;rlimit_files = 1024 86 | 87 | ; Set max core size rlimit for the master process. 88 | ; Possible Values: 'unlimited' or an integer greater or equal to 0 89 | ; Default Value: system defined value 90 | ;rlimit_core = 0 91 | 92 | ; Specify the event mechanism FPM will use. The following is available: 93 | ; - select (any POSIX os) 94 | ; - poll (any POSIX os) 95 | ; - epoll (linux >= 2.5.44) 96 | ; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0) 97 | ; - /dev/poll (Solaris >= 7) 98 | ; - port (Solaris >= 10) 99 | ; Default Value: not set (auto detection) 100 | ;events.mechanism = epoll 101 | 102 | ; When FPM is built with systemd integration, specify the interval, 103 | ; in seconds, between health report notification to systemd. 104 | ; Set to 0 to disable. 105 | ; Available Units: s(econds), m(inutes), h(ours) 106 | ; Default Unit: seconds 107 | ; Default value: 10 108 | ;systemd_interval = 10 109 | 110 | ;;;;;;;;;;;;;;;;;;;; 111 | ; Pool Definitions ; 112 | ;;;;;;;;;;;;;;;;;;;; 113 | 114 | ; Multiple pools of child processes may be started with different listening 115 | ; ports and different management options. The name of the pool will be 116 | ; used in logs and stats. There is no limitation on the number of pools which 117 | ; FPM can handle. Your system will tell you anyway :) 118 | 119 | ; Include one or more files. If glob(3) exists, it is used to include a bunch of 120 | ; files from a glob(3) pattern. This directive can be used everywhere in the 121 | ; file. 122 | ; Relative path can also be used. They will be prefixed by: 123 | ; - the global prefix if it's been set (-p argument) 124 | ; - /usr/local otherwise 125 | include=NONE/etc/php-fpm.d/*.conf 126 | -------------------------------------------------------------------------------- /k8-minikube/pods-services/php-cli.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: extensions/v1beta1 3 | kind: Deployment 4 | metadata: 5 | name: php-cli 6 | spec: 7 | replicas: 1 8 | template: 9 | metadata: 10 | labels: 11 | app: php-cli 12 | tier: backend 13 | track: stable 14 | spec: 15 | containers: 16 | - 17 | env: 18 | - 19 | name: WEB_ROOT 20 | value: /www 21 | - 22 | name: MYSQL_USER 23 | valueFrom: 24 | secretKeyRef: 25 | key: MYSQL_USER 26 | name: service-credentials 27 | - 28 | name: MYSQL_PASS 29 | valueFrom: 30 | secretKeyRef: 31 | key: MYSQL_PASS 32 | name: service-credentials 33 | - 34 | name: MYSQL_HOST 35 | valueFrom: 36 | secretKeyRef: 37 | key: MYSQL_HOST 38 | name: service-credentials 39 | - 40 | name: MYSQL_PORT 41 | valueFrom: 42 | secretKeyRef: 43 | key: MYSQL_PORT 44 | name: service-credentials 45 | - 46 | name: MYSQL_NAME 47 | valueFrom: 48 | secretKeyRef: 49 | key: MYSQL_NAME 50 | name: service-credentials 51 | - 52 | name: REDIS_USER 53 | valueFrom: 54 | secretKeyRef: 55 | key: REDIS_USER 56 | name: service-credentials 57 | - 58 | name: REDIS_PASS 59 | valueFrom: 60 | secretKeyRef: 61 | key: REDIS_PASS 62 | name: service-credentials 63 | - 64 | name: REDIS_HOST 65 | valueFrom: 66 | secretKeyRef: 67 | key: REDIS_HOST 68 | name: service-credentials 69 | - 70 | name: REDIS_PORT 71 | valueFrom: 72 | secretKeyRef: 73 | key: REDIS_PORT 74 | name: service-credentials 75 | - 76 | name: MEMCACHED_USER 77 | valueFrom: 78 | secretKeyRef: 79 | key: MEMCACHED_USER 80 | name: service-credentials 81 | - 82 | name: MEMCACHED_PASS 83 | valueFrom: 84 | secretKeyRef: 85 | key: MEMCACHED_PASS 86 | name: service-credentials 87 | - 88 | name: MEMCACHED_HOST 89 | valueFrom: 90 | secretKeyRef: 91 | key: MEMCACHED_HOST 92 | name: service-credentials 93 | - 94 | name: MEMCACHED_PORT 95 | valueFrom: 96 | secretKeyRef: 97 | key: MEMCACHED_PORT 98 | name: service-credentials 99 | - 100 | name: HASH_SALT 101 | valueFrom: 102 | secretKeyRef: 103 | key: HASH_SALT 104 | name: service-credentials 105 | image: "registry.ng.bluemix.net/alexanderallen/php-cli:latest" 106 | name: php-cli 107 | imagePullPolicy: IfNotPresent 108 | 109 | volumeMounts: 110 | 111 | - mountPath: /www 112 | name: sites-local-storage 113 | readOnly: false 114 | 115 | - mountPath: /www-data 116 | name: shared-volume 117 | readOnly: false 118 | 119 | - image: "registry.ng.bluemix.net/alexanderallen/app-image:latest" 120 | name: app-sidecar 121 | imagePullPolicy: IfNotPresent 122 | env: 123 | # Location inside the application image containing desired code. 124 | - name: RSYNC_SOURCE 125 | value: /app 126 | # Shared volume where to copy the data to (could be emptyDir, PV/NFS, etc.). 127 | - name: RSYNC_DEST 128 | value: /www/app 129 | volumeMounts: 130 | 131 | - mountPath: /www 132 | name: sites-local-storage 133 | readOnly: false 134 | 135 | - mountPath: /www-data 136 | name: shared-volume 137 | readOnly: false 138 | 139 | # Development. 140 | - mountPath: /root/app-sync.sh 141 | name: sites-local-storage 142 | subPath: nginx-php-container-cluster/k8-minikube/build/app-image/app-sync.sh 143 | 144 | imagePullSecrets: 145 | - 146 | name: image-pull 147 | volumes: 148 | - 149 | name: sites-local-storage 150 | persistentVolumeClaim: 151 | claimName: local-sites-claim 152 | 153 | # Ephemeral tmpfs (ramdisk) volume for sharing code. 154 | - name: shared-volume 155 | emptyDir: {} 156 | -------------------------------------------------------------------------------- /docs/DEMO.md: -------------------------------------------------------------------------------- 1 | # Drupal sites on the IBM Container Service 2 | 3 | ## Preamble: Why Kubernetes? 4 | The shift towards cloud-native deployment models allows developers to package their applications consistently between staging and production, and helps them scale their deployments horizontally across a large pool of compute resources more quickly. 5 | 6 | In contrast to other cloud-native approaches - like Platform-as-a-Service with Heroku or Cloud Foundry - container orchestration system like Kubernetes are "less-opinionated" and offer a great amount of flexibility at the cost of a single prescribed set of guidelines, which can make them more attractive to those migrating from a virtual machine or bare metal approach rather than towards PaaS directly. 7 | 8 | ## What this Proof of Concept shows 9 | This PoC shows how one might migrate a traditional web-server, application-server, and database-server based application into a container-based model that depends on cloud services in order to speed application development by reducing time spent on managing servers across a large deployment target environment. 10 | 11 | 1. [Functional Drupal site running on the IBM Cloud](#1-functional-drupal-site-running-on-the-ibm-cloud) 12 | 2. [Clearly defined and easy to implement process for pushing code updates](#2-clearly-defined-and-easy-to-implement-process-for-pushing-code-updates) 13 | 3. [Synchronize or migrate one database to another database](#3-synchronize-or-migrate-one-database-to-another-database) 14 | 4. [Taking advantage of a continous integration pipeline](#4-taking-advantage-of-a-continous-integration-pipeline) 15 | 16 | ## 1. Functional Drupal site running on the IBM Cloud 17 | A managed Kubernetes cluster from the IBM Cloud Container Service provides the fabric on which to install a set of NGINX and PHP-FPM containers that run Drupal. 18 | 19 | These containers package custom site code and the underlying Kubernetes fabric can bind those containers to data services, load balancers, and storage volumes provided by the IBM Cloud. 20 | 21 | ### 1.1 Initial environment setup 22 | The [initial setup](INITIAL-SETUP.md) instructions show how to set up a Kubernetes cluster and provision the MySQL, Redis, and Memcached services needed by the Drupal cluster. 23 | 24 | ### 1.2 First container cluster deployment 25 | Once the fabric and services are configured, you can build container images and deploy them to the IBM Container Service manually or through an automated pipeline. The [container deployment instructions](DEPLOY-CONTAINERS.md) describe how. 26 | 27 | ### 1.3 Complete Drupal configuration 28 | Connecting to Drupal to finish installation. Once all of the containers have gotten to the `Running` state (you can see status with 'kubectl get pods') you can find the public IP of the Drupal cluster with `kubectl get services`. Because the `settings.php` file has been set up to get the MySQL connection information from the Kubernetes environment, it's a shorter process than normal. 29 | 30 | ## 2. Clearly defined and easy to implement process for pushing code updates 31 | Once the initial environment is set up, you can initiate additional build, test, and deploy workflows by committing code to specific folders in this repository. This simulates GitHub or BitBucket web hooks. 32 | 33 | ### 2.1 Updating the underlying NGINX and PHP container images 34 | You can commit updated NGINX or PHP version files to the `config` directory. This will in turn trigger base image rebuilds in the DevOps pipeline, and in turn rebuild custom Drupal-based `code` images on top and deploy them. 35 | 36 | ### 2.2 Updating custom code and triggering code layer rebuilds 37 | You can commit code that should be layered on top of base NGINX, PHP, and Drupal installation by changing code in the `code` directory. This will trigger a custom code rebuild and deploy. 38 | 39 | ## 3. Synchronize or migrate one database to another database 40 | Ongoing management of the Drupal cluster can be performed with arbitrary shell commands and `drush` commands invoked by logging into the PHP-CLI container. This container could also be extended to run arbitrary commands on startup through the DevOps pipeline. 41 | 42 | ### 3.1. Using the PHP CLI container to execute arbitrary commands 43 | You can exec into the PHP CLI container to [run arbitrary bash or MySQL commands](PHP-CLI-DRUSH.md). 44 | 45 | ### 3.2. Using the PHP CLI container to execute migration commands 46 | You can exec into the PHP CLI container to [run the `transfer-data.sh` and `transfer-files.sh` scripts injected from the `code/drush` directory](PHP-CLI-DRUSH.md). For example: `kubectl exec ${PHP_CLI_CONTAINER_NAME} /root/drush/transfer-files.sh` and `kubectl exec ${PHP_CLI_CONTAINER_NAME} /root/drush/transfer-data.sh` 47 | 48 | ### 3.3. Using a PHP FPM container to execute `drush` commands 49 | You can exec into the PHP FPM container to [run the `drush-status.sh` script injected from the `code/drush` directory](PHP-CLI-DRUSH.md). For example: `kubectl exec ${PHP_FPM_CONTAINER_NAME} /var/www/drupal/drush/drush-status.sh` 50 | 51 | ## 4. Taking advantage of a continuous integration pipeline 52 | The [pipeline setup instructions](PIPELINE-SETUP.md) show how IBM DevOps can be used with user-defined scripts and webhooks to initiate build, test, and deployment flows. These can incorporate unit test scripts, security vulnerability assessments, and blue/green rolling deploys. These workflows can reuse build tool Docker images as well, which is a new feature of IBM DevOps services. 53 | 54 | ### 4.1. Checking in configuration or code updates 55 | Once configured, the pipeline will detect changes to the top level `config` and `code` directories and trigger new build and deploy processes depending on the change. 56 | 57 | ### 4.2 Synchronizing data from production to staging 58 | You can also use the pipeline UI to execute data and file synchronization. You can also set up arbitrary script execution by extending this model. 59 | -------------------------------------------------------------------------------- /scripts/kubernetes/php-cli.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: extensions/v1beta1 3 | kind: Deployment 4 | metadata: 5 | name: php-cli 6 | spec: 7 | replicas: 1 8 | strategy: 9 | rollingUpdate: 10 | maxSurge: 1 11 | maxUnavailable: 1 12 | type: RollingUpdate 13 | template: 14 | metadata: 15 | labels: 16 | app: php-cli 17 | tier: backend 18 | track: stable 19 | spec: 20 | containers: 21 | - 22 | env: 23 | - 24 | name: BUILD_TIME 25 | value: 'REPLACE_AT_BUILD_TIME' 26 | - 27 | name: HASH_SALT 28 | valueFrom: 29 | secretKeyRef: 30 | key: HASH_SALT 31 | name: service-credentials 32 | - 33 | name: MYSQL_USER_STG 34 | valueFrom: 35 | secretKeyRef: 36 | key: MYSQL_USER_STG 37 | name: service-credentials 38 | - 39 | name: MYSQL_PASS_STG 40 | valueFrom: 41 | secretKeyRef: 42 | key: MYSQL_PASS_STG 43 | name: service-credentials 44 | - 45 | name: MYSQL_HOST_STG 46 | valueFrom: 47 | secretKeyRef: 48 | key: MYSQL_HOST_STG 49 | name: service-credentials 50 | - 51 | name: MYSQL_PORT_STG 52 | valueFrom: 53 | secretKeyRef: 54 | key: MYSQL_PORT_STG 55 | name: service-credentials 56 | - 57 | name: MYSQL_NAME_STG 58 | valueFrom: 59 | secretKeyRef: 60 | key: MYSQL_NAME_STG 61 | name: service-credentials 62 | - 63 | name: REDIS_USER_STG 64 | valueFrom: 65 | secretKeyRef: 66 | key: REDIS_USER_STG 67 | name: service-credentials 68 | - 69 | name: REDIS_PASS_STG 70 | valueFrom: 71 | secretKeyRef: 72 | key: REDIS_PASS_STG 73 | name: service-credentials 74 | - 75 | name: REDIS_HOST_STG 76 | valueFrom: 77 | secretKeyRef: 78 | key: REDIS_HOST_STG 79 | name: service-credentials 80 | - 81 | name: REDIS_PORT_STG 82 | valueFrom: 83 | secretKeyRef: 84 | key: REDIS_PORT_STG 85 | name: service-credentials 86 | - 87 | name: MEMCACHED_USER_STG 88 | valueFrom: 89 | secretKeyRef: 90 | key: MEMCACHED_USER_STG 91 | name: service-credentials 92 | - 93 | name: MEMCACHED_PASS_STG 94 | valueFrom: 95 | secretKeyRef: 96 | key: MEMCACHED_PASS_STG 97 | name: service-credentials 98 | - 99 | name: MEMCACHED_HOST_STG 100 | valueFrom: 101 | secretKeyRef: 102 | key: MEMCACHED_HOST_STG 103 | name: service-credentials 104 | - 105 | name: MEMCACHED_PORT_STG 106 | valueFrom: 107 | secretKeyRef: 108 | key: MEMCACHED_PORT_STG 109 | name: service-credentials 110 | - 111 | name: MYSQL_USER_PRD 112 | valueFrom: 113 | secretKeyRef: 114 | key: MYSQL_USER_PRD 115 | name: service-credentials 116 | - 117 | name: MYSQL_PASS_PRD 118 | valueFrom: 119 | secretKeyRef: 120 | key: MYSQL_PASS_PRD 121 | name: service-credentials 122 | - 123 | name: MYSQL_HOST_PRD 124 | valueFrom: 125 | secretKeyRef: 126 | key: MYSQL_HOST_PRD 127 | name: service-credentials 128 | - 129 | name: MYSQL_PORT_PRD 130 | valueFrom: 131 | secretKeyRef: 132 | key: MYSQL_PORT_PRD 133 | name: service-credentials 134 | - 135 | name: MYSQL_NAME_PRD 136 | valueFrom: 137 | secretKeyRef: 138 | key: MYSQL_NAME_PRD 139 | name: service-credentials 140 | - 141 | name: REDIS_USER_PRD 142 | valueFrom: 143 | secretKeyRef: 144 | key: REDIS_USER_PRD 145 | name: service-credentials 146 | - 147 | name: REDIS_PASS_PRD 148 | valueFrom: 149 | secretKeyRef: 150 | key: REDIS_PASS_PRD 151 | name: service-credentials 152 | - 153 | name: REDIS_HOST_PRD 154 | valueFrom: 155 | secretKeyRef: 156 | key: REDIS_HOST_PRD 157 | name: service-credentials 158 | - 159 | name: REDIS_PORT_PRD 160 | valueFrom: 161 | secretKeyRef: 162 | key: REDIS_PORT_PRD 163 | name: service-credentials 164 | - 165 | name: MEMCACHED_USER_PRD 166 | valueFrom: 167 | secretKeyRef: 168 | key: MEMCACHED_USER_PRD 169 | name: service-credentials 170 | - 171 | name: MEMCACHED_PASS_PRD 172 | valueFrom: 173 | secretKeyRef: 174 | key: MEMCACHED_PASS_PRD 175 | name: service-credentials 176 | - 177 | name: MEMCACHED_HOST_PRD 178 | valueFrom: 179 | secretKeyRef: 180 | key: MEMCACHED_HOST_PRD 181 | name: service-credentials 182 | - 183 | name: MEMCACHED_PORT_PRD 184 | valueFrom: 185 | secretKeyRef: 186 | key: MEMCACHED_PORT_PRD 187 | name: service-credentials 188 | image: "registry.ng.bluemix.net/orod/code-php-cli:latest" 189 | imagePullPolicy: Always 190 | name: php-cli 191 | imagePullPolicy: Always # Set to IfNotPresent to improve performance at the expense of caching. 192 | volumeMounts: 193 | - 194 | mountPath: /var/www/drupal/web/sites/default/files-stg 195 | name: sites-stg-local-storage 196 | readOnly: false 197 | - 198 | mountPath: /var/www/drupal/web/sites/default/files-prd 199 | name: sites-prd-local-storage 200 | readOnly: false 201 | imagePullSecrets: 202 | - 203 | name: image-pull 204 | volumes: 205 | - 206 | name: sites-stg-local-storage 207 | persistentVolumeClaim: 208 | claimName: sites-stg-lv-claim 209 | - 210 | name: sites-prd-local-storage 211 | persistentVolumeClaim: 212 | claimName: sites-prd-lv-claim 213 | -------------------------------------------------------------------------------- /code/sites/default/default.services.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | session.storage.options: 3 | # Default ini options for sessions. 4 | # 5 | # Some distributions of Linux (most notably Debian) ship their PHP 6 | # installations with garbage collection (gc) disabled. Since Drupal depends 7 | # on PHP's garbage collection for clearing sessions, ensure that garbage 8 | # collection occurs by using the most common settings. 9 | # @default 1 10 | gc_probability: 1 11 | # @default 100 12 | gc_divisor: 100 13 | # 14 | # Set session lifetime (in seconds), i.e. the time from the user's last 15 | # visit to the active session may be deleted by the session garbage 16 | # collector. When a session is deleted, authenticated users are logged out, 17 | # and the contents of the user's $_SESSION variable is discarded. 18 | # @default 200000 19 | gc_maxlifetime: 200000 20 | # 21 | # Set session cookie lifetime (in seconds), i.e. the time from the session 22 | # is created to the cookie expires, i.e. when the browser is expected to 23 | # discard the cookie. The value 0 means "until the browser is closed". 24 | # @default 2000000 25 | cookie_lifetime: 2000000 26 | # 27 | # Drupal automatically generates a unique session cookie name based on the 28 | # full domain name used to access the site. This mechanism is sufficient 29 | # for most use-cases, including multi-site deployments. However, if it is 30 | # desired that a session can be reused across different subdomains, the 31 | # cookie domain needs to be set to the shared base domain. Doing so assures 32 | # that users remain logged in as they cross between various subdomains. 33 | # To maximize compatibility and normalize the behavior across user agents, 34 | # the cookie domain should start with a dot. 35 | # 36 | # @default none 37 | # cookie_domain: '.example.com' 38 | # 39 | twig.config: 40 | # Twig debugging: 41 | # 42 | # When debugging is enabled: 43 | # - The markup of each Twig template is surrounded by HTML comments that 44 | # contain theming information, such as template file name suggestions. 45 | # - Note that this debugging markup will cause automated tests that directly 46 | # check rendered HTML to fail. When running automated tests, 'debug' 47 | # should be set to FALSE. 48 | # - The dump() function can be used in Twig templates to output information 49 | # about template variables. 50 | # - Twig templates are automatically recompiled whenever the source code 51 | # changes (see auto_reload below). 52 | # 53 | # For more information about debugging Twig templates, see 54 | # https://www.drupal.org/node/1906392. 55 | # 56 | # Not recommended in production environments 57 | # @default false 58 | debug: false 59 | # Twig auto-reload: 60 | # 61 | # Automatically recompile Twig templates whenever the source code changes. 62 | # If you don't provide a value for auto_reload, it will be determined 63 | # based on the value of debug. 64 | # 65 | # Not recommended in production environments 66 | # @default null 67 | auto_reload: null 68 | # Twig cache: 69 | # 70 | # By default, Twig templates will be compiled and stored in the filesystem 71 | # to increase performance. Disabling the Twig cache will recompile the 72 | # templates from source each time they are used. In most cases the 73 | # auto_reload setting above should be enabled rather than disabling the 74 | # Twig cache. 75 | # 76 | # Not recommended in production environments 77 | # @default true 78 | cache: true 79 | renderer.config: 80 | # Renderer required cache contexts: 81 | # 82 | # The Renderer will automatically associate these cache contexts with every 83 | # render array, hence varying every render array by these cache contexts. 84 | # 85 | # @default ['languages:language_interface', 'theme', 'user.permissions'] 86 | required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions'] 87 | # Renderer automatic placeholdering conditions: 88 | # 89 | # Drupal allows portions of the page to be automatically deferred when 90 | # rendering to improve cache performance. That is especially helpful for 91 | # cache contexts that vary widely, such as the active user. On some sites 92 | # those may be different, however, such as sites with only a handful of 93 | # users. If you know what the high-cardinality cache contexts are for your 94 | # site, specify those here. If you're not sure, the defaults are fairly safe 95 | # in general. 96 | # 97 | # For more information about rendering optimizations see 98 | # https://www.drupal.org/developing/api/8/render/arrays/cacheability#optimizing 99 | auto_placeholder_conditions: 100 | # Max-age at or below which caching is not considered worthwhile. 101 | # 102 | # Disable by setting to -1. 103 | # 104 | # @default 0 105 | max-age: 0 106 | # Cache contexts with a high cardinality. 107 | # 108 | # Disable by setting to []. 109 | # 110 | # @default ['session', 'user'] 111 | contexts: ['session', 'user'] 112 | # Tags with a high invalidation frequency. 113 | # 114 | # Disable by setting to []. 115 | # 116 | # @default [] 117 | tags: [] 118 | # Cacheability debugging: 119 | # 120 | # Responses with cacheability metadata (CacheableResponseInterface instances) 121 | # get X-Drupal-Cache-Tags and X-Drupal-Cache-Contexts headers. 122 | # 123 | # For more information about debugging cacheable responses, see 124 | # https://www.drupal.org/developing/api/8/response/cacheable-response-interface 125 | # 126 | # Not recommended in production environments 127 | # @default false 128 | http.response.debug_cacheability_headers: false 129 | factory.keyvalue: 130 | {} 131 | # Default key/value storage service to use. 132 | # @default keyvalue.database 133 | # default: keyvalue.database 134 | # Collection-specific overrides. 135 | # state: keyvalue.database 136 | factory.keyvalue.expirable: 137 | {} 138 | # Default key/value expirable storage service to use. 139 | # @default keyvalue.database.expirable 140 | # default: keyvalue.database.expirable 141 | # Allowed protocols for URL generation. 142 | filter_protocols: 143 | - http 144 | - https 145 | - ftp 146 | - news 147 | - nntp 148 | - tel 149 | - telnet 150 | - mailto 151 | - irc 152 | - ssh 153 | - sftp 154 | - webcal 155 | - rtsp 156 | 157 | # Configure Cross-Site HTTP requests (CORS). 158 | # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS 159 | # for more information about the topic in general. 160 | # Note: By default the configuration is disabled. 161 | cors.config: 162 | enabled: false 163 | # Specify allowed headers, like 'x-allowed-header'. 164 | allowedHeaders: [] 165 | # Specify allowed request methods, specify ['*'] to allow all possible ones. 166 | allowedMethods: [] 167 | # Configure requests allowed from specific origins. 168 | allowedOrigins: ['*'] 169 | # Sets the Access-Control-Expose-Headers header. 170 | exposedHeaders: false 171 | # Sets the Access-Control-Max-Age header. 172 | maxAge: false 173 | # Sets the Access-Control-Allow-Credentials header. 174 | supportsCredentials: false 175 | -------------------------------------------------------------------------------- /k8-minikube/POC-MODIFICATIONS.md: -------------------------------------------------------------------------------- 1 | POC MODIFICATIONS 2 | ================= 3 | 4 | This document covers the modifications done by @rallen to the POC. Not all these 5 | modifications are meant to be ported back to the main POC, rather it's a POC to 6 | the POC. 7 | 8 | The purpose of this exercise is to demonstrate alternate Docker build workflows 9 | (mainly), as well as provide a working example of how to develop against 10 | Kubernetes using a local, single-cluster Minikube environment for faster 11 | prototyping and without impacting any cloud resources. 12 | 13 | ### Image PUSHes to IBM Container Registry 14 | 15 | There is no image pushing happening at this moment for all the Dockerfiles in 16 | built in the k8-minikube directory. All these builds are local-only. 17 | 18 | The custom (dumbed-down) build script that I created for building my Dockerfiles 19 | does not have docker push enable (i.e., not pushing to IBM Container Registry atm). 20 | 21 | 22 | ### Modifications 23 | 24 | - Simplified build from config- and code- matrix to three base images: nginx, 25 | php-fpm, and php-cli. 26 | - The PHP-CLI image is built on top (layered) of the PHP-FPM image. 27 | - Instead of having three (3x) code images, there's only one. 28 | - Tracked down the location of community image configuration files, and copied 29 | them back to the POC (to this sub-POC, the k8-minikube folder). 30 | - Enabled the /status and /ping pages in the PHP-FPM process to test 31 | communication between containers. This is not something you would obviously 32 | have enabled in prod, but it is extremely useful here. 33 | - Successfully tested communication between NGINX and PHP-FPM using the /status 34 | and /ping PHP-FPM pages. 35 | - Created two bash scripts, `bash-nginx.sh` and `bash-php-fpm.sh` that show 36 | how to automatically find the container name in the cluster and "exec" bash 37 | into it for quick debugging (it is a bash one-liner command). 38 | - Updated the build scripts (simplified into `build-base-images.sh`). 39 | - NO "CUSTOM" POC code is being used atm, because the assumption is that this code 40 | will be coming from an external repo (per @chris comments). 41 | 42 | Other modifications (cont.d') 43 | 44 | - Image builds are being done on the alexanderallen namespace instead of jjdojo 45 | to avoid any conflicts with current POC images present on the cloud or locally. 46 | 47 | ### Things This Other POC Does Not Have 48 | 49 | There are some extremely useful and important things present in the current IBM 50 | POC that my own POC lacks, and does not try to emulate. 51 | 52 | Some Kubernetes components are endemic to the cloud provider that they're 53 | running on. Some examples of things the IBM POC provides that are IBM specific 54 | and would never work on Minikube, or any other provider for that matter (Google 55 | Container Environment, Amazon AWS, Azure, etc.): 56 | 57 | - Volumes and Volume Claims: These are provider specific and necessary. 58 | - Storage volume initialization scripts (i.e., code-php-fpm/start.sh). 59 | - Networking: LoadBalancer works on differently in each provider, and is not 60 | even supported by MiniKube, MiniKube has to resort to use NodePort instead of 61 | LoadBalancer, AWS requires different configuration, etc. 62 | - Provider plugin configuration and usage: The IBM POC has done a really nice 63 | job of documenting how to setup and use the different `bx` components. 64 | - Secrets: This POC is not using setting up any, and piggy-backing on any 65 | secrets configuration done in the IBM Cloud POC (recycling them in the 66 | various container manifests). 67 | - Decoupled configuration: This sub-POC a lot, if not most of IBM Cloud's 68 | variables that are used in the Dockerfile build process for simplification, 69 | not something you would really want to do in real life. 70 | - Provider-specific scripts: The IBM Cloud POC has plenty of awesome devops 71 | scripts to automate all the things build and deploy, this POC has 1/10 of that 72 | effort. 73 | 74 | ### Code Image (sidecar, volume?) 75 | 76 | The PHP-FPM, PHP-CLI, and Nginx images have no reason to constantly change, and 77 | therefore there should be no reason to have a multi-tiered 6 image matrix for 78 | these 3 applications. These 3 applications are fairly static and should not have 79 | any (ideally) application code in them. 80 | 81 | The application code should ideally live in it's own container, which gets 82 | constantly rebuilt (automatically) as code pushes come into the a pre-configured 83 | git repository. 84 | 85 | This means that in this POC-of-POC's world, there would only be around 4 images 86 | total: PHP-FPM, PHP-CLI, NGINX, and CODE image(s). 87 | 88 | ### Layered Workflow (for Docker images) 89 | 90 | The layered workflow means that the PHP-CLI and PHP-FPM images are no longer 91 | separate. This means that: 92 | 93 | - There's no need to re-install PHP-FPM twice on two images with completely 94 | different purpose. 95 | - It ensures that the PHP-FPM and PHP-CLI images have the exact same underlying 96 | components (PHP-FPM and all it's system dependencies). 97 | - Any updates to the PHP-FPM image are made only ONCE, as opposed to have to 98 | update particular segments of both the PHP-FPM and PHP-CLI image anytime a fix 99 | is done to the (former) PHP-FPM-CONFIG image. 100 | - The PHP-CLI image installs just what it needs, namely drush and composer. 101 | - There is no need to have drush and composer in the PHP-FPM image, because the 102 | PHP-FPM container will never use drush and composer. 103 | 104 | ### Community Images Dockerfile Instructions 105 | 106 | There are some instructions in the upstream Docker files we're using for PHP-FPM 107 | and NGINX that we are duplicating (with the same exact values) in our downstream 108 | images. 109 | 110 | I've removed these duplicate instructions, such as `EXPOSE 9000`, because IMO 111 | they create the sense that the upstream images are not setting those, when in 112 | fact they are. 113 | 114 | The same thing applies to launching the PHP-FPM process. The Docker community 115 | image we're using for PHP-FPM already has the `CMD` and `ENTRYPOINT` 116 | instructions that start the process with the values we need, therefore there's 117 | no need to specify those in our Dockerfiles, unless we plan to modify those 118 | upstream values, which we are not doing ATM (and there's no need to either, at 119 | least for now). 120 | 121 | I've successfully launched the PHP-FPM container and started that same process 122 | using just the upstream `CMD` et al instructions. 123 | 124 | ### Community Image Configuration Files 125 | 126 | I have tracked down the location of the PHP-FPM configuration files in the 127 | community images, and copied them back to my POC. 128 | 129 | When you launch the PHP-FPM container for the first time, you don't know what is 130 | configuring what and where, unless you read the Dockerfile on http://docker.hub. 131 | 132 | I think that copying those configuration files to the same repository that 133 | builds our custom Dockerfiles gives more visibility of what's the current 134 | configuration being used (for example, the community image won't even have vim 135 | included). By having these conf files "locally", it's easier to read them. 136 | 137 | I've also added the `COPY` instruction to copy those configuration files back 138 | into PHP-FPM image. What this means is that using this pattern we can more 139 | visibly fine-tune the PHP-FPM configuration, without populating the Dockerfile 140 | with customization instructions (they can be done in the configuration files 141 | directly). It's less error prone, easier to debug, and provides more control. 142 | The downside is that you won't inherit any "hotfixes" done to those upstream 143 | config files. 144 | 145 | In my personal POC I used this approach to enable the /status and /ping pages on 146 | the PHP-FPM container, for example, and successfully test connectivity between 147 | the NGINX and PHP-FPM containers. 148 | 149 | I've documented the workflow on how to do this on the HELPME.md document. 150 | 151 | --- 152 | 153 | Updated 10/25, Wednesday, Oct. 2017 (rallen) 154 | -------------------------------------------------------------------------------- /docs/DEPLOY-CONTAINERS.md: -------------------------------------------------------------------------------- 1 | ## Building and deploying the initial set of containers 2 | Now that the Kubernetes cluster and MySQL, Redis, and Memcached services have been provisioned, it's time to start a set of containers (pods) on the Kubernetes worker nodes. We do this through a set of declarative YAML files, which define not only the containers to start, but the storage and network configuration that they depend on. 3 | 4 | ## Review the Docker build files 5 | There are 6 Docker images, 3 for base configuration and 3 custom code which build upon the base configuration. 6 | 7 | The first set of three (NGINX, PHP-FPM, and PHP-CLI) provide the underlying web server and PHP environment needed by Drupal. These are rebuilt based on new version numbers provided in text files in the `config` directory, which in turn triggers a rebuild of the second set of three `code` images. 8 | 9 | 1. The [`scripts/docker/config-nginx/Dockerfile`](../scripts/docker/config-nginx/Dockerfile) provides a base level of NGINX configured to delegate PHP requests to PHP-FPM. 10 | 11 | 2. The [`scripts/docker/config-php-fpm/Dockerfile`](../scripts/docker/config-php-fpm/Dockerfile) provides a base level of operating system packages with PHP-FPM and extensions. 12 | 13 | 3. The [`scripts/docker/config-php-cli/Dockerfile`](../scripts/docker/config-php-cli/Dockerfile) provides a base level of OS packages with PHP-CLI and extensions. 14 | 15 | The second set of three (NGINX, PHP-FPM, and PHP-CLI) provide image builds specific to Drupal, Drush, and custom code that is pushed to the `code` directory. They are rebuilt when anything in that directory changes. 16 | 17 | 1. The [`scripts/docker/code-nginx/Dockerfile`](../scripts/docker/code-nginx/Dockerfile) builds on the base image and adds static code to the `/var/www/html` directory. 18 | 19 | 2. The [`scripts/docker/code-php-fpm/Dockerfile`](../scripts/docker/code-php-fpm/Dockerfile) installs Drupal via Composer, then copies over custom code. It also mounts and configures the volume needed at runtime through its `start.sh` script. 20 | 21 | 3. The [`scripts/docker/code-php-cli/Dockerfile`](../scripts/docker/code-php-cli/Dockerfile) installs Drush via Composer, then copies over custom code. It also mounts and configures both the staging and production volumes needed at runtime through its `start.sh` script. 22 | 23 | ## Review the Kubernetes container deployment configuration files 24 | The Kubernetes deployment files instantiate containers based on the `code` images (never just the `config` images). 25 | 26 | We set up two container clusters, one for a "Staging" environment and one for a "Production" environment. They share the same base Docker images. 27 | 28 | - The [`scripts/kubernetes/persistent-volumes.yaml`](../scripts/kubernetes/persistent-volumes.yaml) files defines two 20 GB storage volumes (one for staging, one for production) that can be mounted by many containers (`ReadWriteMany`). The containers then reference these volumes in their own configuration files. 29 | - The [`scripts/kubernetes/php-fpm-stg.yaml`](../scripts/kubernetes/php-fpm-stg.yaml) and [`scripts/kubernetes/php-fpm-prd.yaml`](../scripts/kubernetes/php-fpm-prd.yaml) files describe the pod/deployment for the PHP-FPM containers in each environment. They specify how many containers from the given image and tag to start, what port to listen on, the environment variables that map to the service credentials, and where to mount the storage volume. 30 | - Similarly, the [`scripts/kubernetes/nginx-stg.yaml`](../scripts/kubernetes/nginx-stg.yaml) and [`scripts/kubernetes/nginx-prd.yaml`](../scripts/kubernetes/nginx-prd.yaml) files describe the pod/deployment for the NGINX containers. They specify how many containers from the given image and tag to start (1, for now), what port to listen on, what IP address to bind to, the environment variables that map to the service credentials, and where to mount the storage volume. 31 | - Finally, the [`scripts/kubernetes/php-cli.yaml`](../scripts/kubernetes/php-cli.yaml) configures the single shared CLI container that is used to manage files and data from both environments and synchronize data ([`code/drush/transfer-data.sh`](../code/drush/transfer-data.sh)) and files ([`code/drush/transfer-files.sh`](../code/drush/transfer-files.sh)) from production to staging. 32 | 33 | ## Build container images and push to the private registry 34 | If you haven't already installed the Container Service plugin for the `bx` CLI you installed when setting up the Kubernetes clusters, do it now: 35 | 36 | ```bash 37 | # Configure the plugin if you haven't yet 38 | bx plugin install container-service -r Bluemix 39 | bx login -a https://api.ng.bluemix.net 40 | bx cs init 41 | ``` 42 | 43 | Next, list the clusters already provisioned on IBM Cloud, and get the Kubernetes configuration information. 44 | ```bash 45 | bx cs clusters # Find your cluster, and input into next command 46 | bx cs cluster-config $CLUSTER_NAME 47 | ``` 48 | 49 | Copy the `export` line from the previous command to configure kubectl to point to your cluster. 50 | 51 | ```bash 52 | # Configure kubectl 53 | export KUBECONFIG=/Users/$USER_HOME_DIR/.bluemix/plugins/container-service/clusters/$CLUSTER_NAME/kube-config-$DATA_CENTER-$CLUSTER_NAME.yml 54 | ``` 55 | 56 | Finally, test your connection by interacting with your cluster. 57 | ```bash 58 | # Confirm cluster is ready 59 | kubectl get nodes 60 | 61 | # Run the visual dashboard proxy and open it with a browser on your workstation at http://127.0.0.1:8001/ui 62 | kubectl proxy 63 | ``` 64 | 65 | ## Configure your namespace 66 | The Dockerfiles in this repo are hardcoded to the `orod` image registry namespace. You need to create your own namespace and update the deployment manifests that reference images. 67 | 68 | Install the IBM Cloud Container Registry CLI plugin: 69 | ```bash 70 | bx plugin install container-registry -r Bluemix 71 | bx login -a https://api.ng.bluemix.net 72 | ``` 73 | 74 | Create a namespace: 75 | ```bash 76 | bx cr namespace-add $MY_NAMESPACE 77 | bx cr namespaces # List namespaces 78 | bx cr login # To enable pushing images 79 | ``` 80 | 81 | Configure scripts with your namespace. You will need to replace `orod` in 82 | - [build-containers.sh](../scripts/build-containers.sh) 83 | - [php-cli.yaml](../scripts/kubernetes/php-cli.yaml) 84 | - [nginx-stg.yaml](../scripts/kubernetes/nginx-stg.yaml) 85 | - [php-fpm-stg.yaml](../scripts/kubernetes/php-fpm-stg.yaml) 86 | - [nginx-prd.yaml](../scripts/kubernetes/nginx-prd.yaml) 87 | - [php-fpm-prd.yaml](../scripts/kubernetes/php-fpm-prd.yaml) 88 | 89 | Finally, you may have to [create an `imagePull` token](https://console.bluemix.net/docs/containers/cs_cluster.html#bx_registry_other) that allows your container cluster to access images in your private container registry. 90 | 91 | ## Build the container images 92 | Run this script to build the containers and push them to your registry: 93 | ```bash 94 | cd scripts/pipeline 95 | ./build-on-config-change.sh 96 | ``` 97 | 98 | ## Deploy the container images to the Kubernetes cluster 99 | 100 | ```bash 101 | # Create an image pull token for the given registry. The kubectl command doesn't like the backslashed wrapped lines presented here for readability, so change it all to one line before you run. 102 | bx cr token-list 103 | bx cr token-get $TOKEN_ID 104 | kubectl --namespace default create secret docker-registry image-pull \ 105 | --docker-server="registry.ng.bluemix.net" \ 106 | --docker-username="token" \ 107 | --docker-password="${TOKEN}" \ 108 | --docker-email="${YOUR_EMAIL}" 109 | 110 | ./rolling-code-deploy.sh 111 | ``` 112 | 113 | The YAML files in this directory reference the same image names (including the name of our registry namespace) as in the `build-on-config-change.sh` script. These is the hand-off point between image build/push, and Kubernetes deploy. 114 | 115 | ## Specify a non-floating LoadBalancer IP 116 | Obtain the available IPs assigned to your cluster (look for "is_public: true") 117 | ```bash 118 | kubectl get cm ibm-cloud-provider-vlan-ip-config -n kube-system -o yaml 119 | ``` 120 | 121 | Set an IP address for Staging and Production in the `spec.loadBalancerIP` value inside [`scripts/kubernetes/nginx-prd.yaml`](../scripts/kubernetes/nginx-prd.yaml) and [`scripts/kubernetes/nginx-stg.yaml`](../scripts/kubernetes/nginx-stg.yaml). 122 | 123 | For example: 124 | ```bash 125 | apiVersion: v1 126 | kind: Service 127 | metadata: 128 | name: nginx-prd 129 | spec: 130 | loadBalancerIP: $IP 131 | ... 132 | --- 133 | ``` 134 | 135 | ## Setup Ingress (replaces LoadBalancer) 136 | So far, we have configured LoadBalancer as the service type for the "nginx-prd" service. We can use the Ingress type instead to give us more flexibility with specifying routes from a single endpoint and also us to use a hostname instead of floating IPs to access our application. Detailed docs here: https://console.bluemix.net/docs/containers/cs_apps.html#cs_apps_public_ingress. 137 | 138 | 1) Remove the `type: LoadBalancer` line from [`scripts/kubernetes/nginx.yaml`](../scripts/kubernetes/nginx-prd.yaml) 139 | 140 | 2) Obtain your "Ingress subdomain". 141 | ```bash 142 | bx cs cluster-get $CLUSTER_NAME 143 | ``` 144 | 145 | 3) Edit [`scripts/kubernetes/ingress/ingress.yaml`](../scripts/kubernetes/ingress/ingress.yaml) to include your subdomain. 146 | 147 | 4) Redeploy your "nginx-prd" service 148 | ```bash 149 | kubectl delete service nginx-prd 150 | kubectl apply -f scripts/kubernetes/nginx-prd.yaml 151 | ``` 152 | 153 | 5) Deploy the ingress service 154 | ```bash 155 | kubectl apply -f scripts/kubernetes/ingress/ingress.yaml 156 | ``` 157 | 158 | 6) Once ingress is up (may take a minute), access your application via your domain. 159 | 160 | ## Tear down the containers 161 | If you want to cleanly install the environment, for example to push a new set of container versions, use the following script: 162 | 163 | ```bash 164 | cd scripts/pipeline 165 | ./rolling-code-deploy.sh 166 | ``` 167 | -------------------------------------------------------------------------------- /docs/PIPELINE-SETUP.md: -------------------------------------------------------------------------------- 1 | # Toolchain Introduction 2 | 3 | This toolchain will enable your containers to automatically build and push to a registry as well as deploy to a Kubernetes cluster hosted on the IBM Cloud. This toolchain will be comprised of multiple pipelines, one for each major component of the cluster. Ideally there would be four different layers in build/deploy process: 4 | 5 | 1. Push code to repository 6 | 2. Check to see what directories have been changed 7 | - If config directory has been changed, rebuild base images and code layer 8 | - If code directory has been changed, only build the code layer 9 | 3. Build images 10 | 4. Test images with Vulnerability Advisor. 11 | 5. Deploy to staging environment 12 | 6. Deploy to production environment 13 | 14 | Each of these layers could come from a different repository and could be built and deployed when code is pushed. 15 | 16 | For the purpose of this POC, we have one repo that contains all of our images and custom code. Our finished toolchain will appear as follows: 17 | 18 | ![Completed Toolchain](img/completedToolchain.PNG) 19 | 20 | # Building the Toolchain 21 | 22 | 1. To get started, click on the "hamburger" menu at the top left of Bluemix and select **Dev Ops**. 23 | 24 | ![IBM Cloud Menu](img/bluemixMenu.PNG) 25 | 26 | 2. Click on Toolchains on the left pane 27 | 3. Click Create Toolchain 28 | 4. Scroll down to Other Templates and select **Build Your Own Toolchain** 29 | 30 | ![Build your own template](img/customTemplate.PNG) 31 | 32 | 5. Name toolchain and click **Create** 33 | 6. Click on **Add a Tool** 34 | 35 | ![Build your own template](img/addTool.png) 36 | 37 | 7. Click on **Git Repos and Issue Tracking** 38 | 39 | ![Git Integration](img/gitTool.PNG) 40 | 41 | 8. Fill in details 42 | 43 | ![Git Settings](img/gitSettings.PNG) 44 | 45 | - Select Clone 46 | - Enter the URL to Dan's repo 47 | - Enter a Repository name (or not) 48 | - Make sure the **Track deployment of code changes** checkbox is checked. This will allow the pipeline to trigger automatically with code changes. 49 | - Click **Create Integration** 50 | 9. Add another tool 51 | 10. Select **Delivery Pipeline** 52 | 53 | ![Git Settings](img/deliveryPipeline.png) 54 | 55 | - Name the pipeline **nginx** 56 | - Click **Create Integration** 57 | 58 | 11. Before moving on, we need to get an IBM Cloud API key. Click on **Manage** at the top right, hover over **Security** and select **IBM Cloud API keys**. 59 | 60 | 12. Click on **Create**, give your key a name and description, click Create. 61 | 62 | 13. Click *Show* and copy your API key. Make absolutely sure that you copied the key correctly because after you leave this page, you will not be able to see the key again. 63 | 64 | 14. Go back to your toolchain. You can get there by clicking on the menu at the top left, selecting **DevOps**, then selecting your toolchain from the list. 65 | 66 | 15. On the toolchain page, click on the Delivery Pipeline. 67 | 68 | ![Click on Delivery Pipeline](img/toolchainPage.PNG) 69 | 70 | 16. Click **Add Stage** 71 | 72 | 17. Make changes to **Input** tab. 73 | - Give stage a name 74 | - Ensure git URL and branch are correct 75 | - Ensure that **Run jobs whenever a change is pushed to Git** is selected. This will allow for the container images to build automatically. 76 | 77 | ![Build Input](img/buildInput.PNG) 78 | 79 | 18. Click on the **Jobs** tab. Click on **Add Job** and select **Build**. 80 | - Under *Builder Type* select **Container Registry**. 81 | - Under *API Key* see if your IBM Cloud API Key appears. If not, click on **Add an existing API Key** and enter the API key that you copied earlier. 82 | - For *IBM Cloud Container Registry namespace* enter **jjdojo** 83 | - In *Docker image name* enter **nginx** 84 | 85 | ![Build Stage](img/buildJob.PNG) 86 | 87 | - In the *Build Script* section enter the following: 88 | ```bash 89 | echo "Calling the build script" 90 | cd scripts/pipeline 91 | . ./buildImage.sh 92 | ``` 93 | - Leave the rest as it is and click on **Save** at the bottom of the stage. 94 | 95 | 19. Next, create another stage and name it **Test**. In this stage you can run any custom test scripts or use the built-in Vulnerability Advisor. 96 | 97 | 20. Click on the jobs tab, add a new job, and select **Test**. 98 | 99 | 21. Under *Tester Type*, select **Vulnerability Advisor** 100 | - Under *API Key* slect the key for your org or enter a new one 101 | - Under *Bluemix Container Registry Namespace* enter your Namespace 102 | - Select the *Docker Image Name* and *Docker Image Tag* that you want to test. 103 | - Add any additional testing scripts in the *Test Script* area. 104 | - When done, click *Save* 105 | 106 | ![Test Stage](img/testStage.PNG) 107 | 108 | 22. Once you are back on the pipeline page, click **Add Stage** again. Now we need to add our stage for deploying to the staging environemnt. 109 | 110 | 23. Name the stage **Staging** and make sure the input is coming from the previous build stage as seen below. 111 | 112 | ![Deploy stage input](img/deployInput.PNG) 113 | 114 | 24. Next, click on the **Jobs** tab and click **Add job** and select **Deploy**. 115 | - For *Deployer Type* select **kubernetes** 116 | - Enter your API Key under *API Key* 117 | - Select the cluster that you would like to deploy to. 118 | - In the *Deploy Script* section, enter the following: 119 | 120 | ```bash 121 | #!/bin/bash 122 | 123 | . scripts/pipeline/pipelineDeployScripts/nginx-deploy.sh 124 | ``` 125 | ![Deploy Job](img/deployJob.PNG) 126 | 127 | - Next we need to add an environment variable to tell the script which environment we are deploying to. Click on the *Environment Properties* tab. 128 | 129 | - Click *Add Property*, select *Text Property*, and under name enter **ENVIRONMENT** and under *Value* enter **stg**. 130 | 131 | ![Stg environment variable](img/EnvVar.PNG) 132 | 133 | - When done, click on **Save** 134 | 135 | 25. Next, we need to create another deployment stage but this time we will deploy to the production environment. Repeat steps **22 - 24** but this time, name the stage **Production**, and for the environment property, enter **prd**. The deploy script for both environments will be the same. 136 | 137 | 26. We should now have four stages in our nginx pipeline. This pipeline will handle the building, testing, and deploying of the nginx container in two different environments. We now need to add pipelines for our other containers. Click on the toolchain name at the top left of the page to take you back to the toolchain page. 138 | 139 | ![Finished pipeline](img/finishedPipeline.PNG) 140 | 141 | 27. Follow steps **14 - 25** to create pipelines for the other images while making sure to change the image names for the respective pipeline as well as making sure that the registry namespace and targeted cluster remains the same. Be sure to change the deploy script for each pipeline as follows: 142 | 143 | --- 144 | - **Note that php-cli does not need to be deployed into both environments** 145 | - For php-cli: 146 | ```bash 147 | #!/bin/bash 148 | 149 | . scripts/pipeline/pipelineDeployScripts/php-cli-deploy.sh 150 | 151 | ``` 152 | 153 | --- 154 | - For php-fpm: 155 | ```bash 156 | #!/bin/bash 157 | 158 | . scripts/pipeline/pipelineDeployScripts/php-fpm-deploy.sh 159 | 160 | ``` 161 | 162 | --- 163 | 164 | 165 | 28. Next, we need to add the step to build the persistent volumes. Create one more pipeline and name it **Persistent Volumes** 166 | 167 | 29. Click on it to configure the pipeline and add a new stage. 168 | 169 | 30. Add a new **Deploy** job, for *Deployer Type* select **Kubernetes**, enter your API Key, and select your target cluster. 170 | 171 | 31. For the in the *Deploy Script* section, enter the following: 172 | ```bash 173 | #!/bin/bash 174 | kubectl apply -f scripts/kubernetes/persistent-volumes.yaml 175 | ``` 176 | 177 | ![Persistent Volume Deploy](img/persistentVolumeDeploy.PNG) 178 | 179 | 29. When done, click **Save**. 180 | 181 | 30. Now we just need to add one last pipeline that will allow us to manually run scripts to transfer files and data between environments. Click on the toolchain name at the top left to go back to the toolchain page. 182 | 183 | 31. Click *Add a Tool* 184 | - Select **Delivery Pipeline** 185 | - Name the pipeline **Data Sync** 186 | - Once the pipeline has been created, click on it to configure the stages. 187 | 188 | 32. Click *Add Stage* and name the stage **Transfer Files** 189 | - On the *Input* tab, change the *Stage Trigger* to *Run jobs only when this stage is run manually* 190 | 191 | 33. Click on the *Jobs* tab 192 | - Click *Add Job* and select *Deploy* 193 | - Name the job **Transfer Files** 194 | - Set the *Deployer Type* to **Kubernetes** 195 | - Verify the *API Key*, *Target*, and *Kubernetes Cluster* 196 | - For the *Deploy Script* enter the following: 197 | ```bash 198 | #!/bin/bash 199 | 200 | echo $(kubectl get pod -l "app=php-cli" -o jsonpath='{.items[0].metadata.name}') 201 | 202 | kubectl exec $(kubectl get pod -l "app=php-cli" -o jsonpath='{.items[0].metadata.name}') /root/drush/transfer-files.sh 203 | 204 | ``` 205 | - Click *Save* 206 | 34. Add another stage 207 | - Name the stage **Transfer Data** 208 | - Click on the *Input* tab and set the *Stage Trigger* to **Run jobs only when this stage is run manually** 209 | 210 | 35. Click on the *Jobs* tab 211 | - Click *Add Job* and select *Deploy* 212 | - Name the job **Transfer Data** 213 | - Set the *Deployer Type* to **Kubernetes** 214 | - Verify the *API Key*, *Target*, and *Kubernetes Cluster* 215 | - For the *Deploy Script* enter the following: 216 | ```bash 217 | #!/bin/bash 218 | 219 | echo $(kubectl get pod -l "app=php-cli" -o jsonpath='{.items[0].metadata.name}') 220 | 221 | kubectl exec $(kubectl get pod -l "app=php-cli" -o jsonpath='{.items[0].metadata.name}') /root/drush/transfer-data.sh 222 | 223 | ``` 224 | - Click *Save* 225 | 226 | 30. Our toolchain is now configured and should look similar to the image below: 227 | 228 | ![Completed Toolchain](img/completedToolchain.PNG) 229 | 230 | All that we have to do now is push a change to our repo to automatically kick off the pipeline. 231 | --------------------------------------------------------------------------------