Mount a file as a volume in Cloud Run
Cloud Run has redefined the serverless paradigm a few years ago. Cloud Run has also made a promise: portability on any Kubernetes cluster with Knative installed on top of them. I demonstrated this portability which is great!
Knative and Cloud Run implement the same APIs and the same principle: You create a container, you deploy it, and it scales automatically according to your traffic.
To ensure and to achieve this scalability, Cloud Run and Knative come with a major constraint in the container paradigm
The container must be stateless. You can’t mount a volume on your container
So, today (July 2021): It’s impossible to mount a volume on Cloud Run. But….
Secret management on Cloud Run
Cloud Run hosts applications’ services and, like most of apps, they need to have access to secrets: API Keys, database credentials, private keys,…
On Google Cloud, the recommended tool to manage secrets is Secret Manager.
Cloud Run proposes a convenient integration with Secret Manager, and because Cloud Run wants to be as portable as possible on any Kubernetes cluster, it implements similar secret management features. You can load a secret value:
- Either into environment variables
- Or mount it as a volume.
Mount a secret as a volume in Cloud Run
This feature is great and compliant with Kubernetes best practices. To achieve it with Cloud Run, you need to create a secret in Secret Manager :
# Create a secret name
gcloud secrets create medium# Create a secret version
echo "my secret" | gcloud secrets versions add --data-file=- medium
Then, you can deploy your Cloud Run service with the secret mounted as a volume :
gcloud beta run deploy <SERVICE_NAME> --image <IMAGE> \
--update-secrets=/secrets/mysecret=medium:latest
And, finally, read it as any file from your service, here in Golang for example (file server.go
) :
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)var fileLocation = "/secrets/mysecret"func main() {
http.HandleFunc("/", func (w http.ResponseWriter,
r *http.Request) {
f, err :=os.Open(fileLocation)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w,err)
return
}
b, err := ioutil.ReadAll(f)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w,err)
return
}
fmt.Println(string(b))
fmt.Fprint(w,string(b))
return
})
http.ListenAndServe(":8080", nil)
}
Call your service and Yeah, you have my secret
displayed! You successfully mount a secret as a file and read it in your code!
Hey you read a secret as a file! Why not read a secret file as a file?
Mount a (secret) file in Cloud Run
Similarly as a secret value, you can create a secret from a file! Let’s load a file as a secret, like that :
gcloud secrets versions add --data-file=./server.go medium
Try again and Boom, you have mounted a file in Cloud Run!!
You can get the latest version of the secret immediately because we defined the latest
version of the medium
secret during the deployment. You can define a static version if you prefer.
Use cases and limits
Before discussing the use cases, let’s have a look at the limits:
- You can only read the file content, not write or update it, except if you create a new secret version
- Your secret content is limited to 64KiB
Because of those constraints, the best use case is to load a configuration file as a volume.
- A configuration file doesn’t need to be updated by the application
- A configuration file can contain secrets
- A configuration file is small and fits within the 64KiB constraint
Of course, you can load several (secret) files in different volumes when you deploy your Cloud Run service.
Cloud Run and Volumes
Cloud Run does not yet support the volumes, but you can simulate this support thanks to Secret Manager integration and for a specific use case. I used it for a Spring boot deployment and it’s really helpful!
It’s great for configuration, secret management and to prevent to push sensitive data in Git repository.