Останнім часом дуже часто пишуть про 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
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
Запускаємо 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
Немає коментарів:
Дописати коментар