<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Бложек сурового пэхэпэшника]]></title>
  <link href="http://andrey.janzen.su//atom.xml" rel="self"/>
  <link href="http://andrey.janzen.su//"/>
  <updated>2013-04-10T13:08:43+07:00</updated>
  <id>http://andrey.janzen.su//</id>
  <author>
    <name><![CDATA[Андрей Янцен]]></name>
    <email><![CDATA[andrey@janzen.su]]></email>
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Отправка уведомлений на почту при деплое проекта]]></title>
    <link href="http://andrey.janzen.su//blog/2011/08/19/otpravka-uviedomlienii-na-pochtu-pri-dieploie-proiekta/"/>
    <updated>2011-08-19T12:02:00+07:00</updated>
    <id>http://andrey.janzen.su//blog/2011/08/19/otpravka-uviedomlienii-na-pochtu-pri-dieploie-proiekta</id>
    <content type="html"><![CDATA[<p>В нашем проекте при каждом деплое разработчики, тестеры и ещё пара людей получают замечательные письма:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Subject: Наш проект версии v1.1.1 обновлён на сервере 'testing'
</span><span class='line'>
</span><span class='line'>user1 выложил следующие обновления на сервер 'testing':
</span><span class='line'>
</span><span class='line'>Коммиты по задачам:
</span><span class='line'>http://jira.local/browse/PROJECT-1234
</span><span class='line'>
</span><span class='line'>Полный список коммитов с предыдущего обновления:
</span><span class='line'>4392a53 Thu Aug 18 17:50:32 2011 +0700 user1 / [PROJECT-1234] сделал полезное
</span><span class='line'>f2fcfe2 Thu Aug 18 17:37:53 2011 +0700 user1 / сделал страшное
</span><span class='line'>cb1fcbe Wed Aug 17 15:18:10 2011 +0700 user2 / зарефакторил
</span><span class='line'>
</span><span class='line'>Изменения по файлам:
</span><span class='line'> file1                 |    4 ++--
</span><span class='line'> file2                 |    8 ++++----
</span><span class='line'> file3                 |    8 ++++----
</span><span class='line'> 3 files changed, 10 insertions(+), 10 deletions(-)</span></code></pre></td></tr></table></div></figure>


<p>Такое решение помогло нам избавиться от вопросов тестеров &#8220;Ну что, выложили уже исправление бага XXX?&#8221;, &#8220;Что нового на тестовом сервере?&#8221;. Так же - все члены команды, отдел внедрения и руководство в курсе, что происходит с кодом на серверах.
Для работы используется git, capistrano (+ multistage), php, bash (+ некоторые консольные утилитки). Если интересно - заходим под кат.</p>

<!-- more -->




<h1>Алгоритм работы</h1>


<ul><li>Обновляем код на сервере testing (cap testing deploy)</li><li>После deploy:restart срабатывает хук, создающий тег в репозитории. Тег формируется на основе версии проекта (хранится в конфиге, в репозитории), названии staging-сервера и названии релиза</li><li>В репозитории срабатывает хук. Если пришёл не тег - игнорируем, если же тег:<ul><li>Распиливаем его на компоненты: версия, сервер</li><li>Определяем предыдущий тег на этом же сервере</li><li>Если тега нет - значит эта первая установка и генерировать различия не стоит, там может быть несколько тысяч коммитов</li><li>Если же есть тег - генерируем список различий, выдёргиваем из них список задач; составляем список изменённых файлов</li><li>Отправка сгенерированного письма</li></ul></li></ul>


<h1>Создание тега</h1>


<p>Про настройку capistrano и capistrano-multistage уже где только не написано, поэтому я только расскажу, как у нас добавляется тег.</p>

<p>Будем считать, что у нас в корне репозитория есть файл <i>configs.ini</i>, который содержит ключ <i>runtime.version</i>. За основу был взят <a href='https://gist.github.com/381852'>gist#381852</a>.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">namespace</span> <span class="ss">:deploy</span> <span class="k">do</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">after</span> <span class="s2">&quot;deploy:restart&quot;</span><span class="p">,</span> <span class="s2">&quot;deploy:git:push_deploy_tag&quot;</span>
</span><span class='line'>  <span class="n">namespace</span> <span class="ss">:git</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Place release tag into Git and push it to server.&quot;</span>
</span><span class='line'>    <span class="n">task</span> <span class="ss">:push_deploy_tag</span> <span class="k">do</span>
</span><span class='line'>      <span class="n">user</span> <span class="o">=</span> <span class="sb">`git config --get user.name`</span><span class="o">.</span><span class="n">strip</span>
</span><span class='line'>      <span class="n">email</span> <span class="o">=</span> <span class="sb">`git config --get user.email`</span><span class="o">.</span><span class="n">strip</span>
</span><span class='line'>      <span class="n">version</span> <span class="o">=</span> <span class="sb">`git cat-file -p </span><span class="si">#{</span><span class="n">real_revision</span><span class="si">}</span><span class="sb">:configs.ini | fgrep runtime.version | awk -F &#39;[ =]+&#39; &#39;{print $2}&#39;`</span><span class="o">.</span><span class="n">strip</span>
</span><span class='line'>      <span class="nb">puts</span> <span class="sb">`git tag v</span><span class="si">#{</span><span class="n">version</span><span class="si">}</span><span class="sb">-</span><span class="si">#{</span><span class="n">stage</span><span class="si">}</span><span class="sb">-</span><span class="si">#{</span><span class="n">release_name</span><span class="si">}</span><span class="sb"> </span><span class="si">#{</span><span class="n">real_revision</span><span class="si">}</span><span class="sb"> -m &quot;Deployed by </span><span class="si">#{</span><span class="n">user</span><span class="si">}</span><span class="sb"> &lt;</span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="sb">&gt;&quot;`</span>
</span><span class='line'>      <span class="nb">puts</span> <span class="sb">`git push --tags`</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Что здесь происходит:<ul><li>Извлекаем данные текущего пользователя (имя и почту) из конфига гита</li><li>Берём файл configs.ini из устанавливаемой ревизии и выдёргиваем версию</li><li>Создаём аннотированный тэг. В аннотации указываем, кто и когда задеплоил</li><li>Публикуем теги</li></ul></p>

<h1>Обрабатываем обновление репозитория</h1>


<p>Хуку <i>pre-receive</i> на вход (stdin) подётся 3 значения: предыдущая и текущая ревизии, refname.
Читаем входящие параметры и убеждаемся, что пришёл тег:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="k">while </span><span class="nb">read </span>oldrev newrev refname
</span><span class='line'><span class="k">do</span>
</span><span class='line'><span class="k">    </span><span class="nv">rev_type</span><span class="o">=</span><span class="k">$(</span>git cat-file -t <span class="nv">$newrev</span> 2&gt;/dev/null<span class="k">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">case</span> <span class="s2">&quot;$refname&quot;</span>,<span class="s2">&quot;$rev_type&quot;</span> in
</span><span class='line'>        refs/tags/*,tag<span class="o">)</span>
</span><span class='line'>        ;;
</span><span class='line'>    <span class="k">esac</span>
</span><span class='line'><span class="k">done</span>
</span></code></pre></td></tr></table></div></figure>


<p>Выделяем название тега, разбиваем его на части, ищем предыдущий тег для этого сервера:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">tag</span><span class="o">=</span><span class="k">${</span><span class="nv">refname</span><span class="p">##refs/tags/</span><span class="k">}</span>
</span><span class='line'>
</span><span class='line'><span class="nv">version</span><span class="o">=</span><span class="sb">`</span><span class="nb">echo</span> <span class="nv">$tag</span> | cut -d- -f1<span class="sb">`</span>
</span><span class='line'><span class="nv">server</span><span class="o">=</span><span class="sb">`</span><span class="nb">echo</span> <span class="nv">$tag</span> | cut -d- -f2<span class="sb">`</span>
</span><span class='line'><span class="nv">prevtag</span><span class="o">=</span><span class="k">$(</span>git describe --tags --abbrev<span class="o">=</span>0 --match<span class="o">=</span><span class="s2">&quot;*-$server-*&quot;</span> <span class="nv">$newrev</span>^ 2&gt;/dev/null<span class="k">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Если в $prevtag пусто - значит это первая установка на сервер. Если версия у нового и старого тега совпадает - это обновление, если же нет - установка новой версии. Таким образом мы генерируем корректный заголовок письма.</p>

<p>Начнём формировать тело письма. Сперва - определим кто осмелился задеплоить:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nb">eval</span> <span class="k">$(</span>git <span class="k">for</span>-each-ref --shell --format<span class="o">=</span><span class="s1">&#39;</span>
</span><span class='line'><span class="s1">          tagger=%(taggername)</span>
</span><span class='line'><span class="s1">          tagged=%(taggerdate)&#39;</span> <span class="nv">$refname</span>
</span><span class='line'>        <span class="k">)</span>
</span><span class='line'><span class="nb">echo</span> <span class="s2">&quot;$tagger выложил следующие обновления на сервер &#39;$server&#39;:&quot;</span> &gt; msg
</span></code></pre></td></tr></table></div></figure>


<p>Теперь разберём коммиты по задачам. Последние в Jira именуются по маске &lt;алиас проекта&gt;-&lt;id задачи&gt;, все разработчики в обязательном порядке указывают алиас задачи (uppercase) в коммите. Если задача крупнее чем на 30 минут и требует более 1 коммита - создаётся ветка, по алиасу задачи, и тогда в коммитах эту самую задачу мы уже не упоминаем. Итого, чтобы достать список задач нам нужно выполнить не сложную обработку регуляркой:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>git log <span class="nv">$rev_range</span> --abbrev-commit --pretty<span class="o">=</span><span class="s2">&quot;format:%s&quot;</span> &gt; tmpfile
</span><span class='line'>php &gt;tickets <span class="s">&lt;&lt;END</span>
</span><span class='line'><span class="s">&lt;?php</span>
</span><span class='line'><span class="s">\$f = file_get_contents(&quot;$tmp&quot;);</span>
</span><span class='line'><span class="s">if (preg_match_all(&quot;#([A-Z.]+-\d+)#&quot;, \$f, \$matches)) {</span>
</span><span class='line'><span class="s">    \$matches[1] = array_unique(\$matches[1]);</span>
</span><span class='line'><span class="s">    foreach (\$matches[1] as \$match) {</span>
</span><span class='line'><span class="s">        echo &#39;$JIRA_HOST/browse/&#39;, \$match, PHP_EOL;</span>
</span><span class='line'><span class="s">    }</span>
</span><span class='line'><span class="s">}</span>
</span><span class='line'><span class="s">END</span>
</span></code></pre></td></tr></table></div></figure>


<p>Если в итоге файл <i>tmpfile</i> не пуст - добавляем его к телу письма.
Дальше идёт информация, которая интересует только разработчиков проекта: списки коммитов и изменённых файлов:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nb">echo</span> <span class="s2">&quot;Полный список коммитов с предыдущего обновления:&quot;</span> &gt;&gt; msg
</span><span class='line'>git log <span class="nv">$rev_range</span> --no-merges --abbrev-commit --pretty<span class="o">=</span><span class="s2">&quot;format:%h %ad %an / %s&quot;</span> &gt;&gt; msg
</span><span class='line'>
</span><span class='line'><span class="nb">echo</span> -e <span class="s2">&quot;\n\nИзменения по файлам:&quot;</span> &gt;&gt;msg
</span><span class='line'>git diff --stat<span class="o">=</span>140,110 <span class="nv">$rev_range</span> &gt;&gt;msg
</span></code></pre></td></tr></table></div></figure>


<p>Ну и, наконец, тупая отправка письма:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>cat msg | mail -s <span class="s2">&quot;$subject&quot;</span> <span class="nv">$MAIL_TO</span>
</span></code></pre></td></tr></table></div></figure>


<p>Все файлы можно взять на гитхабе: <a href="https://github.com/zvirusz/git-deploy-notify">https://github.com/zvirusz/git-deploy-notify</a></p>

<p>P.S. Если кто-нибудь поможет переписать кусок кода, выдёргивающий имена задач, на perl/bash - я буду очень рад.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Реанимируем dctc для борьбы с ложными файлами]]></title>
    <link href="http://andrey.janzen.su//blog/2011/01/05/rieanimiruiem-dctc-dlia-borby-s-lozhnymi-failami/"/>
    <updated>2011-01-05T14:39:00+07:00</updated>
    <id>http://andrey.janzen.su//blog/2011/01/05/rieanimiruiem-dctc-dlia-borby-s-lozhnymi-failami</id>
    <content type="html"><![CDATA[<p>Года 4 назад у меня родилась идея - написать бота, который бы отслеживал активность в нашем локальном DC-хабе - просто логировал чат и выполнял простые команды в приват. За пару вечеров был найден прекрасный консольный клиент - <a href='http://ac2i.homelinux.com/dctc/'>dctc</a>, с которым можно взаимодействовать через unix-socket; а ещё за пару вечеров - написан простенький бот на php. При создании бота были внесены первые поправки в код dctc, чтобы он не посылал странные команды на сервер. Через некоторое время меня сделали модератором на этом самом хабе и я начал безжалостно банить. Но удобной системы для отслеживания количества банов у конкретных людей не было, но администратор каждые 5 минут генерировал информацию по текущим банам - ещё несколько убитых вечеров - и вот администрация обрела лёгкую возможность отслеживать порядковый номер бана.</p>

<p>Спустя ещё несколько месяцев в сети стали появляться фэйки - это когда, например, скачиваешь Шрэка, а там вовсе даже не Шрэк, а красная шапочка и трое непонятных мужиков&#8230; Некоторое время боролись своими силами, но душа поэта не выдержала - и я решил улучшить бота.</p>

<!-- more -->


<ol>
<li>В код dctc (v0.85.9) была добавлена обработка и хранение IP-адреса пользователя, проверка пользователя на online\offline, возможность отправки сырых команд на сервер.  Обмен файлами в современных сетях по-прежнему невозможен - клиент не дружит с TTH.</li>
<li>В бота, находящегося на стороне сервера, добавлена функция отсылки &#8220;уведомлений&#8221; пользователям - человеку в общий чат и в ЛС отправляется указанное сообщение, после чего пользователь перенаправляется на несуществующий хаб. Т.о. если человек повторно зашёл на хаб - можно с достаточной долей уверенности сказать, что человек прочитал уведомление.</li>
<li>Так же - при появлении пользователя на хабе к нему автоматически отправляются запросы на поиск нескольких самых распространённых TTH.</li>
<li>В контекстном меню файлов добавилась функция &#8220;сообщить о фейке&#8221; - при использовании которой отправляется сообщение модераторам хаба с указанием TTH файла.</li>
<li>Полностью переписан мой бот: функции для работы с DCTC вынесены в отдельный класс и этот класс расширяется функциями, специфичными для нашего хаба.</li>
<li>Скрипт поиска решено было разбить на 2 части - одна из них лишь отправляет запросы на поиск, а вторая - занимается анализом ответов на эти самые запросы.</li>
<li>Из-за того, что скрипт был внедрён спустя некоторое время, а не сразу же после возникновения проблемы - количество банов за фейки у пользователя ищется через общую таблицу банов, а не через отдельную.</li>
<li>Перед баном - пользователю отсылается уведомление о наличии у него некорректных файлов и только если такое уведомление уже отсылалось - применяется бан на период, зависящий от количества подобных нарушений.</li>
</ol>


<p>При работе всей системы используются следующие таблицы:</p>

<ul>
<li><i>bans</i>: Глобальное хранилище банов</li>
<li><i>fake_check</i>: Лог поиска фейков</li>
<li><i>fake_list</i>: Список зарегистрированных фейков</li>
<li><i>fake_names_order</i>: Списки файлов с запрещёнными и разрешёнными именами для каждого фейка</li>
<li><i>fake_bans</i>: Баны за фейки, используется только для логирования</li>
<li><i>fake_notifies</i>: Уведомления о наличии фейков, используется только для логирования</li>
<li><i>fake_counter2</i>: Количество файлов, не отнесённых ни в разрешённый, ни в запрещённый списки</li>
</ul>


<p>Архив со всеми (кажется) необходимыми файлами: <a href="http://narod.ru/disk/2770034001/dctc.tar.bz2.html">dctc.tar.bz2.html</a>.
Файлы:</p>

<ul>
<li><i>dctc.patch</i>: Патч, который нужно наложить на dctc версии 0.85.9</li>
<li><i>dctc.sh</i>: Скрипт, которым я запускаю dctc. Он так же меняет права на доступ к сокету, т.о. всё члены группы dctc смогут с ним работать.</li>
<li><i>dctcController.php</i>: Базовый класс для работы с сокетом dctc</li>
<li><i>dctcController_tech.php</i>: Класс с настройками и дополнительными функциями для нашего хаба</li>
<li><i>fakes.php</i>: Страница для управления базой фейков</li>
<li><i>reply_analizer.php</i>: Анализатор ответов поиска - ведёт различные логи, уведомляет и банит пользователей</li>
<li><i>search.php</i>: Инициирует поиск 20 произвольных фейков</li>
<li><i>tables.sql</i>: Дамп структуры таблиц</li>
</ul>


<p>Накладываем патч:
Скачиваем <a href="http://ac2i.homelinux.com/dctc/dctc-0.85.9.tar.gz">http://ac2i.homelinux.com/dctc/dctc-0.85.9.tar.gz</a>, распаковываем, командуем <code>patch -p1 -ddctc-0.85.9 &lt; dctc.patch</code>.
Основная цель патча - добавить в dctc поддержку команды <a href="http://www.teamfair.info/wiki/index.php?title=$UserIP">$UserIP</a> - дабы мы могли узнавать IP-адрес пользователя, которого хотим банить.
Самое интересное так это то, что dctc нормально работает после моего патча - c/c++ я знаю только по университетскому курсу. Всё как-то на php больше быдлокожу&#8230;</p>

<p>Компилируем, устанавливаем, запускаем dctc. Как его запускать - смотрим в dctc.sh.</p>

<p>Дальше нужно поправить класс dctcController_tech - команды на бан и уведомление у вас, скорее всего, другие. Да и hubCharset, возможно, тоже отличается.
В переменной $_socketPathFile лежит путь до файла, в котором сохраняется путь до сокета. Вот такое вот извращение.
Для ограничения частоты поиска используется shared memory - дёшево и сердито. <a href="http://ru2.php.net/manual/en/sem.installation.php">Убедитесь</a>, что ваш php это умеет.</p>

<p>Инициализируем БД и через fakes.php записываем всякие нехорошие TTH в базу.</p>

<p>Затем в фоне (или в скрине, кому как больше нравится) запускаем скрипт reply_analizer.php - он будет молча делать свои грязные делишки.</p>

<p>Дальше настраиваем периодический запуск search.php и наслаждаемся массовыми расстрелами.</p>

<p>Надеюсь кому-нибудь в чём-нибудь да поможет.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[QR для оплаты по СМС]]></title>
    <link href="http://andrey.janzen.su//blog/2010/06/28/qr-dlia-oplaty-po-sms/"/>
    <updated>2010-06-28T17:32:00+07:00</updated>
    <id>http://andrey.janzen.su//blog/2010/06/28/qr-dlia-oplaty-po-sms</id>
    <content type="html"><![CDATA[<p>Сегодня первый раз в жизни производил оплату услуги через SMS, и чтобы не нажимать на неудобные, маленькие кнопочки смартфона я просто открыл <a href="http://qrcode.kaywa.com/">qrcode.kaywa.com</a> и заполнил необходимые поля на компьютере, затем скормил картинку сканеру штрих-кодов на телефоне — и получил готовую SMS (с заполненным номером и текстом) — оставалось только нажать «отправить».</p>

<p>Так вот, почему бы вам, господам принимающим оплату через SMS не упростить жизнь пользователю и не добавить небольшую картинку рядом с информацией о платеже? Благо делается это предельно просто — например код по ссылке <a href="http://chart.apis.google.com/chart?cht=qr&amp;chs=135x135&amp;chl=SMSTO:01:abcdef123">chart.apis.google.com/&#8230;/</a> сгенерирует сообщение на номер 01 с текстом abcdef123. Да и существуют готовые решения для генерации QR-кодов для популярных языков программирования, если у вас нет желания зависеть от гугла.</p>

<p>Реализовать не сложно (разве что в дизайн вписать), а пользователю — приятно.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Расширяем функционал Monopoly City Streets]]></title>
    <link href="http://andrey.janzen.su//blog/2009/10/23/rasshiriaiem-funktsional-monopoly-city-streets/"/>
    <updated>2009-10-23T11:13:00+07:00</updated>
    <id>http://andrey.janzen.su//blog/2009/10/23/rasshiriaiem-funktsional-monopoly-city-streets</id>
    <content type="html"><![CDATA[<p>Разработчиками в MCS, к сожалению, не предусмотрена возможность застройки улицы большим количеством зданий за пару кликов мышкой. Но ведь игра написана на JS, так что ничего нам не мешает написать небольшой букмарклет, который и позволит нам добавить требуемый функционал.</p>

<p>Весь код MCS находится в файле <a href="http://assets.monopolycitystreets.com/monopoly.1255614067.js">monopoly.1255614067.js</a>. Если посмотреть firebug&#8217;ом, что происходит в процессе покупки здания — мы увидим, что используются 2 функции — <code>MCS.BUILD.showLocations()</code> (для отображения маркеров доступных мест) и <code>MCS.BUILD.buyBuilding()</code> (для, собственно, покупки здания). После размышлений у меня получился следующий код:</p>

<!-- more -->


<div><script src='https://gist.github.com/0c8801a3c4d48c25d2d1.js'></script>
<noscript><pre><code></code></pre></noscript></div>


<p>Перед началом работы скрипт проводит несколько проверок: наличие класса MCS (в противном случае мы точно не на сайте игры), наличие авторизации у пользователя, а так же проверяется текущая выделенная улица. Далее выдаётся запрос для выяснения типа здания — список последних берётся из кода игры. Проверяется наличие денег у игрока и наличие свободных мест на улице. Затем выясняем количество домов для постройки и приступаем к этому увлекательному занятию. Чтобы пользователь не скучал — не забываем отобразить «песочные часы» в верхней панели. И, наконец, выводится количество построенных домов.</p>

<p>Чтобы преобразовать этот скрипт в 1 строку — используем <a href="http://javascriptcompressor.com/">javascriptcompressor.com/</a>, в результате у нас почти есть букмарклет — остаётся только добавить &#8216;javascript:&#8217; в начало полученной строки.</p>

<p>P.S. В качестве бонуса — букмарклет для отображения стоимости текущей улицы без зданий:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">javascript</span><span class="o">:</span><span class="k">if</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">MCS</span> <span class="o">!=</span> <span class="s2">&quot;undefined&quot;</span><span class="o">&amp;&amp;</span><span class="nx">MCS</span><span class="p">.</span><span class="nx">STREET</span><span class="p">.</span><span class="nx">getStreetData</span><span class="p">().</span><span class="nx">data</span><span class="p">)</span><span class="nx">alert</span><span class="p">(</span><span class="nx">MCS</span><span class="p">.</span><span class="nx">STREET</span><span class="p">.</span><span class="nx">getStreetData</span><span class="p">().</span><span class="nx">data</span><span class="p">.</span><span class="nx">p</span><span class="o">*</span><span class="mi">1000</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p><em>UPD</em> Букмарклет для пакетного удаления зданий:</p>

<div><script src='https://gist.github.com/d8a91535a519f931e0aa.js'></script>
<noscript><pre><code></code></pre></noscript></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Отслеживание iframe]]></title>
    <link href="http://andrey.janzen.su//blog/2009/10/16/otsliezhivaniie-iframe/"/>
    <updated>2009-10-16T22:36:00+07:00</updated>
    <id>http://andrey.janzen.su//blog/2009/10/16/otsliezhivaniie-iframe</id>
    <content type="html"><![CDATA[<p>Однажды мне надоели жалобы клиентов, вроде &#8220;на моём сайте вирус, сделайте что-нибудь!&#8221;. Объяснять людям об опасности сохранения паролей от ФТП на небезопасной машине так же надоело. Гениальное оказалось рядом - обычно поражаются файлы <code>index.*</code> и <code>default.*</code> - так почему бы не отслеживать изменение этих файлов. Сказано - сделано.</p>

<!-- more -->


<div><script src='https://gist.github.com/5345868.js'></script>
<noscript><pre><code></code></pre></noscript></div>


<p>Параметры:</p>

<p><code>XFERLOG</code> - путь до файла <code>xferlog</code> (в vsftpd - нужно включить генерацию оного в конфиге, в proftpd - это параметр <code>TransferLog</code> конфигурации) в Вашей системе</p>

<p><code>MATCH_FILES</code> - список исследуемых имён файлов (без расширений)</p>

<p><code>EXCLUDE_LIST</code> - список игнорируемых файлов</p>

<p>Сохраняем скрипт, выставляем права на исполнение, добавляем в крон.</p>

<p>Всем спасибо за внимание, надеюсь кому-нибудь будет полезно.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Как установить Linux из сети]]></title>
    <link href="http://andrey.janzen.su//blog/2009/03/02/kak-ustanovit-linux-iz-seti/"/>
    <updated>2009-03-02T12:07:00+07:00</updated>
    <id>http://andrey.janzen.su//blog/2009/03/02/kak-ustanovit-linux-iz-seti</id>
    <content type="html"><![CDATA[<p>Буквально вчера хабраюзер <a href="http://habrahabr.ru/users/arceny/">Arceny</a> поведал нам об <a href="http://habrahabr.ru/blogs/linux/53219/">установке дебиана с USB-flash</a>. В статье он упомянул о возможности установки <q>С помощью загрузки по сети (<abbr title='Preboot Execution Environment'>PXE</abbr> boot)</q>, благодаря чему я решил наконец написать материал по созданию сервера pxe boot.</p>

<!-- more -->


<p>Повествование будет вестись на примере всё той же ОС — Debian (так что пути и команды у вас могут отличаться).
Для начала — поставим <abbr title='Trivial File Transfer Protocol'>tftp</abbr>-сервер. Смотрим доступные варианты (<code>apt-cache search tftp | fgrep server</code>):</p>

<ul>
<li>atftpd</li>
<li>dnsmasq</li>
<li>tftpd</li>
<li>tftpd-hpa</li>
</ul>


<p>Сначала я поставил tftpd-hpa, т.к. о нём рассказывалось в <a href="http://www.debian-administration.org/articles/478">мануале</a>, с которого я начинал создание сервера. Затем я перешёл на atftpd, т.к. мне потребовалось логирование. Рассмотрим оба варианта:</p>

<ul>
<li><p>tftpd-hpa:<br />
Устанавливаем пакет, редактируем файл <code>/etc/default/tftpd-hpa</code>, дабы демон знал, что ему стоит жить:<pre><code>#Defaults for tftpd-hpa
RUN_DAEMON="yes"
OPTIONS="-l -s /var/lib/tftpboot"</code></pre>
Создаём нужный каталог: <code>mkdir /var/lib/tftpboot</code>, и стартуем сервис <code>invoke-rc.d tftpd-hpa start</code></p></li>
<li><p>atftpd:<br />
После установки обнаруживаем, что по-умолчанию этот сервис работает через inetd. Я, почему-то, не фанат этого метода, поэтому первым делом закомментировал соответствующую строку в <code>/etc/inetd.conf</code>. Затем редактируем <code>/etc/default/atftpd</code>:<pre><code>USE_INETD=false
OPTIONS="--daemon --tftpd-timeout 1500 --no-multicast --retry-timeout 20 --logfile /var/log/atftpd.log --maxthread 100 --verbose=5 /var/lib/tftpboot"</code></pre>
<code>--tftpd-timeout</code> — время жизни потока, при отсутствии запросов<br />
<code>--retry-timeout</code> — таймаут отдачи файла<br />
у меня установлены большие таймауты, т.к. сервис транслируется в городскую локальную сеть, которая периодически прогибается от обилия абонентов, и не справляется с потоком запросов.<br />
Ну и, как и в предыдущем пункте, — создаём нужный каталог: <code>mkdir /var/lib/tftpboot</code>, и стартуем сервис <code>invoke-rc.d atftpd start</code></p></li>
</ul>


<p>Теперь у нас есть tftp-сервер. Один шаг мы сделали :)
Далее — настройка dhcp:</p>

<ul>
<li><p>dhcp3:<br />
В конфигурационный файл <code>/etc/dhcp3/dhcpd.conf</code> добавляем информацию о загрузке:<pre><code>filename "pxelinux.0";
next-server <tftp-server-ip>;</code></pre>
Если tftpd установлен на том же сервере, что и dhcpd — директиву next-server можно опустить. И перезапускаем dhcp3-server: <code>invoke-rc.d dhcp3-server restart</code></p></li>
<li><p>dnsmasq:<br />
В конфиг <code>/etc/dnsmasq.conf</code> дописываем:<pre><code>dhcp-boot=pxelinux.0,&lt;tftp-server-hostname&gt;,&lt;tftp-server-ip&gt;</pre></code>
Опять же, если сервера tftp и dhcp совпадают — последние 2 атрибута можно не заполнять. Перезапускаем: <code>invoke-rc.d dnsmasq restart</code></p></li>
</ul>


<p>Шаг второй пройден. Дальше — непосредственно настройка pxe-boot.
Идём на <a href="ftp://ftp.debian.org/debian/dists/lenny/main/">ftp.debian.org</a>, выбираем каталог <code>installer-*</code>, соответствующий необходимой архитектуре. Затем переходим в директорию <code>current/images/netboot</code> внутри мы найдём файлы <code>netboot.tar.gz</code> и <code>gtk/netboot.tar.gz</code> — это файлы текстовой и графической установки соответственно. Выбираем понравившийся, качаем и распаковываем в <code>/var/lib/tftpboot</code>.
Осталось убедиться, что зазрешён доступ на 69 UDP порт, и можно пользоваться прелестями сетевой установки.</p>

<p>В качестве бонуса прилагаю меню, которое получилось у меня в результате нескольких дней издевательств. В текущей конфигурации возможна установка нескольких ОС (установка Debian возможна как в текстовом, так и в графическом режиме), загрузка xUbuntu (через nfs), SystemRescueCD (через http-boot), memtest86 (через tftp). Установка всех ОС возможна для 2 архитектур — x86 и x64. Т.к. у меня лежит внутрисетевое зеркало репозиториев Ubuntu 8.10 и Debian 5.0, то установщики не предлают выбор репозитория, а автоматически сливают всё с него.</p>

<p><a href="https://docs.google.com/file/d/0BxaH7RICY3lbbndQZDFSZkY4dFk/edit?usp=sharing">Архив</a> <code>/var/lib/tftpboot</code> (без малого 200мб), а так же — <a href="https://docs.google.com/file/d/0BxaH7RICY3lbcmppYjhld3d6QXc/edit?usp=sharing">файлы конфигурации</a> для установщиков Ubuntu и Debian: в них указаны репозитории + Ubuntu разбивается на Ubuntu, kUbuntu, xUbuntu, edUbuntu.</p>

<p>В этой конфигурации есть один большой минус, который мне лень исправить: при открытии меню подгружается порядка 70 файлов, бОльшая часть из которых не нужна пользователю. Что бы избавиться от этого — отображение подменю нужно перевести из подключения меню инклудами в их вызов через ядро <code>vesamenu.c32</code>, которое идёт с pxeboot. Так же — не помешает очистить каталоги меню от дубликатов — большая часть конфигов для меню x64 и x86 совпадает.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Настройка маршрутизации по двум провайдерам]]></title>
    <link href="http://andrey.janzen.su//blog/2008/07/25/nastroika-marshrutizatsii-po-dvum-provaidieram/"/>
    <updated>2008-07-25T13:48:00+07:00</updated>
    <id>http://andrey.janzen.su//blog/2008/07/25/nastroika-marshrutizatsii-po-dvum-provaidieram</id>
    <content type="html"><![CDATA[<p>Вчера столкнулся с небольшой проблемой — на машине с Win2k3 установлены 2 сетевых карты, 2 провайдера. Проблема оказалась следующая: подсети пересекаются (точнее — совпадают). Было решено использовать за основной шлюз 1го провайдера, а по внутрисетевым ресурсам гулять — через 2го. И всё бы ничего, но машина должна обслуживать входящие соединения с обоих интерфейсов. Но, благодаря статическим маршрутам, ответы на запросы из подсети <code>10.0.0.0/8</code>, пришедшей со стороны первого провайдера уходили через канал второго провайдера, что было, мягко говоря, не тем, что нужно. Как решить эту проблему под линухом — я знал (и тоже поведаю в этой заметке). Немного погуглив был найден вариант решения (в msdn&#8217;e наткнулись на управления приоритетами соединений). Коллега (WAJIM, привет) подумал — и нашёл 2й вариант. Потом немного (совсем немного) подумал я — и по аналогии появился 2й вариант решения для линуха :)
Итого, под катом вас ожидает 4 варианта решения задачи маршрутизации по 2м провайдерам — 2 под виндовс и 2 под линукс.</p>

<!-- more -->


<p>Дано:</p>

<ul>
<li>2 физических фаервола, по совместительству являющихся шлюзами (<code>192.168.1.10</code> и <code>192.168.2.10</code>)</li>
<li>2 сетевых интерфейса (lan1 — <code>192.168.1.101</code> и lan2 — <code>192.168.2.101</code>)</li>
<li>желание заставить это добро работать так, как нужно нам</li>
</ul>


<p>Чтож&#8230; приступимс.</p>

<ul>
<li>Windows

<ul>
<li>Управление приоритетом сетевых подключений:<br />
Необходимо создать 3 маршрута:
<pre><code>route -p add 0.0.0.0 mask 0.0.0.0 192.168.1.10 metric 1
route -p add 10.0.0.0 mask 255.0.0.0 192.168.1.10 metric 1
route -p add 10.0.0.0 mask 255.0.0.0 192.168.2.10 metric 1</code></pre></li>
</ul>


<p>  Далее идём в Сетевые подключения -> Дополнительно -> Дополнительные параметры, перемещаем lan2 вверх, чтобы это соединение оказалось выше lan1. Готово.</p>

<ul>
<li>Приоритет в таблице маршрутизации:
Опять же — создаём 3 маршрута. Только изменим метрики
<pre><code>route -p add 0.0.0.0 mask 0.0.0.0 192.168.1.10 metric 1
route -p add 10.0.0.0 mask 255.0.0.0 192.168.1.10 metric 2
route -p add 10.0.0.0 mask 255.0.0.0 192.168.2.10 metric 1</code></pre>
И никаких танцев с приоритетом интерфейсов. Считаю этот метод оптимальным.<br />
<em>UPD</em>: метрика интерфеса, приоритет которого выше (см. предыдущий пункт) не должна быть наименьшей.</li>
</ul>
</li>
<li>Linux

<ul>
<li>Приоритет в таблице маршрутизации: <br />
Тут почти тоже самое, что и в предыдущем пункте (только синтаксис чуток различается)
<pre><code>route add default gw 192.168.1.10 metric 0
route add -net 10.0.0.0/8 gw 192.168.1.10 metric 1
route add -net 10.0.0.0/8 gw 192.168.2.10 metric 0</code></pre></li>
<li>iproute2: <br />
Собственно, для этого решения необходимо наличие установленного пакета iproute2. В дебиане — <code>apt-get install iproute</code>.<br />
В этом случае нам понадобится 2 маршрута
<pre><code>route add default gw 192.168.1.10 metric 0
route add -net 10.0.0.0/8 gw 192.168.2.10 metric 0</code></pre>
Создадим 2 таблицы маршрутизации:
<pre><code>echo '10 lan1' >> /etc/iproute2/rt_tables
echo '11 lan2' >> /etc/iproute2/rt_tables</code></pre>
Добавляем в эти таблицы правила маршрутизации:
<pre><code>ip route add default via 192.168.1.10 table lan1
ip rule add from 192.168.1.101 table lan1
ip route add 127.0.0.0/8 dev lo table lan1
ip route add default via 192.168.2.10 table lan2
ip rule add from 192.168.2.101 table lan2
ip route add 127.0.0.0/8 dev lo table lan2</code></pre>
Последние правила — для того, чтобы пакеты с локального интерфейса не терялись.<br />
Так же не стоит забывать, что линукс при перезагрузки очищает таблицы и правила маршрутизации, потому рекомендую создать хитрый скрипт в папке /etc/network/if-up.d. У меня там лежит скрипт такого содержания:
<pre><code>#!/bin/sh -e
case "$IFACE" in
eth1)
ip route add default via 192.168.1.10 table lan1
ip rule add from 192.168.1.101 table lan1
ip route add 127.0.0.0/8 dev lo table lan1
;;
eth2)
route del default gw 192.168.2.101
route add -net 10.0.0.0/8 gw 192.168.2.10 1
ip route add default via 192.168.2.10 table lan2
ip rule add from 192.168.2.101 table lan2
ip route add 127.0.0.0/8 dev lo table lan2
;;
esac</code></pre></li>
</ul>
</li>
</ul>


<p>Выбор за вами. Скажу лишь что было решено остановиться на вторых вариантах для обоих систем (изменение метрики на windows и iproute2 на debian).</p>

<p>Кому интересна тема маршрутизации в линуксе — рекомендую почитать вот эту вещь <a href="http://lartc.org/howto/">lartc.org/howto/</a>
Надеюсь, кому‐ нибудь эта информация окажется полезной.</p>

<p>И ещё раз, коллеги — с праздником :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Каскадирование squid'ов]]></title>
    <link href="http://andrey.janzen.su//blog/2008/06/27/kaskadirovaniie-squidov/"/>
    <updated>2008-06-27T19:53:00+07:00</updated>
    <id>http://andrey.janzen.su//blog/2008/06/27/kaskadirovaniie-squidov</id>
    <content type="html"><![CDATA[<p>Во времена слишком дорогого анлима (64кбита — 1000руб), сотворили с товарищами кластер проксей, дабы увеличить суммарную пропускную способность. Время шло, цены менялись. Сейчас они уже более дружелюбны — мбитный анлим (с ночным удвоением скорости) стоит всё те же 1000руб. Но, не смотря на это, кластером всё ещё иногда пользуемся. Решил вот поделиться с общественностью методом создания такого добра, вдруг кому будет интересно.</p>

<p>Для опытов нам потребуется:</p>

<ol>
<li>1 сервер с установленным на нём squid&#8217;ом + ещё какой-нибудь проксёй (если хотите, чтобы этот сервер был не только центральным, но и делился инетом).
я расскажу про поднятие кластера на базе дебиана, 2 интернет-каналов и 3 сквидов (сквиды для родительских проксе выбраны по 2 причинам: из-за возомжности предоставления статистики по текущим соединениям; из-за лени искать альтернативу)</li>
<li>Любое количество компьютеров, с любыми ОС и любыми http-проксями</li>
<li>Опционально — апач, пхп и скрипт <a href="http://samm.kiev.ua/sqstat/">SqStat</a> — для просмотря активности прокси</li>
</ol>


<!-- more -->


<p>Будем считать что у нас есть 2 машины — <code>192.168.1.1</code> (у неё так же есть 2й ип, со 2м провайдером — <code>192.168.2.1</code>) и <code>192.168.1.2</code>.</p>

<p>Немного погуглив — я нашёл заметку без малого 5летней давности — <a href="http://ksimute.trancom.ru/squid-2-chans.shtml">Squid 2 chans</a>. Из неё было выцеплено 2 момента — как, собственно, организовать кластер и как проксировать на разных интерфейсах.</p>

<p>Для начала — создадим дополнительные сервисы squid&#8217;a.
Сперва — немного подправим конфиг сервера (<code>/etc/squid/squid.conf</code>). Т.к. кэш мне не нужен — я его отрубил. Делается это установкой параметра <code>cache_dir</code> следующим образом:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>cache_dir null /var/spool/squid</span></code></pre></td></tr></table></div></figure>


<p>Т.к. отключили кэш — отрубаем и лог записи в кэш</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>cache_store_log none</span></code></pre></td></tr></table></div></figure>


<p>Откроем доступ к прокси для всех (нужно вставить до записи <code>http_access deny purge</code>):</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>acl all src 0.0.0.0/0.0.0.0
</span><span class='line'>http_access allow all</span></code></pre></td></tr></table></div></figure>


<p>Откроем доступ к прокси по протоколу cache_mgr с локалхоста (я не помню, какие параметры указаны в дефолтном конфиге — так что не помешает перед вставкой проверить, что уже есть):</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>acl manager proto cache_object # это строка, скорее всего, уже есть в конфиге
</span><span class='line'>http_access allow manager localhost
</span><span class='line'>http_access deny manager</span></code></pre></td></tr></table></div></figure>


<p>Слушать порт <code>8080</code> (по дефолту — <code>3128</code>):</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>http_port 8080</span></code></pre></td></tr></table></div></figure>


<p>Для ограничения доступа к прокси — я предпочитаю использовать iptables (по-умолчанию — все пакеты отбрасываются), а не аутентификацию, поэтому прокся из конфига открыта для всех:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>iptables -N proxy
</span><span class='line'>iptables -A proxy -j REJECT
</span><span class='line'>iptables -I proxy -s 192.168.1.2 -j ACCEPT
</span><span class='line'>iptables -I INPUT -p tcp --dport 8080 -j proxy
</span></code></pre></td></tr></table></div></figure>


<p>Теперь добавим родительские прокси (говорю сразу — с приведённой ниже конфигурацией — ничерта не заработает. но дальше, по ходу повествования — ошибка будет найдена и исправлена :)):</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>cache_peer 127.0.0.1 parent 8081 0 no-query no-digest round-robin <span class="nv">weight</span><span class="o">=</span>4
</span><span class='line'>cache_peer 127.0.0.1 parent 8082 0 no-query no-digest round-robin <span class="nv">weight</span><span class="o">=</span>1
</span><span class='line'>cache_peer 192.168.1.2 parent 8080 0 no-query no-digest round-robin <span class="nv">weight</span><span class="o">=</span>4
</span></code></pre></td></tr></table></div></figure>


<p>Итого — 3 родительских прокси, с которых не будет запрашиваться кэш (<code>no-query</code>) и cache-digest (<code>no-digest</code>), родители будут циклически переключаться (<code>round-robin</code>). Ну и разный <code>weight</code> — ибо 2й канал у меня слабый.
Запрещаем сквиду ходить напрямую (не через родителей):</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>never_direct allow all
</span></code></pre></td></tr></table></div></figure>


<p>Создаём 2 копии конфига — <code>/etc/squid/squid_2.conf</code> и <code>/etc/squid/squid_3.conf</code>. Удаляем из них строки, начинающиеся на <code>cache_peer</code>, <code>never_direct</code>. Изменяем значение параметра <code>http_port</code> на <code>8081</code> и <code>8082</code> соответственно. Изменяем пути до логов (здесь и далее по тексту — изменения будут указываться только для <code>squid_2</code>, для <code>squid_3</code> — изменения аналогичны):</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>access_log /var/log/squid_2/access.log
</span><span class='line'>cache_log /var/log/squid_2/cache.log
</span></code></pre></td></tr></table></div></figure>


<p>Так же не помешает создать этот путь и сменить владельца, дабы сквид мог писать логи:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>mkdir /var/log/squid_2
</span><span class='line'>chown proxy:proxy /var/log/squid_2
</span></code></pre></td></tr></table></div></figure>


<p>Указываем расположение pid-файла:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>pid_filename /var/run/squid_2.pid
</span></code></pre></td></tr></table></div></figure>


<p>Расположение кэша я не менял — все 3 прокси спокойно работают с 1 каталогом. Но <code>squid -z</code> (инициализировать кэш) перед первым запуском сделать не помешает.</p>

<p>Теперь приступим к созданию init-скриптов. В файле <code>/etc/init.d/squid</code> комментируем код, ответственный за инициализацию кэша (т.к. скрипт не рассчитан на null-кэш):</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="c"># if [ -d &quot;$cdr&quot; -a! -d &quot;$cdr/00&quot; ]</span>
</span><span class='line'><span class="c"># then</span>
</span><span class='line'><span class="c"># log_warning_msg «Creating squid spool directory structure»</span>
</span><span class='line'><span class="c"># $DAEMON -z</span>
</span><span class='line'><span class="c"># fi</span>
</span></code></pre></td></tr></table></div></figure>


<p>Копируем <code>/etc/init.d/squid</code> => <code>/etc/init.d/squid_2</code>.
Изменяем <code>squid_2</code>:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">NAME</span><span class="o">=</span>squid_2
</span><span class='line'>...
</span><span class='line'><span class="nv">SQUID_ARGS</span><span class="o">=</span><span class="s2">&quot;-D -sYC -f /etc/squid/squid_2.conf&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Так же, для душевного равновесия, можно изменить сообщения, которые выдаёт скрипт:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>log_daemon_msg «Starting Squid HTTP proxy» «squid_2»
</span><span class='line'>...
</span><span class='line'>log_daemon_msg «Stopping Squid HTTP proxy» «squid_2»
</span><span class='line'>...
</span><span class='line'>log_daemon_msg «Restarting Squid HTTP proxy» «squid_2»
</span></code></pre></td></tr></table></div></figure>


<p>Для следующего фокуса — в начале инит-скрипта должны быть примерно следующие строки:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="c"># Default-Start: 2 3 4 5</span>
</span><span class='line'><span class="c"># Default-Stop: 0 1 6</span>
</span></code></pre></td></tr></table></div></figure>


<p>Настраиваем запуск свеже созданного сервиса с системой:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>update-rc.d squid_2 defaults
</span></code></pre></td></tr></table></div></figure>


<p>Запускаем дочерние прокси:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>invoke-rc.d squid_2 start
</span><span class='line'>invoke-rc.d squid_3 start
</span></code></pre></td></tr></table></div></figure>


<p>Вот он, момент истины — запускаем центральный прокси</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>invoke-rc.d squid start
</span></code></pre></td></tr></table></div></figure>


<p>и…</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>FATAL: ERROR: cache_peer 127.0.0.1 specified twice
</span></code></pre></td></tr></table></div></figure>


<p>… и нас жестоко обламывают.</p>

<p>Немного подумав (руки до гугля не доходят) — заменяем в одной записи <code>127.0.0.1</code> => <code>localhost</code>.
Запускаем, и вот оно — счастье.
Сквид на 2й машине можно настроить копированием <code>/etc/squid/squid.conf</code> и удалением оттуда упоминаний о <code>cache_peer</code> и <code>never_direct</code> + поправить права доступа по протоколу <code>cache_mgr</code>.
Для тестирования — настроить любимый браузер на использования прокси и открыть любую страницу. Смотрим <code>/var/log/squid/access.log</code>, там должны быть примерно следующие строки:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>1214499645.364 15335 89.189.176.111 TCP_MISS/206 214755 GET ru.download.nvidia.com/Windows/177.41/177.41_geforce_winxp_64bit_international_whql.exe — ROUNDROBIN_PARENT/192.168.1.2 application/octet-stream
</span><span class='line'>1214499646.148 11138 89.189.176.111 TCP_MISS/206 534572 GET ru.download.nvidia.com/Windows/177.41/177.41_geforce_winvista_64bit_international_whql.exe — ROUNDROBIN_PARENT/127.0.0.1 application/octet-stream
</span><span class='line'>1214499650.695 3564 89.189.176.111 TCP_MISS/206 370947 GET ru.download.nvidia.com/Windows/177.41/177.41_geforce_winvista_64bit_international_whql.exe — ROUNDROBIN_PARENT/localhost application/octet-stream
</span><span class='line'>1214499658.899 52092 89.189.176.111 TCP_MISS/206 1115575 GET ru.download.nvidia.com/Windows/177.41/177.41_geforce_winvista_64bit_international_whql.exe — ROUNDROBIN_PARENT/192.168.1.2 application/octet-stream
</span></code></pre></td></tr></table></div></figure>


<p>Далее по плану — SqStat. Скачиваем архив, распаковываем в любую www-папку, переименовываем <code>config.inc.php.defaults</code> => <code>config.inc.php</code> и правим:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="x">$squidhost[0]=«localhost»;</span>
</span><span class='line'><span class="x">$squidport[0]=8080;</span>
</span><span class='line'><span class="x">$cachemgr_passwd[0]=&quot;&quot;;</span>
</span><span class='line'><span class="x">$resolveip[0]=false; # для красоты — можно и true поставить :)</span>
</span><span class='line'><span class="x">$hosts_file[0]=«hosts.txt»;</span>
</span><span class='line'><span class="x">$group_by[0]=«host»;</span>
</span><span class='line'>
</span><span class='line'><span class="x">$squidhost[]=«localhost»;</span>
</span><span class='line'><span class="x">$squidport[]=8081;</span>
</span><span class='line'><span class="x">$cachemgr_passwd[]=&quot;&quot;;</span>
</span><span class='line'><span class="x">$resolveip[]=false;</span>
</span><span class='line'><span class="x">$hosts_file[]=«hosts.txt»;</span>
</span><span class='line'><span class="x">$group_by[]=«host»;</span>
</span><span class='line'>
</span><span class='line'><span class="x">$squidhost[]=«localhost»;</span>
</span><span class='line'><span class="x">$squidport[]=8082;</span>
</span><span class='line'><span class="x">$cachemgr_passwd[]=&quot;&quot;;</span>
</span><span class='line'><span class="x">$resolveip[]=false;</span>
</span><span class='line'><span class="x">$hosts_file[]=«hosts.txt»;</span>
</span><span class='line'><span class="x">$group_by[]=«host»;</span>
</span><span class='line'>
</span><span class='line'><span class="x">$squidhost[]=«localhost»;</span>
</span><span class='line'><span class="x">$squidport[]=8081;</span>
</span><span class='line'><span class="x">$cachemgr_passwd[]=&quot;&quot;;</span>
</span><span class='line'><span class="x">$resolveip[]=false;</span>
</span><span class='line'><span class="x">$hosts_file[]=«hosts.txt»;</span>
</span><span class='line'><span class="x">$group_by[]=«host»;</span>
</span><span class='line'>
</span><span class='line'><span class="x">$squidhost[]=«192.168.1.2»;</span>
</span><span class='line'><span class="x">$squidport[]=8080;</span>
</span><span class='line'><span class="x">$cachemgr_passwd[]=&quot;&quot;;</span>
</span><span class='line'><span class="x">$resolveip[]=true;</span>
</span><span class='line'><span class="x">$hosts_file[]=«hosts.txt»;</span>
</span><span class='line'><span class="x">$group_by[]=«host»;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Открываем браузером sqstat.php и наблюдаем за активностью.</p>

<p>Так же выкладываю немного изменённую версию SqStat — дополнительно отображает суммарную скорость\объём по параметру группировки — <a href="https://docs.google.com/file/d/0BxaH7RICY3lbMHRlRm00T3lpZ2M/edit?usp=sharing">sqstat.class.php</a>, файл нужно положить в папку с оригинальным sqstat, заменив оригинал.</p>

<p>В пассивном режиме 3 сквида жрут 2.3% памяти, что в пересчёте на МБ составляет 14,72.</p>

<p>P.S. # squid -v</p>

<p>Squid Cache: Version 2.6.STABLE5</p>

<p><strong>UPD</strong>: все же, надеюсь, понимают, что при скачивании в 1 поток через такую прокси — увеличения скорости не будет? для увеличения скорости необходимо запустить закачку с n потоками, где n — количество проксей в кластере (если веса одинаковы).</p>
]]></content>
  </entry>
  
</feed>
