GCP: Django Deploy via Kubernetes II (GKE)
Continued from GCP: Django Deploy via Kubernetes I (Local), in this tutorial, we'll finally deploy the app to GKE.
-
When the app is deployed to Google Cloud Platform, it uses the Gunicorn server. Gunicorn doesn't serve static content, so the app uses Cloud Storage to serve static content.
Create a Cloud Storage bucket and make it publicly readable. Replace
<your-gcs-bucket>
with a bucket name we like. For example, we could use our project ID (django-poll-app-216501) as a bucket name:gsutil mb gs://<your-gcs-bucket> gsutil defacl set public-read gs://<your-gcs-bucket>
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ gsutil mb gs://django-poll-app-216501 Creating gs://django-poll-app-216501/... (venv) Kihyucks-Air:django_tutorial kihyuckhong$ gsutil defacl set public-read gs://django-poll-app-216501 Setting default object ACL on gs://django-poll-app-216501/...
-
Gather all the static content locally into one folder:
python manage.py collectstatic
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ python manage.py collectstatic ... 119 static files copied to '/Users/kihyuckhong/Documents/Django/Kubernetes/python-docs-samples/container_engine/django_tutorial/static'.
-
Upload the static content to Cloud Storage:
gsutil rsync -R static/ gs://<your-gcs-bucket>/static
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ gsutil rsync -R static/ gs://django-poll-app-216501/static
-
In
mysite/settings.py
, set the value ofSTATIC_URL
to this URL, replacing<your-gcs-bucket>
with your bucket name.http://storage.googleapis.com/<your-gcs-bucket>/static/
... # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.8/howto/static-files/ # [START staticurl] STATIC_URL = '/static/' STATIC_URL = 'https://storage.googleapis.com/django-poll-app-216501/static/' # [END staticurl]
-
To initialize GKE, go to the GCP Console. Wait for the "Kubernetes Engine is getting ready.
This may take a minute or more" message to disappear.
-
# [START kubernetes_deployment] apiVersion: extensions/v1beta1 kind: Deployment metadata: name: polls labels: app: polls spec: replicas: 3 template: metadata: labels: app: polls spec: containers: - name: polls-app # Replace with your project ID or use `make template` image: gcr.io/
/polls # 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 env: # [START cloudsql_secrets] - name: DATABASE_USER valueFrom: secretKeyRef: name: cloudsql key: username - name: DATABASE_PASSWORD valueFrom: secretKeyRef: name: cloudsql key: password # [END cloudsql_secrets] ports: - containerPort: 8080 gcloud container clusters create polls \ --scopes "https://www.googleapis.com/auth/userinfo.email","cloud-platform" \ --num-nodes 3 --zone "us-east4-c"
(venv) Kihyucks-Air:mysite kihyuckhong$ gcloud container clusters create polls \ > --scopes "https://www.googleapis.com/auth/userinfo.email","cloud-platform" \ > --num-nodes 3 --zone "us-east4-c" ... Creating cluster polls...done. Created [https://container.googleapis.com/v1/projects/django-poll-app-216501/zones/us-east4-c/clusters/polls]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-east4-c/polls?project=django-poll-app-216501 kubeconfig entry generated for polls. NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS polls us-east4-c 1.9.7-gke.6 35.199.51.68 n1-standard-1 1.9.7-gke.6 3 RUNNING
-
After the cluster is created, use the
kubectl
command-line tool, which is integrated with thegcloud
tool, to interact with our GKE cluster. Becausegcloud
andkubectl
are separate tools, make surekubectl
is configured to interact with the right cluster.After creating our cluster, we need to get authentication credentials to interact with the cluster. To authenticate for the cluster, run the following command:
gcloud container clusters get-credentials CLUSTER_NAME --zone "us-east4-c"
This command configures kubectl to use the cluster we created.
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ gcloud container clusters get-credentials polls --zone us-east4-c --project django-poll-app-216501 Fetching cluster endpoint and auth data. kubeconfig entry generated for polls.
-
We need several secrets to enable our GKE app to connect with our Cloud SQL instance. One is required for instance-level access (connection), while the other two are required for database access. For more information about the two levels of access control, see Instance Access Control.
The two Secrets to enable our Kubernetes Engine application to access the data in our Cloud SQL instance are:
- The cloudsql-instance-credentials Secret contains the service account.
- The cloudsql-db-credentials Secret provides the database user account and password.
Create these Secrets:
-
To create the secret for instance-level access, provide the location of the key we downloaded when we created our service account:
kubectl create secret generic cloudsql-oauth-credentials --from-file=credentials.json=[PROXY_KEY_FILE_PATH]
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ kubectl create secret generic cloudsql-oauth-credentials --from-file=credentials.json=/Users/kihyuckhong/Documents/Django/Kubernetes/django-poll-app-216501-f4ffa0d2f930.json secret "cloudsql-oauth-credentials" created
Initailly, I got this error: "The connection to the server localhost:8080 was refused - did you specify the right host or port?"
This indicates that kubectl is not properly configured.
To check if
kubectl
is properly configured by getting the cluster state:$ kubectl cluster-info
If we see a URL response,
kubectl
is correctly configured to access our cluster. But if we see a message similar to the above,kubectl
is not correctly configured or not able to connect to a Kubernetes cluster.Somehow, either minikube has been stopped or uninstalled. So, I had to reinstall and start it, and everything starts working fine.
$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.28.2/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/ $ minikube start
-
Create the secrets needed for database access:
kubectl create secret generic cloudsql --from-literal=username=[PROXY_USERNAME] --from-literal=password=[PASSWORD]
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ kubectl create secret generic cloudsql --from-literal=username=khong --from-literal=password=postgres secret "cloudsql" created
-
Retrieve the public Docker image for the Cloud SQL proxy.
docker pull b.gcr.io/cloudsql-docker/gce-proxy:1.05
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ docker pull b.gcr.io/cloudsql-docker/gce-proxy:1.05
-
Build a Docker image, replacing
<your-project-id>
with your project ID.docker build -t gcr.io/<your-project-id>/polls .
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ docker build -t gcr.io/django-poll-app-216501/polls . ... Successfully built 4687297fcf19 Successfully tagged gcr.io/django-poll-app-216501/polls:latest
-
Configure
docker
to usegcloud
as a credential helper, so that we can push the image to Google Container Registry:gcloud auth configure-docker
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ gcloud auth configure-docker The following settings will be added to your Docker config file located at [/Users/kihyuckhong/.docker/config.json]: { "credHelpers": { "gcr.io": "gcloud", "us.gcr.io": "gcloud", "eu.gcr.io": "gcloud", "asia.gcr.io": "gcloud", "staging-k8s.gcr.io": "gcloud", "marketplace.gcr.io": "gcloud" } } Do you want to continue (Y/n)? Y Docker configuration file updated.
-
Push the Docker image. Replace
<your-project-id>
with your project ID.docker push gcr.io/<your-project-id>/polls
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ docker push gcr.io/django-poll-app-216501/polls
-
Create the GKE resource:
kubectl create -f polls.yaml
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ kubectl create -f polls.yaml deployment "polls" created service "polls" created
-
After the resources are created, there should be three
polls
pods on the cluster. Check the status of the pods:kubectl get pods
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ kubectl get pods NAME READY STATUS RESTARTS AGE polls-c648d749d-6b2w7 0/2 ContainerCreating 0 16s polls-c648d749d-mzq72 0/2 ContainerCreating 0 16s polls-c648d749d-t67b2 0/2 ContainerCreating 0 16s (venv) Kihyucks-Air:django_tutorial kihyuckhong$ kubectl get pods NAME READY STATUS RESTARTS AGE polls-c648d749d-6b2w7 2/2 Running 0 7m polls-c648d749d-mzq72 2/2 Running 0 7m polls-c648d749d-t67b2 2/2 Running 0 7m
Wait a few minutes for the pod statuses to turn to
Running
.If the pods are not ready or if we see restarts, we can get the logs for a particular pod to figure out the issue:
kubectl logs <your-pod-id>
-
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ kubectl cluster-info Kubernetes master is running at https://35.188.242.239 GLBCDefaultBackend is running at https://35.188.242.239/api/v1/namespaces/kube-system/services/default-http-backend:http/proxy Heapster is running at https://35.188.242.239/api/v1/namespaces/kube-system/services/heapster/proxy KubeDNS is running at https://35.188.242.239/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy kubernetes-dashboard is running at https://35.188.242.239/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy Metrics-server is running at https://35.188.242.239/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy
After the pods are ready, we can get the public IP address of the load balancer:
(venv) Kihyucks-Air:django_tutorial kihyuckhong$ kubectl get services polls NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE polls LoadBalancer 10.47.247.135 35.188.234.3 80:32279/TCP 13m
Navigate to the EXTERNAL-IP address in our browser to see the Django basic landing page and access the admin console.
GCP (Google Cloud Platform)
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization