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.
- 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 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 минут, не распределяться контейнеры равномерно по кластеру, не распределяться нагрузка.