23

I'm trying to get Kubernetes to download images from a Google Container Registry from another project. According to the docs you should create an image pull secret using:

$ kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL

But I wonder what DOCKER_USER and DOCKER_PASSWORD I should use for authenticating with Google Container Registry? Looking at the GCR docs it says that the password is the access token that you can get by running:

$ gcloud auth print-access-token

This actually works... for a while. The problem seems to be that this access token expires after (what I believe to be) one hour. I need a password (or something) that doesn't expire when creating my image pull secret. Otherwise the Kubernetes cluster can't download the new images after an hour or so. What's the correct way to do this?

Johan
  • 37,479
  • 32
  • 149
  • 237

6 Answers6

23

This is really tricky but after a lot of trail and error I think I've got it working.

  1. Go to the Google Developer Console > Api Manager > Credentials and click "Create credentials" and create a "service account key"

  2. Under "service account" select new and name the new key "gcr" (let the key type be json)

  3. Create the key and store the file on disk (from here on we assume that it was stored under ~/secret.json)

  4. Now login to GCR using Docker from command-line:

    $ docker login -e your@email.se -u _json_key -p "$(cat ~/secret.json)" https://eu.gcr.io

This will generate an entry for "https://eu.gcr.io" in your ~/.docker/config.json file. 6. Copy the JSON structure under "https://eu.gcr.io" into a new file called "~/docker-config.json", remove newlines! For example:

  1. Base64 encode this file:

    $ cat ~/docker-config.json | base64

  2. This will print a long base64 encoded string, copy this string and paste it into an image pull secret definition (called ~/pullsecret.yaml):

apiVersion: v1
  kind: Secret
  metadata:
    name: mykey
  data:
    .dockercfg: <paste base64 encoded string here>
  type: kubernetes.io/dockercfg
  1. Now create the secret:

$ kubectl create -f ~/pullsecret.yaml 10. Now you can use this pull secret from a pod, for example:

apiVersion: v1
kind: Pod
metadata: 
  name: foo
  namespace: awesomeapps
spec: 
  containers: 
    - image: "janedoe/awesomeapp:v1"
      name: foo
  imagePullSecrets: 
    - name: mykey

or add it to a service account.

Lucas Mähn
  • 736
  • 2
  • 6
  • 19
Johan
  • 37,479
  • 32
  • 149
  • 237
  • Thanks for sharing! I got very close but made the mistake of trying to use my existing `~/.docker/config` which already had gcr.io credentials, but I believe they were tied to the wrong service account... or something. Anyway, a couple notes: when creating the new service account (Step 2) I had to choose a Role, so I picked Project > Viewer, which seems to have worked. Also, with the `base64` command, you can remove the `cat` and should add `-w 0` to avoid newlines: `base64 -w 0 ~/docker-config.json` – Tobias J Oct 19 '16 at 03:14
  • 1
    Also, it appears that they've [changed the name of the Secret type from `dockercfg` to `dockerconfigjson`](http://kubernetes.io/docs/user-guide/images/), which is what I used. I'm on k8s 1.4.0. – Tobias J Oct 19 '16 at 03:18
  • 1
    Aand one more comment... in step 5, you left out the top-level `{ "auths":` json key. Removing the newlines from that file also isn't really necessary, but you do want to narrow down your docker config to only have the auth(s) you want copied to the Kubernetes secret. – Tobias J Nov 07 '16 at 04:22
  • This works great! Thanks! On step 4. the -e flag is deprecated in version v1.11.0 https://docs.docker.com/engine/deprecated/#/e-and---email-flags-on-docker-login – Humber Jan 29 '17 at 10:38
  • 1
    on Mac, if you inspect the config the credentials might be hidden behind osxkeychain. In order to see the credentials, go into docker preferences, disable "Securely store Docker logins in max osx keychain", restart docker then follow the steps above agin. – Steve Sheldon Oct 24 '18 at 09:19
  • The "Securely store Docker logins in max osx keychain" option does not seem to exist anymore so after the docker login step I edited `~/.docker/config.json` and manually removed the line referencing oskeychain". I then ran the docker login command again and voila, the auth token appeared in `~/.docker/config.json` – jamiet Jan 25 '22 at 22:21
17

It is much easier with kubectl

kubectl create secret docker-registry mydockercfg \
  --docker-server "https://eu.gcr.io" \
  --docker-username _json_key \
  --docker-email not@val.id \
  --docker-password=$(cat your_service_account.json)

One important detail after you download your_service_account.json from google is to join all the lines in the json into one row. For this you could replace cat with paste:

  --docker-password=$(paste -s your_service_account.json)
flurdy
  • 3,782
  • 29
  • 31
Gramic
  • 416
  • 8
  • 18
5

This answer ensures that only one set of docker credentials gets included in your Kubernetes secret, and handles trimming newlines for you.

Follow the same first three steps from Johan's great answer:

  1. Go to the Google Developer Console > Api Manager > Credentials and click "Create credentials" and create a "service account key"

  2. Under "service account" select new and name the new key "gcr" (let the key type be json)

  3. Create the key and store the file on disk (from here on we assume that it was stored under ~/secret.json)

Next, run these commands to generate and inject the required Docker credentials into your cluster:

export GCR_KEY_JSON=$(cat ~/secret.json | tr -d '\n')
mv ~/.docker/config.json ~/.docker/config-orig.json
cat >~/.docker/config.json <<EOL
{
  "auths": {
    "gcr.io": {}
  }
}
EOL
docker login --username _json_key --password "$GCR_KEY_JSON" https://gcr.io
export DOCKER_CONFIG_JSON_NO_NEWLINES=$(cat ~/.docker/config.json | tr -d '\n')
mv ~/.docker/config-orig.json ~/.docker/config.json
cat >secrets.yaml <<EOL
apiVersion: v1
kind: Secret
metadata:
  name: gcr-key
data:
  .dockerconfigjson: $(echo -n ${DOCKER_CONFIG_JSON_NO_NEWLINES} | base64 | tr -d '\n')
type: kubernetes.io/dockerconfigjson

EOL
kubectl create -f secrets.yaml

When you specify Pods that pull images from GCR, include the gcr-key secret name in your spec section:

spec:
  imagePullSecrets:
    - name: gcr-key
  containers:
  - image: ...
Henrik
  • 9,714
  • 5
  • 53
  • 87
Rich Kuzsma
  • 1,557
  • 1
  • 13
  • 6
  • 2
    This helped me massively, thank you. Just a heads up for anyone else, I went through this and was scratching my head as to why it didn't work, it was because our pods run in a non-default namespace and the key needs to be in the same namespace - define the namespace in metadata section of secrets.yaml. newbie error I know, but this might save someone else the same pain. – jamiet Jan 26 '22 at 10:17
  • This is the best method for now. You can even make it into a script! – Adel Khial Jun 29 '22 at 17:01
2

You can also grant the service account your cluster runs as access to the GCS bucket:

  eu.artifacts.{project-id}.appspot.com

This answer has a few gsutil commands to make that happen.

Community
  • 1
  • 1
mattmoor
  • 1,677
  • 14
  • 9
  • Yes, the Google Cloud blog also mentions this: https://medium.com/google-cloud/using-single-docker-repository-with-multiple-gke-projects-1672689f780c – Sander Apr 26 '17 at 08:28
1

From the official ways, you can:

$ docker login -e 1234@5678.com -u _json_key -p "$JSON_KEY" https://gcr.io

Note: The e-mail is not used, so you can put whatever you want in it.

Change gcr.io to whatever is your domain shown in your Google Container Registry (e.g. eu.gcr.io).

To get that $JSON_KEY:

  1. Go to API Manager > Credentials
  2. Click "Create credentials" > Service account key:
    • Service account: New service account
      • Name: Anything you want, like Docker Registry (read-only)
      • Role: Storage (scroll down) > Storage Object Viewer
    • Key type: JSON
  3. Download as keyfile.json
  4. JSON_KEY=$(cat keyfile.json | tr '\n' ' ')
  5. Now you can use it.

Once logged in you can just run docker pull. You can also copy the updated ~/.dockercfg to preserve the settings.

Wernight
  • 36,122
  • 25
  • 118
  • 131
1

No image pull secret is needed, it can be done by an IAM configuration

I tried other answers but I can't get the Image Pull Secret approach working.

However I found that this can be done by Granting access to the Compute Engine default service account in the project where the Kubernetes cluster is. This service account was created automatically by GCP.

As described here: https://cloud.google.com/container-registry/docs/access-control#granting_users_and_other_projects_access_to_a_registry

You need to execute the following command to grant access to the Cloud Storage bucket serving the Container Registry

gsutil iam ch serviceAccount:[EMAIL-ADDRESS]:objectViewer gs://[BUCKET_NAME]

BUCKET_NAME:

artifacts.[PROJECT-ID].appspot.com for images pushed to gcr.io/[PROJECT-ID], or
[REGION].artifacts.[PROJECT-ID].appspot.com, where [REGION] is:
us for registry us.gcr.io
eu for registry eu.gcr.io
asia for registry asia.gcr.io

EMAIL-ADDRESS:

The email address of the service account called: **Compute Engine default service account** in the GCP project where the Kubernetes cluster run
Pusker György
  • 398
  • 1
  • 11