1. Introduction
Running websites and applications is hard.
Things go wrong when they shouldn't, servers crash, increase in demand causes more resources to be utilized, and making changes without downtime is complicated and stressful.
Imagine a tool that could help you do all that and even allow you to automate it! With GKE, all of that is not only possible, it's easy! In this codelab, you assume the role of a developer running an ecommerce website for a fictional company—Fancy Store. Due to problems with scaling and outages, you're tasked with deploying your application to GKE!
The exercises are ordered to reflect a common cloud developer's experience:
- Create a GKE cluster.
- Create a Docker container.
- Deploy the container to GKE.
- Expose the container via a service.
- Scale the container to multiple replicas.
- Modify the website.
- Roll out a new version with zero downtime.
Architecture diagram
What you'll learn
- How to create a GKE cluster
- How to create a Docker image
- How to deploy Docker images to Kubernetes
- How to scale an application on Kubernetes
- How to perform a rolling update on Kubernetes
Prerequisites
- A Google Account with administrative access to create projects or a project with a project-owner role
- A basic understanding of Docker and Kubernetes (If you lack a basic understanding, then please review Docker and Kubernetes now.)
2. Environment setup
Self-paced environment setup
If you don't already have a Google Account, then you must create one. Sign into Google Cloud Console and create a new project.
Remember, the project ID is a unique name across all Google Cloud projects (the name above has already been taken and will not work for you, sorry!). It will be referred to as PROJECT_ID
later.
Next, you'll need to enable billing in the Cloud Console to use Google Cloud resources. New users of Google Cloud are eligible for a $300 free trial. If you're not a new user, then don't worry because the codelab shouldn't cost you more than a few dollars. However, the codelab could cost you more money if you use more resources or leave them running (see "clean up" section at the end). For more information, see Pricing.
Cloud Shell
While you can remotely operate Google Cloud and GKE with your laptop, you will use Cloud Shell—a command-line environment running in the Cloud—for the codelab.
This Debian-based virtual machine is loaded with all the development tools you'll need. It offers a persistent 5GB home directory and runs in Google Cloud, greatly enhancing network performance and authentication. This means that all you will need for this codelab is a browser (yes, it works on a Chromebook).
- To activate Cloud Shell from the Cloud Console, simply click Activate Cloud Shell (it should only take a few moments to provision and connect to the environment).
Once connected to Cloud Shell, you should see that you are already authenticated and that the project is already set to your PROJECT_ID
.
gcloud auth list
Command output
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Command output
[core] project = <PROJECT_ID>
If, for some reason, the project is not set, simply issue the following command:
gcloud config set project <PROJECT_ID>
Looking for your PROJECT_ID
? Check out what ID you used in the setup steps or look it up in the Cloud Console dashboard:
Cloud Shell also sets some environment variables by default, which may be useful as you run future commands.
echo $GOOGLE_CLOUD_PROJECT
Command output
<PROJECT_ID>
- Finally, set the default zone and project configuration.
gcloud config set compute/zone us-central1-f
You can choose a variety of different zones. For more information, see Regions & Zones.
3. Create a GKE cluster
Now that you have your working developer environment, you need a GKE cluster to deploy your website to! Before you create a cluster, you need to ensure that the proper APIs are enabled. Run the following command to enable the containers API:
gcloud services enable container.googleapis.com
Now, you can create your cluster! Follow the steps below to create a cluster named fancy-cluster with 3 nodes:
gcloud container clusters create fancy-cluster --num-nodes 3
It may take several minutes for the cluster to be created. Afterward, run the following command and see the cluster's three worker virtual machine (VM) instances:
gcloud compute instances list
Output:
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS gke-fancy-cluster-default-pool-ad92506d-1ng3 us-east4-a n1-standard-1 10.150.0.7 XX.XX.XX.XX RUNNING gke-fancy-cluster-default-pool-ad92506d-4fvq us-east4-a n1-standard-1 10.150.0.5 XX.XX.XX.XX RUNNING gke-fancy-cluster-default-pool-ad92506d-4zs3 us-east4-a n1-standard-1 10.150.0.6 XX.XX.XX.XX RUNNING
You can also view your cluster and related information in the Cloud Console. Click the menu button in the top-left corner, scroll down to Kubernetes Engine, and click Clusters. You should see your cluster named fancy-cluster.
Congratulations! You created your first cluster!
4. Clone source repository
Because this is an existing website, you only need to clone the source from the repository so that you can focus on creating Docker images and deploying to GKE.
Run the following commands to clone the source repository to your Cloud Shell instance and change it to the appropriate directory. You will also install the Node.js dependencies so that you can test your application before deploying it.
cd ~ git clone https://github.com/googlecodelabs/monolith-to-microservices.git cd ~/monolith-to-microservices ./setup.sh
That clones the repository, changes the directory, and installs the dependencies needed to locally run your application. It may take a few minutes for that script to run.
Do your due diligence and test your application. Run the following command to start your web server:
cd ~/monolith-to-microservices/monolith npm start
Output:
Monolith listening on port 8080!
You can preview your application by clicking the web preview icon in the Cloud Shell menu and selecting Preview on port 8080.
That should open a new window where you can see your Fancy Store in action!
You can close that window after viewing the website. Press Control+C
(Windows or Mac) in the terminal window to stop the web server process.
5. Create Docker container with Cloud Build
Now that your source files are ready to go, it's time to Dockerize your application!
Normally, you would have to take a two-step approach that entails building a Docker container and pushing it to a registry to store the image that GKE pulls from. However, you can make life easier by using Cloud Build to create the Docker container and put the image in the Container Registry with a single command! (To view the manual process of creating a docker file and pushing it, see Quickstart for Container Registry.)
Cloud Build compresses the files from the directory and moves them to a Cloud Storage bucket. The build process then takes the files from the bucket and uses the Dockerfile to run the Docker build process. Because you specified the --tag
flag with the host as gcr.io
for the Docker image, the resulting Docker image gets pushed to the Container Registry.
First, you need to enable the Cloud Build API by running the following command:
gcloud services enable cloudbuild.googleapis.com
After the API is enabled, run the following command in Cloud Shell to start the build process:
cd ~/monolith-to-microservices/monolith gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 .
That process takes a few minutes, but after it's completed, you can see the following output in the terminal:
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ID CREATE_TIME DURATION SOURCE IMAGES STATUS 1ae295d9-63cb-482c-959b-bc52e9644d53 2019-08-29T01:56:35+00:00 33S gs://<PROJECT_ID>_cloudbuild/source/1567043793.94-abfd382011724422bf49af1558b894aa.tgz gcr.io/<PROJECT_ID>/monolith:1.0.0 SUCCESS
To view your build history or watch the process in real time, you can go to the Cloud Console. Click the menu button in the top-left corner, scroll down to Ci/CD, then click Cloud Build, and finally click History. There, you can see a list of your previous builds, but there should only be the one that you created.
If you click on Build id, then you can see all the details for that build, including the log output.
On the build details page, you can view the container image that was created by clicking on the Image name in the build information section.
6. Deploy container to GKE
Now that you containerized your website and pushed the container to the Container Registry, you can deploy it to Kubernetes!
To deploy and manage applications on a GKE cluster, you must communicate with the Kubernetes cluster-management system. You typically do that by using the kubectl command-line tool.
Kubernetes represents applications as Pods, which are units that represent a container (or group of tightly coupled containers). The Pod is the smallest deployable unit in Kubernetes. Here, each Pod only contains your monolith container.
To deploy your application, you need to create a Deployment. A Deployment manages multiple copies of your application—called replicas—and schedules them to run on the individual nodes in your cluster. In this case, the Deployment will run only one Pod of your application. Deployments ensure that by creating a ReplicaSet. The ReplicaSet is responsible for making sure that the number of replicas specified are always running.
The kubectl create deployment
command causes Kubernetes to create a Deployment named monolith on your cluster with 1 replica.
Run the following command to deploy your application:
kubectl create deployment monolith --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0
Verify deployment
To verify that the Deployment was created successfully, run the following command (It may take a few moments for the Pod status to be "Running"):
kubectl get all
Output:
NAME READY STATUS RESTARTS AGE pod/monolith-7d8bc7bf68-htm7z 1/1 Running 0 6m21s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.27.240.1 <none> 443/TCP 24h NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE deployment.apps/monolith 1 1 1 1 20m NAME DESIRED CURRENT READY AGE replicaset.apps/monolith-7d8bc7bf68 1 1 1 20m
That output shows you several things. You can see your Deployment, which is current; your ReplicaSet, with a desired Pod count of one; and your Pod, which is running. Looks like you successfully created everything!
To individually view your resources, you can run the following commands:
# Show pods kubectl get pods # Show deployments kubectl get deployments # Show replica sets kubectl get rs #You can also combine them kubectl get pods,deployments
To see the full benefit of Kubernetes, you can simulate a server crash, delete the Pod, and see what happens!
Copy your pod name from the previous command and run the following command to delete it:
kubectl delete pod/<POD_NAME>
If you are fast enough, you can run the previous command to see all again and you should see two Pods, one terminating and the other creating or running:
kubectl get all
Output:
NAME READY STATUS RESTARTS AGE pod/monolith-7d8bc7bf68-2bxts 1/1 Running 0 4s pod/monolith-7d8bc7bf68-htm7z 1/1 Terminating 0 9m35s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.27.240.1 <none> 443/TCP 24h NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE deployment.apps/monolith 1 1 1 1 24m NAME DESIRED CURRENT READY AGE replicaset.apps/monolith-7d8bc7bf68 1 1 1 24m
Why did that happen? The ReplicaSet saw that the pod was terminating and triggered a new pod to keep up the desired replica count. Later on, you'll see how to scale to ensure that you have several instances running so that if one goes down, your users won't see any downtime!
7. Expose GKE deployment
You deployed your application to GKE, but you don't have a way of accessing it outside of the cluster. By default, the containers you run on GKE are not accessible from the internet because they do not have external IP addresses. You must explicitly expose your application to traffic from the internet via a Service resource. A Service provides networking and IP support for your app's Pods. GKE creates an external IP and a load balancer (subject to billing) for your app.
Run the following command to expose your website to the internet:
kubectl expose deployment monolith --type=LoadBalancer --port 80 --target-port 8080
Output:
service/monolith exposed
Accessing the service
GKE assigns the external IP address to the Service resource—not the Deployment. If you want to find the external IP that GKE provisioned for your application, you can inspect the Service with the kubectl get service command:
kubectl get service
Output:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE monolith 10.3.251.122 203.0.113.0 80:30877/TCP 3d
After you determine the external IP address for your app, copy it. Point your browser to that URL (such as http://203.0.113.0) to check whether your app is accessible.
You should see the same website that you tested earlier! Congratulations! Your website fully runs on Kubernetes!
8. Scale GKE deployment
Now that you have a running instance of your app in GKE and exposed it to the internet, your website has become extremely popular! You need a way to scale your app to multiple instances so that you can handle the traffic. Learn to scale your application to up to three replicas.
Run the following command to scale your deployment up to three replicas:
kubectl scale deployment monolith --replicas=3
Output:
deployment.apps/monolith scaled
Verify scaled deployment
To verify that the Deployment was scaled successfully, run the following command:
kubectl get all
Output:
NAME READY STATUS RESTARTS AGE pod/monolith-7d8bc7bf68-2bxts 1/1 Running 0 36m pod/monolith-7d8bc7bf68-7ds7q 1/1 Running 0 45s pod/monolith-7d8bc7bf68-c5kxk 1/1 Running 0 45s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.27.240.1 <none> 443/TCP 25h service/monolith LoadBalancer 10.27.253.64 XX.XX.XX.XX 80:32050/TCP 6m7s NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE deployment.apps/monolith 3 3 3 3 61m NAME DESIRED CURRENT READY AGE replicaset.apps/monolith-7d8bc7bf68 3 3 3 61m
You should see three instances of your Pod running. Also, note that your Deployment and ReplicaSet now have a desired count of three.
9. Make changes to the website
Your marketing team asked you to change your website's homepage. They think that it should be more informative by explaining what your company is and what you actually sell. In this section, you'll add some text to the homepage to make the marketing team happy! It looks like one of our developers already created the changes with the file name index.js.new
. You can copy the file to index.js
and your changes should be reflected. Follow the instructions below to make the appropriate changes.
Run the following commands, copy the updated file to the correct file name, and print its contents to verify the changes:
cd ~/monolith-to-microservices/react-app/src/pages/Home mv index.js.new index.js cat ~/monolith-to-microservices/react-app/src/pages/Home/index.js
The resulting code should look like this:
/* Copyright 2019 Google LLC 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 https://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 React from "react"; import { makeStyles } from "@material-ui/core/styles"; import Paper from "@material-ui/core/Paper"; import Typography from "@material-ui/core/Typography"; const useStyles = makeStyles(theme => ({ root: { flexGrow: 1 }, paper: { width: "800px", margin: "0 auto", padding: theme.spacing(3, 2) } })); export default function Home() { const classes = useStyles(); return ( <div className={classes.root}> <Paper className={classes.paper}> <Typography variant="h5"> Fancy Fashion & Style Online </Typography> <br /> <Typography variant="body1"> Tired of mainstream fashion ideas, popular trends and societal norms? This line of lifestyle products will help you catch up with the Fancy trend and express your personal style. Start shopping Fancy items now! </Typography> </Paper> </div> ); }
You updated the React components, but you need to build the React app to generate the static files. Run the following command to build the React app and copy it into the monolith public directory:
cd ~/monolith-to-microservices/react-app npm run build:monolith
Now that your code is updated, you need to rebuild your Docker container and publish it to the Container Registry. You can use the same command as earlier, except this time, you'll update the version label!
Run the following command to trigger a new Cloud Build with an updated image version of 2.0.0:
cd ~/monolith-to-microservices/monolith #Feel free to test your application npm start gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0 .
Press Control+C
(Windows or Mac) in the terminal window to stop the web server process.
In the next section, you'll use that image to update your application with zero downtime.
10. Update website with zero downtime
The changes are completed and the marketing team is happy with your updates! It's time to update the website without interruption to the users. Follow the instructions below to update your website.
GKE's rolling updates ensure that your application remains up and available even when the system replaces instances of your old container image with your new one across all the running replicas.
From the command line, you can tell Kubernetes that you want to update the image for your Deployment to a new version with the following command:
kubectl set image deployment/monolith monolith=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0
Output:
deployment.apps/monolith image updated
Verify Deployment
You can validate your Deployment update by running the following command:
kubectl get pods
Output:
NAME READY STATUS RESTARTS AGE monolith-584fbc994b-4hj68 1/1 Terminating 0 60m monolith-584fbc994b-fpwdw 1/1 Running 0 60m monolith-584fbc994b-xsk8s 1/1 Terminating 0 60m monolith-75f4cf58d5-24cq8 1/1 Running 0 3s monolith-75f4cf58d5-rfj8r 1/1 Running 0 5s monolith-75f4cf58d5-xm44v 0/1 ContainerCreating 0 1s
You see three new Pods being created and your old pods being shut down. You can tell by the ages which are new and which are old. Eventually, you will only see three Pods again, which will be your three updated Pods.
To verify your changes, navigate to the external IP of the load balancer again and notice that your app has been updated.
Run the following command to list the services and view the IP address if you forgot it:
kubectl get svc
Your website should display the text that you added to the homepage component!
11. Clean up
Delete Git repository
cd ~ rm -rf monolith-to-microservices
Delete Container Registry images
NOTE: If you created other versions, then you can use the same syntax to delete those images as well. This codelab assumes that you only have two tags.
# Delete the container image for version 1.0.0 of our monolith gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 --quiet # Delete the container image for version 2.0.0 of our monolith gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0 --quiet
Delete Cloud Build artifacts from Cloud Storage
NOTE: If you used Cloud Build for artifacts other than this codelab, you will have to manually delete your source from the Cloud Storage bucket gs://<PROJECT_ID>_cloudbuild/source
.
# The following command will take all source archives from all builds and delete them from cloud storage # Run this command to print all sources: # gcloud builds list | awk 'NR > 1 {print $4}' gcloud builds list | awk 'NR > 1 {print $4}' | while read line; do gsutil rm $line; done
Delete GKE service
kubectl delete service monolith kubectl delete deployment monolith
Delete GKE cluster
gcloud container clusters delete fancy-cluster
NOTE: This command may take a little while.
12. Congratulations!
You deployed, scaled, and updated your website on GKE. You're now experienced with Docker and Kubernetes!