- PVSM.RU - https://www.pvsm.ru -

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

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

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

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

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

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

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

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

Цели

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Docker

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

Docker Hub — это примерно то же самое, что GitHub [11] для git-репозиториев, или реестр npm [12] для 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 [14] Образ Сборка образа из Dockerfile
docker tag [15] Образ Тегирование образа
docker images [16] Образ Вывод списка образов
docker run [17] Контейнер Запуск контейнера на основе образа
docker push [18] Образ Отправка образа в реестр
docker pull [19] Образ Загрузка образа из реестра
docker ps [20] Контейнер Вывод списка контейнеров
docker system prune [21] Образ/Контейнер Удаление неиспользуемых контейнеров и образов

▍Файл Dockerfile

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

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

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

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

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

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

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, его нужно протестировать локально.

▍Сборка

Сначала надо собрать образ [14], указав имя, и, что необзательно, тег (если тег задан не будет, система автоматически назначит образу тег 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 [3]. В качестве сервера — DitigalOcean [28].

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

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

Travis CI

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

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

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

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

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

  • 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 в фоне (это — особенность моего рабочего процесса) и выполняю тесты.

Если вы хотите, чтобы в вашем репозитории выводились бы значки со сведениями о покрытии кода тестами, тут [30] вы можете найти краткую инструкцию об использовании 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 [31]. При работе с Aws будет использоваться утилита aws, и так далее.

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

Я не говорю тут о конкретных командах для сервиса, так как этот аспект в разных случаях может сильно варьироваться. Я лишь приведу общий план действий, выполняемый после подключения по 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== you@example.com" | 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-сервером, для того, чтобы заставить работать [33] обратный прокси Nginx и Let’s Encrypt, нужно изрядно повозиться.

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

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

Итоги

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

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

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

Автор: ru_vds

Источник [38]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/razrabotka/346919

Ссылки в тексте:

[1] хостинге: https://www.reg.ru/?rlink=reflink-717

[2] Image: https://habr.com/ru/company/ruvds/blog/488668/

[3] Travis CI: https://travis-ci.org/

[4] Docker Hub: https://hub.docker.com/

[5] TakeNote: https://github.com/taniarascia/takenote

[6] JAMStack: https://jamstack.org/

[7] Netlify: https://www.netlify.com/

[8] данном: https://www.taniarascia.com/full-stack-cookies-localstorage-react-express/

[9] другом: http://qualitytesting.tech/

[10] Инструкцию: https://docs.docker.com/install/

[11] GitHub: https://github.com/

[12] npm: https://www.npmjs.com/

[13] Docker Desktop: https://www.docker.com/get-started

[14] docker build: https://docs.docker.com/engine/reference/commandline/build/

[15] docker tag: https://docs.docker.com/engine/reference/commandline/tag/

[16] docker images: https://docs.docker.com/engine/reference/commandline/images/

[17] docker run: https://docs.docker.com/engine/reference/run/

[18] docker push: https://docs.docker.com/engine/reference/commandline/push/

[19] docker pull: http://about:blank

[20] docker ps: https://docs.docker.com/engine/reference/commandline/ps/

[21] docker system prune: https://docs.docker.com/engine/reference/commandline/system_prune/

[22] FROM: https://docs.docker.com/engine/reference/builder/#from

[23] COPY: https://docs.docker.com/engine/reference/builder/#copy

[24] WORKDIR: https://docs.docker.com/engine/reference/builder/#workdir

[25] RUN: https://docs.docker.com/engine/reference/builder/#run

[26] EXPOSE: https://docs.docker.com/engine/reference/builder/#expose

[27] ENTRYPOINT: https://docs.docker.com/engine/reference/builder/#entrypoint

[28] DitigalOcean: https://www.digitalocean.com/

[29] жизненного цикла: https://docs.travis-ci.com/user/job-lifecycle/

[30] тут: https://www.taniarascia.com/display-build-status-and-test-coverage/

[31] doctl: https://github.com/digitalocean/doctl

[32] этому: https://phoenixnap.com/kb/how-to-install-docker-on-ubuntu-18-04

[33] работать: https://www.digitalocean.com/community/tutorials/how-to-secure-a-containerized-node-js-application-with-nginx-let-s-encrypt-and-docker-compose

[34] мне: mailto:hello@taniarascia.com

[35] маркетплейсе: https://ruvds.com/ru-rub/marketplace#order

[36] Docker: https://ruvds.com/ru-rub/marketplace/docker#advantages

[37] VPS: https://ruvds.com/

[38] Источник: https://habr.com/ru/post/488668/?utm_source=habrahabr&utm_medium=rss&utm_campaign=488668