If you don't want to use Docker Inc.'s hub service to store either public images for free or private images for money, they packaged their registry application as a container image that you can use to run a private instance. It's a really good example of a containerized application that is simple yet very powerfull, and has a lot of configuration options to allow a wide range of setupds.
I want to share my experiments, problems and most importantly solutions I ran into so it may be easier for you to run one (or easier for me to run another one, later).
Run a local registry
Go to https://github.com/docker/docker-registry, read the readme. The fastest way to get a registry running is:
- install docker
- run the registry
docker run -p 5000:5000 -e GUNICORN_OPTS=[--preload] registry
Or if you want to run it in "detached" mode, add --detach (or -d):
docker run -p 5000:5000 -e GUNICORN_OPTS=[--preload] -d registry
If you're using a local docker, then the registry will be available on localhost:5000. If you're using boot2docker, then the registry will of course be served on port 5000 of the docker server virtual machine, and you can get its IP by running boot2docker ip 2>/dev/null.
The README does not state that the --preload gunicorn option should be added, but you most probably need it.
Ensure "latest" is what you think
To make sure you're realy using the "latest" repository, I'd avise to pull explicitely, and also specify a version other than "latest".
I don't know if options exist to specify a signed version, for example, specify "701fc2aebfb2" as the correct "0.9.0" version, just in case the image maintainer change the container and release a new version under the same name. Who knows?
docker pull registry:0.9.0 docker run -p 5000:5000 registry:0.9.0
It seems that first time I tried it, I got stuck running an "old latest" container. As though it was not the main reason of the problems I were facing, it can be disturbing when you realise that the hash of your current image is not the one claimed by docker hub's registry.
Errors while running migrations
OperationalError: (OperationalError) table version already exists u'\nCREATE TABLE version (\n\tid INTEGER NOT NULL, \n\tPRIMARY KEY (id)\n)\n\n' ()
Image can use a "preload" gunicorn worker to avoid race conditions of multiple workers trying to run sql migrations. I don't even know why this is not default, as the problem seems to appear systematically without (maybe it does not exist if you're using a link to another container for database, or a database server).
docker run -p 5000:5000 -e GUNICORN_OPTS=[--preload] registry
Just curl or open in a browser your registry "/" to be sure everything is good. You should read something like ""docker-registry server"".
You can also open the "/_ping" or "/v1/_ping" path, and read a lot of information about the running environment. This is because the registry is currently running in "dev" flavor that set up DEBUG flag to True by default. Don't worry, production server won't expose anything. If you wanna try, you can run it with -e DEBUG=False and request "/_ping". Woot, nothing anymore.
Now just push something to this new local repo, because that's what we want to do at the end of the day.
docker tag registry:0.9.0 192.168.59.103:5000/myown/registry:0.9.0 docker push 192.168.59.103:5000/myown/registry:0.9.0
If you're using docker >= 1.3.1, which you should, you won't be allowed to push right now. Please see «Allowing an insecure registry endpoint» below.
Docker is using special image names to specify "remotes" (as in git terminology). There are discussions whether this is good or bad, and I hope that a real "remote" concept would be added to docker one day, but right now, that's the way, baby.
I'm using 192.168.59.103:5000 as my own remote because I did use boot2docker, and the IP is docker host virtual machine. If you're running docker locally (linux box, probably), then your host will be localhost.
Allowing an insecure registry endpoint
Since docker 1.3.1, docker won't allow you to push to a repository without certificate-verified HTTPS endpoints. That's a bit harsh, but you can workaround this by explicitely allowing an "insecure endpoint". Insecure endpoints will fallback to HTTP if no HTTPS termination is found.
If you try anyway, you'll get the following message:
FATA Error: Invalid registry endpoint https://192.168.0.103:5000/v1/: Get https://192.168.0.103:5000/v1/_ping: dial tcp 192.168.0.103:5000: i/o timeout. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry 192.168.0.103:5000` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/192.168.0.103:5000/ca.crt
Hopefully, an easy solution can help you workaround this until we setup the HTTPS endpoint.
For linux docker users
Open /etc/docker/default and add the following at the end:
DOCKER_OPTS="$DOCKER_OPTS --insecure-registry 192.168.0.0/16"
(of course, this is not exact as you most probably don't access your local box under the ip my virtualbox currently use for boot2docker ...)
For boot2docker users
boot2docker ssh sudo vi /var/lib/boot2docker/profile
Add the following at the end of the file (that you may very well be creating right now):
Exit the boot2docker console and restart boot2docker.
boot2docker stop boot2docker start
You can now push, for real.
Wrapping it up
You know how to...
- Build a local container running the registry.
- Push and pull from there.
Where are the data?
Docker registry uses two kind of data backends: a search backend, and a data storage backend. We used the defaults here (available in https://github.com/docker/docker-registry/blob/master/config/config_sample.yml):
- Search backend is sqlalchemy+sqlite3
search_backend: _env:SEARCH_BACKEND:sqlalchemy sqlalchemy_index_database: _env:SQLALCHEMY_INDEX_DATABASE:sqlite:////tmp/docker-registry.db
- Data storage backend is in the local filesystem.
storage: local storage_path: _env:STORAGE_PATH:/tmp/registry
Data storage is not persistent unless you run the container with a volume for the storage path (-v /tmp/registry:/tmp/registry). If you destroy and create your container again, you'll loose all the data.