Linux: Docker

Basics
  • Docker уже есть даже на сетевых устройствах, таких как Cisco Catalyst, Cisco ASR. В контейнере можно запустить много всего – например, Minecraft 🙂 Если серьезно – потенциально такой подход позволяет реализовывать CDN.
As an example, why not host your computer game servers directly on the switch instead of connecting to any dedicated server far away – it’s closer locally.

IOX app hosting platform minecraft blog

  • Docker изначально появился как система разворачивания ПО – заворачиваем все ПО для нашего приложения (сайт состоит из веб сервера, субд, настроек, библиотек и проч) в docker образ. Создаем докер файл с конфигом – командами которые необходимо выполнить для разворачивания приложения. Теперь мы можем его развернуть где угодно в любом количестве.
  • Есть централизованное хранилище образов – dockerhub. Выполнив команду сразу происходит скачивание образа и разворачивание. Доверять образам в dockerhub сильно не стоит.
20 Million Miners: Finding Malicious Cryptojacking Images in Docker Hub
  • Созданный сервис запускается в контейнере. Контейнер представляет собой процесс в изолированном пространстве со своей ФС, деревом процессов, может быть ограничение по ОП и процессору, виртуализированная сеть – почти полноценная VM.
  • Docker облегчает процедуру backup/recovery/migration сервисов до простого сохранения образа контейнера и разворачивания (по аналогии с VM) или даже операции pull контейнера из hub.docker.com (docker pull <image_name>)
  • Docker в отличии от VM более Lightweight за счет использования Kernel ОС контейнерами. Недостаток от этого – не получится запустить контейнер если хост машина имеет отличную ОС от контейнера. т.е. Linux контейнеры на Linux хостах, Windows контейнеры на Windows хостах.
  • На каждый Linux до ~500 докеров. Дальше разные косяки вылазят. Далее используя какой нибудь гипервизор (kvm, esxi, etc) можно наплодить штук 10-20 таких операционок и получить довольно легко 10к докеров.

 

Usage

Частично на основе видео

INSTALL/START

Устанавливаем docker

sudo yum install docker
curl -sSL https://get.docker.com/ | sh # ubuntu/debian best way

Запускаем docker как daemon, иначе будет ошибка

systemctl start docker
[root@weril ~]# sudo docker pull centos
Using default tag: latest
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

Добавляем в автозагрузку.

systemctl enable docker
[root@weril ~]# sudo systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

Добавляем в автозагрузку контейнеры.

sudo docker run --restart=always -d vpn
sudo docker run --restart=always -d wp
sudo docker run --restart=always -d sql

 

IMAGES
  • Есть ipsec/l2tp vpn server как docker image, очень удобен по настройке и использованию, работает с клиентами iOS/MAC OS (проверял и Cisco IPsec и L2TP). Подробнее в отдельной статье.
  • Через docker images организуется backup как самих image, так и контейнеров
  • В докер images зачастую нехватает даже самых базовых утилит – nano/vi, ifconfig/ip, ping
yum install iproute
apt install iputils-ping

Получаем image для последнего centos с dockerhub (по умолчанию применяется флаг latest как в примере с mysql). Этой же командой обновляется старый image.

docker pull <image_name>
[root@weril ~]# docker pull mysql/mysql-server:latest
[root@weril ~]# docker pull centos
Using default tag: latest
Trying to pull repository docker.io/library/centos ...
latest: Pulling from docker.io/library/centos
8a29a15cefae: Pull complete
Digest: sha256:fe8d824220415eed5477b63addf40fb06c3b049404242b31982106ac204f6700
Status: Downloaded newer image for docker.io/centos:latest

Просмотр images.

docker images
-bash-4.2$  sudo docker images
REPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
docker.io/mysql                     latest              30f937e841c8        3 days ago          541 MB
docker.io/hwdsl2/ipsec-vpn-server   latest              b9823de3dae3        4 days ago          167 MB
docker.io/httpd                     latest              d4e60c8eb27a        9 days ago          166 MB
docker.io/wordpress                 latest              675af3ca3193        9 days ago          540 MB
docker.io/mysql/mysql-server        latest              716286be47c6        3 weeks ago         381 MB
docker.io/centos                    latest              470671670cac        4 months ago        237 MB

Удаление image по name/ID. Не должно быть контейнеров, использующих image.

docker rmi <cont_name/ID>
docker image rm docker.io/httpd
docker image rm mysql-backup
[root@weril ~]# docker rmi 470671670cac
Error response from daemon: conflict: unable to delete 470671670cac (cannot be forced) - image is being used by running container 38f35fcf081d

Через docker images организуется backup как самих image, так и контейнеров.

Резервное копирование/восстановление image возможно через tar архив или repo с docker images (приватный или публичный).

Важно: постоянные диски с динамически изменяемыми данными, т.к. файлы СУБД бекапиться не должны. Резервное копирование данных ваших СУБД необходимо выполнять штатными средствами самой СУБД, т.к. при простом копировании данных такого диска очень легко получить их неконсистентность и невозможность восстановить работоспособность и данные СУБД в будущем.
sudo docker save -o wordpress.tar wordpress # в файл
sudo docker push busybox # на репо
sudo docker load -i wordpress.tar # (образа не должно быть в системе)

Резервное копирование/восстановление контейнеров простое – создаем образ из контейнера и далее используем бекап схему выше для образов.

sudo docker commit -p wordpress wordpress:container_backup

 

CONTAINERS

Создаем и запускаем докер контейнер. -d позволяет сделать detatch (запуск в режиме демона), -t назначает процессу tty, что позволяет к нему подключатся в последующем.

docker run -d -t --name <cont_name> <image_name>
[root@weril ~]# docker run -d -t --name mysql mysql/mysql-server
[root@weril ~]# docker run -d -t --name wordpress centos

При старте можно сделать mapping порта хоста к докер контейнеру. Первый порт – порт хоста, второй порт – порт контейнера. Так же можно добавлять опции (переменные среды) используя -e/–env или файл с переменными, необходимые для настройки поднимаемого сервиса во время создания. Изменение этих опций не предусмотрено.

docker run -d -t -p <host_port>:<cont_port> --name <cont_name> <image_name>
[root@weril ~]# docker run -d -t -p 80:80 -p 443:443 --name wordpress2 centos
[root@weril ~]# docker run -d -t -p 80:80 -p 443:443 --name wordpress -e WORDPRESS_DB_HOST=172.17.0.4:3306 -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=wordpress -d wordpress
[root@weril ~]# docker run -d -t --name ipsec-vpn-server --env-file ./vpn.env --restart=always -p 500:500/udp -p 4500:4500/udp --privileged hwdsl2/ipsec-vpn-server

Для проброса хостового девайса в контейнер можно использовать keyword “device”.

docker run -it --device
--device Add a host device to the container

Нужно учитывать, что изменение environment variables можно сделать только через переразворачивание контейнера т.к. изменение переменных для уже созданных контейнеров не предусмотрено. И это идеологически неправильно. Если же поменять просто конфиг необходимого сервиса, без изменения env, то каждый перезапуск контейнера будет приводить к возврату изначально указанных в env настроек.

Destroy your container and start a new one up with the new environment variable using docker run -e .... It's identical to changing an environment variable on a running process, you stop it and restart with a new value passed in. Replace the concept of restarting a process with destroying and recreating a new container. If your container contains files that cannot be lost, then you should be using volumes. The other contents of the container filesystem should be either disposable or immutable.

Можно переименовать контейнер.

docker rename <old_cont> <new_cont>

Смотрим какие контейнеры запущены.

docker ps
[root@weril ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                NAMES
38f35fcf081d        centos              "/bin/bash"         29 seconds ago      Up 28 seconds       0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   wordpress2
c9b035ee107f        centos              "/bin/bash"         12 minutes ago      Up 12 minutes                            wordpress

Смотрим все контейнеры.

docker ps -a
[root@weril ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
4056df8ab3cb        centos              "/bin/bash"         5 hours ago         Exited (0) 5 hours ago                          wordpress2
41f09d3974a5        centos              "/bin/bash"         5 hours ago         Exited (0) 24 seconds ago                       wordpress

Подключаемся к контейнеру по имени. Или исполняем команду/скрипт.

docker exec -it <cont_name> <command>
[root@weril ~]# docker exec -it wordpress bash
[root@c9b035ee107f /]#

[root@weril ~]# sudo docker exec -it mysql mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.

Отключаем/запускаем созданный контейнер.

docker stop <cont_name/ID>
docker start <cont_name/ID>
[root@weril ~]# docker stop wordpress
wordpress
[root@weril ~]# docker start wordpress
wordpress

Удаляем container (можно по name/id). Перед удалением нужно отключить.

docker stop <cont_name/ID>
docker rm <cont_name/ID>
[root@weril ~]# docker rm c9b035ee107f
Error response from daemon: You cannot remove a running container c9b035ee107ffb7ac120bbac474fdf4a65fc04cd1bcd93498064231a8b55b43a. Stop the container before attempting removal or use -f
[root@weril ~]# docker stop c9b035ee107f
c9b035ee107f
[root@weril ~]# docker rm c9b035ee107f
c9b035ee107f

Копируем файл(ы) в контейнер с хоста.

docker cp foo.txt mycontainer:/foo.txt
docker cp /home/user/. mycontainer:/

Копируем файл(ы) с контейнера на хост.

docker cp mycontainer:/foo.txt foo.txt
docker cp mycontainer:/. /home/user/
Network

Просмотр состояния сети. По умолчанию host и все контейнеры в одном bridge domain и им выдается адрес из динамического пула.

docker network ls
docker network inspect <network_type>
-bash-4.2$ sudo docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "6c5f8759faa9987533829b17424dec82d5d9477e0f68b6a454a3e090087d9f7a",
        "Created": "2020-05-23T22:04:26.659842657+02:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Containers": {
        "40fac485362f7d453377e22be0d3083ca5ef148f34749b67d75df24fc86d671c": {
                "Name": "wordpress2",
                "EndpointID": "fc7f2a768e75b7a7c90afca49fcf5e4c4a4fd685d9f5afa0af5780755f544964",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "5ef3fcc80f21823726fab2d23783e726261ef7096fbb8875147fb395606dcf42": {
                "Name": "wordpress",
                "EndpointID": "80084e8ea4ba8bf4831254452da84bde5d06e5018bc8ff4bf37e577db7c2a432",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

Если нужно сделать адрес статическим (напр. адрес внутренней БД между контейнерами) – создаем отдельную сеть и переводим все контейнеры в нее (между сетями по умолчанию взаимодействие запрещено). Необходимым контейнерам назначаем статический адрес. Нужно учитывать, что если после этого придется изменить конфиг environment variables для каких-то контейнеров, то нужно будет переразворачивать контейнер (подробнее в описании docker run).

-bash-4.2$ sudo docker network connect --ip 172.17.0.2 bridge mysql
Error response from daemon: User specified IP address is supported on user defined networks only

sudo docker network create --subnet=172.18.0.0/16 mynet
sudo docker network disconnect bridge mysql
sudo docker network connect --ip 172.18.0.55 mynet mysql
sudo docker network disconnect bridge wordpress
sudo docker network connect mynet wordpress

MTU в контейнере судя по всему из-за ИБ нельзя поменять (даже из под root/sudo).

 

 

STATISTICS

Статистика использования ресурсов хоста каждым контейнером.

docker stats
[root@weril ~]# docker stats
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
38f35fcf081d 0.00% 524 KiB / 3.764 GiB 0.01% 1.1 kB / 656 B 0 B / 0 B 1
c9b035ee107f 0.00% 528 KiB / 3.764 GiB 0.01% 446 B / 656 B 0 B / 0 B 1
Логи процесса в Docker (делается через перенаправление логов сервиса в stderr/stdout docker’а)
docker logs <cont_name>
-bash-4.2$ sudo docker logs mysql
-bash-4.2$ sudo docker logs ipsec-vpn-server
dockerfile – разворачиваем контейнер на основе конфига

docker build – <Dockerfile. Пример.

# docker build - <Dockerfile
FROM centos:centos7
MAINTAINER wireplay

RUN yum -y update
RUN yum -y install epel-release
RUN yum -y install httpd
RUN yum -y install git gcc
RUN yum -y install libpcap gcc libpcap-devel libnet libnet-devel

# startup script
#COPY run-wireplay-client.sh /run-wireplay-client.sh
#COPY bittorrent.stream36.pcap /wireplay/bittorrent.stream36.pcap
#RUN chmod -v +x /run-wireplay-client.sh

RUN rm -rf /wireplay
RUN mkdir -p /wireplay
RUN git clone https://github.com/pmcgleenon/wireplay.git
RUN yum -y install make
RUN cd wireplay && make all
Docker Swarm

Ниже на основе ролика.

Docker Swarm – позволяет объединить несколько контейнеров, в том числе на удаленных узлах, между собой в рой/кластер (swarm). Можно создавать кластерное ПО – один докер будет manager (proxy), остальные worker. Manager’ов и worker’ов может быть множество и в случае отказа одного worker происходит исключение worker’а из обработки запросов, но только один Manager в один момент времени является leader.

  • Очень просто разворачивать – одной кнопкой разворачиваешь любое количество контейнеров на большом количестве хост машин (хоть всех которые есть в рое).
  • Очень просто апдейтить – можно апдейтить последовательно контейнеры worker’ов не влияя на сервис. Останавливаем контейнеры, обновляем, возвращаем их в строй. Обновляем следующие.
docker swarm init --advertise-addr 1.1.1.1 - создается swarm, анонсируется адрес 1.1.1.1 как manager. В результате генерируются команды ниже по добавлению worker и manager в swarm.

docker swarm join --token .... 1.1.1.1:xxxx. Ее вводим для присоединения worker'ов к swarm.

docker swarm join-token manager. Ее вводим для присоединения manager к swarm.

docker node ls - смотрим состав swarm.

Внутри нодов можно создавать независимые друг от друга контейнеры, а можно создавать сервисы на swarm. Например, можно создать сервис с веб оболочкой для докера – portainer. В нем можно через gui управлять кластером.

В docker по умолчанию встроена функция балансировки по созданным сервисам в swarm – при получении запроса на любой (не только manager) ноде происходит его перенаправление на рабочие ноды внутри swarm.

В целом штуки в идее крутые, но по факту пока сыро – могут не стартовать сервисы по непонятным причинам или запускаться с задержкой овер 5 минут, не распределяться контейнеры равномерно по кластеру, не распределяться нагрузка.

Leave a Reply