Small Docker Notes

From PaskvilWiki
Jump to: navigation, search

Note - vm and container is used below interchangeably.

Installation and Verification

  # install Docker
$ curl -fsSL https://get.docker.com/ | sh

  # add your-user to docker group to be able to run docker as non-root
$ usermod -aG docker your-user
  # you'll need to log out and in after this, or run all docker commands as root

  # verify the installation
$ docker run hello-world
  # this should download image from docker hub, and print "Hello from Docker." message

As a rule of thumb: `docker` is the "super-system" used to manage docker containers (and more). `docker-compose` does the same tasks as docker, but only on containers defined in docker-compose.yml file in current directory. So, `docker ps -a` will list all containers, `docker-compose ps` will list info about all containers from local docker-compose.yml file.

If something goes wrong, check

$ docker-compose logs

or

$ docker logs container-id

Stack in Separate Containers

Let's create few containers to hold different parts of the stack:

  • nginx
  • php-fpm
  • mysql
  • data volume for mysql
  • kyoto tycoon with python
  • data volume for kyoto tycoon

First, create config file for docker compose, called docker-compose.yml .

nginx:
    # based on latest nginx image
    image: nginx:latest
    ports:
        # map vm's port 80 to local port 8080
        - 8080:80

Now we have defined our first image, let's get it running:

$ docker-compose up -d

  # you should be able to reach the nginx on localhost:8080 now;
  # you can always check running containers, incl. ports map, with
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                           NAMES
e795e45c3e6b        nginx:latest        "nginx -g 'daemon off"   24 hours ago        Up 24 hours         443/tcp, 0.0.0.0:8080->80/tcp   dockertut_nginx_1

  # using container ID or name from `docker ps`, you can get lots of useful info with
$ docker inspect {container-id|name}

Adding php-fpm

Let's now add VM for php-fpm, and edit the docker-compose.yml like this:

nginx:
    build:
        # don't use image, build based on ./docker/nginx/Dockerfile
        ./docker/nginx/
    ports:
        # vm's port 80 will be available as 8080 on localhost
        - 8080:80
    links:
        # has access to 'php' vm
        - php
    volumes:
        # mount current directory as /var/www/html inside the container
        - .:/var/www/html

php:
    image: php:5.5-fpm
    expose:
        # expose port 9000 only to other vm's, not host machine like the 'ports' command
        - 9000
    volumes:
        - .:/var/www/html

Now the nginx image is based on Dockerfile in ./docker/nginx/ folder, which should look like this:

FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/default.conf

See official Dockerfile reference for details.

This will build vm from nginx:latest image, and copy ./docker/nginx/default.conf to /etc/nginx/conf.d/ in the vm. Create this file with the following content:

server {
    listen 80 default_server;
    root /var/www/html;
    index index.html index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log off;
    error_log  /var/log/nginx/error.log error;

    sendfile off;

    client_max_body_size 100m;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_intercept_errors off;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;
    }

    location ~ /\.ht {
        deny all;
    }
}

Note that root /var/www/html; will actually use the mount of our local directory.

Also note fastcgi_pass php:9000; that uses the link created using links: key in the docker-compose.yml.

Now, let's just add index.php in the root folder (where docker-compose.yml resides):

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Hello World!</title>
    </head>
    <body>
        <?php echo "this is php!"; ?>                                                                                                                           
    </body>
</html>

Let's start the images; this time, php image should be pulled as well:

$ docker-compose up -d
Pulling php (php:5.5-fpm)...
5.5-fpm: Pulling from library/php
efd26ecc9548: Already exists                  <-- the nginx image, already pulled
a3ed95caeb02: Download complete
...

If you get "Service 'nginx' needs to be built, but --no-build was passed." message, run `docker-compose build` and again `docker-compose up -d`. Verify all is OK and started:

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                           NAMES
fd04b796e34f        dockertut_nginx     "nginx -g 'daemon off"   4 seconds ago       Up 4 seconds        443/tcp, 0.0.0.0:8080->80/tcp   dockertut_nginx_1
402fb35b762e        php:5.5-fpm         "php-fpm"                5 seconds ago       Up 4 seconds        9000/tcp                        dockertut_php_1

Note that nginx's image name has changed, since we have built a new one.

Now, when you open localhost:8080, you should get "this is php!" message. Open the index.php file, and edit it; the change should be immediately reflected. Sweet!

Data Volumes

Both the nginx and php vm's now use local folder through the volumes directive.

Better yet is to have a separate container; we'll build this container based on some image we're already using (to avoid having yet another image downloaded), but it won't be running - it'll just sit there, collecting data. Change docker-compose.yml like this:

nginx:
    build:
        ./docker/nginx/
    ports:
        - 8080:80
    links:
        - php
    volumes_from:
        - app

php:
    image: php:5.5-fpm
    expose:
        - 9000
    volumes_from:
        - app

app:
    # it's good practice to "reuse" some image, not to have yet another one pulled
    image: php:5.5-fpm
    volumes:
        - .:/var/www/html
    # container won't run - it'll execute `true` and sit there, collecting data
    command: "true"

Let's check all is OK:

$ docker-compose up -d
Creating dockertut_app_1...
Recreating dockertut_php_1...
Recreating dockertut_nginx_1...

  # list all containers (note that 'app' container is not listed in `ps`, since it's not running):
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS                           NAMES
af0110364abf        dockertut_nginx     "nginx -g 'daemon off"   2 minutes ago       Up 2 minutes               443/tcp, 0.0.0.0:8080->80/tcp   dockertut_nginx_1
94a1c3d317dd        php:5.5-fpm         "php-fpm"                2 minutes ago       Up 2 minutes               9000/tcp                        dockertut_php_1
861cca283ceb        php:5.5-fpm         "true"                   2 minutes ago       Exited (0) 2 minutes ago                                   dockertut_app_1

and check that localhost:8080 is still responding.

Adding MySQL

Update the 'php' section:

php:
    build:
        ./docker/php/
    expose:
        - 9000
    links:
        - mysql
    volumes_from:
        - app

And add the following:

mysql:
    image: mysql:latest
    volumes_from:
        - mysqldata
    # set environment variables                                                                                                                                 
    environment:
        MYSQL_ROOT_PASSWORD: secret
        MYSQL_DATABASE: test
        MYSQL_USER: test
        MYSQL_PASSWORD: test

mysqldata:
    image: mysql:latest
    volumes:
        # the /var/lib/mysql will be present in container, and "somewhere" on host
        - /var/lib/mysql
    command: "true"

And, create ./docker/php/Dockerfile:

FROM php:5.5-fpm
RUN docker-php-ext-install pdo_mysql

Now we're all set to `docker-compose build` and `docker-compose up -d`. Let's check what containers we have now:

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                           NAMES
e43265be9680        dockertut_nginx     "nginx -g 'daemon off"   16 seconds ago      Up 14 seconds               443/tcp, 0.0.0.0:8080->80/tcp   dockertut_nginx_1
48cd3b544fb1        dockertut_php       "php-fpm"                19 seconds ago      Up 17 seconds               9000/tcp                        dockertut_php_1
0e4f40d9ae8b        mysql:latest        "docker-entrypoint.sh"   21 seconds ago      Up 20 seconds               3306/tcp                        dockertut_mysql_1
963440e29d89        mysql:latest        "docker-entrypoint.sh"   22 seconds ago      Exited (0) 20 seconds ago                                   dockertut_mysqldata_1
1458644df104        php:5.5-fpm         "true"                   4 minutes ago       Exited (0) 4 minutes ago                                    dockertut_app_1

Now that all is set up, we can check where did docker mount the folder on host, that is seen as /var/lib/mysql in mysqldata instance:

$ docker inspect dockertut_mysqldata_1
...
        "Mounts": [
            {
                "Name": "d1352deaff99b25ee68bc53c07150b12b5eb7028b6182f1c97f71f7e21c8ee1d",
                "Source": "/var/lib/docker/volumes/d1352deaff99b25ee68bc53c07150b12b5eb7028b6182f1c97f71f7e21c8ee1d/_data",
                "Destination": "/var/lib/mysql",
...

The Source is the folder on host that docker mounted as Destination in VM.

If you want all volumes removed when deleting docker container, use

$ docker rm -v container-id

Otherwise, the volumes will stay on your disk, taking up space.

You can also list all volumes registered by docker:

$ docker volume ls

and use this to remove all "dangling" volumes:

$ docker volume rm $(docker volume ls -qf dangling=true)

Let's check if mysql is up and running, through terminal. You can open interactive terminal in image like this:

$ docker exec -it dockertut_mysql_1 /bin/bash
root@0e4f40d9ae8b:/# mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
...
mysql> _

Neat, huh? The -i means to run interactive command, -t to run in terminal.

Manage Docker as non-root User

By creating docker group, and adding users to it, you may select users that can manage docker daemon and processes:

$ sudo groupadd docker
$ sudo usermod -aG docker $USER

Afterwards, restart your machine (or, log out and in, and stop and start the docker daemon), and you should be able to control docker without the need for sudo.