Pull to refresh

Использование deb-пакетов для дистрибъюции кода

Reading time8 min
Views5.7K
В этой статье я хочу рассказать, о том, как можно внедрить систему сборки deb-пакетов для некоторого абстрактного проекта. Плюсов в распространении и развёртывании ПО на основе пакетов несколько:
  • Атомарность пакета (представление продукта в виде одного файла);
  • Наличие скриптов пред/пост установки/удаления ПО;
  • Возможно указания зависимостей для ПО.
Кроме того, при развёртывании ПО на основе пакетов, а не на основе SVN, вы гарантировано защищены от проблем с .svn-папками.

Структура проекта


И так, рассмотрим некоторый web-проект my-app, находящийся под контролем SVN, со следующей файловой структурой:
/|-config
 | |-parameters.ini
 |-htdocs
 | |-index.php
 |-libs
 |-templates


Структура пакета


Для сборки пакетов нам потребуется некоторый набор файлов и скриптов, который мы разместим в папке .package, которую в свою очередь добавим в корень проекта. Структура этой папки будет выглядеть следующим образом:
/|-.structure
 | |-DEBIAN
 | | |-conffiles
 | | |-control
 | | |-postinst
 | | |-postrm
 | | |-preinst
 | | |-prerm
 | | |-templates
 | |-etc
 | |-var
 | | |-log
 | | |-www
 |-package.xml
 |-package.properties

По сути каталог .structure и есть наш будущий пакет. Подкаталог DEBIAN содержит служебные файлы, о которых будет сказано ниже. Все остальные каталоги полностью повторяют иерархию каталогов файловой системы ОС Debian Linux. Файлы package.xml и package.properties содержат сценарии и настройки для создания пакетов с помощью утилиты Apache Ant. Но обо всём по порядку.

Каталог Debian


Каталог DEBIAN содержит файлы настроек проекта и скрипты пред/пост установки/удаления.

Файл conffiles содержит список конфигурационных файлов, которые не должны быть перезаписаны во время установки:
/etc/my-app/parameters.ini

В нашем случае здесь будет указан файл настроек проекта.

Файл control содержит общую информацию о пакете:
Package: my-app
Version: {{{VERSION}}}
Section: user
Priority: optional
Architecture: all
Installed-Size: 0
Maintainer: Mikhail Krestjaninoff <mikhail.krestjaninoff@gmail.com>
Depends: nginx, php5-common (>= 5.2), php5-cli
Description: My application

Отдельного внимания в этом файле заслуживают пункты Version и Depends. Мы специально указываем в качестве версии пакета константу {{{VERSION}}}, так как в дальнейшем при сборке пакета заменим её актуальным значением. В списке зависимостей мы указали только nginx и php, однако, если Ваш проект использует какие-либо дополнительные пакеты (например модули php), Вы можете явно перечислить их, уменьшив тем самым риски неверной работы проекта при выкладке на боевой сервер.

Файл preinst содержит сценарий предустановки:
#!/bin/sh

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

Файл postinst содержит сценарий постустановки. В данном случае сценарий будет выставлять права, настраивать конфиг с помощью утилиты debconf и создавать директории для временных файлов:
#!/bin/sh

if [ configure = "$1" ]; then

    # Set permissions
    chown -R www-data:www-data /etc/my-app/
    chmod -R 0664 /etc/my-app/

    chown -R www-data:www-data /var/www/my-app/
    chmod -R 0664 /var/www/my-app/

    chown -R www-data:www-data /var/log/my-app/
    chmod -R 0664 /var/log/my-app/


    # Set up configuration file
    . /usr/share/debconf/confmodule

    db_input critical db/dsn || true
    db_go
    db_fset db/dsn seen false || true
    db_get db/dsn || true
    DSN=$RET

    DSN=`echo "$DSN" | sed 's/\//\\\\\//g'`;
    sed -i s/{{DSN}}/$DSN/ /etc/my-app/parameters.ini


    # Create directories for temporary files
    CACHE_DIR="/var/www/my-app/templates/cache"
    COMPILED_DIR="/var/www/my-app/templates/compiled"

    if [ ! -d $CACHE_DIR ]
    then
        mkdir -p $CACHE_DIR
        chown -R www-data:www-data $CACHE_DIR
        chmod -R 0664 $CACHE_DIR
    fi

    if [ ! -d $COMPILED_DIR ]
    then
        mkdir -p $COMPILED_DIR
        chown -R www-data:www-data $COMPILED_DIR
        chmod -R 0664 $COMPILED_DIR
    fi

fi

При необходимости в этот файл так же могут быть добавлены сценарии для создания БД проекта. С обновлением БД задача обстоит несколько сложнее. Однако, можно попробовать частично разрешить её хранением в проекте 2-х sql-файлов: с полным набором команд для воспроизведения структуры / данных (первичная установка) и изменениями относительно предыдущего релиза (для обновления).

Файл prerm содержит сценарий предудаления. В данном случае сценарий будет удалять каталоги для временных файлов:
#!/bin/sh

if [ remove == "$1" -o purge == "$1" ];
then
    # Remove directories for temporary files
    CACHE_DIR="/var/www/my-app/templates/cache"
    if [ -e $CACHE_DIR ]; then
        rm -rf $CACHE_DIR
    fi

    COMPILED_DIR="/var/www/my-app/templates/compiled"
    if [ -e $COMPILED_DIR ]; then
        rm -rf $COMPILED_DIR
    fi
fi


Файл templates содержит шаблоны полей для утилиты debconf:
Template: db/dsn
Type: string
Default: postgres://user@passwd:localhost/my-app
Description: Database Source Name. Example: postgres://user@passwd:localhost/my-app

Подробнее о содержимом каталога DEBIAN можно почитать на opennet или в официальной документации.

Скрипты сборки пакета


Теперь, что бы создать полноценный пакет на основе нашего проекта, нам остаётся заполнить файловую структуру пакета (файловую систему с корнем в каталоге .structure данными). Для этого придётся создать небольшой набор сценариев, копирующих нужные данные из файловой системы проекта в файловую систему пакета. Я использовал для этой цели Apache Ant, в результате чего у меня получились файлы package.xml и package.properties.

Файл package.properties содержит в себе настройки для сборки пакета:
package.name=my-app
package.version=1.0.0

Основной сценарий сборки содержится в файле package.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project basedir=".." default="prepare" name="package-builder">

  <property file="./.package/package.properties" />
  <property name="location" value="."/>
  <property name="package.structure" value="${location}/.package/.structure"/>
  <property name="package.source" value="${location}/.package/source"/>
  <property name="package.target" value="${location}/.package/target"/>

  <!-- Init project -->
  <target name="init">
    <mkdir dir="${package.source}" />
    <mkdir dir="${package.target}" />
  </target>

  <!-- Clean project -->
  <target name="clean">
    <delete dir="${package.source}" />
    <delete dir="${package.target}" />
  </target>

  <!-- Prepare project files for package -->
  <target depends="init" name="prepare">

    <copy toDir="${package.source}">
      <fileset dir="${package.structure}">
        <include name="**/*" />
        <exclude name=".svn" />
    </fileset>
    </copy>

    <mkdir dir="${package.source}/etc/${package.name}" />
    <copy toDir="${package.source}/etc/${package.name}">
      <fileset dir="config">
        <include name="**/*" />
        <exclude name=".svn" />
      </fileset>
    </copy>

    <mkdir dir="${package.source}/var/www/${package.name}/htdocs" />
    <copy toDir="${package.source}/var/www/${package.name}/htdocs">
      <fileset dir="htdocs">
        <include name="**/*" />
        <exclude name=".svn" />
      </fileset>
    </copy>

  <mkdir dir="${package.source}/var/www/${package.name}/libs" />
    <copy toDir="${package.source}/var/www/${package.name}/libs">
      <fileset dir="libs">
        <include name="**/*" />
        <exclude name=".svn" />
      </fileset>
    </copy>

    <mkdir dir="${package.source}/var/www/${package.name}/templates" />
    <copy toDir="${package.source}/var/www/${package.name}/templates">
      <fileset dir="templates">
        <include name="**/*" />
        <exclude name=".svn" />
        <exclude name="cache" />
        <exclude name="compiled" />
      </fileset>
    </copy>

  </target>

  <!-- Build packege -->
  <target depends="prepare" name="build">

    <!-- Change owner to root -->
    <exec executable="fakeroot" dir="${package.source}">
      <arg line="chown -R root:root ." />
    </exec>

    <!-- Allow execution for installation scripts -->
    <exec executable="fakeroot" dir="${package.source}/DEBIAN">
      <arg line="chmod 0755 preinst postinst prerm postrm" />
    </exec>

    <!-- Set package version -->
    <exec executable="sed">
      <arg line="-i s/{{{VERSION}}}/${package.version}/ ${package.source}/DEBIAN/control" />
    </exec>

    <!-- Build package -->
    <exec executable="dpkg-deb">
      <arg line="--build ${package.source} ${package.target}/${package.name}_${package.version}_all.deb" />
    </exec>

  </target>

</project>


* This source code was highlighted with Source Code Highlighter.

Сборка пакета


И так, создание каталога .package завершено. Теперь мы можем закоммитить его в SVN вместе с остальным проектом. Когда же нам понадобится собрать пакет (например, отдать релиз/метку для тестирования или выкладки в бой), будет достаточно перейти в каталог .package и выполнить команду ant -f package.xml build, которая создаст для нас в каталоге .package/target новый deb-пакет, готовый к использованию!
Tags:
Hubs:
+23
Comments26

Articles

Change theme settings