For local dev, always set these variables:
gcloud auth login # authenticate yourself to gcloud
# setup ADC so any locally running application can access Google APIs
# Note that credentials will be saved to ~/.config/gcloud/application_default_credentials.json
gcloud auth application-default login
# Set these manually...
export PROJECT_ID="<Your Google Cloud Project ID>"
export REGION="<your region>"
export MY_ORG="<enter your org domain>"
export DOMAIN_NAME="<enter application domain name>"
# Or load from .env
source ../../.env
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export LOG_LEVEL='DEBUG'
export VERSION="0.1"
export REPO=video-intelligence
export SERVICE_NAME=video-intelligence
# Check we're in the correct project
gcloud config list project
gcloud config set project $PROJECT_ID
# Enable APIs
gcloud services enable \
cloudbuild.googleapis.com \
run.googleapis.com \
logging.googleapis.com \
storage-component.googleapis.com \
aiplatform.googleapis.com
# Allow Compute Engine default service account to build with Cloud Build
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:[email protected] \
--role="roles/cloudbuild.builds.builder"
# Allow Compute Engine default service account to access GCS Cloud Build bucket
# Not needed if we use roles/cloudbuild.builds.builder
# gcloud projects add-iam-policy-binding $PROJECT_ID \
# --member="serviceAccount:${PROJECT_NUMBER}[email protected]" \
# --role="roles/storage.admin"
# Grant the required role to the principal
# that will attach the service account to other resources.
# Here we assume your developer account is a member of the gcp-devops group.
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="group:gcp-devops@$MY_ORG" \
--role="roles/iam.serviceAccountUser"
# Allow service account impersonation
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="group:gcp-devops@$MY_ORG" \
--role=roles/iam.serviceAccountTokenCreator
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="group:gcp-devops@$MY_ORG" \
--role roles/cloudfunctions.admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="group:gcp-devops@$MY_ORG" \
--role roles/run.admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="group:gcp-devops@$MY_ORG" \
--role="roles/iap.admin"
# Setup Python environment and install dependencies
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Local streamlit app
streamlit run app.py --browser.serverAddress=localhost
# To build as a container image
docker build -t $SERVICE_NAME:$VERSION .
# To run as a local container
# We need to pass environment variables to the container
# and the Google Application Default Credentials (ADC)
docker run --rm -p 8080:8080 \
-e PROJECT_ID=$PROJECT_ID -e REGION=$REGION \
-e LOG_LEVEL=$LOG_LEVEL \
-e GOOGLE_APPLICATION_CREDENTIALS="/app/.config/gcloud/application_default_credentials.json" \
--mount type=bind,source=${HOME}/.config/gcloud,target=/app/.config/gcloud \
$SERVICE_NAME:$VERSION
# One time setup - create a GAR repo
gcloud artifacts repositories create "$REPO" \
--location="$REGION" --repository-format=Docker
# Allow authentication to the repo
gcloud auth configure-docker "$REGION-docker.pkg.dev"
# Every time we want to build a new version and push to GAR
# This will take a couple of minutes
gcloud builds submit \
--tag "$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/$SERVICE_NAME:$VERSION"
Public service with no authentication:
# Deploy to Cloud Run - this takes a couple of minutes
# Set max-instances to 1 to minimise cost
gcloud run deploy "$SERVICE_NAME" \
--port=8080 \
--image="$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/$SERVICE_NAME:$VERSION" \
--max-instances=1 \
--allow-unauthenticated \
--region=$REGION \
--platform=managed \
--project=$PROJECT_ID \
--set-env-vars=PROJECT_ID=$PROJECT_ID,REGION=$REGION,LOG_LEVEL=$LOG_LEVEL
APP_URL=$(gcloud run services describe $SERVICE_NAME --platform managed --region $REGION --format="value(status.address.url)")
echo $APP_URL
This time, the service will require authentication. It will require a service account that is authorised.
# Deploy to Cloud Run - this takes a couple of minutes
gcloud run deploy "$SERVICE_NAME" \
--port=8080 \
--image="$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/$SERVICE_NAME:$VERSION" \
--max-instances=1 \
--no-allow-unauthenticated \
--region=$REGION \
--platform=managed \
--project=$PROJECT_ID \
--set-env-vars=PROJECT_ID=$PROJECT_ID,REGION=$REGION,LOG_LEVEL=$LOG_LEVEL
APP_URL=$(gcloud run services describe $SERVICE_NAME --platform managed --region $REGION --format="value(status.address.url)")
### Create the External Application Load Balancer
```bash
export SVC_LB_IP="lb-ip"
export SVC_CERT="svc-cert"
export SERVERLESS_NEG="$SERVICE_NAME-serverless-neg"
export BACKEND_SERVICE="$SERVICE_NAME-backend-service"
export URL_MAP="lb-url-map"
export TARGET_HTTP_PROXY="lb-target-proxy"
export FORWARDING_RULE="lb-forwarding-rule"
# Create global static external IP address
gcloud compute addresses create $SVC_LB_IP \
--network-tier=PREMIUM \
--ip-version=IPV4 \
--global
# Or regional
gcloud compute addresses create regional-lb-ip \
--project=$PROJECT_ID \
--network-tier=STANDARD \
--region=$REGION
# Check it and get IP
# We'll create a DNS A record pointing to this IP address later
gcloud compute addresses describe $SVC_LB_IP \
--format="get(address)" \
--global
# Create a managed SSL certificate
gcloud compute ssl-certificates create $SVC_CERT \
--description="video-intel-alb-cert" \
--domains=$DOMAIN_NAME \
--global
# Check the status of the certificate
gcloud compute ssl-certificates list --global
# Create a serverless NEG for the serverless Cloud Run service
gcloud compute network-endpoint-groups create $SERVERLESS_NEG \
--region=$REGION \
--network-endpoint-type=serverless \
--cloud-run-service=$SERVICE_NAME
# Create ALB backend service
gcloud compute backend-services create $BACKEND_SERVICE \
--load-balancing-scheme=EXTERNAL \
--global
# Add the serverless NEG as a backend service
gcloud compute backend-services add-backend $BACKEND_SERVICE \
--global \
--network-endpoint-group=$SERVERLESS_NEG \
--network-endpoint-group-region=$REGION
# Create URL map to route incoming requests to the backend service
gcloud compute url-maps create $URL_MAP \
--default-service $BACKEND_SERVICE
# Create a target HTTPS proxy to route requests to your URL map
gcloud compute target-https-proxies create $TARGET_HTTP_PROXY \
--ssl-certificates=$SVC_CERT \
--url-map=$URL_MAP
# Create a forwarding rule to route incoming requests to the proxy
gcloud compute forwarding-rules create $FORWARDING_RULE \
--load-balancing-scheme=EXTERNAL \
--network-tier=PREMIUM \
--address=$SVC_LB_IP \
--target-https-proxy=$TARGET_HTTP_PROXY \
--global \
--ports=443
If we recheck the ssl certificate, it should be active within a few minutes.
# Check the status of the certificate
gcloud compute ssl-certificates list --global
In addition to requiring an authorised user, this configuration prevents the service from even being exposed to the Internet:
gcloud run services update $SERVICE_NAME \
--ingress internal-and-cloud-load-balancing \
--region=$REGION
export IAP_SA="service-$PROJECT_NUMBER@gcp-sa-iap.iam.gserviceaccount.com"
# Enable the IAP API
gcloud services enable iap.googleapis.com
# Create the required IAP service account
gcloud beta services identity create --service=iap.googleapis.com --project=$PROJECT_ID
# Grant the invoker permission to the service account
gcloud run services add-iam-policy-binding $SERVICE_NAME \
--member="serviceAccount:$IAP_SA" \
--region=$REGION \
--role="roles/run.invoker"
gcloud compute backend-services update $BACKEND_SERVICE --global --iap=enabled
Now in Google Cloud Console: IAP, enable IAP on the backend service. Configure the Consent Screen.
Grant IAP-secured web app user
to your user(s) or group(s).
We can do this from the Cloud Console, or like this:
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="group:gcp-devops@$MY_ORG" \
--role="roles/iap.httpsResourceAccessor"
gcloud beta run services update $SERVICE_NAME \
--region=$REGION --cpu-boost
# Let's set logging level to INFO rather than DEBUG
export LOG_LEVEL='INFO'
export VERSION="0.3"
gcloud builds submit \
--tag "$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/$SERVICE_NAME:$VERSION"
# Deploy to Cloud Run - this takes a couple of minutes
gcloud run deploy "$SERVICE_NAME" \
--project=$PROJECT_ID \
--port=8080 \
--image="$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/$SERVICE_NAME:$VERSION" \
--max-instances=1 \
--no-allow-unauthenticated \
--region=$REGION \
--platform=managed \
--ingress internal-and-cloud-load-balancing \
--cpu-boost \
--set-env-vars=PROJECT_ID=$PROJECT_ID,REGION=$REGION,LOG_LEVEL=$LOG_LEVEL