Chapter 1.2 - Mastering Docker - Container Operations and Management

Container Management

Think of a container as an instance of a class.

Here are some essential commands for managing containers:

Here are some common docker run commands and related operations:


Container Service Management

These commands are used for managing the lifecycle of your containers:


Entering a Container

To interact with a running container, you often need to “enter” it.

First, create a detached (daemon) Docker container:

docker run -itd my-nginx /bin/bash

Then, use docker ps to find its details:

docker ps
# Example Output:
# CONTAINER ID  IMAGE  COMMAND      CREATED          STATUS          PORTS    NAMES
# 6bd0496da64f  nginx  "/bin/bash"  20 seconds ago   Up 18 seconds   80/tcp   high_shirley

Now, you can use the docker exec command to enter the running container:

docker exec -it 6bd0496da64f /bin/bash

While other methods exist, docker exec is the recommended way to enter a Docker container, especially since Docker version 1.3 and later.

Other methods you might encounter (and why exec is preferred):


File Copying

You can easily copy files between your host machine and Docker containers.


Docker Private Registry Setup

You can set up your own private Docker registry to store and manage your Docker images. The official registry image is a simple way to achieve this. For a more robust solution with features like user authentication, web UI, and vulnerability scanning, consider a full-fledged registry management tool like Harbor.

Using registry

  1. Pull the registry image:

    docker pull registry:2
    

    (Note: 2.6.2 is an older specific version; 2 will pull the latest 2.x.x version.)

  2. Create and run the registry container:

    docker run -d \
      -p 5000:5000 \
      --restart=always \
      --name registry \
      registry:2
    

    This command runs the registry in detached mode, maps port 5000 on your host to port 5000 in the container, sets it to always restart, and names the container registry.

    To verify if the registry is running, you can access http://localhost:5000/v2/. If you get an empty JSON object ({}), it’s working. (Note: The IP 192.168.99.100 from the original text is often used in older Docker Toolbox setups. On modern Docker Desktop or Linux installations, localhost or your host’s IP address will be more relevant.)

    Customize Storage Location:

    To persist your registry data outside the container, mount a volume:

    docker run -d \
      -p 5000:5000 \
      --restart=always \
      --name registry \
      -v $HOME/docker/registry_data:/var/lib/registry \
      registry:2
    

    This mounts $HOME/docker/registry_data on your host to /var/lib/registry inside the container.

    Using a Custom Configuration File:

    You can also provide a custom config.yml:

    docker run -d -p 5000:5000 --restart=always --name registry \
        -v "$(pwd)"/config.yml:/etc/docker/registry/config.yml \
        registry:2
    

Pushing Images to Your Private Registry

  1. Pull an image from Docker Hub (if you don’t have one locally):

    docker pull nginx:latest
    
  2. Tag the image with your registry address:

    You need to tag the image with the address of your private registry, including the port. For example, if your registry is running on localhost:5000:

    docker tag nginx:latest localhost:5000/my-nginx:latest
    
  3. Push the tagged image to your private registry:

    docker push localhost:5000/my-nginx:latest
    

    You might encounter an error like Get https://localhost:5000/v1/_ping: http: server gave HTTP response to HTTPS client. This happens because Docker clients default to using HTTPS, but your local registry is running over HTTP.

    To resolve this, you have two main options:

    • Configure your registry for HTTPS (recommended for production).

    • Add your registry address to Docker’s “insecure registries” list (suitable for development/testing).

    We will use the second method for simplicity:

    Modifying Docker Daemon Configuration for Insecure Registries:

    • Linux: Edit or create /etc/docker/daemon.json.

      {
        "insecure-registries": ["localhost:5000"]
      }
      

      (Replace localhost:5000 with your actual registry address if different.)

    • macOS (Docker Desktop): Go to Docker Desktop Preferences (or Settings) -> Daemon -> Docker Engine. Add the insecure-registries entry in the JSON configuration. After modifying, click Apply & Restart.

    After modifying daemon.json, you must restart the Docker daemon for the changes to take effect:

    • Linux: sudo systemctl daemon-reload && sudo systemctl restart docker

    If you encounter an “Internal Server Error” (HTTP 500) during the push, especially on Linux, it might be due to SELinux enforcing access control. You can temporarily disable SELinux for testing:

    sudo setenforce 0
    getenforce # Should output "Permissive"
    

    Important: Disabling SELinux compromises security. For production, configure SELinux policies to allow Docker access to the registry storage.

    Stopping and Removing the registry container:

    docker container stop registry && docker container rm -v registry
    

    The -v flag removes any anonymous volumes associated with the container.

Harbor

For a more comprehensive private registry solution, Harbor is a popular choice. It provides features like a web UI, access control, replication, vulnerability scanning, and content trust. While the registry image is good for basic needs, Harbor is better suited for enterprise environments.

Other registry management tools include Humpback and Rancher (though Rancher is primarily a container management platform, not solely a registry).


Docker REST API

Docker doesn’t just work through the docker command-line interface; it also exposes a powerful REST API that allows you to programmatically control the Docker daemon over HTTP. By default, the Docker remote API listens on a Unix socket (/var/run/docker.sock) for security reasons and doesn’t expose a TCP port. To enable remote HTTP access, you need to configure the Docker daemon to listen on a TCP port. Be aware that enabling the unauthenticated TCP port (2375) is a significant security risk and should only be done in secure, isolated environments or with proper authentication and encryption (e.g., TLS).

Enabling Remote API on CentOS

  1. Edit the Docker service unit file:

    sudo vim /usr/lib/systemd/system/docker.service
    
  2. Locate the ExecStart line and add the TCP listener:

    Original:

    ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
    

    Modified to listen on TCP port 2375 on all interfaces (0.0.0.0) and retain the Unix socket:

    ExecStart=/usr/bin/dockerd -H fd:// -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375 --containerd=/run/containerd/containerd.sock
    

    For production in an internal network, you might want to specify a specific internal IP instead of 0.0.0.0 (e.g., -H tcp://10.105.3.115:2375). You can also change the port 2375 if needed.

  3. Reload systemd daemon and restart Docker service:

    sudo systemctl daemon-reload
    sudo systemctl restart docker
    
  4. Verify the port is listening:

     netstat -nutlp | grep 2375
     # Example output:
     # tcp   0   0 0.0.0.0:2375   0.0.0.0:* LISTEN    [PID]/dockerd
    

Enabling Remote API on macOS (Docker Desktop)

On Docker Desktop for Mac, you cannot directly modify the docker.service file. Instead, you can expose the Docker socket using a proxy container.

docker run -d -v /var/run/docker.sock:/var/run/docker.sock -p 127.0.0.1:2375:2375 bobrik/socat TCP-LISTEN:2375,fork UNIX-CONNECT:/var/run/docker.sock

This command runs a socat container that forwards connections from 127.0.0.1:2375 on your host to the Docker daemon’s Unix socket (/var/run/docker.sock) inside the container. This makes the API available on localhost:2375.

Testing the Remote API

Once enabled, you can test the API using curl or a web browser:

You can also use curl with the Unix socket directly (without exposing a TCP port):

curl --unix-socket /var/run/docker.sock http://localhost/info

Or simply:

Bash

docker info

Docker Run Command Options

The docker run command is highly versatile and allows you to configure nearly every aspect of a new container. Here are some commonly used options:


Uninstalling Older Docker Versions

If you need to perform a clean installation or resolve issues, you might need to remove old Docker packages.

sudo yum remove docker \
  docker-client \
  docker-client-latest \
  docker-common \
  docker-latest \
  docker-latest-logrotate \
  docker-logrotate \
  docker-selinux \
  docker-engine-selinux \
  docker-engine

This command attempts to remove various Docker-related packages commonly found on CentOS/RHEL systems.


Troubleshooting Common Docker Issues

“Create more free space in thin pool or use dm.min_free_space option to change behavior”

This error often indicates that you’re running out of space in the storage driver’s data volume (especially if you’re using devicemapper with loop-lvm).

Warning: Directly manipulating the thin pool or related storage can lead to data loss. Always back up your data before attempting advanced storage fixes.

Refer to the official Docker documentation or GitHub issues (like moby/moby#3182) for solutions, which often involve cleaning up unused images/containers/volumes (docker system prune) or reconfiguring the storage driver.

Image Pull Failures / Slow Pulls

If you’re having trouble pulling images or experiencing very slow downloads from Docker Hub, you can configure registry mirrors to pull images from closer or faster sources.

Edit the Docker daemon configuration file:

Add or modify the registry-mirrors array:

{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com",
    "https://registry.docker-cn.com"
  ],
  "insecure-registries": ["192.168.188.111:2021"]
}

(The insecure-registries part is for your private HTTP registries, as discussed earlier.)

After modifying daemon.json, remember to restart the Docker daemon for the changes to take effect.