-
Overview
- Services must listen for requests on the port defined by the PORT environment variable
- The service must be stateless. Service cannot rely on a persistent local state
- The service must not perform background activities outside the scope of request handling
- Cloud Run allows code to be written in the programming language
- Use a web server to listen on the required port, and process and route incoming requests
- To deploy to Cloud Run, users need to provide a container image
- A container image is a packaging format that includes code, its packages, any needed binary dependencies, the operating system to use, and anything else needed to run your service
- A file named Dockerfile is commonly used to declare how to build the container image
- Custom binaries must be compiled for Linux ABI x86_64
-
Writing services
- When an application running on Cloud Run finishes handling a request, the container instance's access to CPU will be disabled or severely limited
- Do not start background threads or routines that run outside the scope of the request handlers
- Running background threads can result in unexpected behavior because any subsequent request to the same container instance resumes any suspended background activity
- Background activity is anything that happens after HTTP response has been delivered
- Review code to make sure all asynchronous operations finish before a response is delivered
- Check logs for background activity that maybe not readily apparent, and for anything that is logged after the entry for the HTTP request
- In the Cloud Run (fully managed) environment disk storage is an in-memory filesystem
- Files written to disk consume memory otherwise available to service, and can persist between invocations
- Failing to delete files can eventually lead to an out-of-memory error and a subsequent cold start
- Handle all exceptions and do not let service crash on errors
- A crash leads to a cold start while traffic is queued for a replacement container instance
-
Optimizing performance
- Because container instances are scaled as needed, a typical method to improve performance is to initialise the execution environment completely
- This kind of initialisation is called "cold start"
- If a client request triggers a cold start, the container instance startup results in additional latency
- Optimizing for service startup speed minimizes the latency that delays a container instance from serving requests
- When using a dynamic language with dependent libraries, such as importing modules in Node.js, the load time for modules adds latency during a cold start
- Reduce startup latency by minimize the number and size of dependencies to build a lean service
- Reduce startup latency by lazily loading code that is infrequently used, if language supports it
- Reduce startup latency by using code-loading such as PHP's composer autoloader optimization
- In Cloud Run, users cannot assume that service state is preserved between requests
- Cloud Run reuses individual container instances to serve ongoing traffic, so users can declare a variable in global scope to allow its value to be reused in subsequent invocations
- Whether any individual request receives the benefit of this reuse cannot be known ahead of time
- Users can also cache objects in memory if they are expensive to recreate on each service request
- Moving this from the request logic to global scope results in better performance
- The initialization of global variables always occurs during startup, which increases cold start time
- Use lazy initialization for infrequently used objects to defer the time cost and decrease cold start times
- A larger container image size increases security vulnerability because more code is a larger attack surface
- Large images slow build time for container image while many files are downloaded
- Large images results in slower deployment time for service as the container image is prepared for use in a new revision
- It increases network egress costs with Container Registry if container storage bucket is geographically distant from service region
- On Cloud Run, the size of container image does not affect cold start or request processing time and does not count towards the available memory of container
- To build a minimal container, consider working off a lean base image such as alpine, distroless or scratch
- Ubuntu is larger in size, but is a commonly used base image with a more complete out-of-box server environment
- If service has a tool-heavy build process consider using multi-stage builds to keep container light at run time
- Cloud Run supports configurable concurrency
- Cloud Functions does not support concurrency
- Enable a service's container instances to serve multiple requests simultaneously, that is, "concurrently"
- The number of concurrent requests that each container instance can serve is limited by the technology stack and the use of shared resources like global variables and database connections
- Each service request requires some amount of additional memory
- When adjusting concurrency up or down, adjust memory limit as well
- To leverage mutable global state in a concurrent context, take extra steps in code to ensure this is done safely
- Minimize contention by limiting global variables to one-time initialization and reuse as described above under Performance
- When using mutable global variables in a service that serves multiple requests at the same time, use locks or mutexes to prevent race conditions
-
Container security
- Use actively maintained and secure base images such as Container Registry's managed base images. or Docker Hub's official images
- Apply security updates to services by regularly rebuilding container images and redeploying your services
- Include in the container only what is necessary to run service.
- Extra code, packages, and tools are potential security vulnerabilities
- Implement a deterministic build process that includes specific software and library versions
- This prevents unverified code from being included in container
- Set container to run as a user other than root with the Dockerfile USER statement
- Some container images may already have a specific user configured
- Enable the Container Registry image vulnerability scanner for security scanning of container images stored in the Container Registry
- If using Cloud Run for Anthos on Google Cloud, use Binary Authorization to ensure only secure container images are deployed