From 2c4985ef47c78d4f8dbc4d98a5a19f8a46cb90ea Mon Sep 17 00:00:00 2001 From: Bill Prin Date: Tue, 26 Jan 2016 10:32:10 -0800 Subject: [PATCH] Add Django/Postgres/Redis Container Engine example --- .../django_postgres_redis/.gitignore | 3 + .../django_postgres_redis/Makefile | 71 ++++ .../django_postgres_redis/README.md | 364 ++++++++++++++++++ .../django_postgres_redis/env.template | 11 + .../guestbook/.dockerignore | 17 + .../guestbook/.gitignore | 1 + .../guestbook/Dockerfile | 44 +++ .../django_postgres_redis/guestbook/Makefile | 14 + .../guestbook/__init__.py | 0 .../guestbook/guestbook/__init__.py | 0 .../guestbook/guestbook/admin.py | 18 + .../guestbook/guestbook/apps.py | 19 + .../guestbook/guestbook/models.py | 19 + .../guestbook/static/js/controllers.js | 48 +++ .../guestbook/guestbook/templates/index.html | 47 +++ .../guestbook/guestbook/tests.py | 13 + .../guestbook/guestbook/urls.py | 25 ++ .../guestbook/guestbook/views.py | 77 ++++ .../django_postgres_redis/guestbook/manage.py | 24 ++ .../guestbook/mysite/__init__.py | 0 .../guestbook/mysite/settings.py | 149 +++++++ .../guestbook/mysite/urls.py | 26 ++ .../guestbook/mysite/wsgi.py | 22 ++ .../guestbook/requirements.txt | 7 + .../kubernetes_configs/db_password.yaml | 23 ++ .../kubernetes_configs/frontend.yaml | 76 ++++ .../kubernetes_configs/load_tester.yaml | 38 ++ .../kubernetes_configs/postgres.yaml | 68 ++++ .../kubernetes_configs/redis_cluster.yaml | 133 +++++++ .../load_test_image/Dockerfile | 3 + .../load_test_image/Makefile | 9 + .../postgres_image/Dockerfile | 5 + .../postgres_image/Makefile | 11 + 33 files changed, 1385 insertions(+) create mode 100644 container_engine/django_postgres_redis/.gitignore create mode 100644 container_engine/django_postgres_redis/Makefile create mode 100644 container_engine/django_postgres_redis/README.md create mode 100644 container_engine/django_postgres_redis/env.template create mode 100644 container_engine/django_postgres_redis/guestbook/.dockerignore create mode 100644 container_engine/django_postgres_redis/guestbook/.gitignore create mode 100644 container_engine/django_postgres_redis/guestbook/Dockerfile create mode 100644 container_engine/django_postgres_redis/guestbook/Makefile create mode 100644 container_engine/django_postgres_redis/guestbook/__init__.py create mode 100644 container_engine/django_postgres_redis/guestbook/guestbook/__init__.py create mode 100644 container_engine/django_postgres_redis/guestbook/guestbook/admin.py create mode 100644 container_engine/django_postgres_redis/guestbook/guestbook/apps.py create mode 100644 container_engine/django_postgres_redis/guestbook/guestbook/models.py create mode 100644 container_engine/django_postgres_redis/guestbook/guestbook/static/js/controllers.js create mode 100644 container_engine/django_postgres_redis/guestbook/guestbook/templates/index.html create mode 100644 container_engine/django_postgres_redis/guestbook/guestbook/tests.py create mode 100644 container_engine/django_postgres_redis/guestbook/guestbook/urls.py create mode 100644 container_engine/django_postgres_redis/guestbook/guestbook/views.py create mode 100755 container_engine/django_postgres_redis/guestbook/manage.py create mode 100644 container_engine/django_postgres_redis/guestbook/mysite/__init__.py create mode 100644 container_engine/django_postgres_redis/guestbook/mysite/settings.py create mode 100644 container_engine/django_postgres_redis/guestbook/mysite/urls.py create mode 100644 container_engine/django_postgres_redis/guestbook/mysite/wsgi.py create mode 100644 container_engine/django_postgres_redis/guestbook/requirements.txt create mode 100644 container_engine/django_postgres_redis/kubernetes_configs/db_password.yaml create mode 100644 container_engine/django_postgres_redis/kubernetes_configs/frontend.yaml create mode 100644 container_engine/django_postgres_redis/kubernetes_configs/load_tester.yaml create mode 100644 container_engine/django_postgres_redis/kubernetes_configs/postgres.yaml create mode 100644 container_engine/django_postgres_redis/kubernetes_configs/redis_cluster.yaml create mode 100644 container_engine/django_postgres_redis/load_test_image/Dockerfile create mode 100644 container_engine/django_postgres_redis/load_test_image/Makefile create mode 100644 container_engine/django_postgres_redis/postgres_image/Dockerfile create mode 100644 container_engine/django_postgres_redis/postgres_image/Makefile diff --git a/container_engine/django_postgres_redis/.gitignore b/container_engine/django_postgres_redis/.gitignore new file mode 100644 index 000000000000..12100a42fbee --- /dev/null +++ b/container_engine/django_postgres_redis/.gitignore @@ -0,0 +1,3 @@ +.env +guestbook/static + diff --git a/container_engine/django_postgres_redis/Makefile b/container_engine/django_postgres_redis/Makefile new file mode 100644 index 000000000000..804b493988b1 --- /dev/null +++ b/container_engine/django_postgres_redis/Makefile @@ -0,0 +1,71 @@ +GCLOUD_PROJECT:=$(shell gcloud config list project --format="value(core.project)") +ZONE=$(shell gcloud config list compute/zone --format="value(compute.zone)") +CLUSTER_NAME=guestbook +COOL_DOWN=15 +MIN=2 +MAX=15 +TARGET=50 +RC=frontend + +.PHONY: all +all: deploy + +.PHONY: create-cluster +create-cluster: + gcloud container clusters create guestbook \ + --scope "https://www.googleapis.com/auth/userinfo.email","cloud-platform" \ + --num-nodes=$(MIN) + gcloud container clusters get-credentials guestbook + +.PHONY: create-bucket +create-bucket: + gsutil mb gs://$(GCLOUD_PROJECT) + gsutil defacl set public-read gs://$(GCLOUD_PROJECT) + +.PHONY: template +template: + sed -i ".tmpl" "s/\$$GCLOUD_PROJECT/$(GCLOUD_PROJECT)/g" kubernetes_configs/postgres.yaml + sed -i ".tmpl" "s/\$$GCLOUD_PROJECT/$(GCLOUD_PROJECT)/g" kubernetes_configs/frontend.yaml + sed -i ".tmpl" "s/\$$GCLOUD_PROJECT/$(GCLOUD_PROJECT)/g" kubernetes_configs/load_tester.yaml + +.PHONY: deploy +deploy: push template + kubectl create -f kubernetes_config/frontend.yaml + +.PHONY: update +update: + kubectl rolling-update guestbook --image=gcr.io/${GCLOUD_PROJECT}/guestbook + +.PHONY: disk +disk: + gcloud compute disks create pg-data --size 500GB + +.PHONY: firewall +firewall: + gcloud compute firewall-rules create kubepostgres --allow tcp:30061 + +.PHONY: autoscale-on +autoscale-on: + AUTOSCALE_GROUP=$(shell gcloud container clusters describe $(CLUSTER_NAME) --zone $(ZONE) --format yaml | grep -A 1 instanceGroupUrls | awk -F/ 'FNR ==2 {print $$NF}') + gcloud compute instance-groups managed set-autoscaling $(AUTOSCALE_GROUP) \ + --cool-down-period $(COOL_DOWN) \ + --max-num-replicas $(MAX) \ + --min-num-replicas $(MIN) \ + --scale-based-on-cpu --target-cpu-utilization $(shell echo "scale=2; $(TARGET)/100" | bc) + kubectl autoscale rc $(RC) --min=$(MIN) --max=$(MAX) --cpu-percent=$(TARGET) + +.PHONY: db-setup +db-setup: + POSTGRES_POD_NAME=$(shell kubectl get pods | grep postgres | awk '{print $$1}') + kubectl exec $(POSTGRES_POD_NAME) -- /init_db.sh + +.PHONY: migrations +migrations: + FRONTEND_POD_NAME=$(shell kubectl get pods | grep frontend -m 1 | awk '{print $$1}' ) + kubectl exec $(FRONTEND_POD_NAME) -- python /app/manage.py migrate + + +.PHONY: delete +delete: + gcloud container clusters delete guestbook + gcloud compute disks delete pg-data diff --git a/container_engine/django_postgres_redis/README.md b/container_engine/django_postgres_redis/README.md new file mode 100644 index 000000000000..93f59c8567e3 --- /dev/null +++ b/container_engine/django_postgres_redis/README.md @@ -0,0 +1,364 @@ +# Getting started with Django on Google Container Engine (Kubernetes) + +Since this project demonstrates deploying Postgres and Redis to the cluster, it's slightly involved. For a simpler +example of Django on Container Engine/Kubernetes, try the `container_engine/django_tutorial` directory of this +repository, which also deploys Django to Kubernetes but uses a CloudSQL managed MySQL database, no cache, no +secrets, and does not demonstrate autoscaling. + +This repository is an example of how to run a [Django](https://www.djangoproject.com/) +app on Google Container Engine. It was created to go with a slide deck created here. + +https://speakerdeck.com/waprin/deploying-django-on-kubernetes + +This project walks through setting up this project on a Google Container Engine Kubernetes cluster. These +instructions should work on other Kubernetes platforms with some adjustments. should also deploy + on other Kubernetes providers besides Google Container. Specifically, cluster creation steps, disks, load balancers, +, cloud storage options, and node autoscalers should get replaced by their equivalents on your platform. + +This project demonstrates how to create a PostgreSQL database and Redis cache within your cluster. It also contains + an image to simulate load on your cluster to demonstrate autoscaling. The app is inspired by the +[PHP-Redis Guestbook example](https://github.com/kubernetes/kubernetes/tree/master/examples/guestbook/php-redis). + +Please submit any code or doc issues to the issue tracker! + +## Makefile + +Several commands listed below are provided in simpler form via the Makefile. Many of them use the GCLOUD_PROJECT +environment variable, which will be picked up from your gcloud config. Make sure you set this to the correct project, + + gcloud config set project + +There are more Makefiles in sub-directories to help build and push specific images. + +## Pre-requisites + +1. Install [Docker](https://www.docker.com/). + +1. Create a project in the [Google Cloud Platform Console](https://console.cloud.google.com). + +1. [Enable billing](https://console.cloud.google.com/project/_/settings) for your project. + +1. [Enable APIs](https://console.cloud.google.com/flows/enableapi?apiid=datastore,pubsub,storage_api,logging,plus) +for your project. The provided link will enable all necessary APIs, but if you wish to do so manually you will need Datastore, Pub/Sub, Storage, and Logging. + +1. Install the [Google Cloud SDK](https://cloud.google.com/sdk) + + $ curl https://sdk.cloud.google.com | bash + $ gcloud init + +1. Create a cluster for the bookshelf application: + + `gcloud container clusters create guestbook \ + --scope "https://www.googleapis.com/auth/userinfo.email","cloud-platform" \ + --num-nodes 2 + gcloud container clusters get-credentials guestbook` + +The get-credentials commands initializes the kubectl CLI tool with the cluster you just created. + +Alternatively, you can use the Makefile: + + make create-cluster + +### A note about cost + +The --num-nodes flag in the cluster create specifies how many instances are created. Container Engine orchestrator is +free up to 5 instances, but you will pay for the instances themselves, so to minimize costs just create 1 node. + + At the end of the tutorial, run + + gcloud container clusters delete guestbook + gcloud compute disks delete pg-data + +or + + make delete + +To delete the cluster and not get charged for continued use. Deleting resources you are not using is especially +important if you run the autoscaling example to create many instances. + + +## Running PostgreSQL and Redis + +The Django app depends on a PostgreSQL and Redis service. While this README explains how to deploy those services within +the Kubernetes cluster, for local purposes you may want to run them locally or elsewhere. Looking in mysite/settings.py, +you can see the app looks for several environment variables. + + +The first environment variable `NODB`, if set to 1, uses +a SQLite database and an in-memory cache, allowing the app to run without connecting to a real database. This is useful + to test the app locally without Postgres/Redis, or deploy it to Kubernetes without Postgres/Redis. Instead, it will + use a local SQLite file and in-memory cache. In the Kubernetes cluster, each container will have its own SQLite + database and memory-cache, so the persistence and cache storage of the values will not be shared between containers + so will not be right. The NODB setting is just to help debug incrementally and should be turned off. + +Once NODB is disabled, you can connect to an external PostgreSQL or Redis service, or read further to learn how to +setup these services within the cluster. Once you have host values for these services, you can set +POSTGRES_SERVICE_HOST, REDIS_MASTER_SERVICE_HOST, and REDIS_SLAVE_SERIVCE_HOST with their appropriate values when +runnig locally. When running on Kubernetes these variables will be automatically populated. See +guestbook/mysite/settings.py for more detail. + + +If you want to emulate the Kubernetes environment locally, you can use env.template as a starting point for a file that +can export these environment variables locally. Once it's created, you can source it to add them to your local +enviornment: + +# create .env file from env.template example +# source .env + +Don't check the file with your environment variable values into version control. + +## Running locally + +First make sure you have Django installed. It's recommended you do so in a +[virtualenv](https://virtualenv.pypa.io/en/latest/). The requirements.txt +contains just the Django dependency. + + pip install -r requirements.txt + +The app can be run locally the same way as any other Django app. + + # disable Postgres and Redis until we set them up + cd guestbook + export NODB=1 + python manage.py runserver + +# Deploying To Google Container Engine (Kubernetes) + +## Build the Guestbook container + +Within the Dockerfile, NODB is turned on or off. Once you have deployed PostgreSQL or Redis, you can disable this +flag. If you deploy those services within Kubernetes, the environment variables will be automatically populated. +Otherwise you should set the environment variables manually using ENV in the Dockerfile. + +Before the application can be deployed to Container Engine, you will need build and push the image to [Google Container Registry](https://cloud.google.com/container-registry/). + + cd guestbook + # Make sure NODB is enabled and set to 1 in the Dockerfile if you still haven't setup Postgres and REDIS + docker build -t gcr.io/your-project-id/guestbook . + gcloud docker push gcr.io/your-project-id/guestbook + +Alternatively, this can be done using + + make push + +## Deploy to the application + +## Deploying the frontend to Kubernetes + +The Django application is represented in Kubernetes config, called `frontend`. First, replace the +GCLOUD_PROJECT in `kubernetes_config/frontend.yaml` with your project ID. Alternatively, run `make template`, which +should automatically populate the GCLOUD_PROJECT environment variable with your `gcloud config` settings, and replace +$GCLOUD_PROJECT in the kubernets config tmplates with your actual project name. + + + +Once you've finished following the above instructions on how to build your container, it needs to be +pushed to a Docker image registry that Kubernetes can pull from. One option is Docker hub, but in these examples we use + Google Container Registry. + + cd guestbook + make build + make push + +Once the image is built, it can be deployed in a Kubernetes pod. kubernetes_config/frontend.yalm contains the +Replication Controller to spin up Pods with this image, as well as a Service with an external load balancer. However, +the frontend depends on secret passwords for the database, so before it's deployed, a Kubernetes Secret resource with + your database passwords must be created. + +### Create Secrets + + Even if NODB is enabled, the frontend replication controller is configured to use the Secret volume, so it must + be created in your cluster first. + + Kubernetes [Secrets](http://kubernetes.io/v1.1/docs/user-guide/secrets.html) are used to store the database password. + They must be base64 encoded and store in `kubernetes/configs/db.password.yaml`. A template containing an example config + is created, but your actual Secrets shoudl not be added to source control. + + In order to get the base64 encoded password, use the base64 tool + + echo mysecretpassword | base64 + + Then copy and paste that value into the appropriate part of the Secret config (your-base64-encoded-pw-here) + + kubectl create -f kubernetes_configs/db_password.yaml + + In the Postgres and Frontend replication controller, these Secrets are mounted onto their pods. In their Dockerfile, + they are read into environment variables. + +### Create the frontend Service and Replication Controller + + kubectl create -f kubernetes_config/frontend.yaml + +Alternatively this create set can be done using the Makefile + + make deploy + +Once the resources are created, there should be 3 `frontend` pods on the cluster. To see the pods and ensure that +they are running: + + kubectl get pods + +To get more information about a pod, or dig into why it's having problem starting, try + + kubectl describe pod + +If the pods are not ready or if you see restarts, you can get the logs for a particular pod to figure out the issue: + + kubectl logs + +Once the pods are ready, you can get the public IP address of the load balancer: + + kubectl get services frontend + +You can then browse to the external IP address in your browser to see the bookshelf application. + +When you are ready to update the replication controller with a new image you built, the following command will do a +rolling update + + kubectl rolling-update frontend --image=gcr.io/${GCLOUD_PROJECT}/frontend + +which can also be done with the `make update` command. + +## Create the Redis cluster + +Since the Redis cluster has no volumes or secrets, it's pretty easy to setup: + + kubectl create -f kubernetes_config/redis_cluster.yaml + +This creates a redis-master read/write service and redis-slave service. The images used are configured to properly +replicate from the master to the slaves. + +There should only be one redis-master pod, so the replicatoin controller configures 1 replicas. There can be many +redis-slave pods, so if you want more you can do: + + kubectl scale rc redis-slave --replicas=5 + +## Create PostgreSQL + +PostgresSQL will need a disk backed by a Volume to run in Kubernetes. For this example, we create a persistent disk +using a GCE persistent disk: + + gcloud compute disks create pg-data --size 500GB + +or + make disk + +Edit `kubernetes_config/postgres.yaml` volume name to match the name of the disk you just created, if different. + +For Postgres, the secrets need to get populated and a script to initialize the database needs to be added, so a image +should be built: + + cd postgres_image + make build + make push + +Finally, you should be able to create the PostgreSQL service and pod. + + kubectl create -f kubernetes_config/postgres.yaml + +Only one pod can read and write to a GCE disk, so the PostgreSQL replicaiton controller is set to control 1 pod. +Don't scale more instances of the Postgres POD. + +### Re-Deploy The Frontend and Run Migrations + +Now that the database and redis service are created, you should rebuild the frontend guestbook image with NODB +disabled. + + cd guestbook + # edit Dockerfile to comment out NODB + make build + make push + +With this new image, the replication controller should be updated. One way is to do a rolling update + + make update + +This will safely spin down the old images and replace it with the new image. However, for development purposes +it can be quicker to scale the controller to 0 and then back up. + + kubectl scale scale rf frontend --replicas=0 + kubectl scale scale rf frontend --replicas=3 + +Finally, the Django migrations must be run to create the table. This can also be accomplished with kubectl-exec, this +time with the frontend pod. However, make sure your frontend-pod is actually talking to the database. If you earlier +built the image with NODB, rebuild it with NODB commented out. Then you can run the migrations: + + kubectl exec $(FRONTEND_POD_NAME) -- python /app/manage.py migrate + +replacing FRONTEND_POD_NAME with the appropriate name, or automatically: + + make migrations + +### Serve the static content + +When DEBUG is enabled, Django can serve the files directly from that folder. Currently, the app is configured + to serve static content from the files that way to simplify development. For production purposes, you should disable + the DEBUG flag and serve static assets from a CDN. + +The application uses [Google Cloud Storage](https://cloud.google.com/storage) to store static content. You can +alternatively use a CDN of your choice. Create a bucket for your project: + + gsutil mb gs:// + gsutil defacl set public-read gs:// + +or + + make create-bucket + +Collect all the static assets into the static/ folder. + + python manage.py collectstatic + +Upload it to CloudStorage using the `gsutil rsync` command + + gsutil rsync -R static/ gs:///static + +Now your static content can be served from the following URL: + + http://storage.googleapis.com/` + + +### Load Testing and Autoscaling (beta feature) + +Please note that autoscaling is a beta feature. + +load_test_image provides a super minimal load generator - it hits a CPU intensive endpoint in a loop with curl. First +build the image: + + cd load_testing_image + make build + make push + +Then create the Replication Controller and scale some clients: + + kubectl create -f kubernetes_config/load_tester.yaml + kubectl scale rc load --replicas=20 + +to generate load. Then + + make autoscaling + +will create both Node autoscaling (gcloud) and Pod autoscaling (Kubernetes Horizontal Pod Autoscaling). + +Again, by default this will scale to 10 nodes whcih you will be charged for, so pleaes disable autoscaling and scale +the nodes back down or delete the cluster if you don't want to pay for sustained use of the 10 instances. + +For more sophisticated load testing with Kubernetes, see: + +https://cloud.google.com/solutions/distributed-load-testing-using-kubernetes + +## Issues + +Please use the Issue Tracker for any issues or questions. + +## Contributing changes + +* See [CONTRIBUTING.md](CONTRIBUTING.md) + + +## Licensing + +* See [LICENSE](LICENSE) diff --git a/container_engine/django_postgres_redis/env.template b/container_engine/django_postgres_redis/env.template new file mode 100644 index 000000000000..5d737c944b23 --- /dev/null +++ b/container_engine/django_postgres_redis/env.template @@ -0,0 +1,11 @@ +# This is a template that shows the environment variables you should +# populate when running the app locally +# Point to a PostgreSQL database and Redis of your choice +# Redis Master and Slave can have the same host value +export POSTGRES_SERVICE_HOST= +export POSTGRES_SERVICE_PORT= +export REDIS_MASTER_SERVICE_HOST= +export REDIS_MASTER_SERVICE_PORT= +export REDIS_SLAVE_SERVICE_HOST= +export REDIS_SLAVE_SERVICE_PORT= +export DJANGO_PW= diff --git a/container_engine/django_postgres_redis/guestbook/.dockerignore b/container_engine/django_postgres_redis/guestbook/.dockerignore new file mode 100644 index 000000000000..fdc976d48579 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/.dockerignore @@ -0,0 +1,17 @@ +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +env +pip-log.txt +pip-delete-this-directory.txt +.tox +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +*.log +.git diff --git a/container_engine/django_postgres_redis/guestbook/.gitignore b/container_engine/django_postgres_redis/guestbook/.gitignore new file mode 100644 index 000000000000..601474ee8944 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/.gitignore @@ -0,0 +1 @@ +./static/ diff --git a/container_engine/django_postgres_redis/guestbook/Dockerfile b/container_engine/django_postgres_redis/guestbook/Dockerfile new file mode 100644 index 000000000000..61ac285a23f1 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/Dockerfile @@ -0,0 +1,44 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# The Google App Engine python runtime is Debian Jessie with Python installed +# and various os-level packages to allow installation of popular Python +# libraries. The source is on github at: +# https://github.com/GoogleCloudPlatform/python-docker +FROM gcr.io/google_appengine/python + +# Create a virtualenv for the application dependencies. +# If you want to use Python 3, add the -p python3.4 flag. +RUN virtualenv /env + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. This ensures the application is executed within +# the context of the virtualenv and will have access to its dependencies. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +# Install dependencies. +ADD requirements.txt /app/requirements.txt +RUN pip install -r /app/requirements.txt + +# Uncomment the following line to use NODB +# NODB will use SQLite and in-memory cache insstead of Postgres And Redis +# This is useful to test the rest of the Django setup without configuring the database/cache +# Comment it out and rebuild this image once you have Postgres and Redis services in your cluster +ENV NODB 1 + +# Add application code. +ADD . /app + +CMD export DJANGO_PASSWORD=$(cat /etc/secrets/djangouserpw); gunicorn -b :$PORT mysite.wsgi diff --git a/container_engine/django_postgres_redis/guestbook/Makefile b/container_engine/django_postgres_redis/guestbook/Makefile new file mode 100644 index 000000000000..eae3f8dcd007 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/Makefile @@ -0,0 +1,14 @@ +GCLOUD_PROJECT:=$(shell gcloud config list project --format="value(core.project)") + +.PHONY: build +build: + docker build -t gcr.io/$(GCLOUD_PROJECT)/guestbook . + +.PHONY: push +push: build + gcloud docker push gcr.io/$(GCLOUD_PROJECT)/guestbook + +.PHONY: delete +delete: + kubectl delete rc guestbook + kubectl delete service guestbook diff --git a/container_engine/django_postgres_redis/guestbook/__init__.py b/container_engine/django_postgres_redis/guestbook/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/container_engine/django_postgres_redis/guestbook/guestbook/__init__.py b/container_engine/django_postgres_redis/guestbook/guestbook/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/container_engine/django_postgres_redis/guestbook/guestbook/admin.py b/container_engine/django_postgres_redis/guestbook/guestbook/admin.py new file mode 100644 index 000000000000..9a110e1e989b --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/guestbook/admin.py @@ -0,0 +1,18 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.contrib import admin +from .models import Message + +admin.site.register(Message) diff --git a/container_engine/django_postgres_redis/guestbook/guestbook/apps.py b/container_engine/django_postgres_redis/guestbook/guestbook/apps.py new file mode 100644 index 000000000000..68d701dad365 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/guestbook/apps.py @@ -0,0 +1,19 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.apps import AppConfig + + +class PollsConfig(AppConfig): + name = 'guestbook' diff --git a/container_engine/django_postgres_redis/guestbook/guestbook/models.py b/container_engine/django_postgres_redis/guestbook/guestbook/models.py new file mode 100644 index 000000000000..448a4d93e0f5 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/guestbook/models.py @@ -0,0 +1,19 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.db import models + + +class Message(models.Model): + text = models.CharField(max_length=500) diff --git a/container_engine/django_postgres_redis/guestbook/guestbook/static/js/controllers.js b/container_engine/django_postgres_redis/guestbook/guestbook/static/js/controllers.js new file mode 100644 index 000000000000..2d8e8da9dbb3 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/guestbook/static/js/controllers.js @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015 Google Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + + +var guestbookApp = angular.module('guestbook', ['ui.bootstrap']); + + +/** + * Constructor + */ +function GuestbookController() { +} + +GuestbookController.prototype.onSubmit = function () { + this.scope_.messages.push({text: this.scope_.msg}); + this.http_.post('messages/', this.scope_.msg, + function success() { + console.log("updated!"); + }); + console.log(this.scope_.messages); + this.scope_.msg = ""; +}; + +guestbookApp.controller('GuestbookCtrl', function ($scope, $http, $location) { + $scope.controller = new GuestbookController(); + $scope.controller.scope_ = $scope; + $scope.controller.location_ = $location; + $scope.controller.http_ = $http; + $scope.messages = []; + $scope.controller.http_.get("messages/") + .success(function (data) { + $scope.messages = _.pluck(data, 'fields'); + console.log($scope.messages); + }); +}); diff --git a/container_engine/django_postgres_redis/guestbook/guestbook/templates/index.html b/container_engine/django_postgres_redis/guestbook/guestbook/templates/index.html new file mode 100644 index 000000000000..644aaf520088 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/guestbook/templates/index.html @@ -0,0 +1,47 @@ + + + + + Guestbook + + + + + + + +

+

Guestbook

+ +

This guestbook has been visited {{ visited }} times.

+ +
+
+
+ +
+
+
+
+ {% verbatim %} + {{msg.text}} + {% endverbatim %} +
+
+
+ + diff --git a/container_engine/django_postgres_redis/guestbook/guestbook/tests.py b/container_engine/django_postgres_redis/guestbook/guestbook/tests.py new file mode 100644 index 000000000000..d85408e51791 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/guestbook/tests.py @@ -0,0 +1,13 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/container_engine/django_postgres_redis/guestbook/guestbook/urls.py b/container_engine/django_postgres_redis/guestbook/guestbook/urls.py new file mode 100644 index 000000000000..992368475031 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/guestbook/urls.py @@ -0,0 +1,25 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.conf.urls import url +from django.contrib import admin + +from . import views + +urlpatterns = [ + url(r'^$', views.index, name='index'), + url(r'^messages/$', views.messages, name='messages'), + url(r'^admin/', admin.site.urls), + url(r'^compute/$', views.compute, name='compute'), +] diff --git a/container_engine/django_postgres_redis/guestbook/guestbook/views.py b/container_engine/django_postgres_redis/guestbook/guestbook/views.py new file mode 100644 index 000000000000..33b1d85a060a --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/guestbook/views.py @@ -0,0 +1,77 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.conf import settings +from django.core import serializers +from django.http import HttpResponse +from django.shortcuts import render + +from .models import Message + +from django.core.cache import cache + +import math +import logging + +def __update_visited(): + """ Updates the visited count by one in the cache + :return: The visited count in string form. + """ + visited = cache.get('visited') + if not visited: + visited = 0 + else: + visited = int(visited) + visited += 1 + cache.set('visited', str(visited)) + return str(visited) + + +def index(request): + """ Updates the visited count and provides the static URL to build the main + landing page. + """ + logging.info("in the index view") + visited = __update_visited() + context = { + "STATIC_URL": settings.STATIC_URL, + "visited": visited + } + return render(request, 'index.html', context) + + +def messages(request): + """ REST endpoint providing basic operations. GET will return the list of + all messages created so far in JSON form, POST will add a new message to + the list of messages (guestbook). + """ + if request.method == 'GET': + data = serializers.serialize("json", Message.objects.all()) + return HttpResponse(data) + elif request.method == 'POST': + Message.objects.create(text=request.body) + return HttpResponse(request.body) + else: + return HttpResponse("Unsupported HTTP Verb.") + + +def compute(request): + """ Endpoint that does some CPU intensive work to help demonstrate + autoscaling. + """ + logging.info("doing some long calculations") + x = 0.0001 + for i in range(0, 1000000): + x += math.sqrt(x) + return HttpResponse("Performed a lot of work") diff --git a/container_engine/django_postgres_redis/guestbook/manage.py b/container_engine/django_postgres_redis/guestbook/manage.py new file mode 100755 index 000000000000..2062ecd2666d --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/manage.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/container_engine/django_postgres_redis/guestbook/mysite/__init__.py b/container_engine/django_postgres_redis/guestbook/mysite/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/container_engine/django_postgres_redis/guestbook/mysite/settings.py b/container_engine/django_postgres_redis/guestbook/mysite/settings.py new file mode 100644 index 000000000000..7cef60fc819a --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/mysite/settings.py @@ -0,0 +1,149 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'pf-@jxtojga)z+4s*uwbgjrq$aep62-thd0q7f&o77xtpka!_m' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +# Application definition + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'guestbook' +) + +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', +) + +ROOT_URLCONF = 'mysite.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'mysite.wsgi.application' + +# Database +# https://docs.djangoproject.com/en/1.8/ref/settings/#databases + +# When 'NODB' is enabled,we skip Database and Cache setup. This is useful +# to test the rest of the Django deployment while boostrapping the application. +if os.getenv('NODB'): + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } + } +else: + # Dockerfile reads the DJANGO_PW from the secret into an environment + # variable but its not there on kubectl exec. Soon Kubernetes versions + # will have secrets as environment variables but currently just read it + # from the volume + DJANGO_PW = os.getenv('DJANGO_PASSWORD') + if not DJANGO_PW: + try: + f = open('/etc/secrets/djangouserpw') + DJANGO_PW = f.readline().rstrip() + except IOError: + pass + if not DJANGO_PW: + raise Exception("No DJANGO_PASSWORD provided.") + + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'guestbook', + 'USER': 'django_user', + 'PASSWORD': DJANGO_PW, + 'HOST': os.getenv('POSTGRES_SERVICE_HOST', '127.0.0.1'), + 'PORT': os.getenv('POSTGRES_SERVICE_PORT', 5432) + } + } + + CACHES = { + 'default': { + 'BACKEND': 'redis_cache.RedisCache', + 'LOCATION': [ + '%s:%s' % (os.getenv('REDIS_MASTER_SERVICE_HOST', '127.0.0.1'), + os.getenv('REDIS_MASTER_SERVICE_PORT', 6379)), + '%s:%s' % (os.getenv('REDIS_SLAVE_SERVICE_HOST', '127.0.0.1'), + os.getenv('REDIS_SLAVE_SERVICE_PORT', 6379)) + ], + 'OPTIONS': { + 'PARSER_CLASS': 'redis.connection.HiredisParser', + 'PICKLE_VERSION': 2, + 'MASTER_CACHE': '%s:%s' % ( + os.getenv('REDIS_MASTER_SERVICE_HOST', '127.0.0.1') + , os.getenv('REDIS_MASTER_SERVICE_PORT', 6379)) + }, + }, + } + +# Internationalization +# https://docs.djangoproject.com/en/1.8/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.8/howto/static-files/ + +STATIC_URL = '/static/' +# STATIC_URL = 'https://storage.googleapis.com/delete-me-1156/static/' + +STATIC_ROOT = 'static/' diff --git a/container_engine/django_postgres_redis/guestbook/mysite/urls.py b/container_engine/django_postgres_redis/guestbook/mysite/urls.py new file mode 100644 index 000000000000..adab23eb9af9 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/mysite/urls.py @@ -0,0 +1,26 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.conf import settings +from django.conf.urls import include, url +from django.contrib.staticfiles.urls import staticfiles_urlpatterns + +urlpatterns = [ + url(r'^', include('guestbook.urls')), +] + +# Only serve static files from Django during development +# Use Google Cloud Storage or an alternative CDN for production +if settings.DEBUG: + urlpatterns += staticfiles_urlpatterns() diff --git a/container_engine/django_postgres_redis/guestbook/mysite/wsgi.py b/container_engine/django_postgres_redis/guestbook/mysite/wsgi.py new file mode 100644 index 000000000000..bd3a0d0c2b71 --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/mysite/wsgi.py @@ -0,0 +1,22 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") + +application = get_wsgi_application() diff --git a/container_engine/django_postgres_redis/guestbook/requirements.txt b/container_engine/django_postgres_redis/guestbook/requirements.txt new file mode 100644 index 000000000000..bafa2aee304b --- /dev/null +++ b/container_engine/django_postgres_redis/guestbook/requirements.txt @@ -0,0 +1,7 @@ +Django==1.9.1 +django-redis-cache==1.6.5 +gunicorn==19.4.5 +hiredis==0.2.0 +psycopg2==2.6.1 +redis==2.10.5 +wheel==0.26.0 diff --git a/container_engine/django_postgres_redis/kubernetes_configs/db_password.yaml b/container_engine/django_postgres_redis/kubernetes_configs/db_password.yaml new file mode 100644 index 000000000000..ededa1da77bb --- /dev/null +++ b/container_engine/django_postgres_redis/kubernetes_configs/db_password.yaml @@ -0,0 +1,23 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# This file creates database secrets +# Replace the placeholders with your base64 encoded passwords +--- +apiVersion: v1 +kind: Secret +metadata: + name: db-passwords +data: + djangouserpw: diff --git a/container_engine/django_postgres_redis/kubernetes_configs/frontend.yaml b/container_engine/django_postgres_redis/kubernetes_configs/frontend.yaml new file mode 100644 index 000000000000..088535bea59f --- /dev/null +++ b/container_engine/django_postgres_redis/kubernetes_configs/frontend.yaml @@ -0,0 +1,76 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# This file configures the guestbook application . The frontend serves +# public web traffic. + +# For more info about Pods see: +# https://cloud.google.com/container-engine/docs/pods/ +# For more info about Replication Controllers: +# https://cloud.google.com/container-engine/docs/replicationcontrollers/ + +apiVersion: v1 +kind: ReplicationController +metadata: + name: frontend + labels: + name: frontend +spec: + replicas: 3 + template: + metadata: + labels: + name: frontend + spec: + containers: + - name: guestbook + # Replace with your project ID or use `make template` + image: gcr.io/$GCLOUD_PROJECT/guestbook + # This setting makes nodes pull the docker image every time before + # starting the pod. This is useful when debugging, but should be turned + # off in production. + imagePullPolicy: Always + ports: + - containerPort: 8080 + volumeMounts: + - name: secrets + mountPath: /etc/secrets + readOnly: true + volumes: + - name: secrets + secret: + secretName: db-passwords + +--- + +# The polls service provides a load-balancing proxy over the polls app +# pods. By specifying the type as a 'LoadBalancer', Container Engine will +# create an external HTTP load balancer. +# For more information about Services see: +# https://cloud.google.com/container-engine/docs/services/ +# For more information about external HTTP load balancing see: +# https://cloud.google.com/container-engine/docs/load-balancer +apiVersion: v1 +kind: Service +metadata: + name: frontend + labels: + name: frontend +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 8080 + selector: + name: frontend diff --git a/container_engine/django_postgres_redis/kubernetes_configs/load_tester.yaml b/container_engine/django_postgres_redis/kubernetes_configs/load_tester.yaml new file mode 100644 index 000000000000..88e6fcfbd916 --- /dev/null +++ b/container_engine/django_postgres_redis/kubernetes_configs/load_tester.yaml @@ -0,0 +1,38 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# This file configures a replication controller to spin up lots of +# load testing images to demonstate autoscaling. + +apiVersion: v1 +kind: ReplicationController +metadata: + name: load + labels: + app: load +spec: + replicas: 1 + template: + metadata: + labels: + app: load + spec: + containers: + - name: load + # Replace with your project ID or use `make template` + image: gcr.io/$GCLOUD_PROJECT/load + # This setting makes nodes pull the docker image every time before + # starting the pod. This is useful when debugging, but should be turned + # off in production. + imagePullPolicy: Always diff --git a/container_engine/django_postgres_redis/kubernetes_configs/postgres.yaml b/container_engine/django_postgres_redis/kubernetes_configs/postgres.yaml new file mode 100644 index 000000000000..8b251ce45286 --- /dev/null +++ b/container_engine/django_postgres_redis/kubernetes_configs/postgres.yaml @@ -0,0 +1,68 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# This creates a Postgres Replication Controller with 1 Pod. +# It mounts a GCE persistent disk and database secrets. + +metadata: + name: postgres +apiVersion: v1 +kind: ReplicationController +spec: + replicas: 1 + selector: + name: postgres + template: + metadata: + labels: + name: postgres + spec: + containers: + - name: postgres + image: gcr.io/$GCLOUD_PROJECT/postgres-pw + # disable this in production + imagePullPolicy: Always + ports: + - containerPort: 5432 + volumeMounts: + - name: postgresdata + mountPath: /usr/local/var/postgres + - name: secrets + mountPath: /etc/secrets + readOnly: true + # PostgreSQL Data + # Replace this with the persistent disk of your choice + # TODO: replace with Persistent Volume + volumes: + - name: postgresdata + gcePersistentDisk: + # your disk name here + pdName: pg-data + fsType: ext4 + - name: secrets + secret: + secretName: db-passwords +--- +metadata: + name: "postgres" +kind: "Service" +apiVersion: "v1" +spec: + ports: + - name: "postgres" + protocol: "TCP" + port: 5432 + targetPort: 5432 + selector: + name: postgres diff --git a/container_engine/django_postgres_redis/kubernetes_configs/redis_cluster.yaml b/container_engine/django_postgres_redis/kubernetes_configs/redis_cluster.yaml new file mode 100644 index 000000000000..e501bb6170fd --- /dev/null +++ b/container_engine/django_postgres_redis/kubernetes_configs/redis_cluster.yaml @@ -0,0 +1,133 @@ +# Copyright 2015 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Redis config taken from PHP Guestbook Example +# https://github.com/kubernetes/kubernetes/tree/master/examples/guestbook + +apiVersion: v1 +kind: Service +metadata: + name: redis-master + labels: + app: redis + tier: backend + role: master +spec: + ports: + # the port that this service should serve on + - port: 6379 + targetPort: 6379 + selector: + app: redis + tier: backend + role: master +--- +apiVersion: v1 +kind: ReplicationController +metadata: + name: redis-master + # these labels can be applied automatically + # from the labels in the pod template if not set + labels: + app: redis + role: master + tier: backend +spec: + # this replicas value is default + # modify it according to your case + replicas: 1 + # selector can be applied automatically + # from the labels in the pod template if not set + # selector: + # app: guestbook + # role: master + # tier: backend + template: + metadata: + labels: + app: redis + role: master + tier: backend + spec: + containers: + - name: master + image: redis + resources: + requests: + cpu: 100m + memory: 100Mi + ports: + - containerPort: 6379 +--- +apiVersion: v1 +kind: Service +metadata: + name: redis-slave + labels: + app: redis + tier: backend + role: slave +spec: + ports: + # the port that this service should serve on + - port: 6379 + selector: + app: redis + tier: backend + role: slave +--- +apiVersion: v1 +kind: ReplicationController +metadata: + name: redis-slave + # these labels can be applied automatically + # from the labels in the pod template if not set + labels: + app: redis + role: slave + tier: backend +spec: + # this replicas value is default + # modify it according to your case + replicas: 2 + # selector can be applied automatically + # from the labels in the pod template if not set + # selector: + # app: guestbook + # role: slave + # tier: backend + template: + metadata: + labels: + app: redis + role: slave + tier: backend + spec: + containers: + - name: slave + image: gcr.io/google_samples/gb-redisslave:v1 + resources: + requests: + cpu: 100m + memory: 100Mi + env: + - name: GET_HOSTS_FROM + value: dns + # If your cluster config does not include a dns service, then to + # instead access an environment variable to find the master + # service's host, comment out the 'value: dns' line above, and + # uncomment the line below. + # value: env + ports: + - containerPort: 6379 diff --git a/container_engine/django_postgres_redis/load_test_image/Dockerfile b/container_engine/django_postgres_redis/load_test_image/Dockerfile new file mode 100644 index 000000000000..8c272bf209ac --- /dev/null +++ b/container_engine/django_postgres_redis/load_test_image/Dockerfile @@ -0,0 +1,3 @@ +FROM ubuntu:16.04 +RUN apt-get update -y && apt-get -y install curl +CMD while true; do curl http://frontend.default.svc.cluster.local/compute/; done diff --git a/container_engine/django_postgres_redis/load_test_image/Makefile b/container_engine/django_postgres_redis/load_test_image/Makefile new file mode 100644 index 000000000000..128fecb6b4c9 --- /dev/null +++ b/container_engine/django_postgres_redis/load_test_image/Makefile @@ -0,0 +1,9 @@ +GCLOUD_PROJECT:=$(shell gcloud config list project --format="value(core.project)") + +.PHONY: build +build: + docker build -t gcr.io/$(GCLOUD_PROJECT)/load . + +.PHONY: push +push: build + gcloud docker push gcr.io/$(GCLOUD_PROJECT)/load diff --git a/container_engine/django_postgres_redis/postgres_image/Dockerfile b/container_engine/django_postgres_redis/postgres_image/Dockerfile new file mode 100644 index 000000000000..16011296b5ff --- /dev/null +++ b/container_engine/django_postgres_redis/postgres_image/Dockerfile @@ -0,0 +1,5 @@ +FROM postgres:9.4 + +ENTRYPOINT [] +CMD export POSTGRES_DB=guestbook; export POSTGRES_USER=django_user; export POSTGRES_PASSWORD=$(cat /etc/secrets/djangouserpw) ;./docker-entrypoint.sh postgres; + diff --git a/container_engine/django_postgres_redis/postgres_image/Makefile b/container_engine/django_postgres_redis/postgres_image/Makefile new file mode 100644 index 000000000000..c0f0dffb95cd --- /dev/null +++ b/container_engine/django_postgres_redis/postgres_image/Makefile @@ -0,0 +1,11 @@ +GCLOUD_PROJECT:=$(shell gcloud config list project --format="value(core.project)") + +.PHONY: build +build: + docker build -t gcr.io/$(GCLOUD_PROJECT)/postgres-pw . + +.PHONY: push +push: build + gcloud docker push gcr.io/$(GCLOUD_PROJECT)/postgres-pw + +