Создание CI/CD-цепочки и автоматизация работы с Docker

Я написала мои первые сайты в конце 90-х. Тогда приводить их в рабочее состояние было очень просто. Был Apache-сервер на каком-нибудь общем хостинге, на этот сервер можно было войти по FTP, написав в браузерной строке нечто вроде ftp://ftp.example.com. Потом надо было ввести имя и пароль и выгрузить файлы на сервер. Другие были времена, всё тогда было проще, чем сейчас.

Создание CI/CD-цепочки и автоматизация работы с Docker

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

Для моего персонального проекта у меня была особая конфигурация. И я знала, что мне нужна возможность разворачивать сайт в продакшне, выполняя всего одно действие: запись кода в ветку master на GitHub. Я, кроме того, знала, что мне, для обеспечения работы моего маленького веб-приложения, не хочется заниматься управлением огромным кластером Kubernetes, или пользоваться технологией Docker Swarm, или поддерживать парк серверов с подами, агентами и всякими другими сложностями. Для того чтобы достичь цели по максимальному упрощению работы, мне понадобилось познакомиться с CI/CD.

Если у вас имеется маленький проект (в нашем случае речь идёт о Node.js-проекте) и вам хотелось бы узнать о том, как автоматизировать развёртывание этого проекта, сделав при этом так, чтобы то, что хранится в репозитории, в точности соответствовало бы тому, что работает в продакшне, то, полагаю, вас может заинтересовать эта статья.

Предварительные требования

Ожидается, что читатель этой статьи имеет базовые знания в области работы с командной строкой и написания Bash-скриптов. Кроме того, ему понадобятся учётные записи Travis CI и Docker Hub.

Цели

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

Вот каким в итоге получился мой рабочий процесс.

Для кода, отправленного в любую ветку репозитория, кроме master, производятся такие действия:

  • Запускается сборка проекта на Travis CI.
  • Выполняются все модульные, интеграционные и сквозные тесты.

Только для кода, который попадает в master, выполняется следующее:

  • Всё то, о чём сказано выше, плюс…
  • Сборка образа Docker на основании текущего кода, настроек и окружения.
  • Размещение образа на Docker Hub.
  • Подключение к продакшн-серверу.
  • Загрузка образа с Docker Hub на сервер.
  • Остановка текущего контейнера и запуск нового, основанного на новом образе.

Если вы совершенно ничего не знаете о Docker, об образах и контейнерах — не беспокойтесь. Я об этом всём вам расскажу.

Что такое CI/CD?

Аббревиатура CI/CD расшифровывается как «continuous integration/continuous deployment» — «непрерывная интеграция/непрерывное развёртывание».

▍Непрерывная интеграция

Непрерывная интеграция — это процесс, в ходе которого разработчики делают коммиты в главное хранилище исходного кода проекта (обычно в ветку master). При этом качество кода обеспечивается путём проведения автоматизированного тестирования.

▍Непрерывное развёртывание

Непрерывное развёртывание — это частое автоматизированное развёртывание кода в продакшне. Вторая часть аббревиатуры CI/CD иногда раскрывается как «continuous delivery» («непрерывная доставка»). Это, в целом, то же самое, что и «непрерывное развёртывание», но «непрерывная доставка» подразумевает необходимость ручного подтверждения изменений перед запуском процесса развёртывания проекта.

Начало работы

Приложение, на котором я это всё осваивала, называется TakeNote. Это — веб-проект, над которым я работаю, предназначенный для того, чтобы делать заметки. Сначала я попыталась сделать JAMStack-проект, или только фронтенд-приложение без сервера, для того чтобы воспользоваться стандартными возможностями по хостингу и развёртыванию проектов, которые предлагает Netlify. По мере того, как росла сложность приложения, мне понадобилось создать и его серверную часть, а это означало, что мне надо было бы сформировать собственную стратегию по автоматизированной интеграции и автоматизированному развёртыванию проекта.

В моём случае приложение представляет собой Express-сервер, работающий в среде Node.js, обслуживающий одностраничное React-приложение и поддерживающий защищённый серверный API. Эта архитектура следует стратегии, которую можно найти в данном руководстве по фуллстек-аутентификации.

Я посоветовалась с другом, который является экспертом по автоматизации, и спросила его о том, что мне надо сделать для того, чтобы всё это работало так, как мне нужно. Он подкинул мне идею о том, как должен выглядеть автоматизированный рабочий процесс, изложенный в разделе «Цели» этой статьи. То, что я поставила перед собой подобные цели, означало, что мне нужно разобраться в том, как пользоваться Docker.

Docker

Docker — это инструмент, который, благодаря технологии контейнеризации, позволяет легко распространять приложения, а также выполнять их развёртывание и запуск в одном и том же окружении даже в том случае, если сама платформа Docker работает в различных средах. Для начала мне нужно было получить в своё распоряжение инструменты командной строки (CLI) Docker. Инструкцию по установке Docker нельзя назвать очень чёткой и понятной, но из неё можно узнать о том, что для того, чтобы сделать первый шаг установки, надо скачать Docker Desktop (для Mac или Windows).

Docker Hub — это примерно то же самое, что GitHub для git-репозиториев, или реестр npm для JavaScript-пакетов. Это — онлайн-репозиторий для образов Docker. Именно к нему подключается Docker Desktop.

Итак, для того чтобы приступить к работе с Docker, нужно сделать две вещи:

После этого можете проверить работоспособность Docker CLI, выполнив следующую команду для проверки версии Docker:

docker -v

Далее, войдите в Docker Hub, введя, когда вас об этом спросят, свое имя пользователя и пароль:

docker login

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

▍Образы

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

# Вывод сведений обо всех образах
docker images

Эта команда выведет таблицу со следующим заголовком:

REPOSITORY     TAG     IMAGE ID     CREATED     SIZE
---

Далее мы будем рассматривать некоторые примеры команд в таком же формате — сначала идёт команда с комментарием, а потом — пример того, что она может вывести.

▍Контейнеры

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

# Перечисление всех контейнеров
docker ps -a
CONTAINER ID     IMAGE     COMMAND     CREATED     STATUS     PORTS     NAMES
---

▍Теги

Тег — это указание на конкретную версию образа.

▍Краткая справка по командам Docker

Вот обзор некоторых часто используемых команд Docker.

Команда

Контекст

Действие

docker build

Образ

Сборка образа из Dockerfile

docker tag

Образ

Тегирование образа

docker images

Образ

Вывод списка образов

docker run

Контейнер

Запуск контейнера на основе образа

docker push

Образ

Отправка образа в реестр

docker pull

Образ

Загрузка образа из реестра

docker ps

Контейнер

Вывод списка контейнеров

docker system prune

Образ/Контейнер

Удаление неиспользуемых контейнеров и образов

▍Файл Dockerfile

Я знаю, как локально запустить приложение для продакшна. У меня есть Webpack-конфигурация, предназначенная для сборки готового React-приложения. Далее, у меня имеется команда, запускающая сервер, основанный на Node.js, на порте 5000. Выглядит это так:

npm i         # установка зависимостей
npm run build # сборка React-приложения
npm run start # запуск Node-сервера

Надо отметить, что у меня нет приложения-примера для этого материала. Но тут, для экспериментов, подойдёт любое простое Node-приложение.

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

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

  • FROM — Эта команда начинает файл. В ней указывается базовый образ, на основе которого строится контейнер.
  • COPY — Копирование файлов из локального источника в контейнер.
  • WORKDIR — Установка рабочей директории для следующих команд.
  • RUN — Запуск команд.
  • EXPOSE — Настройка порта.
  • ENTRYPOINT — Указание выполняемой команды.

Dockerfile может выглядеть примерно так:

# Загрузить базовый образ
FROM node:12-alpine

# Скопировать файлы из текущей директории в директорию app/
COPY . app/

# Использовать app/ в роли рабочей директории
WORKDIR app/

# Установить зависимости (команда npm ci похожа npm i, но используется для автоматизированных сборок)
RUN npm ci --only-production

# Собрать клиентское React-приложение для продакшна
RUN npm run build

# Прослушивать указанный порт
EXPOSE 5000

# Запустить Node-сервер
ENTRYPOINT npm run start

В зависимости от выбранного базового образа вам может понадобиться установить дополнительные зависимости. Дело в том, что некоторые базовые образы (вроде Node Alpine Linux) созданы с целью сделать их как можно более компактными. В результате в них могут отсутствовать некоторые программы, на которые вы рассчитываете.

▍Сборка, тегирование и запуск контейнера

Локальные сборка и запуск контейнера — это, после того, как у нас есть Dockerfile, задачи довольно простые. Прежде чем отправлять образ на Docker Hub, его нужно протестировать локально.

▍Сборка

Сначала надо собрать образ, указав имя, и, что необзательно, тег (если тег задан не будет, система автоматически назначит образу тег latest).

# Сборка образа
docker build -t <image>:<tag> .

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

Sending build context to Docker daemon   2.88MB
Step 1/9 : FROM node:12-alpine
 ---> ...выполнение этапов сборки...
Successfully built 123456789123
Successfully tagged <image>:<tag>

Сборка может занять пару минут — тут всё зависит от того, сколько у вас имеется зависимостей. После завершения сборки можно выполнить команду docker images и взглянуть на описание своего нового образа.

REPOSITORY          TAG               IMAGE ID            CREATED              SIZE
<image>             latest            123456789123        About a minute ago   x.xxGB

▍Запуск

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

# Запуск с использованием локального порта 5000 и порта контейнера 5000
docker run -p 5000:5000 <image>:<tag>

Теперь, когда контейнер создан и запущен, можно воспользоваться командой docker ps для того чтобы взглянуть на сведения об этом контейнере (или можно воспользоваться командой docker ps -a, которая выводит сведения обо всех контейнерах, а не только о работающих).

CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS                      PORTS                    NAMES
987654321234        <image>             "/bin/sh -c 'npm run…"   6 seconds ago        Up 6 seconds                0.0.0.0:5000->5000/tcp   stoic_darwin

Если перейти теперь по адресу localhost:5000 — можно увидеть страницу работающего приложения, которая выглядит точно так же, как страница приложения, работающего в продакшн-окружении.

▍Назначение тега и публикация

Для того чтобы воспользоваться одним из созданных образов на продакшн-сервере, нужно, чтобы у нас была бы возможность загрузить этот образ с Docker Hub. Это значит, что сначала надо создать на Docker Hub репозиторий для проекта. После этого в нашем распоряжении окажется место, куда можно оправить образ. Образ надо переименовать так, чтобы его имя начиналось с нашего имени пользователя на Docker Hub. После этого должно идти название репозитория. В конце имени может располагаться любой тег. Ниже показан пример именования образов по этой схеме.

Теперь можно собрать образ с назначением ему нового имени и выполнить команду docker push для отправки его в репозиторий Docker Hub.

docker build -t <username>/<repository>:<tag> .
docker tag <username>/<repository>:<tag> <username>/<repository>:latest
docker push <username>/<repository>:<tag>

# На практике это может выглядеть, например, так:
docker build -t user/app:v1.0.0 .
docker tag user/app:v1.0.0 user/app:latest
docker push user/app:v1.0.0

Если всё пройдёт как надо, образ будет доступен на Docker Hub и его легко можно будет загрузить на сервер или передать другим разработчикам.

Следующие шаги

К настоящему моменту мы убедились в том, что приложение, в виде контейнера Docker, работает локально. Мы загрузили контейнер на Docker Hub. Всё это значит, что мы уже очень неплохо продвинулись к цели. Теперь надо решить ещё два вопроса:

  • Настройка CI-инструмента для тестирования и развёртывания кода.
  • Настройка продакшн-сервера так, чтобы он мог бы загружать и запускать наш код.

В нашем случае в качестве CI/CD-решения используется Travis CI. В качестве сервера — DitigalOcean.

Надо отметить, что здесь можно воспользоваться и другой комбинацией сервисов. Например, вместо Travis CI можно воспользоваться CircleCI или Github Actions. А вместо DigitalOcean — AWS или Linode.

Мы решили работать с Travis CI, а в этом сервисе у меня уже кое-что настроено. Поэтому сейчас я кратко расскажу о том, как подготовить его к работе.

Travis CI

Travis CI — это инструмент для тестирования и развёртывания кода. Мне не хотелось бы входить в тонкости настройки Travis CI, так как каждый проект уникален, и это не принесёт особой пользы. Но я расскажу об основах, которые позволят вам начать работу в том случае, если вы решите пользоваться Travis CI. Что бы вы ни выбрали — Travis CI, CircleCI, Jenkins, или что-то другое, везде будут применяться похожие методы настройки.

Для того чтобы приступить к работе с Travis CI, перейдите на сайт проекта и создайте учётную запись. Затем интегрируйте Travis CI с вашим GitHub-аккаунтом. Вам, в ходе настройки системы, понадобится указать репозиторий, работу с которым вы хотите автоматизировать, и включить доступ к нему. (Я пользуюсь GitHub, но уверена, что Travis CI может интегрироваться и с BitBucket, и с GitLab, и с другими подобными сервисами).

Каждый раз, когда Travis CI принимается за работу, запускается сервер, выполняющий указанные в конфигурационном файле команды, включая развёртывание соответствующих веток репозитория.

▍Жизненный цикл задания

Конфигурационный файл Travis CI, называемый .travis.yml и хранящийся в корневой директории проекта, поддерживает концепцию событий жизненного цикла задания. Вот эти события, приведённые в том порядке, в котором они происходят:

  • apt addons
  • cache components
  • before_install
  • install
  • before_script
  • script
  • before_cache
  • after_success или after_failure
  • before_deploy
  • deploy
  • after_deploy
  • after_script

▍Тестирование

В конфигурационном файле я собираюсь настроить локальный сервер Travis CI. В качестве языка я выбрала Node 12 версии и указала системе на то, что нужно установить зависимости, необходимые для использования Docker.

Всё, что перечислено в .travis.yml, будет выполняться при выполнении всех pull-запросов во все ветки репозитория, если только не указано иное. Это полезная особенность, так как она означает, что мы можем тестировать весь код, поступающий в репозиторий. Это позволяет знать о том, готов ли код к записи в ветку master, и не нарушит ли он процесс сборки проекта. В этой глобальной конфигурации я устанавливаю всё локально, запускаю сервер разработчика Webpack в фоне (это — особенность моего рабочего процесса) и выполняю тесты.

Если вы хотите, чтобы в вашем репозитории выводились бы значки со сведениями о покрытии кода тестами, тут вы можете найти краткую инструкцию об использовании Jest, Travis CI и Coveralls для сбора и вывода этих сведений.

Итак, вот содержимое файла .travis.yml:

# Установить язык
language: node_js

# Установить версию Node.js
node_js:
  - '12'

services:
  # Использовать командную строку Docker
  - docker

install:
  # Установить зависимости для тестов
  - npm ci

before_script:
  # Запустить сервер и клиент для тестов
  - npm run dev &

script:
  # Запустить тесты
  - npm run test

Здесь заканчиваются те действия, которые выполняются для всех ветвей репозитория и для pull-запросов.

▍Развёртывание

Исходя из предположения о том, что все автоматизированные тесты завершились успешно, мы, что делать необязательно, можем развернуть код на продакшн-сервере. Так как мы хотим делать это лишь для кода из ветки master, мы даём системе соответствующие указания в настройках развёртывания. Прежде чем вы попробуете воспользоваться в своём проекте кодом, который мы рассмотрим дальше, я хотела бы предупредить вас о том, что у вас должен быть реальный скрипт, вызываемый для развёртывания.

deploy:
  # Собрать Docker-контейнер и отправить его на Docker Hub
  provider: script
  script: bash deploy.sh
  on:
    branch: master

Скрипт развёртывания решает две задачи:

  • Сборка, тегирование и отправка образа на Docker Hub средствами CI-инструмента (в нашем случае это Travis CI).
  • Загрузка образа на сервере, остановка старого контейнера и запуск нового (в нашем случае сервер работает на платформе DigitalOcean).

Сначала нужно настроить автоматический процесс сборки, тегирования и отправки образа на Docker Hub. Всё это очень похоже на то, что мы уже делали вручную, за исключением того, что тут нам нужна стратегия назначения образам уникальных тегов и автоматизация входа в систему. У меня были сложности с некоторыми деталями скрипта развёртывания, с такими, как стратегия тегирования, вход в систему, кодировка SSH-ключей, установление SSH-соединения. Но, к счастью, мой бойфренд очень хорошо управляется с bash, как и со многими другими вещами. Он помог мне написать этот скрипт.

Итак, первая часть скрипта — это отправка образа на Docker Hub. Сделать это довольно просто. Использованная мной схема составления тегов подразумевает комбинирование git-хэша и git-тега, если он существует. Это позволяет обеспечить создание уникального тега и упрощает идентификацию сборки, на которой он основан. DOCKER_USERNAME и DOCKER_PASSWORD — это пользовательские переменные окружения, которые можно задать с помощью интерфейса Travis CI. Travis CI автоматически обработает секретные данные так, чтобы они не попали в чужие руки.

Вот первая часть скрипт deploy.sh.

#!/bin/sh
set -e # Остановить скрипт при наличии ошибок

IMAGE="<username>/<repository>"                             # Образ Docker
GIT_VERSION=$(git describe --always --abbrev --tags --long) # Git-хэш и теги

# Сборка и тегирование образа
docker build -t ${IMAGE}:${GIT_VERSION} .
docker tag ${IMAGE}:${GIT_VERSION} ${IMAGE}:latest

# Вход в Docker Hub и выгрузка образа
echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
docker push ${IMAGE}:${GIT_VERSION}

То, какой будет вторая часть скрипта, полностью зависит от того, какой хост вы используете, и от того, как организовано подключение к нему. В моём случае, так как пользуюсь я Digital Ocean, для подключения к серверу используются команды doctl. При работе с Aws будет использоваться утилита aws, и так далее.

Настроить работу сервера было не особенно сложно. Так, я настроила дроплет, основанный на базовом образе. Надо отметить, что выбранная мной система требует выполнения однократной ручной установки Docker и однократного ручного запуска Docker. Я, для установки Docker, использовала Ubuntu 18.04, поэтому вы, если тоже используете Ubuntu, чтобы сделать то же самое, можете просто следовать этому простому руководству.

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

  • Нужно найти контейнер, который сейчас выполняется, и остановить его.
  • Затем нужно, в фоне, запустить новый контейнер.
  • Вам нужно будет установить локальный порт сервера в значение 80 — это позволит входить на сайт по адресу вида example.com, без указания порта, а не пользоваться адресом наподобие example.com:5000.
  • И, наконец, нужно удалить все старые контейнеры и образы.

Вот продолжение скрипта.

# Найти ID работающего контейнера
CONTAINER_ID=$(docker ps | grep takenote | cut -d" " -f1)

# Остановить старый контейнер, запустить новый, очистить систему
docker stop ${CONTAINER_ID}
docker run --restart unless-stopped -d -p 80:5000 ${IMAGE}:${GIT_VERSION}
docker system prune -a -f

Некоторые вещи, на которые стоит обратить внимание

Возможно, когда вы подключитесь к серверу по SSH из Travis CI, вы увидите предупреждение, которое не позволит продолжить установку, так как система будет ждать реакции пользователя.

The authenticity of host '<hostname> (<IP address>)' can't be established.
RSA key fingerprint is <key fingerprint>.
Are you sure you want to continue connecting (yes/no)?

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

echo <public key> | base64 # выводит <публичный ключ, закодированный в base64>

На практике эта команда может выглядеть так:

echo "123.45.67.89 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
NrRFi9wrf+M7Q== [email protected]" | base64

А вот как выглядит то, что она выдаёт — строка в кодировке base64:

MTIzLjQ1LjY3Ljg5IHNzaC1yc2EgQUFBQUIzTnphQzF5YzJFQUFBQUJJd0FBQVFFQWtsT1Vwa0RIcmZIWTE3U2JybVRJcE5MVEdLOVRqb20vQldEU1UKR1BsK25hZnpsSERUWVc3aGRJNHlaNWV3MThKSDRKVzlqYmhVRnJ2aVF6TTd4bEVMRVZmNGg5bEZYNVFWa2JQcHBTd2cwY2RhMwpQYnY3a09kSi9NVHlCbFdYRkNSK0hBbzNGWFJpdEJxeGlYMW5LaFhwSEFac01jaUxxOFY2UmpzTkFRd2RzZE1GdlNsVksvN1hBCnQzRmFvSm9Bc25jTTFROXg1KzNWMFd3NjgvZUlGbWIxenVVRmxqUUpLcHJyWDg4WHlwTkR2allOYnk2dncvUGIwcndlcnQvRW4KbVorQVc0T1pQblRQSTg5WlBtVk1MdWF5ckQyY0U4NlovaWw4YitndzNyMysxbkthdG1Ja2puMnNvMWQwMVFyYVRsTXFWU3NieApOclJGaTl3cmYrTTdRPT0geW91QGV4YW1wbGUuY29tCg==

Вот команда, о которой говорилось выше

install:
  - echo < публичный ключ, закодированный в base64> | base64 -d >> $HOME/.ssh/known_hosts

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

Ещё одна вещь, на которую стоит обратить внимание, это то, что вам может понадобиться запустить весь скрипт развёртывания, представленный в виде одной строки, например — с помощью doctl. Это может потребовать некоторых дополнительных усилий.

doctl compute ssh <droplet> --ssh-command "все команды будут здесь && здесь"

TLS/SSL и балансировка нагрузки

После того, как я сделала всё то, о чём шла речь выше, последней вставшей передо мной проблемой стало то, что у сервера не было SSL. Так как я пользуюсь Node.js-сервером, для того, чтобы заставить работать обратный прокси Nginx и Let’s Encrypt, нужно изрядно повозиться.

Мне совсем не хотелось выполнять все эти SSL-настройки вручную, поэтому я просто создала балансировщик нагрузки и записала сведения о нём в DNS. В случае с DigitalOcean, например, создание автообновляемого самоподписываемого сертификата на балансировщике нагрузки — простая, бесплатная и быстрая процедура. У такого подхода есть и дополнительное преимущество, которое заключается в том, что это, если нужно, позволяет очень просто настроить SSL на множестве серверов, работающих за балансировщиком нагрузки. Это позволяет самим серверам совершенно не «задумываться» о SSL, но при этом использовать, как обычно, порт 80. Так что настройка SSL на балансировщике нагрузки — это гораздо проще и удобнее, чем альтернативные методы настройки SSL.

Теперь можно закрыть на сервере все порты, принимающие входящие соединения — кроме порта 80, используемого для связи с балансировщиком нагрузки, и порта 22 для SSH. В результате попытка прямого обращения к серверу по любым портам, за исключение этих двух, потерпит неудачу.

Итоги

После того, как я сделала всё то, о чём рассказала в этом материале, меня уже не пугала ни платформа Docker, ни концепции автоматизированных CI/CD-цепочек. Я смогла настроить цепочку непрерывной интеграции, в ходе выполнения которой производится тестирование кода до попадания его в продакшн и автоматическое развёртывание кода на сервере. Всё это для меня пока ещё относительно ново, и я уверена, что есть способы улучшить мой автоматизированный рабочий процесс и сделать его эффективнее. Поэтому если у вас есть идеи на этот счёт — дайте мне знать. Надеюсь, эта статья помогла вам в ваших делах. Мне хочется верить, что прочтя её, вы узнали столько же, сколько узнала я, пока разбиралась со всем тем, о чём в ней рассказала.

P.S. В нашем маркетплейсе имеется образ Docker, который устанавливается в один клик. Вы можете проверить работу контейнеров на VPS. Всем новым клиентам бесплатно предоставляются 3 дня для тестирования.

Уважаемые читатели! Пользуетесь ли вы технологиями CI/CD в своих проектах?

Создание CI/CD-цепочки и автоматизация работы с Docker

Источник: habr.com