├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── gke_jinja.json ├── guestbook ├── .dockerignore ├── .gitignore ├── Dockerfile ├── Makefile ├── __init__.py ├── guestbook │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── static │ │ └── js │ │ │ └── controllers.js │ ├── templates │ │ └── index.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── manage.py ├── mysite │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── requirements.txt ├── kubernetes_configs ├── guestbook │ └── guestbook.yaml.jinja ├── load_test │ ├── load_test_image │ │ ├── Dockerfile │ │ └── Makefile │ └── load_tester.yaml.tmpl ├── postgres │ ├── postgres.yaml.jinja │ └── postgres_image │ │ ├── Dockerfile │ │ └── Makefile └── redis │ └── redis.yaml ├── minikube_jinja.json └── requirements-dev.txt /.gitignore: -------------------------------------------------------------------------------- 1 | guestbook/guestbook/migrations/* 2 | kubernetes_configs/guestbook/guestbook_minikube.yaml 3 | kubernetes_configs/guestbook/guestbook_gke.yaml 4 | kubernetes_configs/postgres/postgres_minikube.yaml 5 | kubernetes_configs/postgres/postgres_gke.yaml 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License 9 | Agreement (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an [individual CLA] 13 | (https://developers.google.com/open-source/cla/individual). 14 | * If you work for a company that wants to allow you to contribute your work, 15 | then you'll need to sign a [corporate CLA] 16 | (https://developers.google.com/open-source/cla/corporate). 17 | 18 | Follow either of the two links above to access the appropriate CLA and 19 | instructions for how to sign and return it. Once we receive it, we'll 20 | be able to accept your pull requests. 21 | 22 | ## Contributing A Patch 23 | 24 | 1. Submit an issue describing your proposed change to the repo in question. 25 | 1. The repo owner will respond to your issue promptly. 26 | 1. If your proposed change is accepted, and you haven't already done so, sign a 27 | Contributor License Agreement (see details above). 28 | 1. Fork the desired repo, develop and test your code changes. 29 | 1. Ensure that your code adheres to the existing style in the sample to which 30 | you are contributing. Refer to the 31 | [Google Cloud Platform Samples Style Guide] 32 | (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the 33 | recommended coding standards for this organization. 34 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 35 | 1. Submit a pull request. 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | GOOGLE_CLOUD_PROJECT:=$(shell gcloud config list project --format="value(core.project)") 16 | ZONE=$(shell gcloud config list compute/zone --format="value(compute.zone)") 17 | CLUSTER_NAME=guestbook 18 | COOL_DOWN=15 19 | MIN=2 20 | MAX=15 21 | TARGET=50 22 | DEPLOYMENT=guestbook 23 | GUESTBOOK_POD_NAME=$(shell kubectl get pods | grep guestbook -m 1 | awk '{print $$1}' ) 24 | 25 | .PHONY: all 26 | all: deploy 27 | 28 | .PHONY: create-cluster 29 | create-cluster: 30 | gcloud container clusters create guestbook \ 31 | --scope "https://www.googleapis.com/auth/userinfo.email","cloud-platform" \ 32 | --num-nodes=$(MIN) 33 | gcloud container clusters get-credentials guestbook 34 | 35 | .PHONY: create-bucket 36 | create-bucket: 37 | gsutil mb gs://$(GOOGLE_CLOUD_PROJECT) 38 | gsutil defacl set public-read gs://$(GOOGLE_CLOUD_PROJECT) 39 | 40 | .PHONY: template 41 | template: 42 | # Minikube templates 43 | jinja2 kubernetes_configs/guestbook/guestbook.yaml.jinja minikube_jinja.json --format=json > kubernetes_configs/guestbook/guestbook_minikube.yaml 44 | jinja2 kubernetes_configs/postgres/postgres.yaml.jinja minikube_jinja.json --format=json > kubernetes_configs/postgres/postgres_minikube.yaml 45 | # GKE templates 46 | jinja2 kubernetes_configs/guestbook/guestbook.yaml.jinja gke_jinja.json --format=json > kubernetes_configs/guestbook/guestbook_gke.yaml 47 | jinja2 kubernetes_configs/postgres/postgres.yaml.jinja gke_jinja.json --format=json > kubernetes_configs/postgres/postgres_gke.yaml 48 | 49 | .PHONY: deploy 50 | deploy: push template 51 | kubectl apply -f kubernetes_config/guestbook/guestbook-gke.yaml 52 | 53 | .PHONY: update 54 | update: 55 | kubectl rolling-update frontend --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/guestbook:latest 56 | 57 | .PHONY: disk 58 | disk: 59 | gcloud compute disks create pg-data --size 200GB 60 | 61 | .PHONY: firewall 62 | firewall: 63 | gcloud compute firewall-rules create kubepostgres --allow tcp:30061 64 | 65 | .PHONY: autoscale-on 66 | autoscale-on: 67 | AUTOSCALE_GROUP=$(shell gcloud container clusters describe $(CLUSTER_NAME) --zone $(ZONE) --format yaml | grep -A 1 instanceGroupUrls | awk -F/ 'FNR ==2 {print $$NF}') 68 | gcloud compute instance-groups managed set-autoscaling $(AUTOSCALE_GROUP) \ 69 | --cool-down-period $(COOL_DOWN) \ 70 | --max-num-replicas $(MAX) \ 71 | --min-num-replicas $(MIN) \ 72 | --scale-based-on-cpu --target-cpu-utilization $(shell echo "scale=2; $(TARGET)/100" | bc) 73 | kubectl autoscale rc $(DEPLOYMENT) --min=$(MIN) --max=$(MAX) --cpu-percent=$(TARGET) 74 | 75 | .PHONY: migrations 76 | migrations: 77 | kubectl exec $(GUESTBOOK_POD_NAME) -- python /app/manage.py migrate 78 | 79 | .PHONY: delete 80 | delete: 81 | gcloud container clusters delete guestbook 82 | gcloud compute disks delete pg-data 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Getting started with Django on Kubernetes with Google Container Engine and Minikube 4 | 5 | This is a tutorial repo associated with a serise of blog posts on Medium.com. 6 | It demonstrates how to run [Django](https://www.djangoproject.com/), 7 | Postgres, and Redis all on a Kubernetes cluster using either 8 | [Minikube](https://github.com/kubernetes/minikube) (locally) 9 | or [Google Container Engine](https://cloud.google.com/container-engine/). 10 | 11 | [Part 1](https://medium.com/google-cloud/deploying-django-postgres-redis-containers-to-kubernetes-9ee28e7a146#.24a63psfu) 12 | [Part 2](https://medium.com/google-cloud/deploying-django-postgres-and-redis-containers-to-kubernetes-part-2-b287f7970a33#.vjwzjntcq) 13 | [Part 3](https://medium.com/google-cloud/local-django-on-kubernetes-with-minikube-89f5ad100378#.se1r8ouba) 14 | 15 | **If you're following along part 1 or part 2 of the tutorial, check out the `part1` git branch 16 | (viewable [here](https://github.com/waprin/kubernetes_django_postgres_redis/tree/part1) ) which is an older 17 | branch before the Minikube changes**. 18 | 19 | Since this project demonstrates deploying Postgres and Redis to the cluster, it's slightly involved. For a simpler 20 | example of Django on Container Engine/Kubernetes, try 21 | 22 | https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/container_engine/django_tutorial 23 | 24 | which also deploys Django to Kubernetes but uses a CloudSQL managed MySQL database, 25 | no cache, no secrets, and does not demonstrate autoscaling. 26 | 27 | While this project is written for Google Container Engine and Minikube, 28 | these instructions should 29 | work on other Kubernetes platforms with some adjustments and should also 30 | deploy on other Kubernetes providers besides Google Container. Specifically, 31 | cluster creation steps, disks, load balancers, cloud storage options, and 32 | node autoscalers should get replaced by their equivalents on your platform. 33 | 34 | While this repo focus on Google Container 35 | Engine and Minikube, these instructions should 36 | work on other Kubernetes platforms with some adjustments. Specifically, 37 | cluster creation steps, disks, load balancers, cloud storage options, and 38 | node autoscalers should get replaced by their equivalents on your platform. 39 | 40 | Please submit any code or doc issues to the issue tracker! 41 | 42 | ## Container Engine vs Minikube 43 | 44 | Originally, this project focused only on how to run the project on Container 45 | Engine. As a followup, instructions for how to run the project on a local 46 | Kubernetes cluster using [minikube](https://github.com/kubernetes/minikube) 47 | have been added. Minikube has several advantages. It's free, it works offline, 48 | but it still emulates a local Kubernetes cluster. 49 | 50 | Since Minikube fully emulates a Kubernetes cluster, only a few small changes 51 | need to be made when deploying the project. Notably: 52 | 53 | * The PostgreSQL Deployment requires a Persistent Volume Claim. The Persistent 54 | Volume that is bound to is different. For Container Engine, it's bound to a 55 | GCE Persistent Disk. For Minikube, it's bound to a directory on your local 56 | machine (hostMount) 57 | 58 | * Images for Container Engine are stored on Google Container Registry. Minikube 59 | is not easily able to authenticate and pull these images, so instead, you use 60 | `eval $(minikube docker-env)` to share the Docker daemon with Minikube. Images 61 | you build with `docker build` will then be available to Minkube. Note that 62 | the default imagePullPolicy will be 'Always' for any images without tags, 63 | so imagePullPolicy has been explicitly set to IfNotPresent for all the images. 64 | 65 | * We use [jinja2 CLI](https://github.com/mattrobenolt/jinja2-cli) 66 | to template the configs, allowing Minikube or GKE specific parts to be 67 | included conditionally. 68 | 69 | 70 | will generate the GKE configs, see gke_jinja.json for example input parameters. 71 | 72 | ## Makefile 73 | 74 | Several commands listed below are provided in simpler form via the Makefile. Many of them use the GCLOUD_PROJECT 75 | environment variable, which will be picked up from your gcloud config. Make sure you set this to the correct project, 76 | 77 | gcloud config set project 78 | 79 | There are more Makefiles in sub-directories to help build and push specific images. 80 | 81 | ## Jinja Templates 82 | 83 | This project uses the [jinja 2 CLI](https://github.com/mattrobenolt/jinja2-cli) 84 | to share templates between the GKE config and Minikube config. 85 | 86 | Run: 87 | 88 | pip install -r requirements-dev.txt 89 | 90 | to install the CLI. At that point, you can see `minikube_jinja.json` and 91 | `gke_jinja.json` as examples of variables you need to poplate to generate the 92 | templates. 93 | 94 | make template 95 | 96 | will use the json variables to create the templates. 97 | 98 | ## Container Engine Pre-requisites 99 | 100 | 1. Install [Docker](https://www.docker.com/). 101 | 102 | 1. Create a project in the [Google Cloud Platform Console](https://console.cloud.google.com). 103 | 104 | 1. [Enable billing](https://console.cloud.google.com/project/_/settings) for your project. 105 | 106 | 1. [Enable APIs](https://console.cloud.google.com/flows/enableapi?apiid=compute_component,datastore,pubsub,storage_api,logging,plus) 107 | for your project. The provided link will enable all necessary APIs, but if you wish to do so manually you will need 108 | Compute, Datastore, Pub/Sub, Storage, and Logging. Note: enabling the APIs can take a few minutes. 109 | 110 | 1. [Initialise the Container Engine for the project](https://console.cloud.google.com/kubernetes/list) 111 | 112 | 1. If on OSX or Linux then install the [Google Cloud SDK](https://cloud.google.com/sdk): 113 | 114 | curl https://sdk.cloud.google.com | bash 115 | 116 | or if on Windows then use the Google Cloud Shell (because kubectl doesn't work yet on Windows) which you can start 117 | from the [Google Cloud Platform Console](https://console.cloud.google.com). 118 | 119 | 1. (Re-)Initialise the settings to set the compute zone: 120 | 121 | gcloud init 122 | 123 | 1. Authenticate the CLI: 124 | 125 | gcloud auth application-default login 126 | 127 | 128 | 1. Create a cluster for the bookshelf application 129 | 130 | gcloud container clusters create guestbook --scopes "https://www.googleapis.com/auth/userinfo.email","cloud-platform" --num-nodes 2 131 | gcloud container clusters get-credentials guestbook 132 | 133 | The get-credentials commands initializes the kubectl CLI tool with the cluster you just created. 134 | 135 | Alternatively, you can use the Makefile: 136 | 137 | make create-cluster 138 | 139 | ### Minkube Prerequisites 140 | 141 | Please see the [minikube project](https://github.com/kubernetes/minikube) for 142 | installation instructions. Note that if you're using Docker for Mac, you 143 | should specify the correct driver when you start Minikube. 144 | 145 | minikube start --vm-driver=xhyve 146 | 147 | Or set this permamently with: 148 | 149 | minikube config set vm-driver xhyve 150 | 151 | ### A note about cost 152 | 153 | The --num-nodes flag in the cluster create specifies how many instances are created. Container Engine orchestrator is 154 | free up to 5 instances, but you will pay for the instances themselves, so to minimize costs just create 1 node. 155 | 156 | At the end of the tutorial, run 157 | 158 | gcloud container clusters delete guestbook 159 | gcloud compute disks delete pg-data 160 | 161 | or 162 | 163 | make delete 164 | 165 | To delete the cluster and not get charged for continued use. Deleting resources you are not using is especially 166 | important if you run the autoscaling example to create many instances. 167 | 168 | ## Running PostgreSQL and Redis 169 | 170 | The Django app depends on a PostgreSQL and Redis service. While this README explains how to deploy those services within 171 | the Kubernetes cluster. Looking in mysite/settings.py, 172 | you can see the app looks for several environment variables. 173 | 174 | The first environment variable `NODB`, if set to 1, uses a SQLite database and an in-memory cache, allowing the app to 175 | run without connecting to a real database. This is useful to test the app locally without Postgres/Redis, or deploy it 176 | to Kubernetes without Postgres/Redis. Instead, it will use a local SQLite file and in-memory cache. In the Kubernetes 177 | cluster, each container will have its own SQLite database and memory-cache, so the persistence and cache storage of the 178 | values will not be shared between containers so will not be right. The `NODB` setting is just to help debug 179 | incrementally and should be turned off. 180 | 181 | The Jinja templates also contain a "has_db" variable which optionally attaches 182 | the database password secrets to the guestbook pod. If NODB is eanbled, make 183 | sure your templates are generated with "has_db" set to false, and set it 184 | to true otherwise. 185 | 186 | ## Running without a database/cache and without Kubernetes 187 | 188 | Enter the guestbook directory 189 | 190 | cd guestbook 191 | 192 | First make sure you have Django installed. It's recommended you do so in a 193 | [virtualenv](https://virtualenv.pypa.io/en/latest/). The requirements.txt contains just the Django dependency. 194 | 195 | pip install -r requirements.txt 196 | 197 | The app can be run locally the same way as any other Django app. 198 | 199 | # disable Postgres and Redis until we set them up 200 | export NODB=1 201 | python manage.py runserver 202 | 203 | # Deploying To Google Container Engine or Minikube (Kubernetes) 204 | 205 | ## Build the Guestbook container 206 | 207 | Within the Dockerfile, `NODB` is turned on or off. Once you have deployed PostgreSQL or Redis, you can disable this 208 | flag. If you deploy those services within Kubernetes, the environment variables will be automatically populated. 209 | Otherwise you should set the environment variables manually using ENV in the Dockerfile. 210 | 211 | Before the application can be deployed to Container Engine, you will need 212 | to build the image: 213 | 214 | cd guestbook 215 | # Make sure NODB is enabled and set to 1 in the Dockerfile if you still haven't setup Postgres and REDIS. 216 | export GOOGLE_CLOUD_PROJECT=$(gcloud config list project --format="value(core.project)") 217 | docker build -t gcr.io/$GOOGLE_CLOUD_PROJECT/guestbook . 218 | 219 | ### Container Engine 220 | 221 | For GKE, you would then push the image to [Google Container Registry](https://cloud.google.com/container-registry/). 222 | 223 | gcloud docker push gcr.io/$GCLOUD_PROJECT/guestbook 224 | 225 | or alternatively: 226 | 227 | make push 228 | 229 | ### Minikube 230 | 231 | For Minikube, you don't push the image. Instead, simply make sure that when 232 | you build the image, you are using the Minikube docker daemon: 233 | 234 | $ eval $(minikube docker-env) 235 | # docker build -t gcr.io/$GOOGLE_CLOUD_PROJECT/guestbook 236 | 237 | ## Deploy to the application 238 | 239 | ## Deploying the frontend guestbook to Kubernetes 240 | 241 | Once the image is built, it can be deployed in a Kubernetes pod. 242 | `kubernetes_configs/guestbook/` contains the 243 | Deployment templates to spin up Pods with this image, as well as a Service with 244 | an external load balancer. However, 245 | the frontend depends on secret passwords for the database, so before it's 246 | deployed, a Kubernetes Secret resource with your database passwords must be created. 247 | 248 | Remember to follow the above instructions in "Jinja Templates" to generate 249 | the following configs: 250 | 251 | kubectl apply -f kubernetes_config/guestbook/guestbook_gke.yaml 252 | 253 | or on Minikube: 254 | 255 | kubectl apply -f kubernetes_config/guestbook/guestbook_minikube.yaml 256 | 257 | ### Create Secrets 258 | 259 | Even if `NODB` is enabled, the frontend replication controller is configured to use the Secret volume, so it must 260 | be created in your cluster first. 261 | 262 | Kubernetes [Secrets](http://kubernetes.io/v1.1/docs/user-guide/secrets.html) are used to store the database password. 263 | They must be base64 encoded and store in `kubernetes_configs/db.password.yaml`. A template containing an example config 264 | is created, but your actual Secrets should not be added to source control. 265 | 266 | In order to get the base64 encoded password, use the base64 tool 267 | 268 | echo mysecretpassword | base64 269 | 270 | Then copy and paste that value into the appropriate part of the Secret config 271 | in `kubernetes_configs/postgres/postgres.yaml.jinja` 272 | 273 | In the Postgres and Guestbook deployments, these Secrets are mounted onto their 274 | pods if "has_db" is set to true in the Jinja variables. In their Dockerfile 275 | they are read into environment variables. 276 | 277 | ### Create the guestbook Service and Deployment 278 | 279 | Once the resources are created, there should be 3 `frontend` pods on the cluster. To see the pods and ensure that 280 | they are running: 281 | 282 | kubectl get pods 283 | 284 | To get more information about a pod, or dig into why it's having problem starting, try 285 | 286 | kubectl describe pod 287 | 288 | 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: 289 | 290 | kubectl logs 291 | 292 | Once the pods are ready, you can get the public IP address of the load balancer: 293 | 294 | kubectl get services frontend 295 | 296 | You can then browse to the external IP address in your browser to see the bookshelf application. 297 | 298 | Alternatively on Minikube, there is no external IP, so instead run: 299 | 300 | minikube service 301 | 302 | When you are ready to update the replication controller with a new image you built, the following command will do a 303 | rolling update 304 | 305 | export GCLOUD_PROJECT=$(gcloud config list project --format="value(core.project)") 306 | kubectl rolling-update frontend --image=gcr.io/${GCLOUD_PROJECT}/guestbook:latest 307 | 308 | which can also be done with the `make update` command. If you encounter problems with the rolling-update, then 309 | check the events: 310 | 311 | kubectl get events 312 | 313 | It can happen that the nodes don't have enough resources left. A manual scaling down of the replication controller can 314 | help (see above) or you can resize the cluster to have an additional node: 315 | 316 | gcloud container clusters resize guestbook --size 3 317 | 318 | Give it a few minutes to provision the node, then try the rolling update again. 319 | 320 | ## Create the Redis cluster 321 | 322 | Since the Redis cluster has no volumes or secrets, it's pretty easy to setup: 323 | 324 | kubectl apply -f kubernetes_configs/redis/redis.yaml 325 | 326 | This creates a redis-master read/write service and redis-slave service. The images used are configured to properly 327 | replicate from the master to the slaves. 328 | 329 | There should only be one redis-master pod, so the replication controller configures 1 replicas. There can be many 330 | redis-slave pods, so if you want more you can do: 331 | 332 | kubectl scale deployment redis-slave --replicas=5 333 | 334 | ## Create PostgreSQL 335 | 336 | ### Container Engine 337 | 338 | PostgresSQL will need a disk backed by a Volume to run in Kubernetes. For this example, we create a persistent disk 339 | using a GCE persistent disk: 340 | 341 | gcloud compute disks create pg-data --size 200GB 342 | 343 | or 344 | 345 | make disk 346 | 347 | Edit `kubernetes_configs/postgres/postgres.yaml.jinja` volume name to match the 348 | name of the disk you just created, if different. 349 | 350 | For Postgres, the secrets need to get populated and a script to initialize the database needs to be added, so a image 351 | should be built: 352 | 353 | cd kubernetes_configs/postgres/postgres_image 354 | make build 355 | make push 356 | 357 | ### Minikube 358 | 359 | Create the directory to mount: 360 | 361 | sudo mkdir /data/pv0001/ 362 | sudo chown $(whoami) /data/pv001 363 | 364 | ### Creating the image, PVC, and deployment 365 | 366 | cd kubernetes_config/postgres/postgres_image 367 | make build 368 | make push 369 | 370 | Finally, you should be able to create the PostgreSQL service and pod. 371 | 372 | Again, remember to follow the above instructions in "Jinja Templates" to generate 373 | the following configs: 374 | 375 | kubectl apply -f kubernetes_configs/postgres/postgres_gke.yaml 376 | 377 | or on Minikube: 378 | 379 | kubectl apply -f kubernetes_configs/postgres/postgres_minikube.yaml 380 | 381 | 382 | Only one pod can read and write to a GCE disk, so the PostgreSQL replication controller is set to control 1 pod. 383 | Don't scale more instances of the Postgres pod. 384 | 385 | ### Re-Deploy The Frontend and Run Migrations 386 | 387 | Now that the database and redis service are created, you should rebuild the frontend guestbook image with `NODB` 388 | disabled. 389 | 390 | cd guestbook 391 | # edit Dockerfile to comment out NODB 392 | make build 393 | make push 394 | 395 | With this new image, the replication controller should be updated. One way is to do a rolling update 396 | 397 | make update 398 | 399 | This will safely spin down the old images and replace it with the new image. However, for development purposes 400 | it can be quicker to scale the controller to 0 and then back up. 401 | 402 | kubectl scale --replicas=0 rc/frontend 403 | kubectl scale --replicas=3 rc/frontend 404 | 405 | Finally, the Django migrations must be run to create the table. This can also be accomplished with kubectl-exec, this 406 | time with the frontend pod. However, make sure your frontend-pod is actually talking to the database. If you earlier 407 | built the image with `NODB`, rebuild it with `NODB` commented out. Then you can run the migrations: 408 | 409 | export FRONTEND_POD_NAME=$(kubectl get pods | grep frontend -m 1 | awk '{print $1}') 410 | kubectl exec ${FRONTEND_POD_NAME} -- python /app/manage.py makemigrations 411 | kubectl exec ${FRONTEND_POD_NAME} -- python /app/manage.py migrate 412 | 413 | or: 414 | 415 | make migrations 416 | 417 | ### Serve the static content 418 | 419 | When DEBUG is enabled, Django can serve the files directly from that folder. Currently, the app is configured 420 | to serve static content from the files that way to simplify development. For production purposes, you should disable 421 | the DEBUG flag and serve static assets from a CDN. 422 | 423 | The application uses [Google Cloud Storage](https://cloud.google.com/storage) to store static content. You can 424 | alternatively use a CDN of your choice. Create a bucket for your project: 425 | 426 | gsutil mb gs:// 427 | gsutil defacl set public-read gs:// 428 | 429 | or 430 | 431 | make create-bucket 432 | 433 | Collect all the static assets into the static/ folder. 434 | 435 | python manage.py collectstatic 436 | 437 | Upload it to CloudStorage using the `gsutil rsync` command 438 | 439 | gsutil rsync -R static/ gs:///static 440 | 441 | Now your static content can be served from the following URL: 442 | 443 | http://storage.googleapis.com/` 447 | 448 | 449 | ### Load Testing and Autoscaling (beta feature) 450 | 451 | Please note that autoscaling is a beta feature. 452 | 453 | load_test_image provides a super minimal load generator - it hits a CPU intensive endpoint in a loop with curl. First 454 | build the image: 455 | 456 | cd load_testing_image 457 | make build 458 | make push 459 | 460 | Then create the Replication Controller and scale some clients: 461 | 462 | kubectl apply -f kubernetes_configs/load_tester.yaml 463 | kubectl scale rc load --replicas=20 464 | 465 | to generate load. Then 466 | 467 | make autoscaling 468 | 469 | will create both Node autoscaling (gcloud) and Pod autoscaling (Kubernetes Horizontal Pod Autoscaling). 470 | 471 | Again, by default this will scale to 10 nodes whcih you will be charged for, so pleaes disable autoscaling and scale 472 | the nodes back down or delete the cluster if you don't want to pay for sustained use of the 10 instances. 473 | 474 | For more sophisticated load testing with Kubernetes, see: 475 | 476 | https://cloud.google.com/solutions/distributed-load-testing-using-kubernetes 477 | 478 | ## Issues 479 | 480 | Please use the Issue Tracker for any issues or questions. 481 | 482 | ## Contributing changes 483 | 484 | * See [CONTRIBUTING.md](CONTRIBUTING.md) 485 | 486 | ## Additional Reading 487 | 488 | For another popular Django/Kubernets project + blog post, see: 489 | 490 | https://harishnarayanan.org/writing/kubernetes-django/ 491 | 492 | This project was originally made for a talk/blog post series. 493 | 494 | https://speakerdeck.com/waprin/deploying-django-on-kubernetes 495 | 496 | and you can watch the talk here: 497 | 498 | https://www.youtube.com/watch?v=HKKUgWuIZro 499 | 500 | ## Licensing 501 | 502 | * See [LICENSE](LICENSE) 503 | 504 | This project is owned by Google Copyright 2016 but is not an official 505 | Google-product nor officially maintained or supported by Google. 506 | -------------------------------------------------------------------------------- /gke_jinja.json: -------------------------------------------------------------------------------- 1 | { 2 | "GOOGLE_CLOUD_PROJECT" : "utopian-pact-123818", 3 | "minikube": null, 4 | "guestbook_tag": "v9", 5 | "base64_db_pw": "bXlzZWNyZXRwYXNzd29yZAo=", 6 | "gce_disk_name": "pg_data", 7 | "has_db": "True", 8 | "hot_reload": null 9 | } 10 | -------------------------------------------------------------------------------- /guestbook/.dockerignore: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | 16 | __pycache__ 17 | *.pyc 18 | *.pyo 19 | *.pyd 20 | .Python 21 | env 22 | pip-log.txt 23 | pip-delete-this-directory.txt 24 | .tox 25 | .coverage 26 | .coverage.* 27 | .cache 28 | nosetests.xml 29 | coverage.xml 30 | *,cover 31 | *.log 32 | .git 33 | -------------------------------------------------------------------------------- /guestbook/.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | 16 | static/** 17 | db.sqlite3 18 | -------------------------------------------------------------------------------- /guestbook/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | # The Google App Engine python runtime is Debian Jessie with Python installed 16 | # and various os-level packages to allow installation of popular Python 17 | # libraries. The source is on github at: 18 | # https://github.com/GoogleCloudPlatform/python-docker 19 | FROM gcr.io/google_appengine/python 20 | 21 | # Create a virtualenv for the application dependencies. 22 | # If you want to use Python 3, add the -p python3.4 flag. 23 | RUN virtualenv /env 24 | 25 | # Set virtualenv environment variables. This is equivalent to running 26 | # source /env/bin/activate. This ensures the application is executed within 27 | # the context of the virtualenv and will have access to its dependencies. 28 | ENV VIRTUAL_ENV /env 29 | ENV PATH /env/bin:$PATH 30 | 31 | # Install dependencies. 32 | ADD requirements.txt /app/requirements.txt 33 | RUN pip install -r /app/requirements.txt 34 | 35 | # Uncomment the following line to use NODB 36 | # NODB will use SQLite and in-memory cache insstead of Postgres And Redis 37 | # This is useful to test the rest of the Django setup without configuring the database/cache 38 | # Comment it out and rebuild this image once you have Postgres and Redis services in your cluster 39 | #ENV NODB 1 40 | 41 | # Add application code. 42 | ADD . /app 43 | 44 | CMD export DJANGO_PASSWORD=$(cat /etc/secrets/djangouserpw); gunicorn -b :$PORT mysite.wsgi 45 | -------------------------------------------------------------------------------- /guestbook/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | GOOGLE_CLOUD_PROJECT:=$(shell gcloud config list project --format="value(core.project)") 16 | 17 | .PHONY: build 18 | build: 19 | docker build -t gcr.io/$(GOOGLE_CLOUD_PROJECT)/guestbook:v9 . 20 | 21 | .PHONY: push 22 | push: build 23 | gcloud docker push gcr.io/$(GOOGLE_CLOUD_PROJECT)/guestbook:v9 24 | 25 | .PHONY: delete 26 | delete: 27 | kubectl delete rc guestbook 28 | kubectl delete service guestbook 29 | -------------------------------------------------------------------------------- /guestbook/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waprin/kubernetes_django_postgres_redis/e91fd4b442dd70098d89b0559700a3ede12f1f9e/guestbook/__init__.py -------------------------------------------------------------------------------- /guestbook/guestbook/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waprin/kubernetes_django_postgres_redis/e91fd4b442dd70098d89b0559700a3ede12f1f9e/guestbook/guestbook/__init__.py -------------------------------------------------------------------------------- /guestbook/guestbook/admin.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.contrib import admin 16 | from .models import Message 17 | 18 | admin.site.register(Message) 19 | -------------------------------------------------------------------------------- /guestbook/guestbook/apps.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.apps import AppConfig 16 | 17 | 18 | class PollsConfig(AppConfig): 19 | name = 'guestbook' 20 | -------------------------------------------------------------------------------- /guestbook/guestbook/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waprin/kubernetes_django_postgres_redis/e91fd4b442dd70098d89b0559700a3ede12f1f9e/guestbook/guestbook/migrations/__init__.py -------------------------------------------------------------------------------- /guestbook/guestbook/models.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.db import models 16 | 17 | 18 | class Message(models.Model): 19 | text = models.CharField(max_length=500) 20 | -------------------------------------------------------------------------------- /guestbook/guestbook/static/js/controllers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015 Google Inc. 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | 18 | var guestbookApp = angular.module('guestbook', ['ui.bootstrap']); 19 | 20 | 21 | /** 22 | * Constructor 23 | */ 24 | function GuestbookController() { 25 | } 26 | 27 | GuestbookController.prototype.onSubmit = function () { 28 | this.scope_.messages.push({text: this.scope_.msg}); 29 | this.http_.post('messages/', this.scope_.msg, 30 | function success() { 31 | console.log("updated!"); 32 | }); 33 | console.log(this.scope_.messages); 34 | this.scope_.msg = ""; 35 | }; 36 | 37 | guestbookApp.controller('GuestbookCtrl', function ($scope, $http, $location) { 38 | $scope.controller = new GuestbookController(); 39 | $scope.controller.scope_ = $scope; 40 | $scope.controller.location_ = $location; 41 | $scope.controller.http_ = $http; 42 | $scope.messages = []; 43 | $scope.controller.http_.get("messages/") 44 | .success(function (data) { 45 | $scope.messages = _.pluck(data, 'fields'); 46 | console.log($scope.messages); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /guestbook/guestbook/templates/index.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | Guestbook 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |

28 |

Guestbook

29 | 30 |

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

31 | 32 |
33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 | {% verbatim %} 41 | {{msg.text}} 42 | {% endverbatim %} 43 |
44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /guestbook/guestbook/tests.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /guestbook/guestbook/urls.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.conf.urls import url 16 | from django.contrib import admin 17 | 18 | from . import views 19 | 20 | urlpatterns = [ 21 | url(r'^$', views.index, name='index'), 22 | url(r'^messages/$', views.messages, name='messages'), 23 | url(r'^admin/', admin.site.urls), 24 | url(r'^compute/$', views.compute, name='compute'), 25 | ] 26 | -------------------------------------------------------------------------------- /guestbook/guestbook/views.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.conf import settings 16 | from django.core import serializers 17 | from django.http import HttpResponse 18 | from django.shortcuts import render 19 | 20 | from .models import Message 21 | 22 | from django.core.cache import cache 23 | 24 | import math 25 | import logging 26 | 27 | logger = logging.getLogger('django') 28 | 29 | 30 | def _update_visited(): 31 | """ Updates the visited count by one in the cache 32 | :return: The visited count in string form. 33 | """ 34 | visited = cache.get('visited') 35 | if not visited: 36 | visited = 0 37 | else: 38 | visited = int(visited) 39 | visited += 1 40 | cache.set('visited', str(visited)) 41 | return str(visited) 42 | 43 | 44 | def index(request): 45 | """ Updates the visited count and provides the static URL to build the main 46 | landing page. 47 | """ 48 | 49 | visited = _update_visited() 50 | context = { 51 | "STATIC_URL": settings.STATIC_URL, 52 | "visited": visited 53 | } 54 | return render(request, 'index.html', context) 55 | 56 | 57 | def messages(request): 58 | """ REST endpoint providing basic operations. GET will return the list of 59 | all messages created so far in JSON form, POST will add a new message to 60 | the list of messages (guestbook). 61 | """ 62 | if request.method == 'GET': 63 | data = serializers.serialize("json", Message.objects.all()) 64 | return HttpResponse(data) 65 | elif request.method == 'POST': 66 | Message.objects.create(text=request.body) 67 | return HttpResponse(request.body) 68 | else: 69 | return HttpResponse("Unsupported HTTP Verb.") 70 | 71 | 72 | def compute(request): 73 | """ Endpoint that does some CPU intensive work to help demonstrate 74 | autoscaling. 75 | """ 76 | logging.info("doing some long calculations") 77 | x = 0.0001 78 | for i in range(0, 1000000): 79 | x += math.sqrt(x) 80 | return HttpResponse("Performed a lot of work") 81 | -------------------------------------------------------------------------------- /guestbook/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2015 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import os 17 | import sys 18 | 19 | if __name__ == "__main__": 20 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") 21 | 22 | from django.core.management import execute_from_command_line 23 | 24 | execute_from_command_line(sys.argv) 25 | -------------------------------------------------------------------------------- /guestbook/mysite/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waprin/kubernetes_django_postgres_redis/e91fd4b442dd70098d89b0559700a3ede12f1f9e/guestbook/mysite/__init__.py -------------------------------------------------------------------------------- /guestbook/mysite/settings.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | import os 17 | 18 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = 'pf-@jxtojga)z+4s*uwbgjrq$aep62-thd0q7f&o77xtpka!_m' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'guestbook' 41 | ) 42 | 43 | MIDDLEWARE_CLASSES = ( 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 47 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | 'django.middleware.security.SecurityMiddleware', 51 | ) 52 | 53 | ROOT_URLCONF = 'mysite.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'mysite.wsgi.application' 72 | 73 | # Database 74 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 75 | 76 | # When 'NODB' is enabled,we skip Database and Cache setup. This is useful 77 | # to test the rest of the Django deployment while boostrapping the application. 78 | if os.getenv('NODB'): 79 | DATABASES = { 80 | 'default': { 81 | 'ENGINE': 'django.db.backends.sqlite3', 82 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 83 | } 84 | } 85 | else: 86 | # Dockerfile reads the DJANGO_PW from the secret into an environment 87 | # variable but its not there on kubectl exec. Soon Kubernetes versions 88 | # will have secrets as environment variables but currently just read it 89 | # from the volume 90 | DJANGO_PW = os.getenv('DJANGO_PASSWORD') 91 | if not DJANGO_PW: 92 | try: 93 | f = open('/etc/secrets/djangouserpw') 94 | DJANGO_PW = f.readline().rstrip() 95 | except IOError: 96 | pass 97 | if not DJANGO_PW: 98 | raise Exception("No DJANGO_PASSWORD provided.") 99 | 100 | DATABASES = { 101 | 'default': { 102 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 103 | 'NAME': 'guestbook', 104 | 'USER': 'django_user', 105 | 'PASSWORD': DJANGO_PW, 106 | 'HOST': os.getenv('POSTGRES_SERVICE_HOST', '127.0.0.1'), 107 | 'PORT': os.getenv('POSTGRES_SERVICE_PORT', 5432) 108 | } 109 | } 110 | CACHES = { 111 | 'default': { 112 | 'BACKEND': 'redis_cache.RedisCache', 113 | 'LOCATION': [ 114 | '%s:%s' % (os.getenv('REDIS_MASTER_SERVICE_HOST', '127.0.0.1'), 115 | os.getenv('REDIS_MASTER_SERVICE_PORT', 6379)), 116 | ], 117 | 'OPTIONS': { 118 | 'DB': 1, 119 | 'PARSER_CLASS': 'redis.connection.HiredisParser', 120 | 'CONNECTION_POOL_CLASS': 'redis.BlockingConnectionPool', 121 | 'CONNECTION_POOL_CLASS_KWARGS': { 122 | 'max_connections': 50, 123 | 'timeout': 20, 124 | }, 125 | 'MAX_CONNECTIONS': 1000, 126 | 'PICKLE_VERSION': -1, 127 | }, 128 | }, 129 | } 130 | 131 | # Internationalization 132 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 133 | 134 | LANGUAGE_CODE = 'en-us' 135 | 136 | TIME_ZONE = 'UTC' 137 | 138 | USE_I18N = True 139 | 140 | USE_L10N = True 141 | 142 | USE_TZ = True 143 | 144 | # Static files (CSS, JavaScript, Images) 145 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 146 | 147 | STATIC_URL = '/static/' 148 | # STATIC_URL = 'https://storage.googleapis.com/delete-me-1156/static/' 149 | 150 | STATIC_ROOT = 'static/' 151 | -------------------------------------------------------------------------------- /guestbook/mysite/urls.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.conf import settings 16 | from django.conf.urls import include, url 17 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns 18 | 19 | urlpatterns = [ 20 | url(r'^', include('guestbook.urls')), 21 | ] 22 | 23 | # Only serve static files from Django during development 24 | # Use Google Cloud Storage or an alternative CDN for production 25 | if settings.DEBUG: 26 | urlpatterns += staticfiles_urlpatterns() 27 | -------------------------------------------------------------------------------- /guestbook/mysite/wsgi.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import os 17 | 18 | from django.core.wsgi import get_wsgi_application 19 | 20 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") 21 | 22 | application = get_wsgi_application() 23 | -------------------------------------------------------------------------------- /guestbook/requirements.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | 16 | Django==1.9.1 17 | django-redis-cache==1.6.5 18 | gunicorn==19.4.5 19 | hiredis==0.2.0 20 | psycopg2==2.6.1 21 | redis==2.10.5 22 | wheel==0.26.0 23 | -------------------------------------------------------------------------------- /kubernetes_configs/guestbook/guestbook.yaml.jinja: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | apiVersion: extensions/v1beta1 16 | kind: Deployment 17 | metadata: 18 | name: guestbook 19 | labels: 20 | name: guestbook 21 | spec: 22 | replicas: 3 23 | template: 24 | metadata: 25 | labels: 26 | name: guestbook 27 | spec: 28 | containers: 29 | - name: guestbook 30 | image: gcr.io/{{ GOOGLE_CLOUD_PROJECT }}/guestbook:{{- guestbook_tag -}} 31 | {% if minikube %} 32 | imagePullPolicy: IfNotPresent 33 | {%- endif %} 34 | ports: 35 | - containerPort: 8080 36 | volumeMounts: 37 | {% if has_db -%} 38 | - name: secrets 39 | mountPath: /etc/secrets 40 | readOnly: true 41 | {%- endif -%} 42 | {% if hot_reload %} 43 | - name: reload 44 | mountPath: /app 45 | {%- endif %} 46 | volumes: 47 | {%- if has_db %} 48 | - name: secrets 49 | secret: 50 | secretName: db-passwords 51 | {% endif %} 52 | {%-if hot_reload -%} 53 | - name: reload 54 | hostPath: 55 | path: {{ hotreload_directory }} 56 | {% endif %} 57 | --- 58 | 59 | # The polls service provides a load-balancing proxy over the polls app 60 | # pods. By specifying the type as a 'LoadBalancer', Container Engine will 61 | # create an external HTTP load balancer. 62 | # For more information about Services see: 63 | # https://cloud.google.com/container-engine/docs/services/ 64 | # For more information about external HTTP load balancing see: 65 | # https://cloud.google.com/container-engine/docs/load-balancer 66 | apiVersion: v1 67 | kind: Service 68 | metadata: 69 | name: guestbook 70 | labels: 71 | name: guestbook 72 | spec: 73 | type: LoadBalancer 74 | ports: 75 | - port: 80 76 | targetPort: 8080 77 | selector: 78 | name: guestbook 79 | -------------------------------------------------------------------------------- /kubernetes_configs/load_test/load_test_image/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | 16 | FROM ubuntu:16.04 17 | RUN apt-get update -y && apt-get -y install curl 18 | CMD while true; do curl http://frontend.default.svc.cluster.local/compute/; done 19 | -------------------------------------------------------------------------------- /kubernetes_configs/load_test/load_test_image/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | GCLOUD_PROJECT:=$(shell gcloud config list project --format="value(core.project)") 16 | 17 | .PHONY: build 18 | build: 19 | docker build -t gcr.io/$(GCLOUD_PROJECT)/load . 20 | 21 | .PHONY: push 22 | push: build 23 | gcloud docker push gcr.io/$(GCLOUD_PROJECT)/load 24 | -------------------------------------------------------------------------------- /kubernetes_configs/load_test/load_tester.yaml.tmpl: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | # This file configures a replication controller to spin up lots of 16 | # load testing images to demonstate autoscaling. 17 | 18 | apiVersion: v1 19 | kind: ReplicationController 20 | metadata: 21 | name: load 22 | labels: 23 | app: load 24 | spec: 25 | replicas: 1 26 | template: 27 | metadata: 28 | labels: 29 | app: load 30 | spec: 31 | containers: 32 | - name: load 33 | # Replace with your project ID or use `make template` 34 | image: gcr.io/$GCLOUD_PROJECT/load 35 | 36 | imagePullPolicy: IfNotPresent 37 | -------------------------------------------------------------------------------- /kubernetes_configs/postgres/postgres.yaml.jinja: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | --- 16 | apiVersion: v1 17 | kind: Secret 18 | metadata: 19 | name: db-passwords 20 | data: 21 | djangouserpw: {{ base64_db_pw }} 22 | --- 23 | kind: PersistentVolumeClaim 24 | apiVersion: v1 25 | metadata: 26 | name: postgres-data 27 | spec: 28 | accessModes: 29 | - ReadWriteOnce 30 | resources: 31 | requests: 32 | storage: 5Gi 33 | --- 34 | {% if minikube -%} 35 | apiVersion: v1 36 | kind: PersistentVolume 37 | metadata: 38 | name: pv0001 39 | spec: 40 | accessModes: 41 | - ReadWriteOnce 42 | capacity: 43 | storage: 5Gi 44 | hostPath: 45 | path: /data/pv0001/ 46 | {% else -%} 47 | apiVersion: v1 48 | kind: PersistentVolume 49 | metadata: 50 | name: pv0001 51 | spec: 52 | accessModes: 53 | - ReadWriteOnce 54 | capacity: 55 | storage: 5Gi 56 | gcePersistentDisk: 57 | pdName: {{ gce_disk_name }} 58 | fsType: ext4 59 | 60 | {% endif %} 61 | --- 62 | apiVersion: extensions/v1beta1 63 | kind: Deployment 64 | metadata: 65 | name: postgres 66 | spec: 67 | replicas: 1 68 | template: 69 | metadata: 70 | labels: 71 | name: postgres 72 | spec: 73 | containers: 74 | - name: postgres 75 | image: gcr.io/{{ GOOGLE_CLOUD_PROJECT }}/postgres-pw 76 | imagePullPolicy: IfNotPresent 77 | ports: 78 | - containerPort: 5432 79 | volumeMounts: 80 | - name: postgresdata 81 | mountPath: /usr/local/var/postgres 82 | - name: secrets 83 | mountPath: /etc/secrets 84 | readOnly: true 85 | volumes: 86 | - name: postgresdata 87 | persistentVolumeClaim: 88 | claimName: postgres-data 89 | - name: secrets 90 | secret: 91 | secretName: db-passwords 92 | --- 93 | metadata: 94 | name: postgres 95 | kind: Service 96 | apiVersion: v1 97 | spec: 98 | ports: 99 | - name: postgres 100 | protocol: TCP 101 | port: 5432 102 | targetPort: 5432 103 | selector: 104 | name: postgres 105 | 106 | -------------------------------------------------------------------------------- /kubernetes_configs/postgres/postgres_image/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | FROM postgres:9.4 16 | 17 | ENTRYPOINT [] 18 | CMD export POSTGRES_DB=guestbook; export POSTGRES_USER=django_user; export POSTGRES_PASSWORD=$(cat /etc/secrets/djangouserpw) ;./docker-entrypoint.sh postgres; 19 | 20 | -------------------------------------------------------------------------------- /kubernetes_configs/postgres/postgres_image/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | GOOGLE_CLOUD_PROJECT:=$(shell gcloud config list project --format="value(core.project)") 16 | 17 | .PHONY: build 18 | build: 19 | docker build -t gcr.io/$(GOOGLE_CLOUD_PROJECT)/postgres-pw . 20 | 21 | .PHONY: push 22 | push: build 23 | gcloud docker push gcr.io/$(GOOGLE_CLOUD_PROJECT)/postgres-pw 24 | 25 | 26 | -------------------------------------------------------------------------------- /kubernetes_configs/redis/redis.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License 14 | 15 | # Redis config taken from PHP Guestbook Example 16 | # https://github.com/kubernetes/kubernetes/tree/master/examples/guestbook 17 | 18 | apiVersion: v1 19 | kind: Service 20 | metadata: 21 | name: redis-master 22 | labels: 23 | app: redis 24 | tier: backend 25 | role: master 26 | spec: 27 | ports: 28 | # the port that this service should serve on 29 | - port: 6379 30 | targetPort: 6379 31 | selector: 32 | app: redis 33 | tier: backend 34 | role: master 35 | --- 36 | apiVersion: extensions/v1beta1 37 | kind: Deployment 38 | metadata: 39 | name: redis-master 40 | # these labels can be applied automatically 41 | # from the labels in the pod template if not set 42 | labels: 43 | app: redis 44 | role: master 45 | tier: backend 46 | spec: 47 | # this replicas value is default 48 | # modify it according to your case 49 | replicas: 1 50 | # selector can be applied automatically 51 | # from the labels in the pod template if not set 52 | # selector: 53 | # app: guestbook 54 | # role: master 55 | # tier: backend 56 | template: 57 | metadata: 58 | labels: 59 | app: redis 60 | role: master 61 | tier: backend 62 | spec: 63 | containers: 64 | - name: master 65 | image: redis 66 | resources: 67 | requests: 68 | cpu: 100m 69 | memory: 100Mi 70 | ports: 71 | - containerPort: 6379 72 | --- 73 | apiVersion: v1 74 | kind: Service 75 | metadata: 76 | name: redis-slave 77 | labels: 78 | app: redis 79 | tier: backend 80 | role: slave 81 | spec: 82 | ports: 83 | # the port that this service should serve on 84 | - port: 6379 85 | selector: 86 | app: redis 87 | tier: backend 88 | role: slave 89 | --- 90 | apiVersion: extensions/v1beta1 91 | kind: Deployment 92 | metadata: 93 | name: redis-slave 94 | # these labels can be applied automatically 95 | # from the labels in the pod template if not set 96 | labels: 97 | app: redis 98 | role: slave 99 | tier: backend 100 | spec: 101 | # this replicas value is default 102 | # modify it according to your case 103 | replicas: 2 104 | # selector can be applied automatically 105 | # from the labels in the pod template if not set 106 | # selector: 107 | # app: guestbook 108 | # role: slave 109 | # tier: backend 110 | template: 111 | metadata: 112 | labels: 113 | app: redis 114 | role: slave 115 | tier: backend 116 | spec: 117 | containers: 118 | - name: slave 119 | image: gcr.io/google_samples/gb-redisslave:v1 120 | resources: 121 | requests: 122 | cpu: 100m 123 | memory: 100Mi 124 | env: 125 | - name: GET_HOSTS_FROM 126 | value: dns 127 | # If your cluster config does not include a dns service, then to 128 | # instead access an environment variable to find the master 129 | # service's host, comment out the 'value: dns' line above, and 130 | # uncomment the line below. 131 | # value: env 132 | ports: 133 | - containerPort: 6379 134 | -------------------------------------------------------------------------------- /minikube_jinja.json: -------------------------------------------------------------------------------- 1 | { 2 | "GOOGLE_CLOUD_PROJECT" : "utopian-pact-123818", 3 | "minikube": "True", 4 | "guestbook_tag": "v9", 5 | "base64_db_pw": "bXlzZWNyZXRwYXNzd29yZAo=", 6 | "has_db": "True", 7 | "hot_reload": "True", 8 | "hotreload_directory" : "/Users/waprin/code/django_postgres_redis/guestbook" 9 | } 10 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | Jinja2==2.8 2 | jinja2-cli==0.5.0 3 | MarkupSafe==0.23 4 | --------------------------------------------------------------------------------