Translate

понеділок, 20 квітня 2015 р.

Docker. Setup and configuration

Останнім часом дуже часто пишуть про Docker та його можливості, дехто навіть приписує йому мало не магічні вміння. Тож у цій статті спробуємо розібратись, що таке Docker і чи потрібно все кидати та мігрувати на нього.

Docker - це один із різновидів контейнерної віртуалізації. Контейнерна віртуалізація не використовує віртуальні машини, як у випадку з повною віртуалізацією, а створює віртуальне середовище з власним простором процесів та мережевим стеком. Всі контейнери хосту використовують один екземпляр ядра хостової системи.

Головними перевагами контейнерної віртуалізації є: зручність у адмініструванні, щільніше розміщення віртуальних контейнерів в хост-системі, дещо краща продуктивність в порівнянні з технологіями повної віртуалізації та економніше використання місця на дисковій системі.

Docker - дуже активний та популярний проект, нові версії його виходять мало не щомісяця. Перші релізи Docker працювали з LXC, проте зараз проект використовую власну бібліотеку для роботи з cgroups, namespaces та ін, за допомогою чого відбуваються обмеження процесів і середовищ їх виконання. Говорять, що власна бібліотека для роботи з вищенаведеними системами поки не підтримує всіх можливостей LXC (лімітування використання диску і т.п.), тому можливо варто буде перемкнути драйвер Docker.

Docker використовує клієнт-серверну архітектуру. Docker-клієнт спілкується з демоном Docker, який бере на себе створення, запуск, розподіл контейнерів. Обидва, клієнт і сервер можуть працювати як на одній системі, так і на різних. Клієнт і сервер спілкуються через сокет або через RESTful API.

Мабуть головна особливість Docker в тому, що він зберігає зміни контейнерів в Git репозиторії, попередні ревізії яких можуть бути батьківськими до декількох образів. На практиці це значно економить дисковий простір, робить систему бекапів зручнішою та старт нових контейнерів дуже швидким. Кожну виконану дію (установку нових пакетів, редагування конфігураційних файлів та ін.) можна закомітити в репозиторій і повертатись за потреби до попередніх станів чи створювати кожен новий контейнер вже з цими змінами.
Також є можливість створити власний Докер-регістр, що може слугувати репозиторієм образів для всіх хостів.

Спочатку розповім про те, як встановити Docker. Для установки останньої версії додамо репозиторій та ключ до нього:

# echo deb https://get.docker.com/ubuntu docker main > /etc/apt/sources.list.d/docker.list

# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9

Та встановимо пакет із залежностями до нього:

# aptitude update
# aptitude install lxc-docker
The following NEW packages will be installed:
  aufs-tools{a} lxc-docker lxc-docker-1.5.0{a} 

AUFS - модуль, що забезпечить монтування образів Docker, що у власну чергу складаються з git-ревізій (я писав про це трохи вище).

Останні версії Docker є незалежними від напрацювань проекту LXC і назва пакету та процес установки дещо змінились:

# apt-get update
# apt-get install apt-transport-https ca-certificates

# apt-key adv \
               --keyserver hkp://ha.pool.sks-keyservers.net:80 \
               --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

# echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" | tee /etc/apt/sources.list.d/docker.list

# apt-get update
# apt-get install docker-engine
The following NEW packages will be installed:
  aufs-tools cgroup-lite docker-engine libltdl7 libsystemd-journal0

Репозиторій для кожного релізу Ubuntu - окремий і відповідно замість ubuntu-xenial можуть бути ubuntu-precise, ubuntu-trusty, ubuntu-wily. Тут можна знайти деталі https://docs.docker.com/engine/installation/linux/ubuntulinux/

Запускаємо Docker:

# service docker start

Запустивши демон без ключів, можно побачити які опції та команди підтримує клієнт:

# docker
Usage: docker [OPTIONS] COMMAND [arg...]

A self-sufficient runtime for linux containers.

Options:
  --api-cors-header=                   Set CORS headers in the remote API
  -b, --bridge=                        Attach containers to a network bridge
...

Commands:
    attach    Attach to a running container
    build     Build an image from a Dockerfile
...

Run 'docker COMMAND --help' for more information on a command.

Або просто:

# man docker

Версію Docker та компонентів, з якими він працює, можна побачити наступним чином:

# docker version
Client version: 1.5.0
Client API version: 1.17
Go version (client): go1.4.1
Git commit (client): a8a31ef
OS/Arch (client): linux/amd64
Server version: 1.5.0
Server API version: 1.17
Go version (server): go1.4.1
Git commit (server): a8a31ef

Отже, я використовую останню версію - 1.5.0. Проте лише рік тому останньою версією була 0.8.1.

Статистику по Docker можна побачити так:

# docker info
Containers: 0
Images: 0
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 5
Execution Driver: native-0.2
Kernel Version: 3.13.0-49-generic
Operating System: Ubuntu 14.04.2 LTS
CPUs: 1
Total Memory: 2.939 GiB
Name: docker
ID: AZU6:SV4B:KG4R:CYC3:XCLY:Y4KQ:NFUY:SMQQ:EYO5:SYER:4Z7J:BZMB
WARNING: No swap limit support

Кількість контейнерів, образів, версія ядра ін. Останнє попередження інформує про те, що хост поки не підтримує лімітування пам’яті. Щоб зняти це обмеження необхідно відредагувати параметри запуску ядра в GRUB.

Для першого контейнеру оберемо образ Ubuntu. Ось так можна побачити всі версії, що доступні в Docker Registry Hub:

# sudo docker search ubuntu
NAME                          DESCRIPTION                                    STARS    OFFICIAL  AUTOMATED
ubuntu                        Official Ubuntu base image                     1511     [OK]      
ansible/ubuntu14.04-ansible   Ubuntu 14.04 LTS with ansible                  45                 [OK]
ubuntu-upstart                Upstart is an event-based replacement for ...  24       [OK]      
tutum/ubuntu                  Ubuntu image with SSH access. For the root...  20                 [OK]
sequenceiq/hadoop-ubuntu      An easy way to try Hadoop on Ubuntu            13                 [OK]

Є купа варіантів готових образів, проте краще за все притримуватись офіційних збірок.
Завантажуємо образ:

# sudo docker pull ubuntu
Pulling repository ubuntu
...
499e0f86b235: Download complete 
4faa69f72743: Download complete 

Перший раз образ завантажуватиметься досить довго, проте кожен наступний контейнер буде створений з використанням завантаженого образу майже миттєво.

Переглянути всі завантажені чи власноруч створені образи в системі можна так:

# sudo docker images
REPOSITORY          TAG                   IMAGE ID            CREATED           VIRTUAL SIZE
ubuntu              14.04.2               d0955f21bf24       2 weeks ago        188.3 MB
ubuntu              latest                d0955f21bf24       2 weeks ago        188.3 MB
ubuntu              14.04                 d0955f21bf24       2 weeks ago        188.3 MB
...
ubuntu              trusty                d0955f21bf24       2 weeks ago        188.3 MB
ubuntu              trusty-20150320       d0955f21bf24       2 weeks ago        188.3 MB

Для запуску контейнера використаємо образ, котрий ми попередньо завантажили:

# docker run -i -t ubuntu /bin/bash
root@d2ec8369e5fa:/#

Одразу відбудеться перехід в контейнер (з опцією "-d" переходу не буде). Щоб коректно вийти з нього необхідно натиснути по черзі Ctrl-p + Ctrl-q (Ctrl-d тиснути не треба, це зупинить контейнер).

У разі необхідності установки окремої версії дистрибутиву в контейнері необхідно вказати тег-версію:

# docker run -i -t ubuntu:14.04.2 /bin/bash

При створенні можна одразу задати ім’я хосту, виставити ліміт кількості виділених ядер, обсяг пам’яті і тп.

# docker run -m 128m -c 1 --name="my_test_box2" -it ubuntu /bin/bash

-m 128m - обмеження по пам'яті.
-c 1 - обмеження по CPU.
--name - ім'я контейнеру
-t - виділення псевдо-tty
ubuntu - ім'я образу, з якого буде створений контейнер.

Перегляд всіх запущених контейнерів:

# docker ps
CONTAINER ID     IMAGE           COMMAND      CREATED              STATUS              PORTS     NAMES
d2ec8369e5fa     ubuntu:14.04    /bin/bash    About a minute ago   Up About a minute             s_name

А також всіх доступних (активних і зупинених):

# docker ps -a

Якщо в контейнері були якісь зміни (оновлення, редагування файлів і тп.) дисковий розмір всіх змін можна побачити, додавши ключ "-s":

# docker ps -as
CONTAINER ID     IMAGE           COMMAND         CREATED          STATUS           PORTS     NAMES     SIZE
d2ec8369e5fa     ubuntu:14.04    "/bin/bash"     15 hours ago     Up 15 hours                s_name    2 kB

Надалі можна ці зміни додавати в образ і стартувати вже наступні контейнери з цього образу:

# docker commit d2ec8369e5fa ubuntu

d2ec8369e5fa - id контейнера
ubuntu- ім'я образу

Зупинити/запустити контейнер можна наступним чином:

# docker stop  d2ec8369e5fa
# docker start d2ec8369e5fa

Тобто необхідно вказувати ID контейнера або його ім’я.

Видалити контейнер можна так:

# docker rm d2ec8369e5fa

З ключем -f контейнер попередньо можна і не зупиняти.

Підключитись до працюючого контейнера можна таким чином:

# docker exec -it d2ec8369e5fa bash

Тобто в контейнері буде запущено командний інтерпритатор bash в якому власне і можна виконати все необхідне.

Фізично всі контейнери знаходяться в /var/lib/docker/containers. Якщо зупинити Docker-демон то є можливість поправити деякі параметри контейнеру, скажімо, ім'я:

# vim /var/lib/docker/containers/ced0d29fda1b070e9635f47d64597b4be916a0c8109766b205c300ac56a255e0/config.json

{"ID":"ced0d29fda1b070e9635f47d64597b4be916a0c8109766b205c300ac56a255e0","Created":"2015-04-04T23:04:32.82942973Z","Path":"/bin/bash","Args":[],"Config":{"Hostname":"myubuntubox","Domainname":"","User":"","Memory":134217728,"MemorySwap":0,"CpuShares":0,"Cpuset":"","AttachStdin":true,"AttachStdout":true,"AttachStderr":true,"PortSpecs":null,"ExposedPorts":null,"Tty":true,"OpenStdin":true,"StdinOnce":true,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/bash"],"Image":"ubuntu","Volumes":null,"WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"OnBuild":null},"State":{"Running":false,"Paused":false,"Pid":0,"ExitCode":0,"StartedAt":"2015-04-04T23:04:32.900600277Z","FinishedAt":"2015-04-04T23:12:10.932632571Z"},"Image":"d0955f21bf24f5bfffd32d2d0bb669d0564701c271bc3dfc64cfc5adfdec2d07","NetworkSettings":{"IPAddress":"","IPPrefixLen":0,"Gateway":"","Bridge":"","PortMapping":null,"Ports":null},"ResolvConfPath":"/etc/resolv.conf","HostnamePath":"/var/lib/docker/containers/ced0d29fda1b070e9635f47d64597b4be916a0c8109766b205c300ac56a255e0/hostname","HostsPath":"/var/lib/docker/containers/ced0d29fda1b070e9635f47d64597b4be916a0c8109766b205c300ac56a255e0/hosts","Name":"/my_test_box2","Driver":"aufs","ExecDriver":"native-0.2","MountLabel":"","ProcessLabel":"","Volumes":{},"VolumesRW":{}}

Але змінити можна далеко не всі параметри. Ось і перший мінус, наприклад, в OpenVZ/LXC таких проблем немає.

Є зручна можливість переносу контейнера на інший хост. Пакуємо образ:

# docker save image_name > ~/my_container_for_transfer.tar

І вже розпаковуємо його на іншому:

# docker load < /tmp/my_container_for_transfer.tar

Так, це дуже зручно і просто виглядає. Проте це суть контейнерної віртуалізації, так можна всюди.

Скопіювати дані з контейнера в хост машину також легко:

# docker cp goofy_einstein:/etc .

goofy_einstein - ім'я віртуальної машини. Отже з контейнеру буде скопійована папка /etc в поточну директорію Хост-машини.

Навпаки - складніше. Щоб мати можливість копіювати дані в контейнер з Хост-машини, необхідно при створенні примонтувати директорію в контейнер:

# docker run -v /tmp:/root -t -i ubuntu

-v - опція монтування директорій
/tmp – директорія на хост машині
/root – директорія в контейнері

Як варіант - скопіювати дані в директорію контейнера, що фізично лежить на диску Хост-машини.

За необхідності можна створити власний контейнер, встановити необхідні параметри, програми та все що душа побажає. Для початку створимо такий контейнер з Memcached всередині. Для цього створимо Dockerfile, файл, що описує параметри майбутнього контейнера.

Помістимо таке в Dockerfile:

# cat Dockerfile

############################################################
# Dockerfile to run Memcached Containers
# Based on Ubuntu Image
############################################################

#---- Set the base image to use to Ubuntu
FROM ubuntu

# Set the file maintainer (your name - the file's author)
MAINTAINER Ipeacocks ipeacocks_dog_gmail.com

#ENV HOME /root
#ENV DEBIAN_FRONTEND noninteractive

# Update the default application repository sources list
RUN apt-get update

# Install Memcached
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y memcached

# Port to expose (default: 11211)
EXPOSE 11211

# Default Memcached run command arguments
CMD ["memcached", "-m", "128"]

# Set the user to run Memcached daemon
USER daemon
#-----

Ось на що впливають ці параметри:

FROM - образ, що буде взятий за основу. В даному випадку буде використовуватись офіційний image від Ubuntu.
MAINTAINER - людина, що зібрала контейнер
ENV HOME, ENV DEBIAN_FRONTEND - значення системних змінних. Образ для контейнера буде збиратись в неінтерактивному режимі (не буде прямого доступу до процесу установки). Інакше будуть висвічуватись помилки.
RUN - команда, що виконується при створенні образу для контейнера. Результат виконання команди буде закомічений в образ. У конкретному прикладі будуть оновлені списки пакетів, потім установлений memcached. Для кожної команди буде створюватись окремий комміт в образ під час його збирання.
EXPOSE - порт, котрий буде відкритий для контейнера. Його надалі можна переадресувати в інший порт Хост-машини.
CMD - По філософії Docker, це програма та опції до неї, що буде запущена в контейнері, може бути лише одна інструкція CMD. На відміну від RUN, вона не запускається на протязі збірки образу, а стартує вже при старті самого контейнеру.
USER - користувач від якого будуть проходити запуски всіх RUN, CMD в Dockerfile.

Збираємо образ з Dockerfile:

# docker build -t memcached_img .

Sending build context to Docker daemon  2.56 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu
 ---> d0955f21bf24
Step 1 : MAINTAINER Ipeacocks ipeacocks_dog_gmail.com
 ---> Running in 5aa86976cf39
 ---> fa98ee278f98
Removing intermediate container 5aa86976cf39
Step 2 : RUN apt-get update
 ---> Running in b2407d0b4d58
....
Step 3 : RUN DEBIAN_FRONTEND=noninteractive apt-get install -y memcached
 ---> Running in 85dc0a3e67ff
Reading package lists...
...
Step 4 : EXPOSE 11211
 ---> Running in 02005c03b4d1
 ---> cdabd31d7f59
Removing intermediate container 02005c03b4d1
Step 5 : CMD memcached -m 128
 ---> Running in d01a18bc3669
 ---> 3d2f101eb92b
Removing intermediate container d01a18bc3669
Step 6 : USER daemon
 ---> Running in 56fee3465b1b
 ---> 569e5d6effa3
Removing intermediate container 56fee3465b1b
Successfully built 569e5d6effa3

Отже, 7 директив в Dockerfile призведуть до запуску 7 етапів збирання контейнеру.

Ось і наш новий образ із усіх наявних образів:

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
memcached_img       latest              569e5d6effa3        29 minutes ago      210.8 MB
ubuntu              latest              d0955f21bf24        3 weeks ago         188.3 MB

Тепер можна стартувати контейнер із образу:

# docker run --name memcached_ins -m 256m -d -p 45001:11211 memcached_img

-d - контейнер стартуватиме в фоні, не буде автоматичного переходу.
--name memcached_ins - ім'я майбутнього контейнера.
-m 256m - обмеження по пам'яті.
-p 45001:11211 - перекидання порту. Отже, 45001-порт на Хост-машині буде перекинутий на 11211-порт memcached, що працює в контейнері. 11211-порт має бути описаний як EXPOSED в Dockerfile.
memcached_img - образ з якого буде створений контейнер.

Переглянути налаштування контейнеру також можна запустивши команду inspect. Ось налаштування контейнеру з memcached, створення якого було описано вище:

# docker inspect cee5f9400249
[{
    "Args": [
        "-c",
        "memcached",
        "-m",
        "128"
    ],
    "Config": {
...
        "Cmd": [
            "-m",
            "128"
        ],
        "CpuShares": 0,
        "Cpuset": "",
        "Domainname": "",
        "Entrypoint": [
            "/bin/sh",
            "-c",
            "memcached"
        ],
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "HOME=/root",
            "DEBIAN_FRONTEND=noninteractive"
        ],
        "ExposedPorts": {
            "11211/tcp": {}
        },
        "Hostname": "cee5f9400249",
        "Image": "memcached_img",
        "Memory": 0,
        "MemorySwap": 0,
...
    },
    "Created": "2015-04-05T02:15:19.710276728Z",
    "Driver": "aufs",
    "ExecDriver": "native-0.2",
    "HostConfig": {
...
        "NetworkMode": "bridge",
        "PortBindings": {
            "11211/tcp": [
                {
                    "HostIp": "0.0.0.0",
                    "HostPort": "45001"
                }
            ]
        },
        "Privileged": false,
        "PublishAllPorts": false,
        "VolumesFrom": null
    },
    "HostnamePath": "/var/lib/docker/containers/cee5f94...025a440124544/hostname",
    "HostsPath": "/var/lib/docker/containers/cee5f94...025a440124544/hosts",
    "Id": "cee5f94...025a440124544",
    "Image": "65553a...c82e3f",
    "MountLabel": "",
    "Name": "/memcached_ins",
    "NetworkSettings": {
        "Bridge": "docker0",
        "Gateway": "172.17.42.1",
        "IPAddress": "172.17.0.22",
        "IPPrefixLen": 16,
        "PortMapping": null,
        "Ports": {
            "11211/tcp": [
                {
                    "HostIp": "0.0.0.0",
                    "HostPort": "45001"
                }
            ]
        }
    },
    "Path": "/bin/sh",
    "ProcessLabel": "",
    "ResolvConfPath": "/etc/resolv.conf",
    "State": {
        "ExitCode": 0,
...
    },
    "Volumes": {},
    "VolumesRW": {}
}

Мабуть, останній вивід команди не дуже цікавий, але трохи дає уявлення що і де знаходиться.

Наступний приклад - це Dockerfile для створення контейнеру з Nginx.

# cat Dockerfile

############################################################
# Nginx Dockerfile
# Based on Ubuntu Image
############################################################
FROM ubuntu:14.04

ENV DEBIAN_FRONTEND noninteractive


RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y install nginx

RUN echo "daemon off;" >> /etc/nginx/nginx.conf
RUN mkdir /etc/nginx/ssl
ADD default /etc/nginx/sites-available/default

# Define mountable directories.
VOLUME ["/etc/nginx/sites-enabled", "/etc/nginx/certs", "/etc/nginx/conf.d", "/var/log/nginx", "/var/www/html"]

EXPOSE 80

CMD ["nginx"]
#-----

Змінну ENV DEBIAN_FRONTEND, як варіант, можна поставити глобально на всі операції, проте, здається, цього потребує лише Apt.
ADD додає локальний файл default при збірці контейнера і кладе його за адресою /etc/nginx/sites-available/
VOLUME - примонтує директорію з контейнера у реальну Хост-машину. Тобто надалі для редагування конфігураційних файлів Nginx немає потреби логіниитись в контейнер.

# docker build -t nginx_img .
# docker run --name nginx_ins -d -p 8080:80 nginx_img

Тож запит на 8080 порт Хост-машини відкриє default віртуальних хост, що насправді знаходиться на контейнері. Є звісно і інші способи зв'язку з контейнером, проте перекидування порту - самий тривіальний. Налаштування мережевого стеку для Docker достатньо обмежений на даний час.

Як варіант взаємодії між контейнерами можна використовувати лінкування:

# docker run --link container1:container2 -i -t ubuntu:14.04 /bin/bash

Буде створений container2, що прилінкується уже до працюючого контейнеру container1. Якщо розшарити порт на container1, то його буде також видно на container2. Але можна лінкувати не лише по мережевому порту.

Так можна подивитись push-історію для контейнера:

# docker history memcached_img:latest
IMAGE               CREATED             CREATED BY                                      SIZE
65553a6c2409        About an hour ago   /bin/sh -c #(nop) ENTRYPOINT [/bin/sh -c memc   0 B
d9543a28f062        About an hour ago   /bin/sh -c #(nop) USER daemon                   0 B
...
a1a958a24818        2 weeks ago         /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic   194.5 kB
f3c84ac3a053        2 weeks ago         /bin/sh -c #(nop) ADD file:777fad733fc954c0c1   188.1 MB
511136ea3c5a        22 months ago                                                       0 B

Видаляти образ/образи можна так:

# docker rmi nginx_img memcached_img

Або одразу всі, що є в системі:

# docker rmi $(docker images -q)

Щоб видалити всі (працюючі та зупинені) контейнери необхідно запустити команду:

# docker rm -f $(docker kill $(docker ps -q))

Моя остаточна думка щодо Docker-а збігається з цією, краще за все його використовувати в dev/qa-середовищах. Більше всього мене турбують його обмеження в плані налаштування мережі та майже відсутність можливості переналаштування уже готового працюючого контейнера, що на момент прочитання цієї статті вже може бути не актуальним.

Посилання:
http://containerz.info/
https://serversforhackers.com/getting-started-with-docker
http://www.netpatch.ru/research/docker/2014/03.24-linux-docker-practical-guide.html
http://linux-notes.org/ustanovka-docker-na-centos-redhat-fedora/
http://failshell.io/docker/running-nginx-inside-a-docker-container/
http://blog.oddbit.com/2014/08/11/four-ways-to-connect-a-docker/
http://www.slideshare.net/dotCloud/docker-intro-november
http://www.monblocnotes.com/node/2055
http://www.monblocnotes.com/node/2057
http://www.monblocnotes.com/node/2059
http://www.monblocnotes.com/node/2067
http://www.monblocnotes.com/node/2095
https://bitbucket.org/Balancer/bors-dev/src/e97297ec421cd4c32b50a68bffb3840bd77c8128/clouds/docker.md?at=default
https://jpetazzo.github.io/2013/10/16/configure-docker-bridge-network/
http://xmodulo.com/networking-between-docker-containers.html
http://habrahabr.ru/post/217689/
http://habrahabr.ru/post/247547/
http://habrahabr.ru/company/infobox/blog/240623/
http://habrahabr.ru/post/253877/
http://mrdeveloper.ru/post-43/how-to-launch-gui-app-in-docker
https://www.conetix.com.au/blog/docker-basics-practical-starters-guide

Немає коментарів:

Дописати коментар