Простой скрипт деплоя

    Хочу сказать сразу, цель этого поста не предоставить универсальное решение задачи развертывания кода на сервере, а показать пример, один из вариантов решения такой задачи. Подойдет ли это решение для вашего случая — решать вам.

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

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

    Для выгрузки кода на сервер, для начала необходимо закомитить все изменения в svn. После этого можно запустить скрипт. Скрипт сделает вот что:
    1.выгрузит из SVN код проекта
    2.Запакует код в архив
    3.С помощью scp загрузит архив на сервер
    4.Распакует его на сервере

    Примечание:
    Для того что бы не вводить пароль к ssh, лучше всего воспользоваться возможностью авторизации по ключу. Для этого необходимо использовать программу ssh-keygen, с помощью которой сгенерировать пару public key/private key. Подробное освящение этого вопроса, выходит за рамки данной статьи, так что google вам в помощь, если вы не в теме.

    deploy.sh имеет ряд параметров, информацию по которым вы можете получить запустив программу с ключом -h

    # sh deploy.sh -h
    Code deployer: version 1.2
    Использование: sh deploy.sh [-y 0 | -y 1] [-d path/to/source] [-H your.server.ru] [-u username]
    Пример: sh deploy.sh -y 0 -H your.server.ru -d /tmp -u progger

    Исправьте переменную SVN_PATH, в коде этого скрипта, если изменился SVN репозиторий проекта.
    Текущий SVN: your.svnserver.ru/svn/repos/ypurproject/trunk

    Параметры:
    y - надо ли выгружать envos: 0 - не надо, 1 - надо (долго) (по умолчанию: 0)
    u - пользователь для SSH (по умолчанию: )
    d - путь на сервере (по умолчанию: /opt/hosting/your.server.ru)
    H - доменное имя сервера для SSH соединения (по умолчанию: your.server.ru)
    h - эта помощь


    По умолчанию программа выгрузки уже настроена, и самый простой способ её использования просто запустить её без параметров. Если вам необходимо выгрузить и код envos тоже, используйте ключ -y
    # sh deploy.sh -y 1

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

    Код скрипта:

    #!/bin/bash

    #настройки по умолчанию
    HOST='your.server.ru'
    SVN_PATH='https://your.svnserver.ru/svn/repos/yourptoject/trunk/'
    DOC_ROOT="/opt/hosting/$HOST"
    UPLOAD_ENVOS=0; #надо ли выгружать envos: 0 - не надо, 1 - надо (долго)
    USER=''

    bold='\E[1m'
    ebold='\E[0m'

    while getopts ":y:u:d:H:h" optname
    do
      case $optname in
        "y")
          UPLOAD_ENVOS="$OPTARG"
        ;;
        "d")
          DOC_ROOT="$OPTARG"
        ;;
        "u")
          USER="$OPTARG"
        ;;
        "H")
          HOST="$OPTARG"
        ;;
        "h")
          echo -e "${bold}Yourproject code deployer: version 1.2 $ebold"
          echo "Использование: sh deploy.sh [-y 0 | -y 1] [-d path/to/source] [-H your.server.ru] [-u username]"
          echo -e "${bold}Пример: sh deploy.sh -y 0 -H your.server.ru -d /tmp -u progger $ebold"
          echo ""
          echo "Исправьте переменную SVN_PATH, в коде этого скрипта, если изменился SVN репозиторий проекта.
    Текущий SVN: $SVN_PATH"

          echo ""
          echo -e "${bold}Параметры: $ebold"
          echo " y - надо ли выгружать envos: 0 - не надо, 1 - надо (долго) (по умолчанию: $UPLOAD_ENVOS)"
          echo " u - пользователь для SSH (по умолчанию: $USER)"
          echo " d - путь на сервере (по умолчанию: $DOC_ROOT)"
          echo " H - доменное имя сервера для SSH соединения (по умолчанию: $HOST)"
          echo " h - эта помощь"
          echo ""
          exit 0;
        ;;
        *)
          echo "Unknown parameter or option error with option - $OPTARG"
          exit 1;
        ;;
      esac
    done

    if [ $USER ]
    then
      SSH_HOST="$USER@$HOST"
    else
      SSH_HOST=$HOST
    fi

    #rm -rf ./yourproject-tmp
    echo "* Код будет выгружаться на сервер $SSH_HOST"

    echo '* Выгружаем код из SVN...'
    svn export --force $SVN_PATH ./yourproject-tmp > /dev/null
    if [ $? -ne 0 ]
    then
      exit 1;
    fi;

    echo '* Создаем архив...'
    tar -czf yourproject.tar.gz yourproject-tmp
    if [ $? -ne 0 ]
    then
      exit 1;
    fi;

    echo '* Копируем архив на сервер...'

    scp ./yourproject.tar.gz $SSH_HOST:$DOC_ROOT
    if [ $? -ne 0 ]
    then
      exit 1;
    fi;

    echo '* Распаковываем архив на серверe...'
    ssh $SSH_HOST "cd $DOC_ROOT; tar -xzf yourproject.tar.gz 2> /dev/null && rm -rf $DOC_ROOT/op && mv $DOC_ROOT/yourproject-tmp $DOC_ROOT/op && chmod -R a+w $DOC_ROOT/op"
    if [ $? -ne 0 ]
    then
      exit 1;
    fi;

    #надо ли выгружать envos (надо если новая версия)
    if [ $UPLOAD_ENVOS -eq 1 ]
    then
      echo '* Выгружаем envos (может занять пару минут)...'
      echo '* Пакуем envos...'
      tar -czf envos.tar.gz envos
      if [ $? -ne 0 ]
      then
        exit 1;
      fi;

      echo ' * Копируем архив envos на сервер...'
      scp ./envos.tar.gz $SSH_HOST:$DOC_ROOT
      if [ $? -ne 0 ]
      then
        exit 1;
      fi;

      echo ' * Распаковываем архив envos на сервере...'
      ssh $SSH_HOST "rm -rf $DOC_ROOT/envos && cd $DOC_ROOT/; tar -xzf envos.tar.gz 2> /dev/null"
      if [ $? -ne 0 ]
      then
        exit 1;
      fi;
    fi

    #удаляем временные файлы
    echo '* Чистим...'
    rm -rf ./yourproject-tmp
    echo '* Готово!'

    * This source code was highlighted with Source Code Highlighter.


    Пример вывода:

    # sh deploy.sh
    * Код будет выгружаться на сервер your.server.ru
    * Выгружаем код из SVN…
    * Создаем архив…
    * Копируем архив на сервер…
    yourproject.tar.gz 100% 269KB 89.6KB/s 00:03
    * Распаковываем архив на серверe…
    * Чистим…
    * Готово!

    В случае ошибки на любом из этапов, выполнение команды будет прервано.

    Хочу обратить ваше внимание на параметр -y, который заведует выгрузкой Envos.Framework. Долго думал, убирать этот код или не надо. Решил оставить. Многие из вас, вероятно, пользуются своим любимым каркасом приложений, код которого не требуется выгружать постоянно. Зачастую эти фреймворки не маленькие, и выгрузка может занять лишнее время. Скрипт это учитывает, но если вы используете другой framework, вам нужно будет немного переписать код.

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

    Если вы все еще пользуетесь FTP для выгрузки своего кода, настало время задуматся :)

    Комментарии 62

      0
      Экономить трафик на сжатии в архив нет необходимости — обхожусь одной командой
      ssh exaple_host.com «svn --force --username=export_user --password=export_password export https://example.com/svn/project/ /project/dist»
        +1
        это только в том случае если на сервере есть svn
        Так же часто бывает необходимо, перед выгрузкой, произвести над кодом некоторые манипуляции, например удалить тесты (которые на продакшене не нужны), изменить парметры конфигов.
        Еще одно приемущество подобного подхода, это простота реализации отката до предыдущей версии, в случае непредвиденной ошибки.
          0
          С наличием svn на сервере проблем нет.
          Проблема разных конфигов для дев версии и продакшена решаю отсутсвием конфига в svn :) app.conf.example, а при установке переименовываю и правлю под конкретную среду. В случае если должны изменится сами конфиги можно руками сходить и подправить.

          С откатами на старые версии действительно есть проблема в виде не удаленных файлов от новых ревизий. «rm -rf $DOC_ROOT/op» не могу потому что внутри лежит конфиг и результаты работы приложения. Пока такая проблема возникает не часть — решаю руками :(
          Других проблем с откатом пока не встречал. Подскажите?
            0
            >решаю отсутсвием конфига в svn

            Я решаю намного проще, разделением конфигов с помощью SetEnv ;) В зависимости от окружения подгружается нужный конфиг, причем свой для всех разработчиков. Но sed тоже выход иногда.

            >должны изменится сами конфиги можно руками сходить и подправить

            Ага, и не забыть еще и в svn закинуть то что изменилось.

            >«rm -rf $DOC_ROOT/op» не могу потому что внутри лежит конфиг и результаты работы

            Угу, тож недостаток нехранения конфига в svnю В моем случае решается все это элементарно, даже без всяких SetEnv. Просто перед паковкой архива, надо переименовать конфиг из app.conf.example.production в app.conf

        +1
        Что вам мешает ипользовать VCS для деплоймента?
          –1
          По сути он и используется, только намного гибче. Конкретно в моем случае, админы вообще отказываются ставить на сервера svn, и я их понимаю.
            –3
            Прально, надо git ставить.
              0
              На продакшен сервере вообще никакая VCS не нужна.
                0
                Ей деплоить в сотни раз легче. Скажите это опере у которой на сайте недавно нашли остатки svn.
                  0
                  повторяю, никакая VCS на продакшен сервере не нужна. А то что у оперв там нашли, это вообще не в тему. Я так понимаю там .svn нашли директории. Как это относится к моему примеру, и вообще к наличию SVN на сервере я не понимаю.
            0
            Могу привести несколько причина почему это делать не стоит:
            1. секурность, выставлять репозитории наружу далеко не все позволят.
            2. выкладываться должны релизы, а не транк версия
            3. следует из 2. часто то что видет у себя разработчик и то как это деплоится на сервер не всегда совпадает. К примеру конфиги настройки, куски дебаг кода, ресурсы, далеко не всегда должны уходить в продакшен. Все это должно быть исключено из релиза на стадии сборки

            Первый пункт часто можно обойти, но осташиеся 2 делают деплоймент напрямую из репозитория кода просто бессмысленным
              0
              1. Решается конфигурацией http-сервера.
              2. Можно выложить не только trunk, но и релизный branch.
              3. Если есть такая необходимость, то можно запустить скрипт после обновления из ветки релиза. После теста, полученный код подставляется одной атомарной операцией вместо прежнего.

              В чём минус релиза при помощи VCS?
                0
                например то что обновление из svn не будет мгновенным, то есть ресурс может некоторое время не работать. Например в предложенном вами случае, вы собираетесь запустить скрипт после завершения выгрузки. То есть, если вам надо подменить конфиг, с другими параметрами соединения с БД, все время выгрузки, проект работать не будет (там будут неправельные параметры).
                А теперь выгрузка с помощью скрипта. Вы ДО выгрузке вносите в код нужные изменения, удаляете все лишнее на девелоперской машине (что уже намного безопаснее, не так фатально rm -rf /), потом выгружаете на сервер, и делаете ln -s на новую версию кода. Новая версия появляется практически мгновенно. Так же мгновенно можно вернуть старую.
                  0
                  «После теста, полученный [новый] код подставляется одной атомарной операцией вместо прежнего.»
                    0
                    ну а тогда в чем разница то? У вас скрипт работает на сервере, у меня тот же самый скрипт работает на девелоперской машине. Очевидно что мой вариант безопаснее и проще в использовании. В вашем надо заходить на девсервер. В моем не надо. И не забываем что на продакшен-сервере у программиста может не быть нужных прав.
                      0
                      девсервер продакшн-сервер
                        0
                        Как раз в вашем случае нужен SSH доступ к продакшену, а в моём — не нужен.
                        К вашему скрипту претензий — никаких. Ну кроме, разве что, отсутствия кроссплатформенности.
                          0
                          Гы, а как вы на сервер попадете? :)))))
                            0
                            кросплатворменность есть. На всех системах, нужных програмисту он работает.
                              0
                              Вот вы неугомонны. 1. Windows — вполне годная платформа для программиста. 2. На сервер направится коммит в бранч релиза, который подхватывается post-commit-hook и запускает svn up и пр.
                                0
                                1. а денвер замечательный пакет для установки php :)
                                2. А если не надо выгружать этот релиз? А если сначала надо выгрузить на тестовый сервер?
                                  0
                                  1. Чем плох Денвер у девелопера?
                                  2. Коммит в тестовый бранч.
                                    0
                                    1. не чем. пользуйтесь на здоровье. :))))
                                    2. а зачем? вы уже тут такой огород нагородили. Для чего? Что бы доказать что есть другие решения. Я сам знаю, спасибо.
                                      –1
                                      Да я и не с вами общался (см. выше).
                      0
                      Тем что это экспорт это не релиз.

                      VCS это системы для управления исходным кодом. Процесс доставки приложения это нечто больше чем перезапись фаилов.

                      Реальный пример из моей практики. Приложение на питоне с мордой на extjs. Экст жс это примерно 200 классов разбитых по разным файлам. Которые надо склеить и минифицировать если выкладывать на продакшене. Конфигурация и урлы серверсайда имеют вариант развертки как со встроенным веб сервером для всего так и с использованием mod_wsgi для динамики и обычного apache для отдачи статики. Плюс к этому юнит тесты как для интерфейса так и для сервера. Структура папок организована стандартно мейвенских архетипов.

                      Все это прекрасно лежит на сервере. При достижении стабильного релиза выделяется в отдельные бранчи (вместе с тестом и прочим).

                      Какой коммандой svn/git/hg я могу выполнить деплоймент такой системы?

                        0
                        Естественно, задачи бывают разные. Задачу топикстартера VCS с простым хуком on-commit решает с головой.
                          0
                          Задачи имеют свойство усложняться ;)
                            0
                            Я не знаю задач топикстартера он их не описал. Но деплой продакшена по он-коммиту это мягко говоря опрометчивое решение — даже если считать что код не требует сборок и автоконфигрируется согласно окружению, нет никаких гарантий что бранч стабилен и что последний коммит не внес критическую ошибку.
                              0
                              Для этого есть релиз-менеджер, который этим и занимается. Т.е. это не проблема способа деплоя.
                                0
                                Вот, уже и релиз менеджера привели сюда)) Не находите ли что в итоге ваш вариант оказывается сложнее как в техническом так и организационном плане? Нужен релиз-менеджер, нужны какие-то отдельные ветки, нужно помнить в какую ветку и когда нужно комитить. В моем примере все просто, меняя параметры, можно выгружать куда угодно, а кто этим будет заниматься и когда, это уже дело десятое, хоть релиз-менеджер, хоть любой программист.
                                  0
                                  Такое чувство, что благодаря Вам мы теперь можем пользоваться супер-пупер скриптом для деплоя с огромным магическим потенциалом. Ну прямо всё сам делает и все задачи решает. Искренне рад за Вас!
                                    0
                                    Да нет, опять же напомню про первый абзац. Но ваш вариант хуже, хотя и не отрицаю что в некоторых ситуациях он может быть и приемлем. Вообще, делать билд на сервере не очень хорошая идея. Потому я и отказался от Capistrano.
                                  0
                                  Во! Вы все ближе к правильно поставленомму техпроцессу.
                      +1
                      Я при деплойменте делаю iso-образ, который потом монтирую вместо старого. В таком случае у меня на сервере всегда валяется несколько старых образов со старыми версиями, на которые я быстро могу переключить сервер, так удобнее немножко.
                        0
                        Интересно узнать, почему именно iso? Какие-то преимущества перед другими?
                          0
                          Не знаю даже, просто первым в голову пришло. А какие лучше?
                            0
                            Та я то не знаю, думал Вы какие оценки проводили )
                              0
                              Не, ничего не производил, взял самое распространенное :)
                                0
                                Тогда можно было просто несколько папок держать, зачем монтировать образ? Разве что для ускорения из-за разворачивания в память, если возможности позволяют.
                                  0
                                  Ну основной плюс — readonly. Так, на всякий случай.
                        +1
                        Для деплоя есть Capistrano. www.capify.org/index.php/Capistrano
                        Скрипты писать намного проще, и к тому же есть куча готовых рецептов на каждый случай. capitate.rubyforge.org/recipes/index.html
                          +1
                          вот ну написал же специально первый абзац… и все равно суют Capistranо…
                            0
                            Так если уже все готово, прекрасно работает и удобно, какой смысл делать свои костыли?
                            Вам надо было хоть как-то изучить эту тему и ознакомиться как это делается у людей.

                            Если бы скрипт соответствал тому что написано в первом абзаце, то ладно. А так оно порождает еще больше трудностей и возможных багов. Соответстует только тому, что он неуниверсальный, и для чуть дургой задачи продолжая идти этим же путем нужно будет затратить еще столько же времени.
                            Вобщем каждому свое.
                              0
                              Насчет Capistrano я знаю, и даже дал ссылку на статью, где в том числе рассматривается и он. А в чем смысл? Да в том что мой вариант работает практически на любой машине, и любом сервере. Не нужно дополнительно устанавливать Capistrano, не нужен рубиновый gem и сам ruby, не нужно знать ruby (shell язык должен знать каждый), при этом скрипт получается проще (хочу заметить что там куча кода для красоты, без которго можно обойтись).

                              НО, безусловно, Capistrano тоже имеет право на жизнь.
                          0
                          Чем оно лучше rsync?
                          Чем оно лучше Capistrano?
                          Зачем паковать фреймворк отдельно, если есть svn:externals?
                            0
                            прошу перечитать первый обзац. и не просто перечитать, но еще и подумать над ним.

                            не понимаю каким боком тут svn:externals… Собственно он итак external если чо, но я не понимаю что это меняет.
                              0
                              Ну вот если он external, то он экспортится вместе с проектом.
                                0
                                то есть он не может быть вот тут https://your.svnserver.ru/svn/repos/yourptoject/envos например, да?
                                  0
                                  нет, я не об этом. Он уже есть (по вашим словам) в yourproject.tar.gz, поэтому заливать его во второй раз не нужно?
                                    0
                                    Нет его там нет
                                      0
                                      А надо бы. Причём надо бы добавить посредством svn:externals. Тогда фреймворк экспортился бы и, соответственно, паковался бы вместе с исходниками. Что в итоге уменьшает общий вес «пакета».
                                        0
                                        *бьюсь головой об стену*

                                        Тупим?© Повторяю, envos итак external. Дальше что то объяснять я бессилен… Я так понял вы либо не прочли статью либо просто ничего не поняли. Думайте, потому что как еще нужно разжевать я не знаю.
                                          0
                                          Тупим?©
                                          Не хамите, молодой человек. Тупость — это не осилить man rsync (и, по всей видимости, man ssh за компанию).
                                          Повторяю, envos итак external.
                                          Похоже, мы разговариваем на разных языках. Не вижу смысла продолжать.
                                            0
                                            Никто и не хамит. Я просто уже сказал что envos ледит в другой svn-директории. Это сделано для того что бы один код можно было использовать во многих проектах:

                                            ./project1
                                            ./project2
                                            ./projectN
                                            ./envos

                                            При этом envos external потому что он разрабатывается отдельно, и лежит в другом репозитории. Потому у меня есть возможность паковать только код приложения, без envosа, ибо не всегда нужно выгружать новую версию движка. И вот это то как раз и уменьшает общий вес «пакета».
                                              0
                                              Вы знакомы с svn:externals?
                                    0
                                    …не нужно?.
                              0
                              Мы в своё время для этих целей Buildbot использовали
                                +1
                                как то некроссплатформенно
                                  –3
                                  почему это? Работает везде и в Ubuntu и Gentoo и FreeBSD :) Очень кросплатформено. Или вам надо что бы скрипт еще и в Windows работал? А смысл?
                                  +1
                                  rsync работает гораздо эффективнее, чем tar в данном случае. А еще есть интересное решение — использовать sshfs
                                    0
                                    я sshfs использую для монитрования кода из песочницы. В прниципе обе иде имеют право на жизнь
                                    0
                                    Вас правильно испугало множество зависимостей у Capistrano.

                                    Но есть одно НО — Capistrano надо поставить только на один хост, откуда происходит выкатка.

                                    Сам деплой Capistrano выполняет с деплой-машины посредством SSH и так далее, то есть без необходимости устанавливать его на целевые машины.

                                    Удовольствие: ведение историй выкатки, откат, множество целей, множество задач, простой и ясный язык, расширяемость.

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое