Ojasa Mirai

Ojasa Mirai

Cloud

Loading...

Learning Level

🟢 Beginner🔵 Advanced
🔧 GCP Account Setup⚙️ GCP Compute Overview🚀 Cloud Run Deployment🎯 App Engine Deployment📁 GCP Storage & Hosting🔥 Firebase Hosting🗄️ Firestore Setup⚡ Firestore Realtime💾 Cloud SQL Setup📊 GCP Monitoring🔑 GCP Authentication📈 GCP Scaling & Performance⚡ Firebase Functions💰 GCP Cost Optimization
Cloud/Gcp Deployment/App Engine Deployment

🎯 App Engine Deployment

Introduction

Google App Engine is a fully managed platform for building web applications and APIs. Unlike Cloud Run which requires containerization, App Engine deploys directly from source code and handles all infrastructure management automatically.

Key Learning Outcomes

By the end of this lesson, you'll understand:

  • What App Engine is and when to use it
  • Standard vs. Flexible environments
  • Creating and configuring app.yaml
  • Deploying applications in multiple languages
  • Managing environment variables and secrets
  • Monitoring, logging, and debugging
  • Best practices for App Engine applications

What is Google App Engine?

App Engine is a Platform as a Service (PaaS) that:

  • Automatically scales your application based on demand
  • Manages server infrastructure completely
  • Deploys from source code (no Docker required)
  • Includes built-in monitoring and logging
  • Provides built-in security features
  • Supports multiple programming languages

Standard vs. Flexible Environment

FeatureStandardFlexible
DeploymentFrom source codeFrom Docker image or source
Cold Starts1-10 seconds30+ seconds
ScalingAutomatic, to zeroAutomatic with minimum instances
Supported LanguagesNode.js, Python, Java, GoAny language in Docker
Local DiskNo (read-only FS)Yes (temporary)
Cost ModelPay per instance hourPay per instance + CPU/memory
Background TasksLimited (10 minutes)Full support
Best ForWeb apps, APIsComplex apps, custom runtimes

For beginners: Standard environment is simpler and cheaper.

Getting Started with App Engine

Step 1: Initialize Your App Engine Project

# Initialize a GCP project for App Engine
gcloud app create --project=my-project --region=us-central

# Verify initialization
gcloud app describe

Step 2: Create app.yaml Configuration

The `app.yaml` file tells App Engine how to run your application:

# app.yaml - Basic configuration
runtime: nodejs18
env: standard

# Service name (default service)
service: default

# Environment variables
env_variables:
  NODE_ENV: "production"
  API_KEY: "your-api-key"

# Handlers define URL routing
handlers:
  - url: /.*
    script: auto

# Automatic scaling configuration
automatic_scaling:
  min_instances: 1
  max_instances: 10
  target_cpu_utilization: 0.65
  target_throughput_utilization: 0.75

Step 3: Create Your Application

Node.js Express Application:

// server.js
const express = require('express');
const app = express();

// Health check endpoint (required by App Engine)
app.get('/_ah/health', (req, res) => {
  res.status(200).send('OK');
});

// Liveness check endpoint
app.get('/_ah/liveness', (req, res) => {
  res.status(200).send('OK');
});

// Main endpoints
app.get('/', (req, res) => {
  res.send('Hello from App Engine!');
});

app.get('/api/users', (req, res) => {
  res.json([
    { id: 1, name: 'Alice', email: 'alice@example.com' },
    { id: 2, name: 'Bob', email: 'bob@example.com' }
  ]);
});

app.get('/api/users/:id', (req, res) => {
  const userId = req.params.id;
  res.json({ id: userId, name: `User ${userId}` });
});

app.post('/api/users', express.json(), (req, res) => {
  const newUser = req.body;
  res.status(201).json({ ...newUser, id: 3 });
});

// Error handling
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: 'Internal server error' });
});

// Start server on PORT environment variable
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
});

package.json:

{
  "name": "app-engine-app",
  "version": "1.0.0",
  "description": "Simple App Engine application",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

Step 4: Deploy Your Application

# Deploy to App Engine
gcloud app deploy

# View deployment details
gcloud app describe

# Open application in browser
gcloud app browse

# View recent deployments
gcloud app versions list --service=default

Configuring App Engine

Environment Variables and Configuration

# app.yaml - With environment variables
runtime: nodejs18
env: standard

env_variables:
  NODE_ENV: "production"
  LOG_LEVEL: "info"
  DATABASE_URL: "cloudsql://localhost/mydb"

# For sensitive values, use Secret Manager
env_variables:
  SECRET_KEY: "sm://my-secret-key"

Access environment variables in code:

const databaseUrl = process.env.DATABASE_URL;
const nodeEnv = process.env.NODE_ENV;
const port = process.env.PORT || 8080;

console.log(`Running in ${nodeEnv} mode`);

Static Files and Assets

# app.yaml - With static files
runtime: nodejs18
env: standard

handlers:
  # Serve static assets
  - url: /static
    static_dir: static

  # Serve images
  - url: /images
    static_dir: public/images

  # Route everything else to application
  - url: /.*
    script: auto

Custom Domain Configuration

# Add custom domain
gcloud app custom-domains create example.com

# Configure DNS records
# Add CNAME record: ghs.googlehosted.com
# Or A record: 216.58.217.46

# List domains
gcloud app custom-domains list

Deploying Multiple Services

App Engine supports multiple services (microservices):

# api-service/app.yaml
runtime: nodejs18
env: standard
service: api

handlers:
  - url: /api/.*
    script: auto

automatic_scaling:
  min_instances: 2
  max_instances: 20
# web-service/app.yaml
runtime: nodejs18
env: standard
service: web

handlers:
  - url: /.*
    script: auto

automatic_scaling:
  min_instances: 1
  max_instances: 10

Deploy multiple services:

# Deploy API service
cd api-service
gcloud app deploy

# Deploy web service
cd ../web-service
gcloud app deploy

# Route traffic by domain or path
# api.example.com → api service
# example.com → web service

Working with Cloud SQL

Connect to Cloud SQL from App Engine

// server.js - Cloud SQL connection
const mysql = require('mysql2/promise');

// Connection pool
const pool = mysql.createPool({
  host: '/cloudsql/my-project:us-central1:my-database',
  user: 'app-user',
  password: process.env.DB_PASSWORD,
  database: 'production',
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

// Use connection in endpoint
app.get('/api/products', async (req, res) => {
  const connection = await pool.getConnection();
  try {
    const [rows] = await connection.execute('SELECT * FROM products');
    res.json(rows);
  } catch (error) {
    console.error('Database error:', error);
    res.status(500).json({ error: 'Database error' });
  } finally {
    connection.release();
  }
});

app.yaml configuration for Cloud SQL:

runtime: nodejs18
env: standard

env_variables:
  DB_PASSWORD: "sm://db-password"

# For Flexible environment
cloud_sql_instances:
  - "my-project:us-central1:my-database"

Monitoring and Logging

View Application Logs

# View recent logs
gcloud app logs read

# View logs in real-time
gcloud app logs read -f

# View logs for specific service
gcloud app logs read --service=api

# Export logs to Firestore
gcloud app logs read --limit=1000 > logs.txt

Structured Logging

// server.js - Structured logging
function logStructured(severity, message, data = {}) {
  const logEntry = {
    severity,
    message,
    timestamp: new Date().toISOString(),
    ...data
  };

  console.log(JSON.stringify(logEntry));
}

app.use((req, res, next) => {
  const startTime = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - startTime;
    logStructured('INFO', 'HTTP Request', {
      method: req.method,
      path: req.path,
      status: res.statusCode,
      duration_ms: duration,
      user_agent: req.get('user-agent')
    });
  });

  next();
});

Viewing Metrics

# View request count and latency
gcloud monitoring read-time-series \
  --filter='resource.type="app_engine_app" AND metric.type="appengine.googleapis.com/http/request_count"'

# View error rate
gcloud monitoring read-time-series \
  --filter='resource.type="app_engine_app" AND metric.type="appengine.googleapis.com/http/server_errors"'

Deploying Different Languages

Python Application

# app.yaml - Python runtime
runtime: python39
env: standard

handlers:
  - url: /.*
    script: auto

main.py:

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello from App Engine!'

@app.route('/api/users')
def get_users():
    return jsonify([
        {'id': 1, 'name': 'Alice'},
        {'id': 2, 'name': 'Bob'}
    ])

@app.route('/api/users/<int:user_id>')
def get_user(user_id):
    return jsonify({'id': user_id, 'name': f'User {user_id}'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=False)

requirements.txt:

Flask==2.3.2
Werkzeug==2.3.6

Go Application

# app.yaml - Go runtime
runtime: go119
env: standard

handlers:
  - url: /.*
    script: auto

main.go:

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
)

func main() {
	http.HandleFunc("/", helloHandler)
	http.HandleFunc("/api/users", usersHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	log.Printf("Server listening on port %s", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello from App Engine!")
}

func usersHandler(w http.ResponseWriter, r *http.Request) {
	users := []map[string]interface{}{
		{"id": 1, "name": "Alice"},
		{"id": 2, "name": "Bob"},
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(users)
}

Scaling and Performance

Configuring Scaling

# app.yaml - Scaling configuration
runtime: nodejs18
env: standard

automatic_scaling:
  min_instances: 1           # Minimum instances always running
  max_instances: 50          # Maximum instances for scaling
  target_cpu_utilization: 0.65      # Scale up when CPU > 65%
  target_throughput_utilization: 0.75  # Scale up when throughput high
  min_pending_latency: 30ms  # Scale up with pending requests
  max_pending_latency: 100ms # Scale down if latency drops
  min_idle_instances: 1      # Keep at least 1 warm
  max_concurrent_requests: 50

Handling Warm-up Requests

// server.js - Warm-up request handling
app.get('/_ah/warmup', (req, res) => {
  // Initialize database connections
  // Load caches
  // Perform startup tasks
  res.status(200).send('Warmed up');
});

Best Practices

1. Health Checks

Always implement health check endpoints:

// Health endpoint for load balancing
app.get('/_ah/health', (req, res) => {
  res.status(200).send('OK');
});

// Readiness endpoint (check dependencies)
app.get('/_ah/readiness', (req, res) => {
  // Check database connection
  // Check external service availability
  res.status(200).json({ status: 'ready' });
});

2. Request Timeout Handling

# app.yaml - Request timeout
runtime: nodejs18
env: standard

# Default is 25 seconds for Standard, higher for Flexible
timeout: 30s

3. Connection Pooling

// Reuse database connections
const pool = mysql.createPool({
  connectionLimit: 10,
  queueLimit: 0,
  enableKeepAlive: true,
  keepAliveInitialDelayMs: 0
});

4. Graceful Shutdown

// Handle graceful shutdown
process.on('SIGTERM', async () => {
  console.log('SIGTERM received, shutting down gracefully');

  // Close database connections
  await pool.end();

  // Close HTTP server
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});

Deployment Workflow

# 1. Test locally
npm start  # or python main.py, go run main.go

# 2. Check configuration
cat app.yaml

# 3. Deploy to App Engine
gcloud app deploy --version=v1

# 4. Verify deployment
gcloud app versions list
gcloud app describe

# 5. Test deployed application
gcloud app browse

# 6. Monitor logs
gcloud app logs read -f

# 7. Traffic splitting (canary deployment)
gcloud app services set-traffic default \
  --splits=v1=0.8,v2=0.2

# 8. Rollback if needed
gcloud app services set-traffic default \
  --splits=v1=1.0

Cost Considerations

App Engine Standard pricing includes:

  • Free tier: 28 instance hours per day
  • Beyond that: ~$0.09 per instance hour
  • Cheaper than Compute Engine for variable workloads
  • No charges when idle (scales to zero minimum instances)

Tips for cost optimization:

  • Set appropriate `max_instances` to prevent runaway scaling
  • Use `min_instances: 0` for non-critical apps (allows scaling to zero)
  • Monitor actual instance utilization
  • Use smaller instance types when possible

Key Takeaways

  • **App Engine** is ideal for web applications and APIs without complex infrastructure requirements
  • **Standard environment** is simpler and cheaper; **Flexible** provides more control
  • **app.yaml** is the configuration file that drives deployment behavior
  • **Automatic scaling** responds to traffic demand automatically
  • **Health checks** are essential for reliable deployments
  • **Multiple services** enable microservices architecture
  • **Multiple languages** are supported (Node.js, Python, Java, Go, etc.)
  • **Cloud SQL integration** provides managed database support

Next Steps

Explore advanced patterns with App Engine Scaling & Performance, or learn about Firebase Hosting for static sites and single-page applications.


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn