Непрерывная интеграция и развертывание на примере GitLab CI + Flask + Docker + Docker Swarm. Часть 1. Настройка среды
Мы рассмотрели основы работы с GitLab в статье “Работа с образом GitLab в Vscale”. В этом руководстве обратим внимание на удобный встроенный инструмент GitLab Continous Integration (GitLab CI), который позволяет настроить непрерывную интеграцию и развертывание прямо через GitLab без использования дополнительных инструментов, таких как Jenkins.
Перед началом работы рекомендуется изучить материалы на тему контейнеризации, Docker и Docker Swarm, особенно, если нет опыта работы с этими инструментами. На официальном сайте Docker, есть хорошее пошаговое руководство есть которое поможет быстро войти в курс дела.
Непрерывная интеграция позволяет автоматически запускать различные работы (тестирование, сборка) при наступлении определенных событий, например обновлении кода в репозитории. Кроме того, можно настроить непрерывную доставку приложения - автоматическое развертывание на рабочем сервере при успешной сборке и прохождении тестов.
Знакомство с непрерывной интеграцией мы начнём с настройки рабочей среды и инструментов, а затем рассмотрим создание и докеризацию простого Flask приложения с использованием Docker и Docker Swarm для оркестрации контейнеров на собственных серверах во второй части. Использование контейнеров позволяет инкапсулировать исполняемую среду и даёт возможность масштабировать приложение.
Управление контейнерами и управление нагрузкой - нетривиальная тема и требует значительной подготовки, особенно если планируется самостоятельная настройка системы оркестрации (например, с помощью Kubernetes). Однако, существуют такие инструменты как Docker Swarm или Rancher, которые берут на себя управление контейнерами, внутренними сетями и распределение нагрузок и дают возможность развернуть масштабируемую систему на собственных серверах.
Кроме того, GitLab хорошо поддерживает Docker и позволяет подключать собственные системы хранения образов (GitLab Registry) в несколько простых шагов. А также отслеживать статус выполнения работ по проекту и управлять развернутыми версиями приложения, позволяя при необходимости откатиться на прошлую в один клик.
Предварительные требования
В этой статье используются системы и инструменты, подготовленные в других руководствах, поэтому убедитесь, что сейчас у вас есть:
- сервер с GitLab;
- GitLab защищен с помощью SSL-сертификата;
- в GitLab добавлен SSH-ключ.
Если что-то из этого пропущено, воспользуйтесь статьями:
Установка серверов
Для настройки и запуска приложения в режиме Swarm нам понадобится 2 типа серверов - менеджер и подчиненный. Кроме того, дополнительно один сервер будет выделен под GitLab Runner для выполнения задачи сборки и запуска контейнеров.
В условиях реальной подготовки серверов рекомендуем использовать систему управления конфигураций, например Ansible или Chef, а сейчас используем простой shell-скрипт с использованием Vscale API для создания серверов с предустановленными образами Docker.
Все действия будем производить через терминал в Ubuntu 16.04, что не остановит пользователей других систем - достаточно зайти в панель управления Vscale и вручную создать три разных сервера из образа Docker минимальной конфигурации.
Получение токена для API
В панели управления Vscale в разделе настроек выберем пункт Управление токенами:
Создадим новый токен и скопируем его в буфер обмена:
Работа с Vscale API
Откроем терминал и заменив значение токена, выполним следующую команду для получения идентификаторов добавленных SSH-ключей (инструкция по добавлению ключей в Vscale, если этого ещё не было сделано):
$ curl 'https://api.vscale.io/v1/sshkeys' -H 'X-Token: c55e11381f261a5a25504439587e7a6e3e9c228f53c24b426ba04a8819ee9596'
Мы получим массив ключей и значения их идентификаторов (поле id). Выберем нужный ключ и запомним его id:
[{"key": "ssh-rsa AAAAB3NzaC1yc2eAAAADAQABAAABAQCpjmfj7diGruSbB0lls8gN80Om/RhxC7KuQeNeNteb2DKZCxoTkqDnWNLUiTr3XFGm5HguMGonOb8/5lI5NWdjAwTpe0eayn/Qo7s42md3KjWE2FCjfOMt94VCZV0+/v9ClMY4Vc6FuTIoQzIeflIKR1yt68r11bdeZuaaqfb8Llf2XjIBETPvgigI+rHaXHe2PFjij5dr2nIN3BAdu4peYsBt8Vyki63m7ib3PeoMzW8QytvotbOOg5WAhs4b6fD6mM7iiV1N/GHD5jeSI1XHWZqPD8zIphIgy0wEv4oyy7Zak20TfvDnjfrQZ8xaTm3zT6Nki5Sxxdl223xRwFjX selectel@selectel-Aspire-V3-371", "name": "SSH-KEY", "id": 15645}]
Теперь необходимо получить список доступных образов из которых можно создать сервер. Для всех скалетов нам понадобится Docker, поэтому используем следующую команду для поиска интересующих нас образов:
$ curl 'https://api.vscale.io/v1/images' -H 'X-Token: c55e11381f261a5a25504439587e7a6e3e9c228f53c24b426ba04a8819ee9596' | grep -o -P '.{0,27}docker.{0,10}'
Получим следующий сокращенный ответ:
% Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 10556 100 10556 0 0 27500 0 --:--:-- --:--:-- --:--:-- 27561
"id": "ubuntu_16.04_64_001_docker", "active ion": "Ubuntu_16.04_64_001_docker_1.12.6"},
"id": "ubuntu_16.04_64_001_docker", "active ion": "Ubuntu_16.04_64_001_docker_1.12.6"},
Нас интересует значение с полем id - это идентификатор образа в московском и ленинградском регионах. Они одинаковы, поэтому нам неважно, откуда копировать это значение.
Теперь создадим небольшой скриптовый файл для быстрого создания серверов:
$ nano setup.sh
И запишем следующий текст, заменив название образа на идентификатор, полученный на предыдущем шаге:
#!/bin/bash
curl -i -X POST 'https://api.vscale.io/v1/scalets' -H 'Content-Type: application/json;charset=UTF-8' -H "X-Token: $1" -d '{"make_from":"ubuntu_16.04_64_001_docker","rplan":"small","do_start":true,"name":"Manager","keys":['"$2"'],"location":"spb0"}'
curl -i -X POST 'https://api.vscale.io/v1/scalets' -H 'Content-Type: application/json;charset=UTF-8' -H "X-Token: $1" -d '{"make_from":"ubuntu_16.04_64_001_docker","rplan":"small","do_start":true,"name":"Node1","keys":['"$2"'],"location":"spb0"}'
curl -i -X POST 'https://api.vscale.io/v1/scalets' -H 'Content-Type: application/json;charset=UTF-8' -H "X-Token: $1" -d '{"make_from":"ubuntu_16.04_64_001_docker","rplan":"small","do_start":true,"name":"Runner","keys":['"$2"'],"location":"spb0"}'
После внесения изменения сохраните изменения сочетанием клавиш ‘Ctrl’+’X’.
В этом файле мы обращаемся к Vscale API с задачей создать три сервера минимальной конфигурации, расположенные в Санкт-Петербурге. Для выполнения скрипта необходимо передать два параметра - токен и id SSH-ключа, которые мы получили ранее:
$ sh setup.sh c55e11381f261a5a25504439587e7a6e3e9c228f53c24b426ba04a8819ee9596 15645
Если всё прошло успешно, в панели управления Vscale увидим три новых сервера, созданных из образа Docker:
Защита сервиса докера на управляющем сервере самоподписанными сертификатом
Примечание. Использование TLS и управление центром сертификации - тема, требующая значительной подготовки. Желательно ознакомиться с технологиями OpenSSL, x509 и TLS перед их использованием в реальных проектах.
На финальном этапе развертывания приложения в рабочей Swarm-среде необходимо защищенное соединение между GitLab Runner и сервисом Docker, запущенном на сервере Manager, что видно на схеме ниже:
Схема 1. Процесс развертывания приложения в рабочей среде.
Для этого необходимо использовать единый сертификационный центр для клиента (GitLab Runner) и сервиса Docker (Manager). После создания сертификата и ключа для клиента, их можно использовать для удаленного подключения к сервису Docker и выполнения различных операций. Следует быть максимально осторожными с хранением клиентских сертификатов и ключей, поскольку они дают полное управление надо сервисом Docker.
Обновление сервиса Docker на машине Manager
Подключимся по SSH к серверу Manager (IP адрес доступен в панели управления Vscale) и обновим Docker, поскольку в дальнейшем нам потребуются дополнительные возможности, доступные в старшей версии Docker API. Добавим репозиторий от разработчиков Docker для получения последней версии:
$ apt-get update
$ apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
$ add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
$ apt-get update
Установим последнюю версию Docker:
$ apt-get install docker-ce
Для проверки установки выполним команду:
$ docker version
Вывод команды может отличаться, главное, чтобы обе версии API клиента и сервера были больше 1.24:
Client: Version: 17.09.0-ce API version: 1.32
Go version: go1.8.3
Git commit: afdb6d4
Built: Tue Sep 26 22:42:18 2017
OS/Arch: linux/amd64
Server:
Version: 17.09.0-ce
API version: 1.32 (minimum version 1.12)
Go version: go1.8.3
Git commit: afdb6d4
Built: Tue Sep 26 22:40:56 2017
OS/Arch: linux/amd64
Experimental: false
Создание сертификата и ключей
Оставаясь в сервере Manager, приступим к созданию сертификационного центра (CA). Перейдем в новую директорию:
$ mkdir certificates
$ cd certificates
Для начала нужно создать приватный и публичный RSA-ключ для CA (потребуется придумать кодовое слово длиной не меньше 4 символов):
$ openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus ...............................................................................................................................................................................................................................++ ..................................................++ e is 65537 (0x10001) Enter pass phrase for ca-key.pem: Verifying - Enter pass phrase for ca-key.pem:
Приступим к созданию локального сертификационного центра. Будут запрошены данные связанные с идентификацией центра. На этапе ввода полного квалифицированного доменного имени (FQDN) требуется ввести доменное имя хоста, по которому доступен сервер Manager, но в целях примера (не используйте подобный метод в рабочих машинах!) используем слово manager для обозначения сервера:
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
Enter pass phrase for ca-key.pem: You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. -----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:manager
Email Address []:your@email.com
Теперь создадим приватный ключ для сервера:
$ openssl genrsa -out server-key.pem 4096
Теперь, когда у нас есть сертификационный центр мы можем создать запрос на подпись SSL сертификата (CSR). Поле CN (Common Name) должно совпадать с использованным на предыдущем шаге значением FQDN:
$ openssl req -subj "/CN=manager" -sha256 -new -key server-key.pem -out server.csr
Дополнительно требуется указать IP-адрес сервера Manager (машина, в которой мы сейчас работаем):
$ echo subjectAltName = DNS:manager,IP:95.213.195.76 >> extfile.cnf
Создадим подписанный ключ для сервера:
$ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out server-cert.pem -extfile extfile.cnf
Создадим клиентский ключ, который будем использовать для доступа к сервису Docker:
$ openssl genrsa -out key.pem 4096
Создадим запрос на подпись и дополнительно укажем тип использования ключа - для авторизации:
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
$ echo extendedKeyUsage = clientAuth >> extfile.cnf
Получим подписанный клиентский ключ:
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out cert.pem -extfile extfile.cnf
Теперь можно удалить файлы запросов:
$ rm -v client.csr server.csr
В итоге, мы получили следующие файлы:
$ ls
ca-key.pem ca.srl extfile.cnf server-cert.pem
ca.pem cert.pem key.pem server-key.pem
Оставим терминал с сессией на сервере Manager открытым, поскольку он понадобится нам далее.
Сейчас у нас есть всё необходимое для настройки защищенного доступа между GitLab Runner и рабочей Swarm-средой.
Настройка секретных переменных в GitLab CI
Мы не будем хранить данные клиентских ключей на машине Runner из соображений безопасности. Для таких задач в GitLab CI реализована функция секретных переменных среды.
Создадим новый проект в GitLab (если вы ещё не настроили GitLab - сделать это можно по инструкции):
После создания проекта перейдем в настройки CI / CD:
Теперь откроем область секретных переменных (Secret variables) и будем работать с ней:
Нам необходимо добавить и сохранить три переменные со следующими названиями и значениями файлов, которые мы создали на предыдущем шаге:
- переменная TLSCACERT значение файла ca.pem;
- переменная TLSCERT значение файла cert.pem;
- переменная TLSKEY значение файла key.pem.
Вернёмся к терминалу с сессией на сервере Manager и выполним команду:
$ cat ca.pem
(Сокращенный вывод) -----BEGIN CERTIFICATE----- MIIFgTCCA2mgAwIBAgIJAMzFvrYTSMoxMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX ... bI9XGs39F+r8Si5y6oHqkZHMpRX631i2KRA6k4jBPrZrS0MH3OwsCobuat5T1ONH Kx7TFZSuFO25XIut1WucVn5yPWLTKRniMV7dVws9i9x9Sp2Iamk+w2x1GPO6bHtr BWqdORkUEWMs+DTgX2J989AFh7gnYwHZ2Bo7HKlC6IbOlol7b2E/5p7hWrpe7sf+ oQDn1bhgoauhq2AL4BysJfA3uHoA -----END CERTIFICATE-----
Скопируем это значение и добавим новую секретную переменную в GitLab:
Теперь по этому примеру добавим оставшиеся значения cert.pem и key.pem.
Настройка сервиса Docker на управляющем сервере Manager
По-умолчанию доступ к сервису Docker имеет только пользователь-владелец процесса. Для выполнения операций по развертыванию приложения с удаленного хоста, нам необходимо разрешить подключение извне, при этом использовать TLS-протокол. Мы уже получили необходимые сертификаты и ключи, теперь осталось настроить Docker для работы с ними.
Мы создадим отдельный конфигурационный файл явно прописывающий хосты, по которым будет доступен Docker, поэтому для начала нам нужно удалить стандартный параметр -H, прописывающий хост. Для этого создадим новую директорию docker.service.d, в которой переопределим параметры запуска сервиса:
$ mkdir -p /etc/systemd/system/docker.service.d
Создадим файл настройки:
$ nano /etc/systemd/system/docker.service.d/exec-start.conf
Добавим следующую секцию, для параметра ExecStart требуется сначала очистить предыдущие значения и затем указать новые:
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd
Теперь создадим новый конфигурационный файл:
$ nano /etc/docker/daemon.json
И запишем следующий текст, определяющим использование протокола TLS для доступа к сервису Docker, а также расположение серверного ключа и сертификата:
{
"hosts": ["tcp://0.0.0.0:2376","fd://"],
"tlsverify": true,
"tlscacert": "/root/certificates/ca.pem",
"tlscert": "/root/certificates/server-cert.pem",
"tlskey": "/root/certificates/server-key.pem"
}
Выйдем и сохраним изменения нажатием клавиш ‘Ctrl’+’X’.
Для вступления изменений в силу, перезапустим сервис Docker:
$ systemctl daemon-reload
$ service docker restart
Теперь мы можем подключиться по TLS к нашему сервису Docker по адресу
[IP адрес сервера]:2376
Активация режима Swarm
Для начала рекомендуем изучить материалы на тему режима Swarm, например, на официальном сайте Docker. Swarm - это кластер сервисов Docker, расположенных на различных физических или виртуальных машинах и ведущих себя как единое целое.
Распределение запросов между имеющимися сервисами Docker осуществляется по схеме ingress load balancing, суть которой в том, что любой запрос проходит через внутренний механизм балансировки, а затем перенаправляется на тот сервис, который в данный момент может обслужить запрос.
Масштабирование осуществляется за счет указания количества реплик внутренних сервисов, с которыми мы столкнемся позднее.
Мы активируем режим Docker Swarm на скалете Manager, на котором будет располагаться менеджер этого кластера. Затем мы добавим подчиненный сервис Docker с машины Node1.
В терминале с открытой сессией на сервере Manager выполним команду:
$ docker swarm init
Swarm initialized: current node (r1mbxr2dyuf48zpm5ss0kvwv7) is now a manager. To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-5ihkl37kbs13po7htnj9dzzg3gex4i6iuvjho7910crd0hv895-36jw5epwcw3xwpzmqf1mqgod2 95.213.195.71:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
Как видно из сообщения, текущий сервис Docker стал менеджером и готов к добавлению подчиненных хостов через выполнение указанной команды. Скопируем эту команду и подключимся к серверу Node1 по SSH, для добавления его добавления в Swarm:
$ docker swarm join --token SWMTKN-1-1lhmuomvb060rnom4jqj8gxc565f4wgwadjs9ucvqx6huwvbfc-6vt1ljdhldxtetjv2hnct7sh4 95.213.195.76:2377
Результатом успешного выполнения команды должно стать сообщение:
This node joined a swarm as a worker.
Следующем шагом станет настройка рабочего сервера, который будет выполнять все работы от GitLab CI.
Настройка Gitlab Runner
Финальным этапом настройки среды непрерывной интеграции и развертывания с Docker является подключение рабочего сервиса GitLab CI, на котором будут выполняться все работы по сборке и тестированию приложения.
Можно использовать совместные сервисы выполнения работ, но в данном руководстве рассмотрим создание собственного сервиса на созданном в начале скалете Runner.
Подключимся по SSH к серверу Runner. Сперва необходимо установить GitLab Runner и соединить этот сервер с GitLab.
Добавим репозиторий разработчиков GitLab:
$ apt update
$ apt install curl
$ curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | bash
$ apt-get install gitlab-runner
Теперь вернемся к веб-интерфейсу GitLab для получения URL и registration-token. Снова зайдем в настройки проекта в раздел CI / CD, где мы остановились в прошлый раз. Откроем секцию Runner settings:
В разделе Runner settings есть информация по подключенным рабочим хостам (приватным и открытым):
В блоке Specific Runners есть необходимые значения URL и registration token.
Вернемся в терминал с сессией на сервере Runner и заменив значения на свои выполним команду:
$ gitlab-runner register -n \
--url http://92.53.66.76/ \
--registration-token _Kof1SxCHzVNcwuZZEwx \
--executor docker \
--description "Docker Prod Runner" \
--docker-image "docker:latest" \
--docker-privileged \
--tag-list docker
Registering runner... succeeded runner=_Kof1SxC
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Сейчас у нас зарегистрирован рабочий хост, который будет выполнять все работы, посылаемые GitLab CI.
Осталось настроить SSH доступ для Git-репозитория проекта на GitLab.
Создадим приватный и публичный ключи, заменив свой email-адрес и оставив все запрашиваемые значения по-умолчанию:
$ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
Также нужно добавить информацию о сервере GitLab, на котором расположен Git-репозиторий в список известных хостов для предотвращения ошибок во время подключения (IP адрес нужно заменить на IP адрес сервера GitLab, доступный в панели управления):
$ ssh-keyscan -t rsa 92.53.66.76 >> .ssh/known_hosts
Необходимо добавить публичный ключ в GitLab, чтобы разрешить подключение с сервера Runner. Скопируем значение ключа:
$ cat .ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDMY+G6rmx+AZ6Ow1lVr+4ox0HaAaV4xwthCS3ucyI3KsXVV+ltLU0zkFOP8WZoTXCHo38Fpcu5KwYe3V6L/hZ26fNse6WhJ6EvRmVx7wVHyixhpzKj6Jp9zzAf24SbtrjGgNtN4ASKyouU///a3+gtM+eWQYdxavz6wlJ0xgm8MnDqpbCUv1M7IRWsKhejA9vLSXjfdkxQnxVSCOT/FXb/eDsGRTs7WMXYapqeQ0msTXDCFlNDVQaRWZagXpHLRDkeOhRE2rJ6daj8YNKKx0jaatRKIsICqwUljvPgsrnpF9FiUg8n8PTWyYbz3VpwUoIPnFiFXvbgIn8xLb2/4QkFDoZUgyLI+VgrmZmd0HZPWvW5QbMLZ8vwb/Izi0TG/+qoMm8jas0RaUUp18rQAc4GmCLRsFbzN3DsnME31xFa0y/pwA3LK9ptIRivYq82uP5twq0jXpMSji8w+No7kBI5O9VUHmbRYYYWpn+jeKTxmoVORsrCHpAT7Cub0+Ynyq1M7Em0RMqZgdzLsP9rlLwRkc6ZEgqpVQHDZgwJsnQ5qo/6lr18bD9QHSe5t+SSnUbnkmXkp0xb0ivC4XayxCjYVIOoZV2cqyGa+45s7LY+ngPk0Cg+vSMHV8/enEwu1ABdpoGVjaELJOtw1UBr4y9GCyQ0OhKnrzWmqL6+HnEMDQ== your_email@example.com
И добавим его в GitLab (подробная инструкция по добавлению ключей есть в руководстве по знакомству с GitLab):
Заключение
Мы настроили и подготовили наши виртуальные сервера для обслуживания приложения Flask, создание и докеризацию которого мы рассмотрим во второй части руководства. Текущая инфраструктура позволяет организовывать непрерывную интеграцию и доставку приложений, использующих Docker.
Войдите в службу, чтобы оставить комментарий.
Комментарии
0 комментариев