Pull an image from a private repository within a Kubernetes container
In my previous post I used declarative configuration to deploy a sample app to Kubernetes. The declarative configuration used a YAML (Yet Another Markup Language) manifest.
That app used a simple image I had built locally. I therefore used this line within my manifest to ensure the image would not be pulled when the pod was created:
imagePullPolicy: Never
Most applications will use a private image. And of course that image will need to be stored in a repository that’s accessible to remote staging/production clusters. The imagePullPolicy: Never
line clearly could not be used.
To access it, the Kubernetes cluster needs the ability to authenticate requests to a private registry. That way its requests will be allowed and the image successfully pulled when a new container is created.
Authenticate a request for a private image
If you are using a cloud provider for both your Kubernetes cluster and image registry, that may have its own recommended method to access it (such as IAM). However in this case, my Kubernetes cluster is running locally but I want to pull an image I’ve pushed to my private remote repository in Docker Hub.
I can push and pull images to that private repository when I run the CLI. I can do that because I’ve previously run docker login
(or nerdctl login
). Providing the correct password there generates a file on my local machine (cat ~/.docker/config.json
in the case of a Mac). If you open that file you will see something like this:
{
"auths": {
"https://index.docker.io/v1/": {}
},
"credsStore": "osxkeychain"
}
In your case, that file may contain a token. In which case you could use that token directly from that JSON file: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials.
As you can see above, mine does not contain a token. The Mac is storing the credentials in the KeyChain. That’s not much help for the Kubernetes cluster. It needs its own JSON with a token so that it too can access the repository.
You could generate a new secret on the command line by providing it your Docker credentials. This example creates a secret called regcred
in the current namespace:
kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
For more on that approach, see: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#create-a-secret-by-providing-credentials-on-the-command-line
However that would mean typing your password on the command line. And it’s not entirely clear if the resulting token’s scope is limited to read-only 🤔. Kubernetes will likely only need to pull images from the registry and so there is no need to give it more access than it actually requires (your password gives you full access to both read and write).
You can manually create a token and so ensure that does have read-only access. Sign in to Docker Hub and visit https://hub.docker.com/settings/security:
Give it a name and give it read-only access:
You now need to append your Docker Hub username, a colon, and that token. Then base64-encode that, like this:
echo -n 'username:token' | base64
But Kubernetes expects a JSON file, not a string. So put that generated string in a JSON file (recall that structure from the above /.docker/config.json
):
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "theBase64EncodedStringHere"
}
}
}
… and armed with that temp.json (which you can delete afterwards) you can create a Kubernetes secret from it, as if you had that /.docker/config.json
to begin with:
kubectl create secret generic regcred --from-file=.dockerconfigjson=temp.json --type=kubernetes.io/dockerconfigjson --dry-run=client -o yaml
I used the --dry-run
flag so it did not apply it to the cluster. It’s shown on screen. That lets us copy it to our manifest:
apiVersion: v1
kind: Secret
type: kubernetes.io/dockerconfigjson
metadata:
name: regcred
data:
.dockerconfigjson: anotherLongBase64ValueHere
Note: Pull secrets are stored within a namespace. Here we did not provide a namespace and so it will be stored in the default namespace.
There is one more thing to do. Your deployment(s) do not know about that new secret and so won’t use it for pulls. So where you reference a private image, make sure to add it. For example:
imagePullSecrets:
- name: regcred # <- the name of your secret
containers:
- name: example
image: docker.io/username/private-image:latest
When you next apply the manifest, run kubectl get pods
to make sure you don’t get any errors and that your pod is successfully created. If not, check your username and token are valid for the chosen registry. This example used Docker Hub, however those run by Amazon, Google, and so on will differ.