Hosting A Web App On Google Cloud Using Compute Engine - PCA
Hosting A Web App On Google Cloud Using Compute Engine - PCA
on Google Cloud
Using Compute
Engine
1 hour 30 minutes1 Credit
Rate Lab
GSP662
Overview
There are many ways to deploy web sites within Google Cloud with each solution
offering different features, capabilities, and levels of control. Compute Engine offers a
deep level of control over the infrastructure used to run a web site, but also requires a
little more operational management compared to solutions like Google Kubernetes
Engines (GKE), App Engine, or others. With Compute Engine, you have fine-grained
control of aspects of the infrastructure, including the virtual machines, load balancers,
and more. In this lab you will deploy a sample application, the "Fancy Store"
ecommerce website, to show how a website can be deployed and scaled easily with
Compute Engine.
This hands-on lab lets you do the lab activities yourself in a real cloud environment, not
in a simulation or demo environment. It does so by giving you new, temporary
credentials that you use to sign in and access Google Cloud for the duration of the lab.
1. Click the Start Lab button. If you need to pay for the lab, a pop-up opens for you
to select your payment method. On the left is the Lab Details panel with the
following:
Note: If you see the Choose an account dialog, click Use Another Account.
3. If necessary, copy the Username from the Lab Details panel and paste it into
the Sign in dialog. Click Next.
4. Copy the Password from the Lab Details panel and paste it into
the Welcome dialog. Click Next.
Important: You must use the credentials from the left panel. Do not use your
Google Cloud Skills Boost credentials.Note: Using your own Google Cloud
account for this lab may incur extra charges.
Note: You can view the menu with a list of Google Cloud Products and Services by
clicking the Navigation menu at the top-
left.
Cloud Shell is a virtual machine that is loaded with development tools. It offers a
persistent 5GB home directory and runs on the Google Cloud. Cloud Shell provides
command-line access to your Google Cloud resources.
1. Click Activate Cloud Shell at the top of the Google Cloud console.
When you are connected, you are already authenticated, and the project is set to
your PROJECT_ID. The output contains a line that declares the PROJECT_ID for this
session:
2. (Optional) You can list the active account name with this command:
Output:
ACTIVE: *
ACCOUNT: [email protected]
To set the active account, run:
$ gcloud config set account `ACCOUNT`
5. (Optional) You can list the project ID with this command:
[core]
project = <project_ID>
Example output:
[core]
project = qwiklabs-gcp-44776a13dea667a6
Note: For full documentation of gcloud, in Google Cloud, refer to the gcloud CLI
overview guide.
Certain Compute Engine resources live in regions and zones. A region is a specific
geographical location where you can run your resources. Each region has one or more
zones.
Learn more about regions and zones and see a complete list in Regions & Zones
documentation.
Run the following gcloud commands in Cloud Console to set the default region and
zone for your lab:
From within Cloud Shell, execute the following to create a new Cloud Storage bucket:
gsutil mb gs://fancy-store-$DEVSHELL_PROJECT_ID
Copied!
content_copy
Note: Use of the $DEVSHELL_PROJECT_ID environment variable within Cloud Shell is
to help ensure the names of objects are unique. Since all Project IDs within Google
Cloud must be unique, appending the Project ID should make other names unique as
well.
Click Check my progress to verify the objective.
Assessment completed!
Clone the source code so you can focus on the aspects of deploying to Compute
Engine. Later on in this lab, you will perform a small update to the code to demonstrate
the simplicity of updating on Compute Engine.
./setup.sh
Copied!
content_copy
It will take a few minutes for this script to finish.
cd microservices
npm start
Copied!
content_copy
You should see the following output:
This opens a new window where you can see the frontend of Fancy Store.
Note: Within the Preview option, you should be able to see the Frontend; however, the
Products and Orders functions will not work, as those services are not yet exposed.
6. Close this window after viewing the website and then press CTRL+C in the
terminal window to stop the web server process.
A startup script will be used to instruct the instance what to do each time it is started.
This way the instances are automatically configured.
1. In Cloud Shell, run the following command to create a file called startup-
script.sh:
touch ~/monolith-to-microservices/startup-script.sh
Copied!
content_copy
2. Click Open Editor in the Cloud Shell ribbon to open the Code Editor.
4. Add the following code to the startup-script.sh file. You will edit some of the
code after it's added:
#!/bin/bash
# Install logging monitor. The monitor will automatically pick up logs
sent to
# syslog.
curl -s "https://storage.googleapis.com/signals-agents/logging/google-
fluentd-install.sh" | bash
service google-fluentd restart &
# Install dependencies from apt
apt-get update
apt-get install -yq ca-certificates git build-essential supervisor psmisc
# Install nodejs
mkdir /opt/nodejs
curl https://nodejs.org/dist/v16.14.0/node-v16.14.0-linux-x64.tar.gz | tar
xvzf - -C /opt/nodejs --strip-components=1
ln -s /opt/nodejs/bin/node /usr/bin/node
ln -s /opt/nodejs/bin/npm /usr/bin/npm
# Get the application source code from the Google Cloud Storage bucket.
mkdir /fancy-store
gsutil -m cp -r gs://fancy-store-[DEVSHELL_PROJECT_ID]/monolith-to-
microservices/microservices/* /fancy-store/
# Install app dependencies.
cd /fancy-store/
npm install
# Create a nodeapp user. The application will run as this user.
useradd -m -d /home/nodeapp nodeapp
chown -R nodeapp:nodeapp /opt/app
# Configure supervisor to run the node app.
cat >/etc/supervisor/conf.d/node-app.conf << EOF
[program:nodeapp]
directory=/fancy-store
command=npm start
autostart=true
autorestart=true
user=nodeapp
environment=HOME="/home/nodeapp",USER="nodeapp",NODE_ENV="production"
stdout_logfile=syslog
stderr_logfile=syslog
EOF
supervisorctl reread
supervisorctl update
Copied!
content_copy
5. Find the text [DEVSHELL_PROJECT_ID] in the file and replace it with your
Project ID: qwiklabs-gcp-00-92a317bb4bf4
The line of code within startup-script.sh should now resemble:
gs://fancy-store-qwiklabs-gcp-00-92a317bb4bf4/monolith-to-microservices/
microservices/* /fancy-store/
6. Save the startup-script.sh file, but do not close it yet.
7. Look at the bottom right of Cloud Shell Code Editor, and ensure "End of Line
Sequence" is set to "LF" and not "CRLF".
If this is set to CRLF, click CRLF and then select LF in the drop down.
[BUCKET_NAME] represents the name of the Cloud Storage bucket. This will only be
viewable by authorized users and service accounts by default, so inaccessible through
a web browser. Compute Engine instances will automatically be able to access this
through their service account.
Installs the Logging agent. The agent automatically collects logs from syslog.
Clones the app's source code from Cloud Storage Bucket and installs dependencies.
Configures Supervisor to run the app. Supervisor makes sure the app is restarted if it
exits unexpectedly or is stopped by an admin or process. It also sends the app's stdout
and stderr to syslog for the Logging agent to collect.
When instances launch, they pull code from the Cloud Storage bucket, so you can store
some configuration variables within the .env file of the code.
Note: You could also code this to pull environment variables from elsewhere, but for
demonstration purposes this is a simple method to handle configuration. In production,
environment variables would likely be stored outside of the code.
1. Copy the cloned code into your bucket:
cd ~
rm -rf monolith-to-microservices/*/node_modules
gsutil -m cp -r monolith-to-microservices gs://fancy-store-
$DEVSHELL_PROJECT_ID/
Copied!
content_copy
Note: The node_modules dependencies directories are deleted to ensure the copy is
as fast and efficient as possible. These are recreated on the instances when they start
up.
Click Check my progress to verify the objective.
Assessment completed!
The first instance to be deployed will be the backend instance which will house the
Orders and Products microservices.
Note: In a production environment, you may want to separate each microservice into
their own instance and instance group to allow them to scale independently. For
demonstration purposes, both backend microservices (Orders & Products) will reside on
the same instance and instance group.
Execute the following command to create an e2-standard-2 instance that is
configured to use the startup script. It is tagged as a backend instance so you can
apply specific firewall rules to it later:
Before you deploy the frontend of the application, you need to update the configuration
to point to the backend you just deployed.
1. Retrieve the external IP address of the backend with the following command,
look under the EXTERNAL_IP tab for the backend instance:
NAME: backend
ZONE: us-west1-a
MACHINE_TYPE: e2-standard-2
PREEMPTIBLE:
INTERNAL_IP: 10.142.0.2
EXTERNAL_IP: 35.237.245.193
STATUS: RUNNING
2. Copy the External IP for the backend.
4. In the Code Editor, select View > Toggle Hidden Files in order to see
the .env file.
In the next step, you edit the .env file to point to the External IP of the
backend. [BACKEND_ADDRESS] represents the External IP address of the backend
instance determined from the above gcloud command.
7. In Cloud Shell, run the following to rebuild react-app, which will update the
frontend code:
cd ~/monolith-to-microservices/react-app
npm install && npm run-script build
Copied!
content_copy
8. Then copy the application code into the Cloud Storage bucket:
cd ~
rm -rf monolith-to-microservices/*/node_modules
gsutil -m cp -r monolith-to-microservices gs://fancy-store-
$DEVSHELL_PROJECT_ID/
Copied!
content_copy
Execute the following to deploy the frontend instance with a similar command as
before. This instance is tagged as frontend for firewall purposes:
1. Create firewall rules to allow access to port 8080 for the frontend, and ports
8081-8082 for the backend. These firewall commands use the tags assigned
during instance creation for application:
NAME: backend
ZONE: us-central1-f
MACHINE_TYPE: e2-standard-2
PREEMPTIBLE:
INTERNAL_IP: 10.128.0.2
EXTERNAL_IP: 34.27.178.79
STATUS: RUNNING
NAME: frontend
ZONE: us-central1-f
MACHINE_TYPE: e2-standard-2
PREEMPTIBLE:
INTERNAL_IP: 10.128.0.3
EXTERNAL_IP: 34.172.241.242
STATUS: RUNNING
It may take a couple minutes for the instance to start and be configured.
3. Wait 3 minutes and then open a new browser tab and browse
to http://[FRONTEND_ADDRESS]:8080 to access the website, where
[FRONTEND_ADDRESS] is the frontend EXTERNAL_IP determined above.
4. Try navigating to the Products and Orders pages; these should now work.
Click Check my progress to verify the objective.
Assessment completed!
A managed instance group (MIG) contains identical instances that you can manage as
a single entity in a single zone. Managed instance groups maintain high availability of
your apps by proactively keeping your instances available, that is, in the RUNNING
state. You will be using managed instance groups for your frontend and backend
instances to provide autohealing, load balancing, autoscaling, and rolling updates.
Before you can create a managed instance group, you have to first create an instance
template that will be the foundation for the group. Instance templates allow you to define
the machine type, boot disk image or container image, network, and other instance
properties to use when creating new VM instances. You can use instance templates to
create instances in a managed instance group or even to create individual instances.
To create the instance template, use the existing instances you created previously.
NAME: fancy-be
MACHINE_TYPE: e2-standard-2
PREEMPTIBLE:
CREATION_TIMESTAMP: 2023-07-25T14:52:21.933-07:00
NAME: fancy-fe
MACHINE_TYPE: e2-standard-2
PREEMPTIBLE:
CREATION_TIMESTAMP: 2023-07-25T14:52:15.442-07:00
4. With the instance templates created, delete the backend vm to save resource
space:
1. Next, create two managed instance groups, one for the frontend and one for the
backend:
Configure autohealing
Note: Separate health checks for load balancing and for autohealing will be used.
Health checks for load balancing can and should be more aggressive because these
health checks determine whether an instance receives user traffic. You want to catch
non-responsive instances quickly so you can redirect traffic if necessary.
Assessment completed!
You can learn more about the Load Balancing options on Google Cloud: Overview of
Load Balancing.
Google Cloud offers many different types of load balancers. For this lab you use an
HTTP(S) Load Balancer for your traffic. An HTTP load balancer is structured as follows:
Assessment completed!
Now that you have a new static IP address, update the code on the frontend to point
to this new address instead of the ephemeral address used earlier that pointed to
the backend instance.
1. In Cloud Shell, change to the react-app folder which houses the .env file that
holds the configuration:
cd ~/monolith-to-microservices/react-app/
Copied!
content_copy
2. Find the IP address for the Load Balancer:
NAME: fancy-http-rule
REGION:
IP_ADDRESS: 34.111.203.235
IP_PROTOCOL: TCP
TARGET: fancy-proxy
3. Return to the Cloud Shell Editor and edit the .env file again to point to Public IP
of Load Balancer. [LB_IP] represents the External IP address of the backend
instance determined above.
REACT_APP_ORDERS_URL=http://[LB_IP]/api/orders
REACT_APP_PRODUCTS_URL=http://[LB_IP]/api/products
Note: The ports are removed in the new address because the load balancer is
configured to handle this forwarding for you.
4. Save the file.
cd ~/monolith-to-microservices/react-app
npm install && npm run-script build
Copied!
content_copy
6. Copy the application code into your bucket:
cd ~
rm -rf monolith-to-microservices/*/node_modules
gsutil -m cp -r monolith-to-microservices gs://fancy-store-
$DEVSHELL_PROJECT_ID/
Copied!
content_copy
Now that there is new code and configuration, you want the frontend instances within
the managed instance group to pull the new code.
Since your instances pull the code at startup, you can issue a rolling restart command:
Assessment completed!
backend: https://www.googleapis.com/compute/v1/projects/my-gce-
codelab/zones/us-central1-a/instanceGroups/fancy-fe-mig
status:
healthStatus:
- healthState: HEALTHY
instance: https://www.googleapis.com/compute/v1/projects/my-gce-
codelab/zones/us-central1-a/instances/fancy-fe-x151
ipAddress: 10.128.0.7
port: 8080
- healthState: HEALTHY
instance: https://www.googleapis.com/compute/v1/projects/my-gce-
codelab/zones/us-central1-a/instances/fancy-fe-cgrt
ipAddress: 10.128.0.11
port: 8080
kind: compute#backendServiceGroupHealth
Note: If one instance encounters an issue and is UNHEALTHY it should automatically
be repaired. Wait for this to happen.
If neither instance enters a HEALTHY state after waiting a little while, something is
wrong with the setup of the frontend instances that accessing them on port 8080 doesn't
work. Test this by browsing to the instances directly on port 8080.
3. Once both items appear as HEALTHY on the list, exit the watch command by
pressing CTRL+C.
Note: The application will be accessible via http://[LB_IP] where [LB_IP] is the
IP_ADDRESS specified for the Load Balancer, which can be found with the following
command:
gcloud compute forwarding-rules list --global
Another feature that can help with scaling is to enable a Content Delivery Network
service, to provide caching for the frontend.
If the GFE can't find a cached response for the request, the GFE makes a request
directly to the backend. If the response to this request is cacheable, the GFE stores the
response in the Cloud CDN cache so that the cache can be used for subsequent
requests.
Click Check my progress to verify the objective.
Assessment completed!
Existing instance templates are not editable; however, since your instances are
stateless and all configuration is done through the startup script, you only need to
change the instance template if you want to change the template settings . Now you're
going to make a simple change to use a larger machine type and push that out.
Update the frontend instance, which acts as the basis for the instance template.
During the update, put a file on the updated version of the instance template's image,
then update the instance template, roll out the new template, and then confirm the file
exists on the managed instance group instances.
Modify the machine type of your instance template, by switching from the e2-
standard-2 machine type to e2-small.
1. Run the following command to modify the machine type of the frontend instance:
STATUS: RUNNING
5. Copy the name of one of the machines listed for use in the next command.
7. Run the following to see if the virtual machine is using the new machine type (e2-
small), where [VM_NAME] is the newly created instance:
machineType: https://www.googleapis.com/compute/v1/projects/project-
name/zones/us-central1-f/machineTypes/e2-small
Scenario: Your marketing team has asked you to change the homepage for your site.
They think it should be more informative of who your company is and what you actually
sell.
Task: Add some text to the homepage to make the marketing team happy! It looks like
one of the developers has already created the changes with the file
name index.js.new. You can just copy this file to index.js and the changes should
be reflected. Follow the instructions below to make the appropriate changes.
1. Run the following commands to copy the updated file to the correct file name:
cd ~/monolith-to-microservices/react-app/src/pages/Home
mv index.js.new index.js
Copied!
content_copy
2. Print the file contents to verify the changes:
cat ~/monolith-to-microservices/react-app/src/pages/Home/index.js
Copied!
content_copy
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 { Box, Paper, Typography } from "@mui/material";
export default function Home() {
return (
<Box sx={{ flexGrow: 1 }}>
<Paper
elevation={3}
sx={{
width: "800px",
margin: "0 auto",
padding: (theme) => theme.spacing(3, 2),
}}
>
<Typography variant="h5">Welcome to the Fancy Store!</Typography>
<br />
<Typography variant="body1">
Take a look at our wide variety of products.
</Typography>
</Paper>
</Box>
);
}
You updated the React components, but you need to build the React app to generate
the static files.
3. Run the following command to build the React app and copy it into the monolith
public directory:
cd ~/monolith-to-microservices/react-app
npm install && npm run-script build
Copied!
content_copy
4. Then re-push this code to the bucket:
cd ~
rm -rf monolith-to-microservices/*/node_modules
gsutil -m cp -r monolith-to-microservices gs://fancy-store-
$DEVSHELL_PROJECT_ID/
Copied!
content_copy
For testing purposes, you specify to replace all immediately for speed. In production,
leaving a buffer would allow the website to continue serving the website while updating.
Click Check my progress to verify the objective.
Please roll out the updated instance template to the managed instance group:
backend: https://www.googleapis.com/compute/v1/projects/my-gce-
codelab/zones/us-central1-a/instanceGroups/fancy-fe-mig
status:
healthStatus:
- healthState: HEALTHY
instance: https://www.googleapis.com/compute/v1/projects/my-gce-
codelab/zones/us-central1-a/instances/fancy-fe-x151
ipAddress: 10.128.0.7
port: 8080
- healthState: HEALTHY
instance: https://www.googleapis.com/compute/v1/projects/my-gce-
codelab/zones/us-central1-a/instances/fancy-fe-cgrt
ipAddress: 10.128.0.11
port: 8080
kind: compute#backendServiceGroupHealth
4. Once items appear in the list with HEALTHY status, exit the watch command by
pressing CTRL+C.
Simulate failure
In order to confirm the health check works, log in to an instance and stop the services.
exit
Copied!
content_copy
6. Monitor the repair operations:
7. You can also go to Navigation menu > Compute Engine > VM instances to
monitor through the Console.
Congratulations!
You successfully deployed, scaled, and updated your website on Compute Engine. You
are now experienced with Compute Engine, Managed Instance Groups, Load
Balancers, and Health Checks!