Устанавливаем и обновляем приложение из GIT средствами YUM, rpm и Puppet

в 21:25, , рубрики: puppet, rpm, yum, системное администрирование, метки: , ,

Недавно появилась необходимость автоматизировать внедрение приложений из GIT на сервера.

В данной статье я решил описать свой опыт внедрения.

Поступило интересное задание по установке/обновлению приложения на серверах компании имея следующие данные:
* Приложение расположено в GIT
* Версия приложения можно узнать командой "git tag"
* Список серверов и путь где должно находиться приложение

Поскольку исторически так сложилось, что в компании используется RPM-based OS, то, IMHO, в данном случае наиболее правильным решением было реализовать упаковку приложения в RPM-пакет с последующим распространением его через puppet. Соответственно puppet устанавливает ПО и накатывает необходимый конфигурационный файл по шаблону.

Процесс настройки системы:

  • Настройка yum-репозитория
  • Установка и настройка nginx
  • Скрипт автоматического build с размещением его в yum-репозиторий
  • Автоматическая проверка обновлений и привязка к созданию build
  • Настройка puppet

Настройка yum-репозитория

Тут оказалось всё просто. Поскольку мы собираемся использовать внутренний yum-репозиторий с доступом только с наших серверов, то подписывать его PGP/GPG-ключами нет необходимости.

Создаём папку где будут располагаться наши RPM-пакеты и создаём пастой репозиторий:

# yum install createrepo
# mkdir -p /storage/v0.repo/6/x86_64/RPMS/
# ln -s /storage/v0.repo/6 /storage/v0.repo/6.2
# createrepo /storage/v0.repo/6/x86_64

Создаём конфигурационный файл /etc/yum.repos.d/v0.repo со следующим содержанием:

[v0]
name=v0 Yum Repo
baseurl=http://v0.example.com/$releasever/$basearch/
enabled=1
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/v0.key

По команде "# yum repolist" видим в списке наш репозиторий:

repo id      repo name        status
v0           v0 Yum Repo     7
Установка и настройка nginx

Установка проста: "yum install nginx"

В конфигурационный файл nginx, в секцию http, добавляем строку: "include /etc/nginx/vhosts/*.conf;"

Далее, все файлы в папке /etc/nginx/vhosts/ с окончанием в имени ".conf" будут считаться конфигурационными файлами nginx.
Создаём файл /etc/nginx/vhosts/v0.repo.conf со следующим содержанием:

server {
  listen 80;
  server_name v0.example.com;
  autoindex on;
  root /storage/v0.repo;

  # ограничим доступ к репозиторию
   allow 62.152.38.0/24;
   allow 89.17.37.102;
   deny all;
}

Запустим nginx и пропишем его в стартовых скриптах:

# /etc/init.d/nginx start
# chkconfig nginx on
Скрипт автоматического build с размещением его в yum-репозиторий

Для начала устанавливаем необходимые пакеты:
yum install rpmdevtools

Привожу сам скрипт с комментами: /home/build_user/recordsapp_build.sh

#!/bin/bash

#
#  example to START:  "./recordsapp_build.sh 1.8"
#  1.8 - version of recordsapp
# скрипт необходимо стартовать с параметром -- номером версии 

#  подготавливаем дерево build
#  по умолчанию создаётся ~/rpmbuild/{BUILD,RPMS, SOURCES, SPECS, SRPMS}
rpmdev-setuptree

#  скачиваем приложение из GIT с указанием номера версии
git clone -q git@git.example.com:recordsapp recordsapp-$1

#  Копируем необходимые файлы (в нашем случае это
#  стартовый скрипт приложения sendstats) в папку SOURCES
cp recordsapp-$1/stats/sendstats rpmbuild/SOURCES

#  упаковываем приложение
tar cf recordsapp-$1.tar recordsapp-$1

#  перемещаем архив в SOURCES
#  П.С. можно реализовать упаковку сразу в нужное место
#  (like: tar cf ./rpmbuild/SOURCES/recordsapp-$1.tar recordsapp-$1)
# и обойтись без предыдущего и текущего шага
#  П.П.С. оптимизировать буду после.
mv recordsapp-$1.tar ./rpmbuild/SOURCES

#  чистим мусор 
rm -rf recordsapp-$1

#  подготавливаем spec-файл для создания RPM-пакета
#  и помещаем в соответствующую директорию
spec="./rpmbuild/SPECS/recordsapp.spec"

##### BEGIN - .spec - описываем название и текущуюю версию
echo "Name:         recordsapp" > $spec
echo "Version:      $1" >> $spec
 
#### Дописываем остальные необходимые данные
cat >> $spec << EOF
Release:      av
Summary:      recordsapp. Creator CENSORED. Owner CENSORED
Summary(ru):  recordsapp - созданно CENSORED. Владелец CENSORED
Group:        Applications/Multimedia
License:      GPL2
Source0:      %{_sourcedir}/%{name}-%{version}.tar
Source1:      sendstats
BuildArch:    noarch
BuildRoot:    %{_tmppath}/
URL:          http://example.com/
Packager:     Alexey <CENSORED>
Requires:     coreutils php php-common php-gd
Autoreqprov:  no

%description
recordsapp. Creater: <CENSORED>. Owner: <CENSORED>

%description -l ru
recordsapp - созданно <CENSORED>. Владелец <CENSORED>

%prep
%setup

%build

%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/var/www/recordsapp
cp -r . $RPM_BUILD_ROOT/var/www/recordsapp

#mkdir -p $RPM_BUILD_ROOT/etc/httpd/conf.d
mkdir -p $RPM_BUILD_ROOT/etc/init.d
cp %SOURCE1 $RPM_BUILD_ROOT/etc/init.d

%files
%defattr(-,root,root)
/var/www/recordsapp
/etc/init.d/sendstats
#%config(noreplace) /var/www/recordsapp/config/env.php
#%config(noreplace) /var/www/recordsapp/config/envs/prod.php

%clean
rm -rf $RPM_BUILD_ROOT

EOF

##### ==== END of SPEC-file

# создаём rpm
rpmbuild -ba rpmbuild/SPECS/recordsapp.spec --quiet

#  Помещаем приложение в репозиторий 
sudo mv rpmbuild/RPMS/noarch/recordsapp-$1* /storage/v0.repo/6/x86_64/RPMS/
#sudo createrepo --verbose /storage/v0.repo/6/x86_64/
sudo createrepo --quiet --update /storage/v0.repo/6/x86_64/

rm -rf /home/build_user/rpmbuild

Проверяем:
"$ yum --disablerepo=* --enablerepo=v0 info recordsapp" — покажет последнюю версию приложения
"$ yum --disablerepo=* --enablerepo=v0 provides recordsapp" — покажет все версии приложения, находящиеся в репозитории (может понадобиться при поддержке версионности: откаты на предыдущую версию и т.п.)

recordsapp-1.1-av.noarch : recordsapp. <CENSORED>. Owner <CENSORED>
Repo        : v0
Matched from:
Автоматическая проверка обновлений и привязка к созданию build

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

Чтобы разработчики сами могли «выкатывать» приложение, была составлена договорённость по получению версий через 'git tag'

Скрипт проверки версии "~/.build_recordsapp/check.sh":

#!/bin/bash

# 
#  скрипт проверки версионности по 'git tag' 
#  запуск build с номером новой версии
#

# забираем приложение с сервера GIT:
git clone -q git@git.example.com:recordsapp /home/build_user/.build_recordsapp/recordsapp > /dev/null 2>&1
# обновляем приложение (не нужно, если мы каждый раз забираем приложение
# и удаляем исходники после обработки)
cd /home/build_user/.build_recordsapp/recordsapp && git pull > /dev/null 2>&1

# смотрм последнюю версию git tag - нам выдаёт v1.1 
# - в нашем случае необходимо отрезать лишнюю "v"
recordsapp_TAG=`cd /home/build_user/.build_recordsapp/recordsapp && git tag | sed 's/v//g' | tail -n1`

# just for test:
#recordsapp_TAG="1.6"

# проверяем последнюю версию 
recordsapp_RPM=`sudo yum --disablerepo=* --enablerepo=v0 info recordsapp | grep "Version     :" | sed 's/Version     ://g' |head -1`

# сравниваем версии в репозитории с версией tag 
# и отправляем результат в buil
if [ $recordsapp_TAG == $recordsapp_RPM ]; then
  echo YES > /dev/null
  exit 0;
else
  echo NOT
  cd /home/build_user && ./recordsapp_build.sh $recordsapp_TAG
  exit 0;
fi
Настройка puppet

Не буду описывать детальную настроку puppet — этого добра полно в поисковиках.

Куда писать версию приложения?
Поскольку я не уверен, как правильно надо делать… Слишком гибок в настройках Puppet. Можно в манифест отдельной ноды, можно в общий манифест, можно в темплейт. Ещё не совсем понял идеологию Паппета.
Представляю как это у меня реализовано на момент окончания тестирования:

production/manifests/site.pp:

#...
import "hosts/v0.example.com.pp"
import "hosts/v7.example.com.pp"
#...

production/manifests/hosts/v0.example.com.pp

node "v0.example.com" {
#... Сервер 1 - с указанием конкретной версии релиза приложения
$recordsapp_ip = '127.0.0.113'
$recordsapp_ver = '1.4-av'

  class { "recordsapp_hard":
    recordsapp_hard_template => "default",
  }
}

production/manifests/hosts/v7.example.com.pp

node "v0.example.com" {
#... dev-сервер с указанием последней версии для тестов приложения
$recordsapp_ip = '127.0.0.117'
$recordsapp_ver = 'latest'

  class { "recordsapp_hard":
    recordsapp_hard_template => "default",
  }
}

production/modules/recordsapp_hard/manifests/init.pp

class recordsapp_hard ( $recordsapp_hard_template = "default" ) {

  class { 'yum_v0::default': yum_v0_template => "default" }
  package {
    "recordsapp":
    ensure => $recordsapp_ver,
  }

	file { "/var/www/recordsapp":
		ensure	=> directory,
                recurse => true,
		owner	=> "httpd",
		group	=> "httpd",
#		mode	=> "755",
	}

	file { "/var/www/recordsapp/panel/tmp":
		ensure	=> directory,
		owner	=> "httpd",
		group	=> "httpd",
		mode	=> "0755",
	}

	file { "/var/www/recordsapp/config/env.php":
		owner	=> "httpd",
		group	=> "httpd",
		mode	=> "0644",
		content => template("recordsapp_hard/$recordsapp_hard_template/env.php.erb"),
	}

	file { "/var/www/recordsapp/config/envs/prod.php":
                owner	=> "httpd",
                group	=> "httpd",
                mode	=> "0644",
                content	=> template("recordsapp_hard/$recordsapp_hard_template/prod.php.erb"),
        }

       exec { "recordsapp-chmod-fix":
                path => ["/usr/bin", "/usr/sbin"],
                command => 'find /var/www/recordsapp -exec chmod 0644 {} ; ; find /var/www/recordsapp -type d -exec chmod 0755 {} ;',
                refreshonly => true,
       }

}

production/modules/recordsapp_hard/templates/default/env.php.erb

# ....  тут настройки приложения

production/modules/recordsapp_hard/templates/default/prod.php.erb

# ....  и тут настройки приложения
#...
define("SELFIP", "<%= recordsapp_ip -%>");
#...

В результате мы получаем достаточно гибкую систему управления версиями.
На dev-серверах мы имеем последний релиз приложения без участия системного администратора. Разработчик может сам развернуть приложение на тестовом сервере обновив tag в git, а на сервера production устанавливается конкретный оттестированный релиз теми же средствами, но с указанием конкретного номера версии в манифесте puppet.

Автор: hard0ff

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js