Для чего используется Docker?

Как разработчик, вы наверняка слышали о Docker в какой-то момент своей профессиональной жизни. И вы знаете, что это стало важной технологией, которую должен знать любой разработчик приложений.

Мы отправимся в путешествие, чтобы узнать, что такое Docker и что вы можете с ним делать. К концу мы также создадим, опубликуем и запустим наш первый образ Docker. Но сначала давайте заложим основу нашей истории.

Содержание:

Немного из истории контейнеров

Docker - это среда выполнения контейнера. Многие думают, что Docker был первым в своем роде, но это неправда - контейнеры Linux существуют с 1970-х годов.

Докер важен как для сообщества разработчиков, так и для сообщества контейнеров, потому что он сделал использование контейнеров простым.

Что такое контейнеры?

Контейнеры или контейнеры Linux - это технология, которая позволяет нам изолировать определенные процессы ядра и заставить их думать, что они единственные, которые работают на совершенно новом компьютере.

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

Другими словами, вам не нужно, чтобы внутри вашей хост-ОС была установлена ​​совершенно другая ОС (называемая гостевой ОС). Вы можете иметь несколько контейнеров, работающих в одной ОС, без установки нескольких разных гостевых ОС.

Разница между виртуальными машинами и контейнерами Docker (Источник: Docker)

Это делает контейнеры намного меньше, быстрее и эффективнее. В то время как виртуальная машина может раскручиваться примерно за минуту и ​​может весить несколько гигабайт, контейнер весит в среднем от 400 до 600 МБ (самые большие).

Они раскручиваются за секунды. Это в основном потому, что им не нужно раскручивать всю операционную систему перед запуском процесса.

История контейнеров

История контейнеров начинается в 1979 году с Unix v7.

В 1979 году в версии 7 Unix был введен системный вызов chroot, который стал началом того, что мы сегодня знаем как виртуализацию процессов.

Вызов chroot позволял ядру изменить видимый корневой каталог процесса и его детей.

Проще говоря, процесс думает, что он работает на машине отдельно, потому что его файловая система отделена от всех других процессов. Тот же самый системный вызов был введен в BSD в 1982 году. Но только два десятилетия спустя, было первое широко распространенное его применение.

В 2000 году хостинг-провайдер искал лучшие способы управления веб-сайтами своих клиентов, поскольку все они были установлены на одном компьютере и конкурировали за одни и те же ресурсы.

Это решение было вызвано jails, и это была одна из первых реальных попыток изолировать материал на уровне процесса. Jails позволил любому пользователю FreeBSD разделить систему на несколько независимых, меньших систем (которые называются jails). Каждый jails может иметь свою собственную конфигурацию IP и конфигурацию системы.

Это было первым решением, расширившим возможности использования chroot не только для разделения на уровне файловой системы, но и для виртуализации пользователей, сети, подсистем и так далее.

В 2008 году был запущен LXC (LinuX Containers). В то время это была первая и наиболее полная реализация системы управления контейнерами. Он использовал группы управления, пространства имен и многое из того, что было создано до того момента. Самым большим достижением было то, что он использовался прямо из ядра Unix и не требовал никаких исправлений.

Докер

В 2010 году Соломон Хайкс и Себастьен Пал создали Docker во время группы стартапов Y Combinator. В 2011 году платформа была запущена.

Первоначально Хайкс начал проект Docker во Франции как часть внутреннего проекта dotCloud, компании PaaS, которая была закрыта в 2016 году.

В то время Docker мало что добавил к среде выполнения контейнеров - самым большим вкладом Docker в экосистему контейнеров была осведомленность. Его простой в использовании интерфейс командной строки и концепции демократизировали использование контейнеров для обычных разработчиков, а не только для компаний, занимающихся глубоким взломом.

После 2013 года несколько компаний начали использовать Docker в качестве среды выполнения контейнеров по умолчанию, поскольку он стандартизировал использование контейнеров во всем мире.

В 2013 году Red Hat объявила о сотрудничестве с Docker, в 2014 пришло время Microsoft, AWS, Stratoscale и IBM.

В 2016 году была анонсирована первая версия Docker для ОС. Windocks выпустила перенос проекта Docker OSS, предназначенного для работы в Windows. И к концу того же года Microsoft объявила, что Docker теперь изначально поддерживается в Windows через Hyper-V.

В 2019 году Microsoft анонсировала WSL2, который позволил Docker работать в Windows без необходимости в виртуализированной машине на Hyper-V. Docker теперь является мультиплатформенным, но при этом все еще использует контейнерный подход Linux.

Наконец, в 2020 году Docker стал мировым выбором для контейнеров. Это произошло не потому, что он лучше других, а потому, что он объединяет все реализации на единой простой в использовании платформе с интерфейсом командной строки.

Как работает докер?

Docker упаковывает приложение и все его зависимости в виртуальный контейнер, который может работать на любом сервере Linux. Вот почему мы называем их контейнерами. Потому что у них есть все необходимые зависимости, содержащиеся в едином программном обеспечении.

Докер состоит из следующих элементов:

  • демон, который используется для создания, запуска и управления контейнерами;
  • высокоуровневый API, который позволяет пользователю общаться с демоном;
  • и CLI, интерфейс, который мы используем, чтобы сделать все доступным.

Контейнеры Docker

Контейнеры - это абстракции уровня приложения. Они объединяют весь код, библиотеки и зависимости. Это позволяет нескольким контейнерам работать на одном хосте. Поэтому вы можете более эффективно использовать ресурсы этого хоста.

Каждый контейнер работает как изолированный процесс в пользовательском пространстве. Занимает меньше места, чем обычные виртуальные машины, благодаря своей многоуровневой архитектуре.

Эти слои называются промежуточными образами и создаются каждый раз, когда вы запускаете новую команду в Dockerfile. Например, если у вас есть Dockerfile следующего вида:

FROM node:stable

COPY . /usr/src/app

WORKDIR /usr/src/app

RUN npm install grpc

RUN npm install

ENTRYPOINT ["npm", "start"]

При каждой команде, например, COPY или RUN вы будете создавать еще один слой поверх изображения контейнера. Это позволяет Docker разделять каждую команду на отдельную часть. Поэтому, если вы в конечном итоге node:stable снова воспользуетесь этим изображением. Вам не нужно будет извлекать все его слои, потому что вы уже установили этот образ.

Кроме того, все слои хешируются. Это означает, что Docker может кэшировать слои и оптимизировать время сборки для слоев, которые не менялись в разных сборках. Вам не нужно будет перестраивать и повторно копировать все файлы, если этап COPY не изменился, что значительно сокращает время, затрачиваемое на процессы сборки.

В конце процесса сборки Docker создает новый пустой слой поверх всех слоев, называемый - тонким записываемым слоем. Это тот слой, к которому вы получаете доступ при использовании docker exec -it <container> <command>. Таким образом, вы можете выполнять интерактивные изменения в изображении и фиксировать их docker commit как если бы вы делали с отслеживаемым файлом Git.

Такая архитектура слоев с дифференцированным хешем возможна благодаря файловой системе AuFS. Это многоуровневая файловая система, которая позволяет размещать файлы и каталоги в виде слоев друг над другом.

AuFS создает некоторые проблемы при работе с DnD (Docker в Docker), но это тема для другой статьи.

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

Итак, если у вас уже есть образ Ubuntu, загруженный на ваш компьютер и вы создаете новый образ, основанный на одном или нескольких слоях этого образа, Docker не будет создавать их снова. Он просто повторно использует те же слои.

(Источник: Packt) Описание слоев Docker

Чем хороши контейнеры Docker?

Вы, наверное, слышали культовую фразу «Это работает на моей машине». Почему бы нам не передать эту машину заказчику?

Именно эту проблему решают Docker и контейнеры в целом. Контейнер Docker - это упакованная коллекция всех библиотек и зависимостей приложения, которые уже созданы и готовы к выполнению.

Многие компании перешли с виртуальных машин на контейнеры не только потому, что они намного легче и быстрее запускаются, а и потому, что их чрезвычайно легко поддерживать.

С его помощью можно управлять версиями одного контейнера Dockerfile, поэтому один разработчик (или даже небольшая группа разработчиков) может легко запустить и поддерживать целую экосистему контейнеров. С другой стороны, вам понадобится специалист по инфраструктуре, чтобы иметь возможность запускать и обслуживать виртуальные машины.

(источник: Docker) Ваш центр обработки данных с виртуальными машинами и контейнерами

Значит ли это, что нам больше не нужны виртуальные машины? Нет, виртуальные машины по-прежнему очень нужны, если вы хотите иметь целую операционную систему для каждого клиента или просто нуждаетесь во всей среде как в песочнице.

Виртуальные машины обычно используются в качестве промежуточных уровней, когда у вас есть большая серверная стойка и несколько клиентов будут ее использовать.

Простота использования и ремонтопригодность подводят нас к еще одному важному аспекту того, почему контейнеры так хороши: для компании намного дешевле использовать контейнеры, чем полноценные виртуальные машины. Это не потому, что инфраструктура или оборудование дешевле, а потому, что вам нужно меньше людей для обслуживания контейнеров, а это означает, что вы можете лучше организовать свою команду, чтобы сосредоточиться на продукте, а не на обслуживании.

Что еще касается экономии, одна виртуальная машина среднего размера может запускать от 3 до 8 контейнеров. Это зависит от того, сколько ресурсов используют ваши контейнеры и какую часть базовой ОС необходимо загрузить перед запуском всего приложения.

Некоторые языки, такие как Go, позволяют создавать образ только из скомпилированного двоичного кода и ничего больше. Это означает, что контейнер Docker будет загружать гораздо меньше и следовательно, будет использовать меньше ресурсов. Таким образом, вы можете развернуть больше контейнеров на каждую виртуальную машину и более эффективно использовать свое оборудование.

Поскольку контейнеры сделаны эфемерными, это означает, что все данные внутри них теряются при удалении контейнера. Это здорово, потому что мы можем использовать контейнеры для пакетных задач, таких как CI.

Использование контейнеров вывело DevOps на новый уровень. Теперь вы можете просто запустить множество контейнеров, каждый из которых выполняет один небольшой шаг в конвейере развертывания, а затем просто убить их, не беспокоясь о том, что вы что-то оставили. Отсутствие состояния контейнеров делает их идеальным инструментом для быстрых рабочих нагрузок.

Теперь, давайте разберемся, как мы можем построить один из них!

Что такое образы Docker?

Образы Docker - это инструкции, записанные в специальном файле с именем Dockerfile. Он имеет собственный синтаксис и определяет, какие шаги предпримет Docker для создания вашего контейнера.

Поскольку контейнеры представляют собой только уровни изменений, каждая новая команда, которую вы создаете в образе Docker, будет создавать новый слой в контейнере.

Последний слой, мы называем, тонким слоем с возможностью записи.

Пустой слой, который может быть изменен пользователем и зафиксирован с помощью docker commit команды. Это пример простого изображения для приложения Node.js:

FROM node:stable
COPY . /usr/src/app/
RUN npm install && npm run build
EXPOSE 3000
ENTRYPOINT ["npm", "start"]

В этом простом примере мы создаем новое изображение. Все изображения основаны на существующем изображении или на исходном изображении. Эти образы загружаются из реестра контейнеров, репозитория для хранения образов контейнеров. Самым распространенным из них является Docker Hub, но вы также можете создать частный, используя облачные решения, такие как реестр контейнеров Azure.

Когда вы работаете docker build в том же каталоге, что и Dockerfile, демон Docker начнет создавать образ и упаковывать его, чтобы вы могли его использовать. Затем вы можете запустить docker run <image-name> новый контейнер.

Обратите внимание, что мы предоставляем определенные порты в Dockerfile. Docker позволяет нам разделять сети в рамках нашей собственной ОС, что означает, что вы можете отображать порты со своего компьютера на контейнер и наоборот. Кроме того, вы можете выполнять команды внутри контейнеров с помощью docker exec. Давайте применим эти знания на практике.

Как развернуть ваше Dockerized приложение?

Это будет простое и легкое пошаговое руководство о том, как создать базовый образ Docker с помощью сервера Node.js и запустить его на вашем компьютере.

Сначала запустите новый проект в выбранном вами каталоге и запустите его, npm init -yчтобы создать новый package.json файл. Теперь давайте создадим еще один каталог с именем src. В этом каталоге мы создадим новый файл с именем server.js.

Теперь в вашем package.json файле измените main ключ на src/server.js. Также удалите test созданный скрипт и замените его на "start": "node src/server.js". Ваш файл должен быть таким:

{
  "name": "your-project",
  "version": "1.0.0",
  "description": "",
  "main": "src/server.js",
  "scripts": {
    "start": "node src/server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Теперь создайте файл с именем Dockerfile (без расширения). Напишем наш образ!

FROM node:lts-alpine
COPY . /usr/src/app/
WORKDIR /usr/src/app
EXPOSE 8089
ENTRYPOINT ["npm", "start"]

Поясним это:

  1. Сначала мы получаем образ узла из Docker Hub. Поскольку изображения сохраняются по именам, мы различаем изображения по их тегам. Вы можете проверить все теги здесь .
  2. Затем мы используем COPY для копирования всех файлов в текущем каталоге (используя .) в новый каталог в контейнере с именем /usr/src/app. Каталог создается автоматически. Это необходимо, потому что нам нужны все файлы нашего приложения.
  3. Теперь мы меняем наш начальный каталог на /usr/src/app каталог, чтобы мы могли запускать вещи из корневого каталога нашего приложения.
  4. Выставляем наш порт,
  5. И мы говорим, что как только наш контейнер запустится, мы выполним «npm start».

Создадим образ, запустив docker build . -t simple-node-image. Таким образом мы отметим наше изображение и дадим ему имя.

Вы увидите, что он создаст и загрузит изображение вместе со всеми необходимыми слоями. Запустим этот образ с помощью следующей команды:

docker run -p 80:8089 simple-node-image 

Теперь попробуйте получить доступ localhost:80 и посмотрите, что произойдет:

Для чего используется Docker?

Теперь, когда мы увидели, как создать контейнер Docker, давайте перейдем к некоторым практическим применениям Docker и тем, как получить от него максимальную отдачу.

Эфемерные базы данных

Вы когда-нибудь пытались разработать приложение, для работы которого требуется база данных? Или, что еще хуже, пытались запустить чужое приложение, которому нужна база данных, которую вы не установили?

Старое решение заключалось в том, чтобы сначала установить базу данных, а затем запустить приложение. С Docker вам просто нужно запустить контейнер базы данных. Запустим простой контейнер MongoDB:

$ docker run -p 27017:27017 --name my-ephemeral-db mongo

Это оно! Теперь вы можете получить доступ к своей базе данных со своего компьютера через порт 27017, как обычно.

Постоянные базы данных

Проблема с предыдущим примером заключается в том, что если вы удалите контейнер, все ваши данные будут потеряны. Итак, что произойдет, если вы хотите запустить локальную базу данных без необходимости ее установки, но сохранить данные после ее удаления? Вы можете привязать Docker к тому!

Когда вы привязываете Docker к локальному тому, вы, по сути, монтируете свою файловую систему в контейнер или наоборот. Посмотрим:

$ docker run -p 27017:27017 -v /home/my/path/to/db:/data/db --name my-persistent-db mongo

В этой команде мы монтаж /data/dbв /home/my/path/to/db. Теперь, если мы используем, docker stop my-persistent-dbи docker rm my-persistent-db все наши данные будут продолжать храниться там.

Позже, если нам снова понадобится база данных, мы можем смонтировать ее с помощью той же команды, и все данные вернутся.

Одноразовые инструменты

Еще одна вещь, которую делают все разработчики: мы устанавливаем приложения, которые используем только один раз. Например, этот простой интерфейс командной строки для доступа к старой базе данных или простой графический интерфейс для некоторого сервера CI.

У многих инструментов уже есть контейнеры Docker, и вы можете использовать их таким образом, поэтому вам не нужно устанавливать еще один инструмент в свой блокнот.

Лучший пример - Redis. Он redis-cli встроен в другой контейнер, поэтому вам не нужно устанавливать его redis-cli в свою оболочку, если вы почти не используете его.

Представим, что вы запускаете экземпляр Redis с docker run -d --name redis redis --bind 127.0.0.1 привязкой к интерфейсу localhost. Вы можете получить к нему доступ через другой контейнер, используя:

$ docker run --rm -it --network container:redis redis-cli -h 127.0.0.1

Флаг --rm говорит докер , что он должен удалить контейнер , как только он остановился, в то время как -it флаги сказать ему, что мы хотим интерактивный сеанс (с оболочкой) и мы будем нуждаться в TTY.

Запускайте целые стеки

Если вам нужно протестировать приложение, которое полагается на другое приложение, как бы вы это сделали? Docker упрощает задачу, предоставляя docker-compose. Это еще один инструмент в вашем наборе инструментов, который позволяет вам кодировать docker-compose.yml файл, описывающий вашу среду.

Файл выглядит так:

version: "3.8"
services:
  web:
    build: .
    ports:
      - "5000:5000"

  redis:
    image: "redis:alpine"
    ports:
      - "6379:6379"

Как видите, мы определяем две службы, одна вызывается web и запускается docker build по web.build пути. Это файл Dockerfile.

После этого он выставляет порт 5000 как на хосте, так и в контейнере. Другой сервис redis извлекает и запускает redis образ через порт 6379.

Самое приятное то, что сетевой уровень является общим, другими словами, вы можете получить доступ redis из web службы, просто набрав redis и порт.

Вы можете начать этот файл с простого docker-compose up и увидеть, как происходит волшебство.

Заключение

Это история Docker, как он появился и как работает. Как видите, в большинстве случаев Docker используется для облегчения жизни разработчиков при разработке приложений. Но есть много других применений, таких как уровни инфраструктуры и упрощение обслуживания ваших приложений.

Читайте также

Комментарии ()

    Написать комментарий