Skip to content

Commit 7409fee

Browse files
venourbourgeoisor
andauthored
Add monolith-to-microservices GKE tutorial sample (#1663)
* Add monolith-to-microservices GKE tutorial sample * Insert license headers into Python, JSON, HTML and YAML files * Add license header to start_services.sh * Addressed PR feedback: fixed CSS license headers, updated Python version, added tutorial link to README --------- Co-authored-by: Olivier Bourgeois <3271352+bourgeoisor@users.noreply.github.com>
1 parent 6bd420b commit 7409fee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2766
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: quickstarts-monolith-to-microservices-ci
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- '.github/workflows/quickstarts-monolith-to-microservices-ci.yml'
9+
- 'quickstarts/monolith-to-microservices/**'
10+
pull_request:
11+
paths:
12+
- '.github/workflows/quickstarts-monolith-to-microservices-ci.yml'
13+
- 'quickstarts/monolith-to-microservices/**'
14+
15+
jobs:
16+
build:
17+
runs-on: ubuntu-22.04
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- name: Build home-app container
22+
run: |
23+
docker build -t home-app quickstarts/monolith-to-microservices/containerized/home_app
24+
25+
- name: Build book-details-app container
26+
run: |
27+
docker build -t book-details-app quickstarts/monolith-to-microservices/containerized/book_details_app
28+
29+
- name: Build book-reviews-app container
30+
run: |
31+
docker build -t book-reviews-app quickstarts/monolith-to-microservices/containerized/book_reviews_app
32+
33+
- name: Build images-app container
34+
run: |
35+
docker build -t images-app quickstarts/monolith-to-microservices/containerized/images_app
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Monolith to Microservices on GKE
2+
3+
4+
This code accompanies a tutorial series (https://cloud.google.com/kubernetes-engine/docs/learn/cymbal-books/overview) that shows you how to take a monolith, modularize the code, containerize each module, and deploy the container images to a Google Kubernetes Engine (GKE) cluster.
5+
6+
## Contents
7+
8+
This sample contains three versions of the Cymbal Books app:
9+
10+
- `monolith/`: The original monolithic Flask app
11+
- `modular/`: A modular version with separate Flask services communicating over HTTP
12+
- `containerized/`: Dockerized services and a Kubernetes manifest for GKE deployment
13+
14+
## Architecture
15+
16+
The monolith is incrementally broken into four modules:
17+
18+
- **Home app**: Serves the main page and coordinates other services
19+
- **Book details app**: Provides information about books
20+
- **Book reviews app**: Returns user reviews for each book
21+
- **Images app**: Serves book cover images
22+
23+
Each service is containerized and deployed into its own Pod. A corresponding Kubernetes Service enables internal communication.
24+
25+
## Setup
26+
27+
### Prerequisites for the containerize part of the tutorial
28+
29+
- A Google Cloud project with billing enabled
30+
31+
---
32+
33+
### Step 1: Build Container Images
34+
35+
From the `containerized/` directory:
36+
37+
```bash
38+
docker build -t home-app ./home_app
39+
docker build -t book-details-app ./book_details_app
40+
docker build -t book-reviews-app ./book_reviews_app
41+
docker build -t images-app ./images_app
42+
```
43+
44+
### Step 2: Push to Artifact Registry
45+
Replace placeholders with your own values:
46+
47+
```bash
48+
docker tag home-app REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/home-app
49+
docker push REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/home-app
50+
# Repeat for other images...
51+
```
52+
53+
### Step 3: Update Kubernetes Manifest
54+
Edit kubernetes-manifest.yaml and replace image paths with your Artifact Registry URLs:
55+
56+
```bash
57+
# Example
58+
image: us-west1-docker.pkg.dev/my-project/my-repo/home-app:v1
59+
```
60+
61+
### Step 4: Deploy to GKE
62+
63+
```bash
64+
kubectl apply -f kubernetes-manifest.yaml
65+
```
66+
67+
### Step 5: Access the App
68+
After deployment, find the external IP of the home service:
69+
70+
```bash
71+
kubectl get services
72+
```
73+
74+
Then open the app in your browser:
75+
76+
```bash
77+
http://<EXTERNAL-IP>
78+
```
79+
80+
## License
81+
Apache 2.0 © Google LLC
82+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Dockerfile for book_details_app
2+
FROM python:3.13-alpine
3+
4+
WORKDIR /app
5+
6+
COPY requirements.txt .
7+
RUN pip install --no-cache-dir -r requirements.txt
8+
9+
COPY book_details_app.py .
10+
COPY data/ ./data/
11+
12+
CMD ["python", "book_details_app.py"]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright 2025 Google LLC
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+
# https://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 flask import Flask, jsonify
17+
import json
18+
import os
19+
20+
app = Flask(__name__)
21+
22+
DATA_DIR = 'data'
23+
24+
def read_json_file(filename):
25+
with open(os.path.join(DATA_DIR, filename), 'r') as file:
26+
return json.load(file)
27+
28+
@app.route('/books')
29+
def get_books():
30+
books = [read_json_file(f'book-{i}.json') for i in range(1, 4)]
31+
return jsonify(books)
32+
33+
@app.route('/book/<int:book_id>')
34+
def get_book(book_id):
35+
book = read_json_file(f'book-{book_id}.json')
36+
return jsonify(book)
37+
38+
if __name__ == '__main__':
39+
app.run(host='0.0.0.0', port=8080)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": 1,
3+
"title": "Zephyr's Timepiece",
4+
"author": "Aria Clockwork",
5+
"year": 2023,
6+
"description": "In a world where time is a tangible substance, a young clockmaker discovers she can manipulate the fabric of time itself, leading to unforeseen consequences in her steampunk-inspired city.",
7+
"image_url": "zephyrs_timepiece.jpg"
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": 2,
3+
"title": "Fungi Frontier",
4+
"author": "Myco Sporesworth",
5+
"year": 2024,
6+
"description": "A mycologist leads an expedition to explore a newly discovered underground ecosystem entirely composed of sentient fungi, challenging the team's understanding of consciousness and life itself.",
7+
"image_url": "fungi_frontier.jpg"
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": 3,
3+
"title": "Melodic Mechanics",
4+
"author": "Harmony Gearsmith",
5+
"year": 2025,
6+
"description": "In a city where every machine runs on music, a tone-deaf engineer must find a way to keep the city's vital systems running when a mysterious silence begins to spread.",
7+
"image_url": "melodic_mechanics.jpg"
8+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Flask==2.3.2
2+
Werkzeug==2.3.4
3+
Jinja2==3.1.2
4+
requests==2.30.0
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Dockerfile for book_reviews_app
2+
FROM python:3.13-alpine
3+
4+
WORKDIR /app
5+
6+
COPY requirements.txt .
7+
RUN pip install --no-cache-dir -r requirements.txt
8+
9+
COPY book_reviews_app.py .
10+
COPY data/ ./data/
11+
12+
CMD ["python", "book_reviews_app.py"]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2025 Google LLC
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+
# https://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 flask import Flask, jsonify
17+
import json
18+
import os
19+
20+
app = Flask(__name__)
21+
22+
DATA_DIR = 'data'
23+
24+
def read_json_file(filename):
25+
with open(os.path.join(DATA_DIR, filename), 'r') as file:
26+
return json.load(file)
27+
28+
@app.route('/book/<int:book_id>/reviews')
29+
def get_reviews(book_id):
30+
reviews = read_json_file(f'reviews-{book_id}.json')
31+
return jsonify(reviews)
32+
33+
if __name__ == '__main__':
34+
app.run(host='0.0.0.0', port=8080)

0 commit comments

Comments
 (0)