Ссылки:
1 2 3 4 |
https://selectel.ru/blog/what-is-docker/ https://habr.com/ru/company/timeweb/blog/594533/ https://www.youtube.com/watch?v=QF4ZF857m44&ab_channel=%D0%90%D1%80%D1%82%D0%B5%D0%BC%D0%9C%D0%B0%D1%82%D1%8F%D1%88%D0%BE%D0%B2 https://github.com/amatiashov/YT-Docker-lesson |
Вводное:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
Контейнеры — это способ упаковать приложение и все его зависимости в единый образ. Этот образ запускается в изолированной среде, не влияющей на основную операционную систему. Контейнеры позволяют отделить приложение от инфраструктуры: разработчикам не нужно задумываться, в каком окружении будет работать их приложение, будут ли там нужные настройки и зависимости. Они просто создают приложение, упаковывают все зависимости и настройки в единый образ. Затем этот образ можно запускать на других системах, не беспокоясь, что приложение не запустится. Docker — это платформа для разработки, доставки и запуска контейнерных приложений. Docker позволяет создавать контейнеры, автоматизировать их запуск и развертывание, управляет жизненным циклом. Он позволяет запускать множество контейнеров на одной хост-машине. Контейнеризация похоже на виртуализацию, но это не одно и то же. Виртуализация работает как отдельный компьютер, со своим виртуальным оборудованием и операционной системой. При этом внутри одной ОС можно запустить другую ОС. В случае контейнеризации виртуальная среда запускается прямо из ядра основной операционной системы и не виртуализирует оборудование. Это означает, что контейнер может работать только в той же ОС, что и основная. АПри этом так как контейнеры не виртуализируют оборудование, они потребляют намного меньше ресурсов. Docker: Решает проблемы зависимостей и рабочего окружения. Изоляция и безопасность. Ускорение и автоматизация развертывания приложений и масштабируемость. Контейнеры приближают к микросервисной архитектуре(приложение не монолитно). Docker compose — одновременно развернуть несколько контейнеров. Хранение данных в Docker. |
Хранение данных в Docker.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Эфемерность. Это означает, что контейнеры могут быть в любой момент остановлены, перезапущены или уничтожены. При этом все накопленные данные в контейнере будут потеряны. Поэтому приложения нужно разрабатывать так, чтобы они не полагались на хранилище данных в контейнере, это называется принципом Stateless. Это хорошо подходит для приложений или сервисов, которые не сохраняют результаты своей работы. Например, функции расчета или преобразования данных: им на вход поступил один набор данных, они его преобразовали или рассчитали и вернули результат. Все, ничего никуда сохранять не нужно. Но далеко не все приложения такие, и есть много данных, которые нужно сохранить. В контейнерах для этого предусмотрены несколько способов. Тома (Docker volumes) Это способ, при котором докер сам создает директории для хранения данных. Их можно сделать доступными для разных контейнеров, чтобы они могли обмениваться данными. По умолчанию эти директории создаются на хост-машине, но можно использовать и удаленные хранилища: файловый сервер или объектное хранилище. Монтирование каталога (bind mount) В этом случае директория сначала создается в хост-системе, а уже потом монтируется в докер контейнеры. Но этот способ не рекомендуется, потому что он усложняет резервное копирование, миграцию и совместное использование данных несколькими контейнерами. |
Docker daemon
1 2 3 |
Это сервис, через который осуществляется все взаимодействие с контейнерами: создание и удаление, запуск и остановка. Этот сервис работает в фоновом режиме и получает команды от интерфейса командной строки или API. |
Docker client (клиент)
1 2 |
Это интерфейс командной строки для управления docker daemon. Мы пользуемся этим клиентом, когда создаем и разворачиваем контейнеры, а клиент отправляет эти запросы в docker daemon. |
Docker image (образ)
1 2 3 4 5 6 7 |
Это неизменяемый файл (образ), из которого разворачиваются контейнеры. Приложения упаковываются именно в образы, из которых потом уже создаются контейнеры. Приведем аналогию на примере установки операционной системы. В дистрибутиве (образе) ОС есть все, что необходимо для ее установки. Но этот образ нельзя запустить, для начала его нужно «развернуть» в готовую ОС. Так вот, дистрибутив для установки ОС — это Docker image, а установленная и работающая ОС — это Docker container. Но контейнеры обычно разворачиваются одной командой — это намного проще и быстрее, чем установка ОС. |
Docker container (контейнер)
1 |
Это уже развернутое из образа и работающее приложение. |
Docker Registry
1 2 3 4 5 6 |
Это репозиторий с докер-образами. Разработчики создают образы своих программ и выкладывают их в репозиторий, чтобы их можно было скачать и воспользоваться ими. Распространенный публичный репозиторий — Docker Hub. В нем собраны образы множества популярных программ или платформ: базы данных, веб-серверы, компиляторы, операционные системы и так далее. Также можно создать свой приватный репозиторий, например внутри компании. Разработчики будут размещать там образы, которые будут использоваться всей компанией. |
Dockerfile
1 2 3 |
Dockerfile — это инструкция для сборки образа. Это простой текстовый файл, содержащий по одной команде в каждой строке. В нем указываются все программы, зависимости и образы, которые нужны для разворачивания образа. |
Пример Dockerfile
1 2 3 4 5 6 7 8 |
FROM python:3 COPY main.py / CMD [ "python", "./main.py" ] Первая строчка означает, что за основу мы берем образ с названием python версии 3 это называется базовый образ. Docker найдет его в docker registry, скачает и будет использовать за основу. Вторая строчка означает, что нужно скопировать файл main.py в корень файловой системы контейнера. Третья строчка означает, что нужно запустить python и передать ему в качестве параметра название файла main.py |
Все эти команды выполняются в Docker client, который отправляет их в docker daemon:
1 2 3 4 5 6 7 |
Команда docker build (зеленая) читает dockerfile и собирает образ. Команда docker pull (красная) скачивает образ из docker registry. По умолчанию docker скачивает образы из публичного репозитория Docker Hub. Но можно создать свой репозиторий и настроить докер, чтобы он работал с ним. Команда docker run (черная) берет образ и разворачивает его в контейнер. |
Установка Docker на debian\ubuntu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Cинхронизируем пакетную базу apt и установим нужные зависимости: sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release Импортируем GPG-ключ для репозитория docker: curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg Добавим новый репозиторий в список apt: echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null Установим докер: sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io По умолчанию, доступ к docker daemon есть только у пользователя root. Чтобы с докером могли работать и другие пользователи, их нужно добавить в специальную группу — docker. Выполните эту команду из под обычного пользователя: sudo usermod -aG docker $USER После этого необходимо перелогиниться, чтобы изменение вступило в силу. |
Запуск контейнера
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Попробуем запустить какое-нибудь готовое приложение. Выполните команду: docker run ubuntu echo 'hello from ubuntu' Команда docker run создает и запускает контейнер из образа. В этом примере мы создаем контейнер из образа ubuntu, затем выполняем в нем команду echo ‘hello from ubuntu’. Но так как у нас чистая установка докера и мы не скачали ни одного образа, докер сначала найдет этот образ в публичном репозитории Docker Hub, скачает, а потом создаст из него контейнер. В следующий раз, когда нам понадобится образ ubuntu, докер уже не будет его скачивать. После выполнения команды в терминале появится строка hello from ubuntu, и контейнер сразу остановится. Теперь выполним другую команду: docker run -it ubuntu Эта команда запустит контейнер в интерактивном режиме, то есть контейнер запустится и будет ждать дальнейших команд. При этом мы окажемся внутри операционной системы контейнера: запустится оболочка (bash), и мы сможем выполнять какие-то команды внутри контейнера. Чтобы выйти из контейнера, введите команду exit. |
Создание собственного образа и запуск контейнера
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
Теперь создадим HelloWorld-приложение на Python, обернем его в образ и запустим. Cоздадим директорию, в которой мы будем работать и перейдем в нее: mkdir first-docker-app cd first-docker-app Создадим файл main.py и запишем в него одну строчку кода: echo 'print("Hello from python");' >> main.py Проверим, что наша программа работает(В консоли должно выйти сообщение "Hello from python"): python main.py Теперь нужно обернуть его в докер-образ. Для этого создадим файл Dockerfile и напишем в нем три строчки: vim Dockerfile -------------- FROM python:3 COPY main.py / CMD [ "python", "./main.py" ] -------------- В первой строке мы указываем образ, который берем за основу. Так как мы пишем приложение на Python, нужно чтобы в нашем образе он уже был установлен. Самый простой способ это сделать — использовать готовый официальный образ с Docker Hub. Цифра 3 — это тег. Он означает, что нужно использовать третью версию Python. Вместо этого можно было бы использовать тег latest, который означает самую последнюю версию, или можно было указать номер конкретной версии, например 3.8.8. Во второй строчке мы копируем наш файл main.py в корневую директорию образа. Третья строчка — запускаем python и передаем ему в качестве параметра имя нашего файла Теперь из этого докер-файла можно собирать образ: !!! Параметр -t обозначает имя нашего образа, мы назвали его first-docker-app. docker build -t first-docker-app . Проверим список установленных у нас образов: docker images Теперь создадим контейнер из нашего образа и запустим его: docker run first-docker-app |
Список полезных команд docker:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
Посмотреть список всех контейнеров: docker ps -a Остановить все докер контейнеры: docker stop $(docker ps -a -q) Удалить все докер контейнеры: docker rm $(docker ps -a -q) Запустить контейнер с последующим удалением: docker run --rm ubuntu echo 'hello from ubuntu' Посмотреть список всех скачанных образов: docker images Удалить докер образ(перед удаление требуется остановить): docker rmi name_image Чтобы принудительно удалить образ, добавьте флаг -f: docker rmi -f name_image Что бы удалить все образы docker rmi $(docker images -q) Получить список всех контейнеров, созданных из определенного образа: docker ps -a --filter ancestor=name_image |
Еще раз для тех кто в танке, напримере python образа и нашего приложения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
!!! apt install docker docker.io !!! Запоминаем что задача докер это выполнить приложение (ну тоесть поработали и если работы нет, закончили) 0. Создаем папку testfile в каталоге opt и переходим в нее mkdir /opt/testfile cd /opt/testfile 1. Создаем файл нашего приложения: vim app.py ---------- print("Hello, world") ---------- 2. Создаем Dockerfile: vim Dockerfile -------------- FROM python:3.6 # используем образ python 3.6 RUN mkdir -p /usr/src/app/ # создаем каталог /usr/src/app/ WORKDIR /usr/src/app/ # указываем рабочий каталог COPY . /usr/src/app/ # копируем файлы из текущего домашнего каталога в контейнер CMD ["python", "app.py"] # собственно ради чего мы тут собрались, выполняем наше приложение app.py -------------- 3. Производим сборку контейнера из текущего каталога и наш контейнер называем hellow-world cd /opt/testfile docker build -t hellow-world . 4. Проверяем что у нас там собралось: docker images 5. Запускаем наш контейнер: docker run hellow-world 6. После когда наш контейнер отработал мы можем выполнить запуск нашего контейнера уже под другим именем например hello docker run --name hello hellow-world 7. Посмотреть все отработанные контейнеры и их статус можно командой docker ps -a 8. Удаление контейнера docker rm ID-контейнера 9. Когда у вас например 50 контейнеров удаление может затянутся, "docker ps" умеет выводить ID контейнеров docker ps -a -q docker rm $(docker ps -qa) |
Пример приложения работающего подольше \ бесконечно и запуск его в режиме демона:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
0. Редактируем наше приложение: vim app.py ---------- import time while True: print("hello, World!") time.sleep(1) ---------- 1. Производим сборку контейнера и запуск (-d запускает контейнер в режиме демона) docker build -t hello2 . docker run --name hello2 -d hello2 2. Проверяем что там у нас работает: docker ps -a 3. Для остановки контейнера выполним следующие docker stop hello2 4. Для запуска контейнера с дальнейшим удалением после остановки выполнения (docker stop) docker run --name hello2 -d --rm hello2 |
Пошли дальше чуть посложнее web приложение:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
0. Готовим приложение, создаем каталог, переходим в него и создаем первый файл app.py mkdir /opt/webp cd /opt/webp vim app.py ---------- import os import json import datetime from flask import Flask app = Flask(__name__) BASE_FOLDER = os.path.dirname(os.path.abspath(__file__)) RESOURCE_DIR = os.path.join(BASE_FOLDER, "resources") @app.route('/') def hello_world(): with open(os.path.join(RESOURCE_DIR, "response.json")) as f: return "%s - %s" % (json.loads(f.read()).get("payload"), datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S")) if __name__ == "__main__": app.run(host="0.0.0.0", port=8080, debug=True) ------------ 1. Готовим Докер файл vim Dockerfile -------------- FROM python:3.6 # используем образ python3.6 RUN mkdir -p /usr/src/app/ # создаем каталог в контейнере WORKDIR /usr/src/app/ # указываем рабочий каталог нашего приложения COPY . /usr/src/app/ # копируем локальные файлы в контейнер RUN pip install --no-cache-dir -r requirements.txt # устанавливаем зависимости для python EXPOSE 8080 #нехрена не пробросит, наши пожелания что мы хотели бы пробросить порт, при запуске укажем что нам нужно #ENV TZ Europe/Moscow # передаем значение переменно окружения для отображения правильного времени, это можно передать при запуске контейнера CMD ["python", "app.py"] # выполняем наше приложение -------------- 2. Готовим requirements.txt, файл зависимостей !!! Если бы без docker для python запуска можно было бы просто выполнить "pip install -r requirements.txt" vim requirements.txt -------------------- flask==1.1.1 -------------------- 4. Готовим файл с ресурсами mkdir resource vim response.json ----------------- { "payload": "Hello, World!!!." } ----------------- 5. Собираем контейнер: docker build -t web-hello . docker images 6. Запуск контейнера и пробрасываем порты: docker run -d --rm --name web -p 8080:8080 web-hello 7. Передача переменной TZ в контейнер и запуск контейнера: docker run -d --rm --name web -p 8080:8080 -e TZ=Europe/Moscow web-hello 8. Монтирование локального каталога в докер (!!! Сейчас этот способ менее популярен) docker run --name hello4 --rm -p 8080:8080 -v /opt/web-hello/resources:/usr/src/app/resources web-hello run - запускаем контейнер --name hello4 - желаемое имя контейнера --rm - в случае остановки удалить контейнер -p 8080:8080 - желаем пробросить порт 8080 -v /opt/web-hello/resources:/usr/src/app/resources - пробросить локальную_папку:в_контейнер web-hello имя используемого образа для контейнера 9. Использование docker volume docker volume ls - посмотреть какие доступны виртуальные разделы docker volume create web - создаем раздел web |
Работа с mongodb и введение использование volume
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
0. Готовим директорию !!! для локального теста нужно установить pip install pymongo storage uuid mkdir /opt/testdb cd /opt/testdb 1. Создаем файл app.py ---------------------- cat > app.py << "EOF" import time from uuid import uuid4 from storage import MongodbService storage = MongodbService.get_instance() for _ in range(5): dto = { "_id": str(uuid4()), "payload": str(uuid4()), "field2": str(int(time.time())) } storage.save_data(dto) for data in storage.get_data(): print(data) EOF ---------------------- 2. Создаем файл storage.py -------------------------- cat > storage.py << "EOF" from pymongo import MongoClient class MongodbService(object): _instance = None _client = None _db = None @classmethod def get_instance(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls, *args, **kwargs) cls.__init__(cls._instance, *args, **kwargs) return cls._instance def __init__(self): self._client = MongoClient("localhost", 27017) self._db = self._client.youtube_db def get_data(self): return list(self._db.statistics.find()) def save_data(self, dto): return self._db.statistics.insert_one(dto) EOF -------------------------- 3. Запуск mongo docker run --rm -d -p 27017:27017 mongo 4. Тестирование нашего приложения python app.py - будут добавляется данные в базу данных 5. при остановке контейнера данные бд будут удалены !!! ВНИМАНИЕ НАКОПЛЕННЫЕ ДАННЫЕ БД УДАЛЯТСЯ docker stop id-mongo |
Docker-compose
1 2 3 4 5 6 7 |
0. перешли в каталог куда мы скачали docker-compose 1. запустили на выполнение docker-compose up -d 2. Посмотрели что там docker ps -a 3. Надоело остановили работу docker-compose down |
Docker-hub push
1 2 3 4 5 |
0. Перешли в каталог с файлами для сборки 1. Выполняем сборку docker build -t Ваш_логин_доке_хаб/имя_приложения . 2. Заливаем образ на докер хаб docker push -t Ваш_логин_доке_хаб/имя_приложения |