Automatically Deploy Generative AI Go Web Application from Version Control to Cloud Run

1. Overview

Deploying a web application for the first time can be intimidating. Even after the first deployment, if the process is too much work, you might avoid deploying new versions of your application. With continuous deployment, you can automatically deploy changes of your application with ease.

In this lab, you write a web application and configure Cloud Run to automatically deploy your application when a change is made to the source code of your application. Then you modify your application and deploy it again.

What you will learn

  • Write a web application with Cloud Shell Editor
  • Store your application code in GitHub
  • Automatically deploy your application to Cloud Run
  • Add generative AI to your application using Vertex AI

2. Prerequisites

  1. If you do not already have a Google account, you must create a Google account.
    • Use a personal account instead of a work or school account. Work and school accounts may have restrictions that prevent you from enabling the APIs needed for this lab.
  2. If you do not already have a GitHub account, you must create a GitHub account

3. Project setup

  1. Sign-in to the Google Cloud Console.
  2. Enable billing in the Cloud Console.
    • Completing this lab should cost less than $1 USD in Cloud resources.
    • You can follow the steps at the end of this lab to delete resources to avoid further charges.
    • New users are eligible for the $300 USD Free Trial.
    • Attending a Gen AI for Devs event? A $1 USD credit may be available.
  3. Create a new project or choose to reuse an existing project.

4. Open Cloud Shell Editor

  1. Navigate to Cloud Shell Editor
  2. If the terminal doesn't appear on the bottom of the screen, open it:
    • Click the hamburger menu Hamburger menu icon
    • Click Terminal
    • Click New TerminalOpen new terminal in Cloud Shell Editor
  3. In the terminal, set your project with this command:
    • Format:
      gcloud config set project [PROJECT_ID]
      
    • Example:
      gcloud config set project lab-project-id-example
      
    • If you can't remember your project id:
      • You can list all your project ids with:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
        
      Set project id in Cloud Shell Editor terminal
  4. If prompted to authorize, click Authorize to continue. Click to authorize Cloud Shell
  5. You should see this message:
    Updated property [core/project].
    
    If you see a WARNING and are asked Do you want to continue (Y/N)?, then you have likely entered the project ID incorrectly. Press N, press Enter, and try to run the gcloud config set project command again.

5. Enable APIs

In the terminal, enable the APIs:

gcloud services enable \
  run.googleapis.com \
  cloudbuild.googleapis.com \
  aiplatform.googleapis.com

This command may take a few minutes to complete, but it should eventually produce a successful message similar to this one:

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.

6. Configure Git

  1. Set your global git user email:
    git config --global user.email "you@example.com"
    
  2. Set your global git user name:
    git config --global user.name "Your Name"
    
  3. Set your global git default branch to main:
    git config --global init.defaultBranch main
    

7. Write your code

To write an application in Go:

  1. Navigate to the home directory:
    cd ~
    
  2. Create the codelab-genai directory:
    mkdir codelab-genai
    
  3. Navigate to the codelab-genai directory:
    cd codelab-genai
    
  4. Initialize a go.mod file to declare our module:
    go mod init codelab-genai
    
  5. Create a main.go file:
    touch main.go
    
  6. Open the main.go file in Cloud Shell Editor:
    cloudshell edit main.go
    
    An empty file should now appear in the top part of the screen. This is where you can edit this main.go file. Show that code goes in the top section of the screen
  7. Edit main.go and paste the following code into it:
    package main
    
    import (
        "fmt"
        "log"
        "net/http"
        "os"
    )
    
    func main() {
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintln(w, "Hello, world!")
        })
    
        port := os.Getenv("PORT")
    
        if port == "" {
            port = "8080"
        }
        if err := http.ListenAndServe(":"+port, nil); err != nil {
            log.Fatal(err)
        }
    }
    
    After a few seconds, Cloud Shell Editor will save your code automatically. This code responds to http requests with our "Hello world!" greeting.

Your initial code for your application is finished and ready to be stored in version control.

8. Create a repository

  1. Return to the Cloud Shell terminal on the bottom of your screen.
  2. Ensure that you are still in the correct directory:
    cd ~/codelab-genai
    
  3. Initialize your git repository
    git init -b main
    
  4. Log in to the GitHub CLI
    gh auth login
    
    Press Enter to accept the default options and follow the instructions in the GitHub CLI tool including:
    1. What account do you want to log into? GitHub.com
    2. What is your preferred protocol for Git operations on this host? HTTPS
    3. Authenticate Git with your GitHub credentials? Y (Skip if this one does not appear.)
    4. How would you like to authenticate GitHub CLI? Login with a web browser
    5. Copy your one-time code
    6. Open https://github.com/login/device
    7. Paste your one-time code
    8. Click Authorize github
    9. Complete your login
  5. Confirm you are logged in:
    gh api user -q ".login"
    
    If you have logged in successfully, this should output your GitHub username.
  6. Create a GITHUB_USERNAME variable
    GITHUB_USERNAME=$(gh api user -q ".login")
    
  7. Confirm you have created the environment variable:
    echo ${GITHUB_USERNAME}
    
    If you have successfully created the variable, this should output your GitHub username.
  8. Create an empty GitHub repository named codelab-genai:
    gh repo create codelab-genai --private
    
    If you receive the error:
    GraphQL: Name already exists on this account (createRepository)
    
    Then you already have a repository named codelab-genai. You have two options to continue following this tutorial:
  9. Add the codelab-genai repository as the remote origin:
    git remote add origin https://github.com/${GITHUB_USERNAME}/codelab-genai
    

9. Share your code

  1. Confirm you are in the correct directory:
    cd ~/codelab-genai
    
  2. Add all files in the current directory to this commit:
    git add .
    
  3. Create the first commit:
    git commit -m "add http server"
    
  4. Push your commit to main branch of the origin repository:
    git push -u origin main
    

You can run this command and visit the resulting URL to view your application code on GitHub:

echo -e "\n\nTo see your code, visit this URL:\n \
    https://github.com/${GITHUB_USERNAME}/codelab-genai/blob/main/main.go \n\n"

10. Set up automatic deployments

  1. Leave the Cloud Shell Editor tab open. We will return to this tab later.
  2. In a new tab, visit the Cloud Run page
  3. Select the correct Google Cloud Project in the console Google Cloud Console project dropdown
  4. Click CONNECT REPO
  5. Click SET UP WITH CLOUD BUILD
    1. Select GitHub as the Repository Provider
    2. Click on the Authenticate and click on Continue.
      • If you're not logged into your GitHub account in the browser, log in with your credentials.
    3. After logging in, you'll see a message on the Cloud Run page stating The GitHub App is not installed on any of your repositories.
    4. Click on the INSTALL GOOGLE CLOUD BUILD button.
      • On the Installation Setup page, select Only select repositories and choose the codelab-genai repository which you created through CLI.
      • Click on Install
      • Note: If you have a lot of GitHub repositories, this can take a few minutes to load.
    5. Select your-user-name/codelab-genai as the Repository
    6. Leave Branch as ^main$
    7. Click Go, Node.js, Python, Java, .NET Core, Ruby or PHP via Google Cloud's buildpacks
    8. Click Save
  6. Scroll down to Authentication
  7. Click Allow unauthenticated invocations
  8. Click CREATE

Once the build finishes (which will take several minutes), run this command and visit the resulting URL to view your running application:

echo -e "\n\nOnce the build finishes, visit your live application:\n \
    "$( \
        gcloud run services list | \
        grep codelab-genai | \
        awk '/URL/{print $2}' | \
        head -1 \
    )" \n\n"

11. Change your code

  1. Return to the Cloud Shell terminal on the bottom of your screen.
  2. Ensure that you are still in the correct directory:
    cd ~/codelab-genai
    
  3. Reopen main.go in the Cloud Shell Editor
    cloudshell edit main.go
    
  4. Install the Vertex AI SDK for Go:
    go get cloud.google.com/go/vertexai/genai
    
  5. Install the Metadata library for Go to get the current project ID:
    go get cloud.google.com/go/compute/metadata
    
  6. Replace the code into your main.go file with:
    package main
    
    import (
        "context"
        "fmt"
        "log"
        "net/http"
        "os"
    
        "cloud.google.com/go/compute/metadata"
        "cloud.google.com/go/vertexai/genai"
    )
    
    func main() {
        ctx := context.Background()
        var projectId string
        var err error
        projectId = os.Getenv("GOOGLE_CLOUD_PROJECT")
        if projectId == "" {
            projectId, err = metadata.ProjectIDWithContext(ctx)
            if err != nil {
                return
            }
        }
        var client *genai.Client
        client, err = genai.NewClient(ctx, projectId, "us-central1")
        if err != nil {
            return
        }
        defer client.Close()
    
        model := client.GenerativeModel("gemini-1.5-flash-001")
    
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            animal := r.URL.Query().Get("animal")
            if animal == "" {
                animal = "dog"
            }
    
            resp, err := model.GenerateContent(
                ctx,
                genai.Text(
                    fmt.Sprintf("Give me 10 fun facts about %s. Return the results as HTML without markdown backticks.", animal)),
            )
    
            if err != nil {
                w.WriteHeader(http.StatusServiceUnavailable)
                return
            }
    
            if len(resp.Candidates) > 0 && len(resp.Candidates[0].Content.Parts) > 0 {
                htmlContent := resp.Candidates[0].Content.Parts[0]
                w.Header().Set("Content-Type", "text/html; charset=utf-8")
                fmt.Fprint(w, htmlContent)
            }
        })
    
        port := os.Getenv("PORT")
    
        if port == "" {
            port = "8080"
        }
        if err := http.ListenAndServe(":"+port, nil); err != nil {
            log.Fatal(err)
        }
    }
    

12. Redeploy

  1. Ensure that you are still in the correct directory in Cloud Shell:
    cd ~/codelab-genai
    
  2. Run these commands to commit a new version of your application to your local git repository:
    git add .
    git commit -m "add latest changes"
    
  3. Push the changes to GitHub:
    git push
    
  4. Once the build finishes, run this command and visit your deployed application:
    echo -e "\n\nOnce the build finishes, visit your live application:\n \
        "$( \
            gcloud run services list | \
            grep codelab-genai | \
            awk '/URL/{print $2}' | \
            head -1 \
        )" \n\n"
    

It may take several minutes for the build to finish before you can see your changes.

You can see the history of all revisions here: https://console.cloud.google.com/run/detail/us-central1/codelab-genai/revisions

13. (Optional) Audit your Vertex AI use

Like with other Google Cloud services you can audit Vertex AI operations. Audit logs help you answer the questions, "Who did what, where, and when?". Administrative audit logs for Vertex AI are enabled by default. To audit requests to generate content you have to To enable Data Access audit logs:

  1. In the Google Cloud console, go to the Audit Logs page:

    If you use the search bar to find this page, then select the result whose subheading is IAM & Admin.
  2. Ensure that the existing Google Cloud project is the one where you create your Cloud Run application.
  3. In the Data Access audit logs configuration table, find select Vertex AI API from the Service column.
  4. In the Log Types tab, select the Data Access audit log type "Admin Read" (ADMIN_READ).
  5. Click Save.

After enabling it you will be able to see audit logs for each invocation of the application. To see the audit logs with invocation details do the following:

  1. Return to your deployed application and refresh the page to trigger the log.
  2. In the Google Cloud console, go to the Log Explorer page:

  3. In the query window type:
    LOG_ID("cloudaudit.googleapis.com%2Fdata_access")
    protoPayload.serviceName="aiplatform.googleapis.com"
    
  4. Click Run query.

Audit logs capture the use of Vertex AI API but they do not let you observe workload related data such as prompts or response details.

14. (Optional) Increase observability of your AI workload

Audit logs do not capture workload related information. In order to increase observability of your workloads you have to explicitly log this information. You can use your favorite logging framework to do this. The following steps demonstrate one way to do it using Go's structured logging library.

  1. Reopen main.go in the Cloud Shell Editor
    cloudshell edit ~/codelab-genai/main.go
    
  2. Change your import block to include Go's structured logging and JSON libraries:
    import (
        "context"
        "encoding/json"
        "fmt"
        "log"
        "log/slog"
        "net/http"
        "os"
    
        "cloud.google.com/go/compute/metadata"
        "cloud.google.com/go/vertexai/genai"
    )
    
  3. After initializing your Vertex client (line 33), add the following lines to initialize a structured logger that uses the right fields for Google Cloud Logging:
    opts := &slog.HandlerOptions{
    	Level: slog.LevelDebug,
    	ReplaceAttr: func(group []string, a slog.Attr) slog.Attr {
            if a.Key == slog.LevelKey {
                return slog.Attr{Key: "severity", Value: a.Value}
            }
            if a.Key == slog.MessageKey {
                return slog.Attr{Key: "message", Value: a.Value}
            }
            return slog.Attr{Key: a.Key, Value: a.Value}
    	},
    }
    
    jsonHandler := slog.NewJSONHandler(os.Stdout, opts)
    slog.SetDefault(slog.New(jsonHandler))
    
  4. After checking for a response to GenerateContent (line 69) add the following lines inside the if block:
    jsonBytes, err := json.Marshal(resp)
    if err != nil {
        slog.Error("Failed to marshal response to JSON", "error", err)
    } else {
        jsonString := string(jsonBytes)
        slog.Debug("Complete response content", "json_response", jsonString)
    }
    
    This code writes to stdout information about generated content using structured logging format. A logging agent in Cloud Run captures output printed to stdout and writes this format to Cloud Logging.
  5. Reopen Cloud Shell and type the following command to ensure that you are in the correct directory:
    cd ~/codelab-genai
    
  6. Commit the changes:
    git commit -am "Observe generated content"
    
  7. Push changes to GitHub to trigger the redeployment of the modified version:
    git push
    

After the new version is deployed you can observe the debug information about calls to Vertex AI.

To see your application logs do the following:

  1. In the Google Cloud console, go to the Log Explorer page:

  2. In the query window type:
    LOG_ID("run.googleapis.com%2Fstdout")
    severity=DEBUG
    
  3. Click Run query.

The result of the query shows logs with prompt and Vertex AI response including "safety ratings" that can be used for monitoring safety practices

15. (Optional) Clean up

While Cloud Run does not charge when the service is not in use, you might still be charged for storing the container image in Artifact Registry. You can delete your Cloud project to avoid incurring charges. Deleting your Cloud project stops billing for all the resources used within that project.

If you would like, delete the project:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

16. Congratulations

In this lab, you wrote a web application and configured Cloud Run to automatically deploy your application when a change was made to the source code of your application. Then you modified your application and deployed it again.

If you enjoyed this lab, you can try it again in another coding language or framework:

If you are interested in being included in a user experience (UX) research study to improve the products you worked with today, register here.

Here are some options for continuing your learning: