Using cross-project GCR images in GKE

Deploying private images from Google Container Registry into Google Container Engine is easy — if they’re in the same project. You simply list your GCR image URL in your deployment spec and GKE handles the rest of the authentication magic for you.

This is the trivial case. Many companies have a multi-project setup where build artifacts are created and hosted in one project (call it Builder), and then deployed into various environments (such as Dev, Staging, and Production), each in separate GCP projects. In such a setup, GKE does not magically handle the GCR authentication for you.

This is where kubernetes’ image pull secrets come into play. The trouble is, image pull secrets can be a bit fiddly to set up; I haven’t seen succinct instructions for setting them up specifically with GKE, hence this note. It turns out there’s a very easy but non-obvious way to do it!

In the Builder project, create a new service account (call it “gcr”). Give it Project Viewer role (to access GCR images) and download a JSON key. You’ll use this key in each of your environments.

Now in your first GKE environment, create the imagePullSecret:

kubectl create secret docker-registry gcrsecret 
—- docker-username=_json_key
—- docker-password=”$(cat <your gcr key from step 1>)”
—- docker-server=

gcrsecret is just an arbitrary name for the secret; change it to whatever you like. The docker-email arg doesn’t matter (it’s deprecated in docker login), so you can put anything here. The magic ingredient in this step is docker-username=_json_key that lets you use your service account key as a password.

You now have an imagePullSecret in your GKE cluster! At this point you can use it. This can be done either by declaring the secret on a pod spec, or by attaching it to the service account used by kubernetes. In the type of project I described above, where multiple environments are pulling only from a single GCR source, I find it cleaner to attach the secret to the service account. Use this great trick to do that in a single shot:

kubectl get serviceaccounts default -o json |
jq ‘del(.metadata.resourceVersion)’|
jq ‘setpath([“imagePullSecrets”];[{“name”:”gcrsecret”}])’ |
kubectl replace serviceaccount default -f -

Note that this assumes a few little things:

  1. That you’re using the default service account (if you don’t know, you probably are)
  2. You have jq installed (it’s awesome, you should)
  3. You’re called your secret gcrsecret in the previous step

That’s it! Your GKE cluster can now pull from the Builder project’s GCR. Just repeat for each environment that needs access.