Powered By Blogger
Showing posts with label systemd. Show all posts
Showing posts with label systemd. Show all posts

Wednesday, November 4, 2015

Run a RHEL7 docker registry in a container

I've been doing some testing and research surrounding the RHEL7 docker-registry service, a private Docker registry server, and decided that it would be a good experiment to run it as a container.

To accomplish this, I had to determine how the docker-registry package and service run, and how they are configured.

So on my RHEL7 host I started by installing the docker-registry package from the extras repo:
yum install -y docker-registry

I took a look inside the systemd service unit for docker-registry, which was located at /usr/lib/systemd/system/docker-registry.service, since all the requirements for starting the process would be held here.

Contents of /usr/lib/systemd/system/docker-registry.service:
[Unit]
Description=Registry server for Docker

[Service]
Type=simple
Environment=DOCKER_REGISTRY_CONFIG=/etc/docker-registry.yml
EnvironmentFile=-/etc/sysconfig/docker-registry
WorkingDirectory=/usr/lib/python2.7/site-packages/docker-registry
ExecStart=/usr/bin/gunicorn --access-logfile - --max-requests 100 --graceful-timeout 3600 -t 3600 -k gevent -b ${REGISTRY_ADDRESS}:${REGISTRY_PORT} -w $GUNICORN_WORKERS docker_registry.wsgi:application
Restart=on-failure

[Install]
WantedBy=multi-user.target

As you can see, there's a yaml config file and an environment file (overriding some settings from the yaml.)
There's a working directory, and a startup command.

So once I realized the /etc/sysconfig/docker-registry environment file was overriding settings in the /etc/docker-registry.yml, it was easy to move this into a Dockerfile to build out a container.

Contents of /etc/sysconfig/docker-registry:
# The Docker registry configuration file
# DOCKER_REGISTRY_CONFIG=/etc/docker-registry.yml

# The configuration to use from DOCKER_REGISTRY_CONFIG file
SETTINGS_FLAVOR=local

# Address to bind the registry to
REGISTRY_ADDRESS=0.0.0.0

# Port to bind the registry to
REGISTRY_PORT=5000

# Number of workers to handle the connections
GUNICORN_WORKERS=8

As you'll see, I move the envrionment file settings into docker ENV lines.  I am also overriding where the docker-registry stores it's data, and making the docker registry searchable.  When I run the container I'll be bind mounting a path from my docker host to the docker-registry container.  When the container is shut off, my data will still be there, and it will also come back when I restart my docker-registry container.

I'm not running yum update -y, because there's been some failing dependencies on systemd-libs packages in the latest published containers.

Contents of mydocker-registry Dockerfile:
FROM rhel7:latest
RUN yum --enablerepo=rhel-7-server-extras-rpms install docker-registry -y && \
    yum clean all;
EXPOSE 5000
ENV DOCKER_REGISTRY_CONFIG /etc/docker-registry.yml
ENV SETTINGS_FLAVOR local
ENV REGISTRY_ADDRESS 0.0.0.0
ENV REGISTRY_PORT 5000
ENV GUNICORN_WORKERS 8
ENV SEARCH_BACKEND sqlalchemy
ENV STORAGE_PATH /mnt/registry
WORKDIR /usr/lib/python2.7/site-packages/docker-registry
CMD /usr/bin/gunicorn --access-logfile - --max-requests 100 --graceful-timeout 3600 -t 3600 -k gevent -b ${REGISTRY_ADDRESS}:${REGISTRY_PORT} -w $GUNICORN_WORKERS docker_registry.wsgi:application

Build it:
docker build -t mydocker-registry .

Run it:
docker run --name docker-registry --rm -v /mypath/docker-registry-storage:/mnt/registry:Z -p 5000:5000 mydocker-registry

Now you can tag and push to it.

Tag one of your other images:
Run: 'docker images'
Note one of the id's.

docker tag <container id> localhost:5000/mynewtag

Push it:
docker push localhost:5000/mynewtag

Search for it:
docker search localhost:5000/

This should return the image you just pushed into the registry.


This is an insecure docker-registry setup, and authentication is not configured.  To connect to this docker registry from another docker host to pull or push you must make a change on that system.

To push/pull/search from another docker host:
Edit /etc/sysconfig/docker
Uncomment the line:
INSECURE_REGISTRY='--insecure-registry'

Modify the line to contain:
INSECURE_REGISTRY='--insecure-registry <the ip/hostname where your docker-registry container runs>:5000'

Save it and run: 'systemctl restart docker'

Now you can do a 'docker pull <ip/hostname where your docker-registry container runs>:5000/mynewtag'

You can also take the container one step further and configure it as a systemd service unit.

Monday, November 2, 2015

Real-world Docker Series: Run Dedicated Containers as systemd services

Since running docker run commands to start up many containers when a docker host comes online would become impractical and extremely tedious, we can move these commands into systemd service unit files.

To create your own systemd service unit files, you must store the files in /etc/systemd/system.
It's a good practice to name them by prefixing the service name with docker to easily identify what they are later.
Ex: /etc/systemd/system/docker.<containername>.service

Here is an example of a systemd service for a container:
[Unit]
Description=Nginx Container 1
After=docker.service
Requires=docker.service

[Service]
TimeoutStartSec=0
Restart=always
ExecStartPre=-/usr/bin/docker stop Cont1
ExecStartPre=-/usr/bin/docker rm Cont1
ExecStart=/usr/bin/docker run --name Cont1 --rm -v /root/cont1:/usr/share/nginx/html:Z -p 80:80 nginx

[Install]
WantedBy=multi-user.target

If you're unfamiliar with systemd services, there's a few things to notice here.
After: This makes sure the service is started after the docker service.
Requires: This will not run if there is no docker service.
TimeoutStartSec: 0 implies we will not timeout when trying to start this service
Restart: Start the service's process (ExecStart) if it ends
ExecStartPre: You can have many “pre” commands. Notice the '-' at the beginning of the command. This tells the service that if this command fails, move on and do not fail at starting the service. This is important mainly to the restart aspect of this service. The first “pre” command ensures that the container named Cont1 is stopped. The second command ensures it's removed any possible orphaned containers named Cont1.
ExecStart: This is where your docker run command goes, and is the main process that systemd will be watching.
[Install] and WantedBy are replacements for the old chkconfig runlevels. Multi-user.target is the equivalent of run level 3. When we run systemctl enable docker.cont1.service, it will be installed (enabled) at run-level 3.

Caveats:
docker stop <container name> will stop the container temporarily, but systemd will automatically restart the container based on the service we created. Test by running docker stop <container name> then run docker ps. Notice the time the container has been up.

To truly stop the running container, do so by stopping the associated service you created for it.

 systemctl stop <docker.MyContainer'sService>


This is where you may want to introduce some of the power of cgroups (control groups.)  Cgroups allow the administrator to control how much of the host's resources are taken up by child processes.  You would want to add any restrictions for the container's in its service.  This is outside the scope of this series, but you should certainly check out Red Hat's documentation on the topic of cgroups as it pertains to RHEL7, as it has changed significantly with the implementation of systemd in RHEL7.

Next: How to use selinux with docker