<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Артур Атнагулов</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://ru.atnartur.dev/</id>
  <link href="https://ru.atnartur.dev/" rel="alternate"/>
  <link href="https://ru.atnartur.dev/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Артур Атнагулов</rights>
  <subtitle>Блог веб-разработчика</subtitle>
  <title>atnartur</title>
  <updated>2025-02-01T17:45:09.745Z</updated>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="Яндекс.Облако" scheme="https://ru.atnartur.dev/tags/%D0%AF%D0%BD%D0%B4%D0%B5%D0%BA%D1%81-%D0%9E%D0%B1%D0%BB%D0%B0%D0%BA%D0%BE/"/>
    <category term="Django" scheme="https://ru.atnartur.dev/tags/Django/"/>
    <content>
      <![CDATA[<p>В Яндекс.Облаке есть сервисы, позволяющее запустить обработку задач из очереди сообщений. Celery из коробки умеет работать с аналогичными сервисами в Amazon Web Services, а для работы с Яндекс.Облаком нужно лишь немного поправить конфигурацию. Именно об этом и будет эта статья.</p><p>Заодно мы развернем приложение в <a href="https://cloud.yandex.ru/docs/serverless-containers/">Serverless Containers</a> - сервисе Яндекс.Облака, который запускает Docker-контейнер только в тот момент, когда происходит обращение к нему. И уместимся в <a href="https://cloud.yandex.ru/docs/billing/concepts/serverless-free-tier">Serverless Free Tier</a>, чтобы приложение работало почти бесплатно.</p><span id="more"></span><h2 id="Запуск-контейнера"><a href="#Запуск-контейнера" class="headerlink" title="Запуск контейнера"></a>Запуск контейнера</h2><p>Для того, чтобы приложение корректно запустилось в Serverless Containers, нужно добавить переменную окружения <code>PORT</code>, которая будет устанавливать порт для запуска приложения. Итоговый Dockerfile для Django может выглядеть вот так:</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs dockerfile"><span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.10</span><br><br><span class="hljs-keyword">ENV</span> PYTHONUNBUFFERED <span class="hljs-number">1</span><br><br><span class="hljs-keyword">WORKDIR</span><span class="language-bash"> /app</span><br><br><span class="hljs-keyword">COPY</span><span class="language-bash"> requirements.txt .</span><br><br><span class="hljs-keyword">RUN</span><span class="language-bash"> pip install -r requirements.txt</span><br><br><span class="hljs-comment"># указываем переменную окружения</span><br><span class="hljs-keyword">ENV</span> PORT <span class="hljs-number">8000</span><br><br><span class="hljs-keyword">COPY</span><span class="language-bash"> . .</span><br><br><span class="hljs-keyword">RUN</span><span class="language-bash"> python manage.py collectstatic --no-input</span><br><br><span class="hljs-comment"># gunicorn должен быть указан в requirements.txt</span><br><span class="hljs-comment"># projectname нужно заменить на пакет с Django-проектом</span><br><span class="hljs-keyword">CMD</span><span class="language-bash"> gunicorn --<span class="hljs-built_in">chdir</span> src --<span class="hljs-built_in">bind</span> 0.0.0.0:<span class="hljs-variable">$PORT</span> projectname.wsgi</span><br></code></pre></td></tr></table></figure><p>Далее собираем контейнер:</p><ul><li><code>docker build -t myproject .</code> - собираем образ</li><li><code>docker run -ti --rm -p 8000:8000 myproject</code> - запускаем контейнер</li><li>открываем в браузере <a href="http://localhost:8000/">http://localhost:8000</a> и проверяем, кто приложение работает правильно.</li></ul><p>Далее по <a href="https://cloud.yandex.ru/docs/serverless-containers/quickstart">инструкции Яндекс.Облака</a> запускаем контейнер.</p><h2 id="Подключение-к-очереди-сообщений"><a href="#Подключение-к-очереди-сообщений" class="headerlink" title="Подключение к очереди сообщений"></a>Подключение к очереди сообщений</h2><p><a href="https://docs.celeryq.dev/en/stable/">Celery</a> - инструмент для работы с очередями сообщений на Python. Он хорошо интегрируется с Django, но также может использоваться и без него.</p><p>Celery <a href="https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/index.html">поддерживает несколько брокеров сообщений</a>, но нас интересует Amazon SQS в первую очередь, так как Yandex Message Queue имеет поддержку Amazon SQS API.</p><p>Первым делом создаем Message Queue по <a href="https://cloud.yandex.ru/docs/message-queue/quickstart">официальной инструкции</a>.</p><p>По умолчанию Celery настроен на работу с Amazon SQS. Чтобы настроить Celery для работы с Yandex Message Queue, немного поменяем <code>settings.py</code>:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># по умолчанию указан Redis, но позже мы установим подключение к Yandex Message Queue</span><br>CELERY_BROKER_URL = os.environ.get(<span class="hljs-string">&quot;CELERY_BROKER_URL&quot;</span>, <span class="hljs-string">&quot;redis://localhost:6379/0&quot;</span>)<br><br><span class="hljs-comment"># указываем дополнительные опции для передачи сообщений, если устанавливается подключение к SQS</span><br>SQS_QUEUE = os.environ.get(<span class="hljs-string">&quot;SQS_QUEUE&quot;</span>)<br><span class="hljs-keyword">if</span> SQS_QUEUE:<br>    CELERY_BROKER_TRANSPORT_OPTIONS = &#123;<br>        <span class="hljs-string">&#x27;is_secure&#x27;</span>: <span class="hljs-literal">True</span>,<br>        <span class="hljs-string">&#x27;predefined_queues&#x27;</span>: &#123;<br>            <span class="hljs-string">&#x27;default&#x27;</span>: &#123;<br>                <span class="hljs-string">&#x27;url&#x27;</span>: SQS_QUEUE,<br>                <span class="hljs-string">&#x27;access_key_id&#x27;</span>: os.environ.get(<span class="hljs-string">&quot;SQS_ACCESS_KEY_ID&quot;</span>),<br>                <span class="hljs-string">&#x27;secret_access_key&#x27;</span>: os.environ.get(<span class="hljs-string">&quot;SQS_SECRET_ACCESS_KEY&quot;</span>),<br>            &#125;<br>        &#125;,<br>        <span class="hljs-string">&#x27;region&#x27;</span>: os.environ.get(<span class="hljs-string">&quot;SQS_REGION&quot;</span>)<br>    &#125;<br></code></pre></td></tr></table></figure><p>Указываем переменные окружения c заменой <code>&#123;ПЕРЕМЕННЫХ&#125;</code> на значения, полученные при создании очереди:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">CELERY_BROKER_URL=sqs://&#123;ACCESS_KEY&#125;:&#123;SECRET_KEY&#125;@message-queue.api.cloud.yandex.net<br>SQS_ACCESS_KEY_ID=&#123;ACCESS_KEY&#125;<br>SQS_SECRET_ACCESS_KEY=&#123;SECRET_KEY&#125;<br>SQS_REGION=&#123;REGION&#125;  # например, ru-central1<br></code></pre></td></tr></table></figure><p><em>Для указания переменных окружения можно использовать модуль <a href="https://pypi.org/project/python-dotenv/">dotenv</a> и  файл <code>.env</code> . Если вы тестируете работу сразу в Яндекс.Облаке, то переменные окружения можно указать <a href="https://cloud.yandex.ru/docs/serverless-containers/operations/manage-revision#revision-env">при создании новой версии контейнера</a>.</em></p><p>Устанавливаем дополнение Celery для работы с SQS:</p><ul><li>pip: <code>pip install celery[sqs]</code></li><li>poetry: <code>poetry add &#39;celery[sqs]&#39;</code></li></ul><p>После этого запускаем проект и пробуем добавить задачу в очередь (например, вызываем какой-нибудь URL, в котором происходит <code>task.delay()</code>).</p><h2 id="Запуск-воркеров-через-POST-запрос"><a href="#Запуск-воркеров-через-POST-запрос" class="headerlink" title="Запуск воркеров через POST-запрос"></a>Запуск воркеров через POST-запрос</h2><p>Организовать обработку сообщений, поступающих в очередь, можно несколькими способами</p><ol><li>запустить celery worker, который будет сам забирать сообщения и обрабатывать их</li><li>настроить триггер в Yandex Message Queue, который будет вызывать <a href="https://cloud.yandex.ru/services/functions">Cloud Function</a> или делать HTTP-запрос куда-либо при появлении нового сообщения.</li></ol><p>Мы воспользуемся вторым способом и настроим HTTP-вызов при появлении сообщения в очередь. Чтобы этот вызов обрабатывался, добавим новый view в Django.</p><p><em><strong>TLDR: ниже есть код готового обработчика запросов для Django</strong></em></p><p>Тело HTTP-запроса с сообщением от Celery выглядит примерно так (некоторые данные скрыты):</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;messages&quot;</span><span class="hljs-punctuation">:</span><br>    <span class="hljs-punctuation">[</span><br>        <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;event_metadata&quot;</span><span class="hljs-punctuation">:</span><br>            <span class="hljs-punctuation">&#123;</span><br>                <span class="hljs-attr">&quot;event_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;event_type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;yandex.cloud.events.messagequeue.QueueMessage&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;created_at&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;2022-09-02T05:38:46.026Z&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;tracing_context&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;cloud_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;folder_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><br>            <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;details&quot;</span><span class="hljs-punctuation">:</span><br>            <span class="hljs-punctuation">&#123;</span><br>                <span class="hljs-attr">&quot;queue_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>                <span class="hljs-attr">&quot;message&quot;</span><span class="hljs-punctuation">:</span><br>                <span class="hljs-punctuation">&#123;</span><br>                    <span class="hljs-attr">&quot;message_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>                    <span class="hljs-attr">&quot;md5_of_body&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>                    <span class="hljs-attr">&quot;body&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;BODY&quot;</span><span class="hljs-punctuation">,</span><br>                    <span class="hljs-attr">&quot;attributes&quot;</span><span class="hljs-punctuation">:</span><br>                    <span class="hljs-punctuation">&#123;</span><br>                        <span class="hljs-attr">&quot;ApproximateFirstReceiveTimestamp&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;1662097126149&quot;</span><span class="hljs-punctuation">,</span><br>                        <span class="hljs-attr">&quot;ApproximateReceiveCount&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;1&quot;</span><span class="hljs-punctuation">,</span><br>                        <span class="hljs-attr">&quot;SentTimestamp&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;1662097126026&quot;</span><br>                    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>                    <span class="hljs-attr">&quot;message_attributes&quot;</span><span class="hljs-punctuation">:</span><br>                    <span class="hljs-punctuation">&#123;</span><span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>                    <span class="hljs-attr">&quot;md5_of_message_attributes&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&quot;</span><br>                <span class="hljs-punctuation">&#125;</span><br>            <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>В этом сообщении нас интересует <code>BODY</code> - в нем содержится json, который отправил Celery, закодированный в base64. Если его раскодировать, мы увидим примерно следующее:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;body&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;BODY2&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;content-encoding&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;utf-8&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;content-type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;application/json&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;headers&quot;</span><span class="hljs-punctuation">:</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;lang&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;py&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;task&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;TASK&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;shadow&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;eta&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;expires&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;group&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;group_index&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;retries&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;timelimit&quot;</span><span class="hljs-punctuation">:</span><br>        <span class="hljs-punctuation">[</span><br>            <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span><br>            <span class="hljs-literal"><span class="hljs-keyword">null</span></span><br>        <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;root_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;parent_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;argsrepr&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;kwargsrepr&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&#123;&#125;&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;origin&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;gen83@yc-serverless&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;ignore_result&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;headers&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;properties&quot;</span><span class="hljs-punctuation">:</span><br>    <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;correlation_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;reply_to&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;delivery_mode&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;delivery_info&quot;</span><span class="hljs-punctuation">:</span><br>        <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;exchange&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;routing_key&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;default&quot;</span><br>        <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;priority&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;body_encoding&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;base64&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;delivery_tag&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;...&quot;</span><br>    <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>Вместо <code>TASK</code> выше будет что-то типа <code>appname.tasks.function_name</code> (т. е. путь до функции-обработчика задачи). <code>BODY2</code> - это опять json в base64 с аргументами команды:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">[</span><br>    <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;value1&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-string">&quot;value2&quot;</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span> <br>    <span class="hljs-punctuation">&#123;</span><span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span> <br>    <span class="hljs-punctuation">&#123;</span><span class="hljs-attr">&quot;callbacks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span> <span class="hljs-attr">&quot;errbacks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span> <span class="hljs-attr">&quot;chain&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span> <span class="hljs-attr">&quot;chord&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">]</span><br></code></pre></td></tr></table></figure><p>В нем - tuple с тремя аргументами: <code>args, kwargs, options</code>. Эта информация должна быть передана в таск для того, чтобы вызвать его с правильными параметрами.</p><p><strong>В итоге код обработчика запросов выглядит так:</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@api_view(<span class="hljs-params">[<span class="hljs-string">&quot;POST&quot;</span>]</span>)  </span><span class="hljs-comment"># декоратор из django rest framework</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">worker_view</span>(<span class="hljs-params">request</span>):<br>    <span class="hljs-comment"># request.data - JSON из тела запроса</span><br>    <span class="hljs-keyword">for</span> message <span class="hljs-keyword">in</span> request.data[<span class="hljs-string">&#x27;messages&#x27;</span>]:<br>        <span class="hljs-comment"># декодируем сообщение</span><br>        data_json = base64.b64decode(message[<span class="hljs-string">&#x27;details&#x27;</span>][<span class="hljs-string">&#x27;message&#x27;</span>][<span class="hljs-string">&#x27;body&#x27;</span>]).decode()<br>        data = app.backend.decode(data_json)<br><br>        <span class="hljs-comment"># находим функцию с таском</span><br>        module_path = data[<span class="hljs-string">&#x27;headers&#x27;</span>][<span class="hljs-string">&#x27;task&#x27;</span>].split(<span class="hljs-string">&#x27;.&#x27;</span>)<br>        package_path = <span class="hljs-string">&quot;.&quot;</span>.join(module_path[:-<span class="hljs-number">1</span>])<br>        function_name = module_path[-<span class="hljs-number">1</span>]<br>        module = importlib.import_module(package_path)<br>        function = <span class="hljs-built_in">getattr</span>(module, function_name)<br><br>        <span class="hljs-comment"># делаем принудительное сохранение результатов (об этом ниже)</span><br>        store_result_original_value = app.conf.task_store_eager_result<br>        app.conf.task_store_eager_result = <span class="hljs-literal">True</span><br><br>        <span class="hljs-comment"># достаем аргументы</span><br>        args, kwargs, options = json.loads(base64.b64decode(data[<span class="hljs-string">&#x27;body&#x27;</span>]))<br><br>        <span class="hljs-comment"># вызываем таск</span><br>        result = function.apply(<br>            args=args, <br>            kwargs=kwargs, <br>            task_id=data[<span class="hljs-string">&#x27;headers&#x27;</span>][<span class="hljs-string">&#x27;id&#x27;</span>], <br>            headers=data[<span class="hljs-string">&#x27;headers&#x27;</span>][<span class="hljs-string">&#x27;headers&#x27;</span>], <br>            **options<br>        )<br><br>        <span class="hljs-comment"># возвращаем настройки в исходное положение</span><br>        app.conf.task_store_eager_result = store_result_original_value<br><br>        <span class="hljs-comment"># возвращаем ответ в зависимости  от успешности обработки задачи</span><br>        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> result.successful():<br>            capture_exception(result.info)<br>            <span class="hljs-keyword">return</span> JsonResponse(&#123;<span class="hljs-string">&quot;status&quot;</span>: <span class="hljs-string">&quot;error&quot;</span>, <span class="hljs-string">&quot;info&quot;</span>: <span class="hljs-built_in">str</span>(result.info)&#125;)<br><br>    <span class="hljs-keyword">return</span> JsonResponse(&#123;<span class="hljs-string">&quot;status&quot;</span>: <span class="hljs-string">&quot;ok&quot;</span>&#125;, status=<span class="hljs-number">200</span>)<br></code></pre></td></tr></table></figure><p><em>Такая обработка задач поддерживает не все возможности Celery, но позволяет запустить простейшую обработку в Serverless Containers</em></p><p>Подключаем его в <code>urls.py</code>:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">path(<span class="hljs-string">&quot;worker/&quot;</span>, worker, name=<span class="hljs-string">&#x27;worker&#x27;</span>)<br></code></pre></td></tr></table></figure><h2 id="Создание-триггера"><a href="#Создание-триггера" class="headerlink" title="Создание триггера"></a>Создание триггера</h2><p>Конкретные шаги по созданию триггера для вызова контейнера при получении сообщения в очереди <a href="https://cloud.yandex.ru/docs/functions/operations/trigger/ymq-trigger-create">описаны в официальной инструкции</a>. </p><p>Подскажу некоторые параметры, которые нужно указывать при создании триггера, на примере команды для консоли:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs bash">yc serverless trigger create message-queue \<br>      --name=NAME \<br>      --queue QUEUE \<br>      --queue-service-account-id SERVICE_ACCOUNT_ID \<br>      --invoke-container-service-account-id SERVICE_ACCOUNT_ID \<br>      --invoke-container-id CONTAINER_ID \<br>      --invoke-container-path CONTAINER_PATH<br></code></pre></td></tr></table></figure><p>замените параметры:</p><ul><li><code>NAME</code> - название триггера</li><li><code>QUEUE</code> - arn очереди (строка вида <code>yrn:yc:ymq:REGION:FOLDER_ID:QUEUE_NAME</code>, можно скопировать на странице очереди в консоли Яндекс.Облака)</li><li><code>SERVICE_ACCOUNT_ID</code> - ID сервисного аккаунта</li><li><code>CONTAINER_ID</code> - ID контейнера</li><li><code>CONTAINER_PATH</code> - URL-адрес в контейнере, который занимается обработкой сообщений из очереди (в нашем случае - это <code>/worker/</code>)</li></ul><p>Можете снова запустить создание задачи в Celery, через некоторое время она должна обработаться. Информацию об обработке можно посмотреть в логах контейнера и в мониторинге очереди в консоли Яндекс.Облака.</p><h2 id="Сохранение-результатов-обработки-задач"><a href="#Сохранение-результатов-обработки-задач" class="headerlink" title="Сохранение результатов обработки задач"></a>Сохранение результатов обработки задач</h2><p>Бывают задачи, когда важно получить информацию об итогах обработки задачи в Celery.<br>Поэтому Celery может сохранять все данные, которые функции с задачами возвращают после своей работы. Если для вашего проекта это не применимо, можете пропустить этот раздел.</p><p>Механизм сохранения работает на базе celery result backends. Нас сейчас интересует <a href="https://docs.celeryq.dev/en/stable/internals/reference/celery.backends.dynamodb.html">AWS DynamoDB backend</a>, так как в Яндексе есть Yandex Database Serverless, <a href="https://cloud.yandex.ru/docs/ydb/docapi/tools/aws-setup">поддерживающая DynamoDB API</a>.</p><p>Создаем Yandex DB Serverless <a href="https://cloud.yandex.ru/docs/ydb/quickstart#serverless">по официальной инструкции</a>. Serverless-режим тарифицируется по использованию и бесплатные квоты.</p><p>Устанавливаем дополнение Celery для работы с DynamoDB:</p><ul><li>pip: <code>pip install celery[sqs,dynamodb]</code></li><li>poetry:  <code>poetry add &#39;celery[sqs,dynamodb]&#39;</code></li></ul><p>Добавляем новую константу в <code>settings.py</code>:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">CELERY_DYNAMODB_ENDPOINT_URL = os.environ.get(<span class="hljs-string">&quot;CELERY_DYNAMODB_ENDPOINT_URL&quot;</span>)<br></code></pre></td></tr></table></figure><p>Устанавливаем значение этой переменной из параметра Document API Endpoint (доступен на странице Yandex DB в консоли Яндекс.Облака).</p><p>Поясню про принудительное сохранение результатов в коде обработчика задачи. Дело в том, что принудительный вызов Celery-задач с помощью <code>apply()</code> возвращает <code>EagerResult</code>, который по умолчанию <a href="https://docs.celeryq.dev/en/stable/userguide/configuration.html#std-setting-task_store_eager_result">не должен сохраняться</a>. В нашей ситуации, наоборот, нужно сохранять такие результаты, поэтому мы сначала устанавливаем настройку в нужное нам положение, а потом возвращаем все как было, чтобы не мешать другим процессам, происходящим в приложении.</p><p>В целом, настройка связи с result backend завершена, можете попробовать запустить задачу с возвращением результатов и проверить, что эти результаты действительно возвращаются.</p><h2 id="Настройка-домена"><a href="#Настройка-домена" class="headerlink" title="Настройка домена"></a>Настройка домена</h2><p>В Яндекс.Облаке есть <a href="https://cloud.yandex.ru/docs/api-gateway/">API Gateway</a> для соединения нескольких сервисов под один хост. У нас по факту лишь один сервис, который предназначен для общения с внешним миром (это наш serverless container, развернутый в самом начале). </p><p>С API Gateway, кроме всего прочего, удобно подсоединять домены к развернутым в облаке приложениям.</p><p>Создадим API Gateway по <a href="https://cloud.yandex.ru/docs/api-gateway/quickstart/">официальной инструкции</a>, но в конфигурации укажем примерно следующее:</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">openapi:</span> <span class="hljs-string">&quot;3.0.0&quot;</span><br><span class="hljs-attr">info:</span><br>  <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span><br>  <span class="hljs-attr">title:</span> <span class="hljs-string">PROJECT</span> <span class="hljs-string">NAME</span><br><span class="hljs-attr">paths:</span><br>  <span class="hljs-string">/&#123;url+&#125;:</span><br>    <span class="hljs-attr">x-yc-apigateway-any-method:</span><br>      <span class="hljs-attr">summary:</span> <span class="hljs-string">Execute</span> <span class="hljs-string">container</span><br>      <span class="hljs-attr">operationId:</span> <span class="hljs-string">container</span><br>      <span class="hljs-attr">parameters:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">explode:</span> <span class="hljs-literal">false</span><br>        <span class="hljs-attr">in:</span> <span class="hljs-string">path</span><br>        <span class="hljs-attr">name:</span> <span class="hljs-string">url</span><br>        <span class="hljs-attr">required:</span> <span class="hljs-literal">false</span><br>        <span class="hljs-attr">style:</span> <span class="hljs-string">simple</span><br>      <span class="hljs-attr">x-yc-apigateway-integration:</span><br>        <span class="hljs-attr">type:</span> <span class="hljs-string">serverless_containers</span><br>        <span class="hljs-attr">container_id:</span> <span class="hljs-string">CONTAINER_ID</span><br>        <span class="hljs-attr">service_account_id:</span> <span class="hljs-string">SERVICE_ACCOUNT_ID</span><br></code></pre></td></tr></table></figure><p>Здесь указано, что все запросы, приходящие на API Gateway, будут переадресовываться на serverless container. Нужно заменить некоторые параметры:</p><ul><li><code>PROJECT NAME</code> - название проекта</li><li><code>CONTAINER_ID</code> - ID контейнера</li><li><code>SERVICE_ACCOUNT_ID</code> - ID сервисного аккаунта</li></ul><p>После создания появится адрес API Gateway, по которому можно будет обращаться к нашему приложению извне.</p><p>Теперь можно <a href="https://cloud.yandex.ru/docs/api-gateway/operations/api-gw-domains">подключить свой домен и настроить HTTPS сертификат с помощью Let’s Encrypt</a> прямо в консоли Яндекс.Облака.</p><h2 id="Free-Tier"><a href="#Free-Tier" class="headerlink" title="Free Tier"></a>Free Tier</h2><p>В начале я сказал, что почти всё использование сервисов, описанных в этой статье, будет бесплатным благодаря <a href="https://cloud.yandex.ru/docs/billing/concepts/serverless-free-tier">Free Tier</a>.</p><p>Действительно, Serverless Containers, Message Queue, YDB Serverless, API Gateway имеют определенный объем услуг, который не тарифицируется. </p><p>Yandex Database может работать на выделенных серверах, и тарифицироваться это будет значительно дороже. Мы использовали Serverless-вариант, входящий в Free Tier.</p><p>Используемый нами <a href="https://cloud.yandex.ru/docs/container-registry/pricing/">Yandex Container Registry</a> тарифицируется. На момент написания статьи 3 руб&#x2F;месяц за 1ГБ хранения. Чтобы сэкономить и здесь, можно настроить <a href="https://cloud.yandex.ru/docs/container-registry/operations/lifecycle-policy/lifecycle-policy-create">автоудаление образов</a>, так как после деплоя контейнера они больше будут не нужны.</p><p>В статье не рассматривалось подключение к PostgreSQL&#x2F;MySQL. Можете ознакомиться с их стоимостью на <a href="https://cloud.yandex.ru/prices">сайте Яндекс.Облака</a> и принять решение об использовании их в своем проекте.</p><h2 id="Автоматизация"><a href="#Автоматизация" class="headerlink" title="Автоматизация"></a>Автоматизация</h2><p>Автоматизировать этот процесс можно, например, с помощью <a href="https://www.terraform.io/">Terraform</a>. У Яндекс.Облака <a href="https://registry.terraform.io/providers/yandex-cloud/yandex/latest">есть провайдер</a>, позволяющий настроить большинство сервисов.</p><p>На момент написания статьи не было возможности заставить Message Queue trigger запускать serverless container, поэтому мне пришлось вызывать консольную команду через <code>yc</code> для этого.</p><p><a href="https://gist.github.com/atnartur/1dfce86d4999796d8d6b9949c60e8d12">Посмотреть конфигурационный файл Terraform</a></p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2022/celery-in-yandex-cloud/</id>
    <link href="https://ru.atnartur.dev/posts/2022/celery-in-yandex-cloud/"/>
    <published>2022-09-02T10:52:22.000Z</published>
    <summary>
      <![CDATA[<p>В Яндекс.Облаке есть сервисы, позволяющее запустить обработку задач из очереди сообщений. Celery из коробки умеет работать с аналогичными сервисами в Amazon Web Services, а для работы с Яндекс.Облаком нужно лишь немного поправить конфигурацию. Именно об этом и будет эта статья.</p>
<p>Заодно мы развернем приложение в <a href="https://cloud.yandex.ru/docs/serverless-containers/">Serverless Containers</a> - сервисе Яндекс.Облака, который запускает Docker-контейнер только в тот момент, когда происходит обращение к нему. И уместимся в <a href="https://cloud.yandex.ru/docs/billing/concepts/serverless-free-tier">Serverless Free Tier</a>, чтобы приложение работало почти бесплатно.</p>]]>
    </summary>
    <title>Развертывание Django+Celery приложения в Яндекс.Облаке с использованием Serverless технологий</title>
    <updated>2025-02-01T17:45:09.745Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Софт" scheme="https://ru.atnartur.dev/categories/%D0%A1%D0%BE%D1%84%D1%82/"/>
    <category term="Задачи" scheme="https://ru.atnartur.dev/tags/%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B8/"/>
    <content>
      <![CDATA[<p>Недавно я рассказал об используемых мною <a href="/posts/2020/time-management-techniques/">техниках управления задачами</a>. Сегодня расскажу о том, какие приложения для управления задачами, событиям и заметками я использую сейчас и использовал ранее. Речь пойдет в первую очередь про организацию личного информационного пространства, а не про командную работу.</p><span id="more"></span><p><img src="/content/2020/time-management-soft/first.png"></p><h2 id="А-может-на-бумаге"><a href="#А-может-на-бумаге" class="headerlink" title="А может на бумаге?"></a>А может на бумаге?</h2><p>Да, действительно, бумага — для многих самый удобный инструмент ведения дел. Как я писал ранее, я ценю телефон за то, что он всегда с собой, не мнется и предоставляет возможность внести правки в любом месте и в любое время без ручки и карандаша. Возможно, вам понравится расписывать задачи дома в удобном кресле с чашечкой кофе и красивым блокнотиком. Тогда все, что написано ниже, скорее всего вам не подходит :)</p><h2 id="С-чего-все-начиналось"><a href="#С-чего-все-начиналось" class="headerlink" title="С чего все начиналось?"></a>С чего все начиналось?</h2><p>Где-то в 2008 году я начал записывать события в календарь своего кнопочного Samsung C3010: дел было немного, а кнопки позволяли делать реально слепой набор <del>а сейчас сенсорные экраны все испортили:(</del>.</p><p><img src="/content/2020/time-management-soft/c3010-0.jpg"> <img src="/content/2020/time-management-soft/c3010-1.jpg"></p><p>С появлением iPad я начал использовать <a href="/posts/2013/wunderlist/">Wunderlist</a>: синхронизация с компьютером, напоминания — красота! Повторяться о нем не будем: после того, как Wunderlist вошел в состав Microsoft, было принято решение о <a href="https://www.wunderlist.com/blog/join-us-on-our-new-journey/">закрытии приложения</a> в пользу развития Microsoft To Do (ниже чуть подробнее о нем).</p><p>Во времена использования использования Nokia Lumia 630 с Windows 8.1 Mobile я пользовался платной версией <a href="https://www.2day-app.com/">2Day</a>. Понятия не имею, что с этим приложением сейчас, но тогда это было чуть ли не единственное адекватное приложение для ведения задач на Windows 8.1 Mobile, а синхронизация с компьютером (правда только на Windows 8.1 или Windows 10) была необходимостью после переезда с Wunderlist.</p><p><a href="/content/2020/time-management-soft/2day.png"><img src="/content/2020/time-management-soft/2day.png" alt="2Day"></a></p><h2 id="Использую-сейчас"><a href="#Использую-сейчас" class="headerlink" title="Использую сейчас"></a>Использую сейчас</h2><h3 id="TickTick"><a href="#TickTick" class="headerlink" title="TickTick"></a><a href="/r/ticktick/">TickTick</a></h3><p><img src="/content/2020/time-management-soft/ticktick.png"></p><p>Менеджер задач с поддержкой проектов, папок проектов, напоминаний, тегов, календаря, таймера-помидорки. Есть подгрузка информации из календарей, трекер привычек, таймер-помидорка, просмотр задач в виде канбан доски (отсутствует на мобильных устройствах). Совсем недавно появилась возможность форматировать текст в описании задач.</p><p>Можно быстро добавлять задачи, указывая прям в тексте <code>^список</code>, дату и время (<code>tom</code>, <code>tod</code>, <code>10:00</code>), <code>!приоритет</code>, <code>#тег</code>, <code>@ответственного</code> (когда есть общий доступ к списку задач).</p><p><a href="/content/2020/time-management-soft/ticktick-quick-add.png"><img src="/content/2020/time-management-soft/ticktick-quick-add.png" alt="Быстрое добавление TickTick"></a></p><p>Перешел на него по совету <a href="https://karamoff.dev/">Никиты Карамова</a> после использования Todoist: по сравнению с ним у TickTick дешевле платная подписка и есть нативное MacOS приложение (а не веб-версия в оболочке операционной системы). Платная подписка нужна из-за большого количества списков.</p><p>Кроме нативного приложения для MacOS еще есть iOS, Android, Windows приложения и расширения для браузеров.</p><h3 id="Google-Calendar"><a href="#Google-Calendar" class="headerlink" title="Google Calendar"></a><a href="https://calendar.google.com/">Google Calendar</a></h3><p><img src="/content/2020/time-management-soft/google-calendar-logo.jpg"></p><p>Веду здесь учет всех событий уже очень давно и на всех устройствах (кроме Samsung C3010, конечно). Умеет подключаться в системные календари Windows, iOS, Android, а также в TickTick и почтовый клиент <a href="https://sparkmailapp.com/">Spark</a> (да, там тоже есть просмотр календаря). Кстати, про облака и синхронизацию есть <a href="/posts/2018/clouds/">отдельная статья</a>.</p><h3 id="Google-Keep"><a href="#Google-Keep" class="headerlink" title="Google Keep"></a><a href="https://keep.google.com/">Google Keep</a></h3><p><img src="/content/2020/time-management-soft/google-keep.png"></p><p>Записываю небольшие заметки и списки. С компьютера открываю в браузере (хотя приходится найти терпение и подождать, пока веб-приложение загрузится), на смартфоне стоит приложение.</p><p>Минус в том, что в содержимом заметок никак нельзя форматировать текст: или пиши просто абзацы, или список. <em>(Недавно нашел расширение для Chrome <a href="https://chrome.google.com/webstore/detail/google-keep-powerup/popfcpchhcihipngccmnfdacakoicolm">Google Keep PowerUp</a>, которое умеет отображать форматированные с помощью markdown заметки.)</em> А для остального есть…</p><h3 id="Google-Docs"><a href="#Google-Docs" class="headerlink" title="Google Docs"></a><a href="https://docs.google.com/">Google Docs</a></h3><p><img src="/content/2020/time-management-soft/google-docs.png"></p><p>Храню здесь заметки побольше, когда возможности офисного онлайн-пакета оправдывают время ожидания его загрузки. Полностью перешел на его использование для большинства документов, почти забыв про Microsoft Office и яблочный пакет и не вспоминая про Libre Office и подобные.</p><h3 id="Pocket"><a href="#Pocket" class="headerlink" title="Pocket"></a><a href="https://getpocket.com/">Pocket</a></h3><p><img src="/content/2020/time-management-soft/pocket.png"></p><p>Pocket, на первый взгляд, не относится к приложениям для ведения задач, заметок или событий. Но на самом деле он является местом, где я накапливаю и читаю материалы из интернета, чтобы потом можно было вернуться к ним через некоторое время.</p><p>Процесс работы: в браузер устанавливается расширение, которое по клику отправляет текущую страницу на скачивание в мобильные устройства. Через некоторое время статью можно будет прочесть на смартфоне даже без подключения к интернету.</p><h2 id="Альтернативные-варианты"><a href="#Альтернативные-варианты" class="headerlink" title="Альтернативные варианты"></a>Альтернативные варианты</h2><h3 id="Todoist"><a href="#Todoist" class="headerlink" title="Todoist"></a><a href="http://todoist.com/">Todoist</a></h3><p><img src="/content/2020/time-management-soft/todoist.png"></p><p>Использовал ранее как приложение для ведения задач. Функционально очень похож на TickTick: есть такое же быстрое добавление, еще б<strong>о</strong>льшее количество приложений, напоминания, общие списки… красота! Также есть возможность записывать древовидные подзадачи (а у TickTick нет такой возможности).</p><p>Минусы: нет нативного MacOS приложения, платная подписка дороже, чем у TickTick, напоминания есть только в платной подписке (при регистрации дается пробный период с платной подпиской).</p><h3 id="Microsoft-ToDo"><a href="#Microsoft-ToDo" class="headerlink" title="Microsoft ToDo"></a><a href="https://todo.microsoft.com/">Microsoft ToDo</a></h3><p><img src="/content/2020/time-management-soft/microsoft-todo.png"></p><p>Приемник Wunderlist, поэтому имеет все те же функции: списки, задачи, подзадачи, напоминания… </p><p>Есть приложения под все современные операционные системы. Интегрирован в экосистему Microsoft (хотя для меня это скорее минус, чем плюс).</p><p>Из интересного: список “Мой день”, который помогает выбрать из общего списка задач только те, которые нужно обязательно сделать сегодня. </p><h3 id="Trello"><a href="#Trello" class="headerlink" title="Trello"></a><a href="https://trello.com/">Trello</a></h3><p><img src="/content/2020/time-management-soft/trello.png"></p><p>Инструмент для хранения карточек по столбцам. Что будет в карточках - задачи, заметки, события - решать вам. В каких столбцах эти карточки будут располагаться - тоже. Можно использовать как угодно и где угодно (есть приложения для Android, iOS и веб-версия). Есть напоминания, подзадачи, прикрепление файлов и множество интеграций. </p><p>Если еще никогда не пользовались Trello, попробуйте! Скорее всего однажды этот сервис вам пригодится.</p><h3 id="Evernote"><a href="#Evernote" class="headerlink" title="Evernote"></a><a href="https://evernote.com/">Evernote</a></h3><p><img src="/content/2020/time-management-soft/evernote.png"></p><p>Очень богатое по функционалу приложение для заметок: блокноты, метки, общий доступ, напоминания, полноценное форматирование. Может заменить Google Docs, Pocket, Google Keep. Конечно же, работает не так шустро, зато решает кучу задач. </p><h3 id="Notion"><a href="#Notion" class="headerlink" title="Notion"></a><a href="http://notion.so/">Notion</a></h3><p><img src="/content/2020/time-management-soft/notion.png"></p><p>Очень функциональный и легкий инструмент для хранения заметок и создания документов. Сравним по функционалу с Google Docs. Есть приложения для Android, iOS и великолепная веб-версия. Вся информация располагается в виде разнообразных блоков: текст, картинка, вставка google-документа, видео…</p><p>Перед началом использования взгляните на <a href="https://www.notion.so/pricing">цены</a>: на бесплатном тарифе доступно только 1000 блоков: при вставке блок используется раз и навсегда - даже после удаления блока счетчик не вернется в прежнее положение. </p><h3 id="AnyDO"><a href="#AnyDO" class="headerlink" title="AnyDO"></a><a href="https://www.any.do/">AnyDO</a></h3><p><img src="/content/2020/time-management-soft/anydo.jpg"></p><p>Минималистичный менеджер для управления задачами. Немного уступает TickTick&#x2F;Todoist по функциям, но является хорошим приложением для управления задачами с приложениями для мобильных и настольных операционных систем. Можно поставить даже на часы, в браузер и умные колонки.</p><h3 id="Google-Задачи"><a href="#Google-Задачи" class="headerlink" title="Google Задачи"></a><a href="https://mail.google.com/tasks/canvas">Google Задачи</a></h3><p><img src="/content/2020/time-management-soft/google-tasks.png"></p><p>Простой сервис для управления задачами с отдельным приложением на Android и iOS и интеграцией в боковую панель в GMail, Google Calendar, Google Docs в компьютерной версии. Есть списки и напоминания — минимализм!</p><hr><p>Надеюсь, этот материал поможет выбрать удобные приложения для организации личного информационного пространства.</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2020/time-management-soft/</id>
    <link href="https://ru.atnartur.dev/posts/2020/time-management-soft/"/>
    <published>2020-01-18T11:20:00.000Z</published>
    <summary>
      <![CDATA[<p>Недавно я рассказал об используемых мною <a href="/posts/2020/time-management-techniques/">техниках управления задачами</a>. Сегодня расскажу о том, какие приложения для управления задачами, событиям и заметками я использую сейчас и использовал ранее. Речь пойдет в первую очередь про организацию личного информационного пространства, а не про командную работу.</p>]]>
    </summary>
    <title>Приложения для управления задачами</title>
    <updated>2025-02-01T17:45:09.747Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Жизнь" scheme="https://ru.atnartur.dev/categories/%D0%96%D0%B8%D0%B7%D0%BD%D1%8C/"/>
    <category term="Книги" scheme="https://ru.atnartur.dev/tags/%D0%9A%D0%BD%D0%B8%D0%B3%D0%B8/"/>
    <category term="Задачи" scheme="https://ru.atnartur.dev/tags/%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B8/"/>
    <content>
      <![CDATA[<p>Тема личной эффективности и управления задачами меня волнует очень давно: <a href="/posts/2013/wunderlist/">обзор на приложение для ведения задач Wunderlist</a> - одна из первых статей в моем блоге. Но о приложениях чуть позже - это лишь инструменты для решения проблем, но не само решение. Сначала поговорим о техниках управления мыслями и задачами. </p><span id="more"></span><p>Ниже описанные техники были испытаны на себе, пережиты и выведены на основе собственного опыта. Недавно многие из них подкрепились книгой <a href="https://www.mann-ivanov-ferber.ru/books/dzhedajskie-texniki/">“Джедайские техники” Максима Дорофеева</a> (по ссылке есть интересная инфографика с общей схемой управления потоками дел). </p><p>Конечно же мой краткий конспект не претендует на полноценное освещение книги. Ее обязательно нужно прочесть полностью, если вы интересуетесь этой темой, так как автор объясняет информацию, используя примеры и аналогии (часть из них появится и в списке ниже) и доказывает эффективность методик. </p><p>Максим Дорофеев несколько раз повторяет: не все практики и техники подойдут каждому. Поэтому в моем списке ниже отдельно будет выделено то, что, на мой взгляд, помогает только мне (но вдруг это и вам тоже поможет). Остальное скорее всего подойдет каждому читателю.</p><h2 id="Техники-управления-делами"><a href="#Техники-управления-делами" class="headerlink" title="Техники управления делами"></a>Техники управления делами</h2><ul><li>Нужно все выгружать из головы: задачи, заметки, события. Мозг все равно забудет, а устройство&#x2F;бумага - нет.</li><li>Нужен план на день. Так можно будет быстрее переходить к выполнению других задач, не думая о том, что бы мне поделать сейчас.</li><li>Наш мозг очень ленивый и не любит много думать: каждую задачу надо расписывать максимально просто и подробно. <em>Плохо: “взять справку” (Какую справку? Где?), “купить” (чтобы потом в магазине думать: “а что же надо было купить-то?”). Хорошо: “купить хлеб и молоко” или “взять справку в отделе кадров на 3 этаже”.</em></li><li>Если времени мало, можно пересмотреть в список задач и сделать какое-то маленькое дело или кусочек от большого.</li><li>В свободные минуты можно пересматривать список задач и упрощать&#x2F;улучшать&#x2F;расписывать их подробнее, чтобы ленивому мозгу было проще работать в дальнейшем.</li><li>Для сохранения порядка нужны ежедневные обзоры списка задач: выкидываем неактуальное, обновляем актуальное, переносим то, что точно не успеем (беспорядок демотивирует).</li><li>Переносить задачи на следующий день - не плохо, если есть причины. Во время переноса нужно помнить не только про срок желаемого выполнения, но и про крайний срок (например, хотел сделать домашку в понедельник, но сдавать ее в пятницу. Значит задача может быть перенесена максимум до четверга.)</li><li>Нужны перерывы в работе: организм устает делать одно и то же долгое время, и ему нужен отдых, особенно мозгу и глазам. Про остальные части тела тоже забывать не стоит. Делать регулярные перерывы в работе поможет <a href="https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%BF%D0%BE%D0%BC%D0%B8%D0%B4%D0%BE%D1%80%D0%B0">техника помодоро</a>.</li><li>В состоянии сильного увлечения выполнением какой-либо задачи тратится много сил на что-то одно, а затем не хватает мотивации доделать остальное. <a href="https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%BF%D0%BE%D0%BC%D0%B8%D0%B4%D0%BE%D1%80%D0%B0">Помидорки спасут и тут</a>.</li><li>Нужно выключать уведомления во время работы. Люди могут подождать 25 минут (во время рабочего периода в помидорке), ничего страшного не случится. Если что-то срочное - можно позвонить.</li><li>Нужно устраивать выходные дни или часы и стараться не думать о работе в это время. Постоянная работа без отдыха ведет к выгоранию: апатия, постоянная усталость и нежелание что-либо делать. Не надо так.</li><li>Нужно следить за организмом и подстраивать выполнение задач под свой режим: в моменты повышенной продуктивности (утром) нужно делать более неприятные и&#x2F;или тяжелые задачи; в моменты пониженной продуктивности (ночь) - то, что попроще и поприятнее.</li><li>Нужно следить за инбоксами организованно: не стоит отвлекаться на мессенжеры и проверку почты во время работы. Лучше выделить отдельный интервал для этого и переносить все, что попросили сделать, в список задач.</li><li>Нормальный сон - это важно, очень важно, прям очень!!! Иначе будут проблемы со здоровьем.</li><li>Абсолютно всё сделать невозможно, нужно уметь отказывать и отказываться.</li></ul><h4 id="Мое-субъективное"><a href="#Мое-субъективное" class="headerlink" title="Мое субъективное"></a>Мое субъективное</h4><ul><li>Нужен общий список для задач со всех контекстов: работа&#x2F;учеба&#x2F;хобби&#x2F;личное. Так проще планировать общую загруженность по всем фронтам.</li><li>Задачи из рабочего трекера задач можно переносить не полностью, а, например, по названию проектов или по паре слов от полной формулировки (есть люди, которым полезно в личном трекере расписать задачу более подробно). В моем случае главное, чтобы была пометка, что надо поработать над этим проектом в этот день.</li><li>Мне проще вести дела в электронном виде: не мнущийся телефон всегда с собой и не требует ручки для внесения изменений.</li><li>Когда работаю не по помидоркам, к вечеру болит голова. Когда не выспался - тоже. Помидорки - тема!</li></ul><p><em>Спасибо Анне Новиковой за помощь в написании статьи.</em></p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2020/time-management-techniques/</id>
    <link href="https://ru.atnartur.dev/posts/2020/time-management-techniques/"/>
    <published>2020-01-10T19:00:00.000Z</published>
    <summary>
      <![CDATA[<p>Тема личной эффективности и управления задачами меня волнует очень давно: <a href="/posts/2013/wunderlist/">обзор на приложение для ведения задач Wunderlist</a> - одна из первых статей в моем блоге. Но о приложениях чуть позже - это лишь инструменты для решения проблем, но не само решение. Сначала поговорим о техниках управления мыслями и задачами. </p>]]>
    </summary>
    <title>Как управлять кучей дел одновременно и не сойти с ума</title>
    <updated>2025-02-01T17:45:09.747Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="Стачка" scheme="https://ru.atnartur.dev/tags/%D0%A1%D1%82%D0%B0%D1%87%D0%BA%D0%B0/"/>
    <category term="конференции" scheme="https://ru.atnartur.dev/tags/%D0%BA%D0%BE%D0%BD%D1%84%D0%B5%D1%80%D0%B5%D0%BD%D1%86%D0%B8%D0%B8/"/>
    <content>
      <![CDATA[<p>На <a href="https://nastachku.ru/">Стачку</a> я приехал уже второй раз. По сравнению с <a href="/posts/2017/nastachku2017/">первым разом (2017)</a> все стало НАМНОГО лучше и удобнее. Возможно, улучшения произошли еще в том году, но обстоятельства сложились таким образом, что я не смог съездить на конференцию “Стачка 2018”. В статье - очень краткие конспекты и отзывы о докладах со “Стачки 2019”, а также отзыв о самом мероприятии в целом.</p><span id="more"></span><p><img src="/content/2019/nastachku2019/logo.png" alt="Стачка"></p><p>В этом году удалось найти намного больше полезной информации для себя, глаза иногда разбегались, но изредка были и моменты, когда они не знали, куда бежать. В целом впечатления от конференции исключительно положительные. </p><p>Ниже немного расскажу о некоторых докладах, которые мне особенно понравились. Буду прикреплять ссылки на странички докладов и, чуть позже, на презентации (если они все-таки появятся в сети).</p><h2 id="Управление-продуктом-и-Agile"><a href="#Управление-продуктом-и-Agile" class="headerlink" title="Управление продуктом и Agile"></a>Управление продуктом и Agile</h2><p>В последнее время все больше и больше интересуюсь управлением в IT, поэтому было интересно посетить именно эту секцию. Полностью согласен с авторами подскаста <a href="https://github.com/web-standards-ru/podcast">“Веб-стандарты”</a>: на конференциях нужно посещать не знакомые темы, а то, что менее знакомо. В таком случае можно получить намного больше полезной и информации, а не еще раз услышать то, что уже знаешь.</p><h3 id="STATIK"><a href="#STATIK" class="headerlink" title="STATIK"></a>STATIK</h3><p><a href="https://nastachku.ru/kak-bystro-zapustit-kanban-sistemu-metod-statik-dlya-nachinayuschih">Михаил Вязанкин рассказал о введении Kanban системы с помощью метода STATIK</a>. Если кратко, чтобы ввести Kanban систему, нужно сделать шаги со слайда ниже: </p><p><img src="/content/2019/nastachku2019/statik.jpg" alt="S.T.A.T.I.K"></p><p><em>(Прошу прощения, фотография с задних рядов. Если появятся слайды, приложу в фото в нормальном качестве.)</em></p><p>Все сводится к пониманию процесса разработки всей командой в целом.  Kanban-система сделанная для одного проекта скорее всего не подойдет для другого, нужно всегда смотреть на особенности предметной области и подстраиваться под них и проектировать процесс с учетом целей, задач и темпа проекта.</p><p><img src="/content/2019/nastachku2019/workflow_mapping.jpg" alt="S.T.A.T.I.K"></p><h3 id="Парное-программирование-в-Dodo-Pizza"><a href="#Парное-программирование-в-Dodo-Pizza" class="headerlink" title="Парное программирование в Dodo Pizza"></a>Парное программирование в Dodo Pizza</h3><p><a href="https://nastachku.ru/kak-ne-paritsya-v-pare.-opyt-parnogo-programmirovaniya">Антон Бевзюк поделился опытом парного программирования</a>.</p><p><img src="/content/2019/nastachku2019/dodo_pair_station.jpg" alt="Парная станция"></p><p>В их компании в некоторых отделах у каждого сотрудника на рабочем месте к одному комьютеру подключено по два монитора, мышки и клавиатуры. Это позволяет проводить сессии парного программирования удобным для тела образом. Работа в паре должна вестись с перерывами на 10-15 минут каждый час и не должна превышать 5-6 часов в день, иначе будет накапливаться сильная усталость.</p><p>Чтобы навигатор (тот, кто советует) не терял фокус на работе, а драйвер (тот, кто пишет код) не уставал, кроме перерывов, можно применять разные практики парного программирования:</p><ul><li>При TDD разработке можно чередовать роли при переходе к каждому следующему тесту.</li><li>Для избежания споров драйвер может стать “голосовым вводом” в течение небольшого промежутка времени. (<em>“Друг, просто доверься мне, давай сделаем таким образом, а потом посмотрим, что сделаем с этим кодом”</em>)</li></ul><p>Парное программирование решает несколько задач:</p><ul><li>введение новичков в курс дела,</li><li>ускорение решения сложных задач,</li><li>обучение новым технологиям и передача опыта.</li></ul><p>Вот фото с реального рабочего процесса при таком подходе (обратите внимание на эмоции):</p><p><img src="/content/2019/nastachku2019/dodo_pair.jpg" alt="Парное программирование в Dodo"></p><h3 id="Я-хочу-ехать-а-не-шашечки"><a href="#Я-хочу-ехать-а-не-шашечки" class="headerlink" title="Я хочу ехать, а не шашечки"></a>Я хочу ехать, а не шашечки</h3><p><a href="https://nastachku.ru/ya-hochu-ehat-a-ne-shashechki.-kak-uskorit-komandu-ne-izuchaya-etot-vash-agile">Дмитрий Абрамов рассказал</a>, как с помощью нескольких простых правил значительно улучшить работу команды по Agile:</p><ul><li>Ставьте лимиты на количество задач в определенном статусе - человек не может делать несколько дел одновременно.</li><li>Огромный беклог не нужен: все знают, какие 10 задач самые важные, но никто не знает, что важнее для проекта - 56ая задача в беклоге или 58ая.</li><li>Сам беклог нужно разделить на 3 части (новые задачи, баги и костыли), а затем определить, какой процент времени команда будет тратить на каждую из частей этого беклога.</li><li>Для понимания бизнеса создаем отдельную доску в лоб: Todo, In progress, Done, и неважно, что в разработке In progress делится еще на кучу статусов.</li></ul><p><img src="/content/2019/nastachku2019/checkers.jpg"><img src="/content/2019/nastachku2019/checkers2.jpg"></p><h3 id="О-росте-компании"><a href="#О-росте-компании" class="headerlink" title="О росте компании"></a>О росте компании</h3><p><a href="https://nastachku.ru/kak-sohranit-kachestvo-pri-roste">Ренат Габдуллин поделился опытом сохранения качества при росте компании</a>. Кратко:</p><ul><li>Фиксируем принципы работы компании - куда и зачем мы все движемся.</li><li>“Хочешь похоронить вопрос - задай его в чат”. Поэтому если вопрос порождает обсуждение - нужно менять канал взаимодействия.</li><li>Фиксируем правила работы сотрудников - чем больше компания, тем меньше возможностей поговорить с каждым, установить дружеские отношения и объяснить, почему все так устроено и как нужно делать. Надо научиться быстрее нанимать людей, вводить их в курс дела и быстрее делать кадровые перестановки.</li><li>Находим мотиваторов на уровне линейных руководителей, а не только в высшем руководстве.</li><li>Развиваем сотрудников.</li><li>Фильтруем проекты.</li></ul><p><img src="/content/2019/nastachku2019/chats.jpg"><img src="/content/2019/nastachku2019/baby.jpg"></p><h2 id="Разработка"><a href="#Разработка" class="headerlink" title="Разработка"></a>Разработка</h2><h3 id="Видеозвонки-в-Одноклассниках"><a href="#Видеозвонки-в-Одноклассниках" class="headerlink" title="Видеозвонки в Одноклассниках"></a>Видеозвонки в Одноклассниках</h3><p><a href="https://nastachku.ru/videozvonki-pod-kapotom-ot-millionov-v-sutki-do-100-uchastnikov-v-odnoy-konferencii">Александр Тоболь рассказал</a> о том, как они разрабатывали систему видеозвонков. </p><p>Оказалось, что </p><ul><li>Whatsapp (и еще некоторые приложения) поддерживает только 4 человека в видеоконференции, потому что данные передаются peer to peer, и при увеличении количества участников все упирается в ширину канала.</li><li>Hangouts тратит больше зарядки и трафика, чем конкуренты, потому что в целях оптимизации серверной инфраструктуры и скорости видео с камеры рендерится в двух качествах (HD и похуже) сразу же на устройстве пользователя.</li></ul><p>В итоге в Одноклассниках используются разные подходы при звонках с разным количеством человек. А на последнем слайде в сравнительной таблице Одноклассники по возможностям догнали Zoom (ну что, скайп теперь точно <a href="https://twitter.com/atnartur/status/1120356958001930240">не нужен</a>? :)</p><h3 id="О-жизни-Frontend-разработчика"><a href="#О-жизни-Frontend-разработчика" class="headerlink" title="О жизни Frontend-разработчика"></a>О жизни Frontend-разработчика</h3><p><a href="https://nastachku.ru/karernye-vozmozhnosti-programmista-junior-middle-senior-a-dalshe-to-chto">Душевный рассказ о тернистом жизненном пути Михаила Синякова</a> снова подтвердил мои мысли о том, что в любой момент времени нужно стремиться к бОльшему, менять мир вокруг себя и развиваться, как бы тяжело не было в конкретный момент времени - Михаил прошел путь от студента неИТ факультета УЛГУ, работы охранником за 10к, веб-мастером за 7к до frontend тимлида в Ростелекоме.</p><p>В конце было немного о том, что разделение на junior&#x2F;middle&#x2F;senior скорее не про уровень знаний, а про способность взять на себя ответственность.</p><p>Кстати, это единственный доклад с секции “Карьера и кадры”, который я посетил. Однако он имеет отношение к разработке, поэтому пусть будет в этой часте статьи :) </p><h3 id="PostgreSQL-12-и-неизвестные-БД"><a href="#PostgreSQL-12-и-неизвестные-БД" class="headerlink" title="PostgreSQL 12 и неизвестные БД"></a>PostgreSQL 12 и неизвестные БД</h3><p><a href="https://nastachku.ru/chto-novogo-ozhidaetsya-v-pg12">Олег Бартунов рассказал</a>, что в PostgreSQL 12 все, как обычно, быстрее выше сильнее круче быстрее и так далее. А еще поиск по JSONB полям будет еще быстрее. В общем, надо обновляться.</p><p><a href="https://nastachku.ru/razrabotchiki-ostalis-neizvestny">Александр Миловиднов показал</a> проекты закрывшихся баз данных: почему проект приостановлен, кем он разрабатывался, с какой целью, и главное, какие архитектурные и технологические решения использовались. Некоторый опыт был перенят другими новыми и крутыми проектами.</p><h3 id="Как-делается-оптимизация"><a href="#Как-делается-оптимизация" class="headerlink" title="Как делается оптимизация?"></a>Как делается оптимизация?</h3><p><a href="https://nastachku.ru/kak-delaetsya-optimizaciya">Андрей Аксенов из Avito показал</a>, как быстро обработать большой CSV-файл на C++. Или на Go. Или на Python. Да даже на PHP все работает почти так же. В общем, выбор технологий не так важен, важны алгоритмы и понимание работы компьютера. Хотел вставить сюда скрин страшного C++ кода, но поберегу ваше зрение <del>и мозг</del>, потому что качество фотографии отвратительное.</p><h3 id="Аварии-помогают-учиться"><a href="#Аварии-помогают-учиться" class="headerlink" title="Аварии помогают учиться"></a>Аварии помогают учиться</h3><p><a href="https://nastachku.ru/avarii-pomogayut-uchitsya">Алексей Кирпичников из Контура объяснил</a>, как (сразу после инцидента по шаблону с прикреплением временных меток, скриншотов из чата, мониторинга…) и зачем (для обсуждения с командой и решения проблем в рамках одной команды и всей компании) писать постмортемы - отчеты о произошедших в ИТ инфраструктуре инцидентах.</p><p><img src="/content/2019/nastachku2019/postmortems.jpg"></p><h3 id="Топ-ошибок-при-работе-с-PostgreSQL"><a href="#Топ-ошибок-при-работе-с-PostgreSQL" class="headerlink" title="Топ ошибок при работе с PostgreSQL"></a>Топ ошибок при работе с PostgreSQL</h3><p><a href="https://nastachku.ru/top-oshibok-so-storony-razrabotki-pri-rabote-s-postgresq">Алексей Лесовский попросил</a> быть аккуратнее с ORM, транзакциями; использовать SSD для серверов БД; поправлять конфиги, адаптируя их к железу (а не оставлять дефолты); мониторить нагрузку; не бояться репликации и партиционирования; не использовать Postgres для всего подряд (логи должны быть не в основной БД).</p><h3 id="От-отдела-эксплуатации-до-SRE"><a href="#От-отдела-эксплуатации-до-SRE" class="headerlink" title="От отдела эксплуатации до SRE"></a>От отдела эксплуатации до SRE</h3><p><a href="https://nastachku.ru/dodo-pizza-ot-otdela-ekspluatacii-do-sre">Олег Блохин рассказал</a> о том, как ИТ инфраструктура Dodo Pizza выросла с двух человек и 1 сервера до кучи ресурсов в Azure и как они это все поддерживают.</p><p>В компании создается новая команда SRE, которые по сути является командой разработчиков с хорошими знаниями в системном администрировании и способностями поддерживать проекты на этапе эксплуатации. В ответственность этой команды входят: availability, latency, performance, efficiency, change management, monitoring, emergency response, capacity planning.</p><p><img src="/content/2019/nastachku2019/dodo_sre1.jpg"></p><p><img src="/content/2019/nastachku2019/dodo_sre2.jpg"></p><p>Команду решили назвать именно SRE, а не DevOps, потому что у термина SRE есть сложившееся понимание (благодаря книгам от Google), в то время как DevOps понимается всеми по-разному, а люди, которые откликаются на такие вакансии, обычно слабо понимают и в инфраструктуре, и в коде.</p><p>Также Олег подкрепил мои мысли о том, что книга <a href="https://www.ozon.ru/context/detail/id/140385054/">“Философия DevOps” (в оригинале “Effective DevOps”)</a> не несет в себе достаточно качественной информации. Там в осномнов про “давайте жить дружно”, и лишь один полстранички (из 400) про алерты.</p><p>И совет прочесть хорошие книжки в конце:</p><p><img src="/content/2019/nastachku2019/dodo_sre3.jpg"></p><h3 id="Синхронизируем-кучу-данных-между-сервисами"><a href="#Синхронизируем-кучу-данных-между-сервисами" class="headerlink" title="Синхронизируем кучу данных между сервисами"></a>Синхронизируем кучу данных между сервисами</h3><p><a href="https://nastachku.ru/otlozhennye-dannye-nash-mehanizm-obespecheniya-konsistentnosti">Как это делать - рассказал Андрей Литуненко</a>. В общем, строим инфраструктуру из общей очереди сообщений, Apache Kafka, воркеров, обвешиваем это все бизнес логикой по сохранению во временное хранилище тех данных, которые пока не могут вставиться в БД из-за того, что не пришли некоторые другие данные, и получаем быстро работающую систему. Однако, решение, предложенное Андреем, разрабатывалось некоторое время назад, и в современных реалиях можно было бы сделать проще.</p><h3 id="Как-упростить-разработку-и-поддержку-приложений"><a href="#Как-упростить-разработку-и-поддержку-приложений" class="headerlink" title="Как упростить разработку и поддержку приложений?"></a>Как упростить разработку и поддержку приложений?</h3><p>Абстракции! <a href="https://nastachku.ru/kak-razrabatyvat-podderzhivaemye-prilozheniya-perenosimye-bloki-kontrakty-vzaimodeystviya-patterny-proektirovaniya-i-ih-problemy">Артем Кудряшов продемонстрировал</a> подход по выделению общей абстракции для микросервисного общения.</p><p><img src="/content/2019/nastachku2019/abstract.jpg"></p><h2 id="О-конференции"><a href="#О-конференции" class="headerlink" title="О конференции"></a>О конференции</h2><p>Конференция традиционно проходит в <a href="https://ru.wikipedia.org/wiki/%D0%A3%D0%BB%D1%8C%D1%8F%D0%BD%D0%BE%D0%B2%D1%81%D0%BA%D0%B8%D0%B9_%D0%BC%D1%83%D0%B7%D0%B5%D0%B9-%D0%BC%D0%B5%D0%BC%D0%BE%D1%80%D0%B8%D0%B0%D0%BB_%D0%92._%D0%98._%D0%9B%D0%B5%D0%BD%D0%B8%D0%BD%D0%B0">Ленинском мемориале</a> - музей с лекционными и кинозалами, построенный в память В. И. Ленина. <a href="/posts/2017/nastachku2017/">“Советский ИТ антужар повсюду”</a>, как говорится. </p><p>В 2017 году было много проблем с навигацией и между докладами не было перерывов: в тот момент, когда докладчик понимал, что времени не просто мало, а вообще нет, и начинал в спешке дорассказывать свой доклад, а потом шли вопросы, перекрывая время следующего, мне уже хотелось идти в другую аудиторию на другой доклад, который почему-то находился на совершенно другом этаже и в другом крыле здания. Комментарий в форме отзывов на конференцию не был проигнорирован: в этот раз между всеми докладами были 15-минутные перерывы, докладчики обычно заканчивали основной материал за 15 минут до начала другого доклада, начинали отвечать на вопросы, и можно было спокойно переходить на другую локацию.</p><p>В коридорах стало больше волонтеров и информационных кубов-указателей, которые делали навигацию просто очевидной.</p><p><img src="/content/2019/nastachku2019/badge.jpg" alt="Бейдж"></p><p>Говорят, что бейдж - это лицо конференции. В этот раз это лицо выглядит как-то странно. Вернее сейчас, когда я приехал домой - оно уже почти никак не выглядит, а через месяц, наверное, совсем растворится. Дело в том, что в этот раз используется какая-то странная краска или бумага, которая на данный момент местами почти стерлась.</p><p>Немного расстроил сайт, который постоянно терял авторизацию при работе с мобильника, а примерно через час-два после начала конференции - упал. До сих пор почему-то нет мобильного приложения, в котором можно было бы без боли смотреть расписание докладов. Проблемы с авторизацией делают бессмысленной кнопку “Хочу пойти”, которая помогает сформировать личное расписание докладов. А еще было бы неплохо добавить кнопку экспорта “моего расписания” в Google календарь. </p><p>Воскресный кросс, из-за которого напрочь перекрыли все подъезды к мемориалу - тоже был великолепен. Водители негодовали, участники опаздывали, спикеры первых докладов жаловались, что к ним никто не пришел. В общем, не надо так.</p><blockquote class="twitter-tweet"><p lang="ru" dir="ltr">А вообще доклады на <a href="https://twitter.com/hashtag/%D1%81%D1%82%D0%B0%D1%87%D0%BA%D0%B02019?src=hash&amp;ref_src=twsrc%5Etfw">#стачка2019</a> очень даже хорошие. Иногда даже глаза разбегаются)</p>&mdash; atnartur (@atnartur) <a href="https://twitter.com/atnartur/status/1122016704991780865?ref_src=twsrc%5Etfw">April 27, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p>Спасибо <a href="http://stride.one/">Stride</a> за билеты!</p><p>P. S. Если глаза из орбит не вылезут от еще большого количества фотографий слайдов, то <a href="https://photos.app.goo.gl/bRpKzGCBaK6H6uCaA">можете посмотреть</a>.</p><p>P. P. S. 24 мая 2019 вставлены скриншоты со слайдов вместо фотографий с экрана.</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2019/nastachku2019/</id>
    <link href="https://ru.atnartur.dev/posts/2019/nastachku2019/"/>
    <published>2019-04-28T15:59:33.000Z</published>
    <summary>
      <![CDATA[<p>На <a href="https://nastachku.ru/">Стачку</a> я приехал уже второй раз. По сравнению с <a href="/posts/2017/nastachku2017/">первым разом (2017)</a> все стало НАМНОГО лучше и удобнее. Возможно, улучшения произошли еще в том году, но обстоятельства сложились таким образом, что я не смог съездить на конференцию “Стачка 2018”. В статье - очень краткие конспекты и отзывы о докладах со “Стачки 2019”, а также отзыв о самом мероприятии в целом.</p>]]>
    </summary>
    <title>Стачка 2019</title>
    <updated>2025-02-01T17:45:09.746Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Софт" scheme="https://ru.atnartur.dev/categories/%D0%A1%D0%BE%D1%84%D1%82/"/>
    <category term="Google" scheme="https://ru.atnartur.dev/tags/Google/"/>
    <category term="Wunderlist" scheme="https://ru.atnartur.dev/tags/Wunderlist/"/>
    <category term="Flow" scheme="https://ru.atnartur.dev/tags/Flow/"/>
    <category term="Gitlab" scheme="https://ru.atnartur.dev/tags/Gitlab/"/>
    <category term="Trello" scheme="https://ru.atnartur.dev/tags/Trello/"/>
    <content>
      <![CDATA[<p>Во многих статьях о продуктивности пишут, что у человека всегда должен быть список дел, которые он должен сделат за день. Однако во время работы нередко происходит так, что личные задачи лежат в одном месте, корпоративные - в другом, задачи open source проекта (к примеру) - в третьем. Сегодня мы поговорим о том, как собрать единый список задач в Google Calendar автоматически и в каждый момент времени понимать свою повестку дня. <span id="more"></span></p><p><img src="/content/2018/02/all-in-one-tasks/google-calendar-logo.jpg"></p><h2 id="Почему-Google-Calendar"><a href="#Почему-Google-Calendar" class="headerlink" title="Почему Google Calendar?"></a>Почему Google Calendar?</h2><p><a href="/posts/2018/clouds/">Google сервисы отлично синхронизируются</a> с большинством платформ: iOS, MacOS, Android, даже WindowsPhone! На Windows 10 вроде тоже работала синхронизация, но я ее запускал очень давно, поэтому уверенно утверждать о том, что события адекватно придут на эту операционную систему от Microsoft, не буду.</p><p>С Google сервисами отлично интегрированы различные автоматизаторы типа <a href="/posts/2017/univer-ifttt/">IFTTT</a> или <a href="http://zapier.com/">Zapier</a>, поэтому в случае отсутствия готовых инструментов синхронизации, всегда можно попробовать накостылять что-то свое. А если даже таким образом решить проблему не получится, то для большинства популярных языков программирования есть модули, умеющие общаться с Google API.</p><p>Однако, не все так плохо, и далее Вы в этом убедитесь :)</p><h2 id="А-почему-календарь-Там-же-нет-задач"><a href="#А-почему-календарь-Там-же-нет-задач" class="headerlink" title="А почему календарь? Там же нет задач!"></a>А почему календарь? Там же нет задач!</h2><p>Задачи могут отображаться в календаре сверху как события на весь день. Таким образом, внизу будет график настоящих событий, а наверху будут все задачи. По-моему, это удобно:)</p><p>Есть только одна проблема: нет возможности редактировать информацию из внешних сервисов. Но я считаю, что в этом нет острой необходимости, потому что каждый описанный ниже сервис имеет много специфических функций, которые в любом случае не укладываются в интерфейс календаря. И ведь наша задача не сделать полнофункциональный агрегатор, а лишь просто собрать список задач в одном месте.</p><p>Еще одна важная особенность: в календаре появятся только те задачи, у которых есть крайний срок, иначе непонятно, в какое место календаря их добавлять.</p><h2 id="Внешние-календари"><a href="#Внешние-календари" class="headerlink" title="Внешние календари"></a>Внешние календари</h2><p>Внешние календари подключаются с помощью ссылки. Добавление происходит с помощью кнопки “+”, далее “Добавить по URL”.</p><p><img src="/content/2018/02/all-in-one-tasks/import.png"></p><p>Вставляем туда ссылку, нажимаем на “Добавить календарь”. И готово!</p><p>Таким образом, внешние сервисы смогут доставлять к Вам список своих событий. Но так как календари все-таки внешние, Google по умолчанию не синхронизирует их. </p><p>Чтобы включить синхронизацию, нужно перейти на <a href="https://calendar.google.com/calendar/syncselect">эту страницу</a> и отметить галочками нужные для синхронизации календари.</p><h2 id="Подключение-сервисов"><a href="#Подключение-сервисов" class="headerlink" title="Подключение сервисов"></a>Подключение сервисов</h2><h3 id="Wunderlist"><a href="#Wunderlist" class="headerlink" title="Wunderlist"></a><a href="/posts/2013/wunderlist/">Wunderlist</a></h3><p>Работает через подключение календаря по ссылке. </p><ol><li><a href="http://wunderlist.com/">Заходим в веб-приложение</a></li><li>Нажимаем на стрелочку рядом с именем</li><li>Далее “Параметры учетной записи”</li><li>Чуть ниже будет поле “Канал календаря” со ссылкой. Добавляем ссылку указанным выше способом, и задачи из Wunderlist появляются в Google Calendar!</li></ol><h3 id="Trello-Todoist"><a href="#Trello-Todoist" class="headerlink" title="Trello, Todoist"></a><a href="http://trello.com/">Trello</a>, <a href="http://todoist.com/">Todoist</a></h3><p>Для настройки нужно зарегистрироваться на <a href="http://ifttt.com/">IFTTT</a>, подключить аккаунты Trello и Google Calendar и настроить соответствующую автоматизацию с помощью ссылок ниже. Частично процесс работы с этим сервисом описан <a href="/posts/2017/univer-ifttt/">здесь</a>.</p><ul><li><a href="https://ifttt.com/applets/299812p-when-you-create-a-new-trello-card-add-an-event-to-google-calendar">Апплет для Trelo</a>. </li><li><a href="https://ifttt.com/connect/todoist/google_calendar">Апплет для Todoist</a></li></ul><h3 id="Flow"><a href="#Flow" class="headerlink" title="Flow"></a><a href="http://getflow.com/">Flow</a></h3><p>Flow также поддерживает синхронизацию календаря по ссылке. Подробная инструкция по настройке находится <a href="https://www.getflow.com/support/tasks/calendar-sync">здесь</a>. Нужно отметить, что указанных в статье настроек нет в бета-версии веб-приложения, поэтому нужно по-старинке заходить на <a href="http://app.getflow.com/">http://app.getflow.com</a>.</p><h3 id="Gitlab"><a href="#Gitlab" class="headerlink" title="Gitlab"></a><a href="http://gitlab.com/">Gitlab</a></h3><p>Let’s hardcore begins! Нету у них никакой поддержки экспорта крайних сроков issue. Можно попробовать сделать костыль на <a href="http://zapier.com/">Zapier</a>, который умеет подключаться к обоим сервисам, но я не рискнул это сделать.</p><p><a href="https://github.com/le1ca/gitlab-calendar">На github есть проект</a>, который умеет осуществлять синхронизацию через Webhook. Я дописал поддержку <a href="/posts/2017/edu-open-data/">Docker</a>, кому надо - <a href="https://github.com/atnartur/gitlab-calendar/tree/feat_docker">пользуйтесь</a>. Конечно же минус такого решения: придется заморачиваться с сервером. Но если у Вас нет проблем с этим, перейдем к настройке:</p><ol><li>Переходим в <a href="https://console.developers.google.com/iam-admin/serviceaccounts/">Google Developer Console</a></li><li>Создаем проект</li><li>Создаем сервисный аккаунт</li><li>Создаем ключ сервисного аккаунта, скачиваем его и кладем в папку с проектом</li><li>С помощью верхнего поиска находим <code>Google Calendar API</code> и включаем его </li><li>Создаем календарь в Google Calendar, и даем доступ сервисному аккаунту на внесение изменений</li><li>Отредактировать конфигурационный файл:</li></ol><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br><br>    <span class="hljs-attr">&quot;logLevel&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;INFO&quot;</span><span class="hljs-punctuation">,</span><br><br>    <span class="hljs-attr">&quot;timezone&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Europe/Moscow&quot;</span><span class="hljs-punctuation">,</span><br>    <br>    <span class="hljs-attr">&quot;googleSecretFile&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;НАЗВАНИЕ ФАЙЛА ИЗ 4 ПУНКТА&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;gitlabSecret&quot;</span><span class="hljs-punctuation">:</span>     <span class="hljs-string">&quot;ЛЮБОЕ СЛУЧАЙНОЕ СЛОВО (БУДЕТ ИСПОЛЬЗОВАТЬСЯ ДЛЯ ПРОВЕРКИ ДОСТУПА)&quot;</span><span class="hljs-punctuation">,</span><br>    <br>    <span class="hljs-attr">&quot;gitlabApi&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;enable&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;url&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;https://gitlab.com/&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;token&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;paste_impersonation_token_here&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;inviteAssignees&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <br>    <span class="hljs-attr">&quot;authorizedDomain&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;your-gsuite-domain.net&quot;</span><span class="hljs-punctuation">,</span><br>    <br>    <span class="hljs-attr">&quot;listenPort&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">8080</span><span class="hljs-punctuation">,</span><br>    <br>    <span class="hljs-attr">&quot;ssl&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;enable&quot;</span><span class="hljs-punctuation">:</span>   <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;keyfile&quot;</span><span class="hljs-punctuation">:</span>  <span class="hljs-string">&quot;/etc/letsencrypt/live/domain_name/privkey.pem&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;certfile&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;/etc/letsencrypt/live/domain_name/fullchain.pem&quot;</span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <br>    <span class="hljs-attr">&quot;repoMap&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;НАЗВАНИЕ РЕПОЗИТОРИЯ&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;НАЗВАНИЕ КАЛЕНДАРЯ&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;НАЗВАНИЕ РЕПОЗИТОРИЯ 2&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;НАЗВАНИЕ КАЛЕНДАРЯ 2&quot;</span><br>    <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>    <br>    <span class="hljs-attr">&quot;dropPrivileges&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;enable&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;user&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;nobody&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;group&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;nobody&quot;</span><br>    <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><ol start="8"><li>Выкладываем приложение gitlab-calendar на какой-нибудь сервер. Оно должно запуститься на порту <code>8080</code> (если Вы ничего не меняли)</li><li>Идем на <a href="http://gitlab.com/">http://gitlab.com</a></li><li>Открываем нужный проект, затем Settings &#x2F; Integrations</li><li>Вставляем ссылку на работающее приложение</li><li>В Secret Token вставляем <code>ЛЮБОЕ СЛУЧАЙНОЕ СЛОВО</code>, которое Вы написали в конфигурационном файле в 7 пункте</li><li>Добавлем webhook</li><li>Готово (да, наконец-то)</li></ol><p>Постарался описать все подробно, надеюсь, у Вас получится:)</p><p>Если инструкции по интеграции Вашего любимого сервиса тут нет, <a href="http://atnartur.dev/">напишите мне</a> или <a href="http://github.com/atnartur/blog">дополните</a> статью самостоятельно.</p><p>Продуктивной Вам недели!:)</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2018/all-in-one-tasks/</id>
    <link href="https://ru.atnartur.dev/posts/2018/all-in-one-tasks/"/>
    <published>2018-02-18T15:52:57.000Z</published>
    <summary>
      <![CDATA[<p>Во многих статьях о продуктивности пишут, что у человека всегда должен быть список дел, которые он должен сделат за день. Однако во время работы нередко происходит так, что личные задачи лежат в одном месте, корпоративные - в другом, задачи open source проекта (к примеру) - в третьем. Сегодня мы поговорим о том, как собрать единый список задач в Google Calendar автоматически и в каждый момент времени понимать свою повестку дня.]]>
    </summary>
    <title>Сводим все задачи в Google Calendar</title>
    <updated>2026-06-14T17:55:57.221Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Софт" scheme="https://ru.atnartur.dev/categories/%D0%A1%D0%BE%D1%84%D1%82/"/>
    <category term="облака" scheme="https://ru.atnartur.dev/tags/%D0%BE%D0%B1%D0%BB%D0%B0%D0%BA%D0%B0/"/>
    <category term="синхронизация" scheme="https://ru.atnartur.dev/tags/%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F/"/>
    <category term="Яндекс.Диск" scheme="https://ru.atnartur.dev/tags/%D0%AF%D0%BD%D0%B4%D0%B5%D0%BA%D1%81-%D0%94%D0%B8%D1%81%D0%BA/"/>
    <category term="Google Drive" scheme="https://ru.atnartur.dev/tags/Google-Drive/"/>
    <category term="Dropbox" scheme="https://ru.atnartur.dev/tags/Dropbox/"/>
    <content>
      <![CDATA[<p>Очень часто бывает так, что о сохранности своих данных мы вспоминает только в тот момент, когда они по какой-то причине пропадают. Сегодня я хочу рассказать Вам про свой подход работы с облачными хранилищами, а также о том, как не бояться за сохранность данных, когда сгорает жесткий диск, а телефон попадает в стиральную машину:)</p><span id="more"></span><p><img src="/content/2018/01/clouds/image.jpg"></p><h2 id="Что-нужно-сохранять"><a href="#Что-нужно-сохранять" class="headerlink" title="Что нужно сохранять?"></a>Что нужно сохранять?</h2><p>Давайте сначала подумаем о том, какие данные в принципе могут храниться в наших устройствах:</p><ul><li><strong>Памятные фото&#x2F;видео</strong> - то, что дорого сердцу и хранит воспоминания о Вашей жизни. Это однозначно нужно сохранять, так как этих данных скорее всего больше ни у кого нет.</li><li><strong>Проекты&#x2F;документы</strong> - рабочие файлы, творчество (с исходными файлами), юридическая информация. Потеря таких данных может вызывать не только чувство обиды и печали, но и проблемы в жизни или на работе. Сюда же можно отнести телефонную книгу и пароли.</li><li><strong>Развлечения</strong> - музыка, фильмы, игры. Не восстановилось? Ну и ладно, в основном все это сейчас можно либо заново скачать, либо установить с диска.</li><li><strong>Другое</strong> - возможно, я что-то не упомянул. Пишите в комментарии, если здесь стоит добавить еще какую-то важную категорию данных.</li></ul><h2 id="Как-сохранять"><a href="#Как-сохранять" class="headerlink" title="Как сохранять?"></a>Как сохранять?</h2><p>Далее я предложу несколько сервисов для хранения каждого типа данных. Чтобы не запутаться в том, где какие данные лежат, нужно установить для себя некоторые правила хранения: фотографии лежат тут, документы лежат здесь, и так далее… Тогда в нужный момент получить доступ к данным будет просто.</p><ul><li><strong>Памятные фото&#x2F;видео</strong><ul><li><strong><a href="http://photos.google.com/">Google Фото</a></strong> - этот сервис первым предоставил пользователям возможность хранить неограниченный объем (немного сжатых, но приемлимых для домашнего архива) фотографий и видео. Конечно же есть возможность включить автозагрузку с мобильного устройства. Время от времени напоминает о том, что произошло в этот день несколько лет назад, а так же делает автоматические фильмы и коллажи на основе загруженных фотографий.</li><li><strong><a href="/r/ydisk">Яндекс.Диск</a></strong> - совсем недавно также разрешили загружать неограниченный объем фотографий, если они загружаются с мобильного устройства. Функция напоминания о том, что произошло  несколько лет назад также в наличии. Кроме того, есть возможность публикации фотографий в социальные сети.</li></ul></li><li><strong>Проекты&#x2F;документы</strong><ul><li><strong><a href="http://drive.google.com/">Google Диск</a></strong> - это не просто хранилище файлов, но и онлайн офис, который почти ничем не уступает офисному пакету от Microsoft или Apple. Пользователи могут не только делиться файлами и папками, но и совместно редактировать их.</li><li><strong><a href="/r/ydisk">Яндекс.Диск</a></strong> - а на втором месте снова продукт от Яндекса, в котором <a href="https://yandex.ru/company/services_news/2015/1116">около двух лет назад появился</a> Microsoft Office Online и разрешил пользователям редактировать документы, не выходя из <del>дома</del> онлайн-интерфейса. Стоит отметить, что у Яндекса самая быстрая синхронизация данных, так как большинство серверов находится в России.</li><li><strong>Сервисы <a href="http://google.ru/">Google</a></strong> могут сохранять еще и <a href="http://contacts.google.com/">контакты</a> и <a href="http://calendar.google.com/">события</a>. Многие пользователи Android-устройств смогут уже сейчас найти там свои данные, а владельцы iPhone, iPad и другой продукции Apple предпочтут <a href="http://icloud.com/">iCloud</a>. Но стоит отметить, что гугловые сервисы могут синхронизироваться с устройствами на iOS и Android, а Apple не может похвастаться такой возможностью.</li><li><strong>Git</strong>, как уже неоднократно <a href="/posts/2017/edu-open-data/">говорилось</a>, должен использовать каждый уважающий себя разработчик. Кроме всего прочего, git-хостинги типа <a href="http://github.com/">Github</a>, <a href="http://gitlab.com/">Gitlab</a> или <a href="http://bitbucket.org/">Bitbucket</a> устанавливают просто космические ограничения на размеры файлов в репозиториях. А ограничения по количеству пользователей обходятся просто правильным выбором из трех вышеуказанных сервисов:)</li></ul></li><li><strong>Развлечения</strong><ul><li><strong><a href="/r/disk">Яндекс.Диск</a></strong> - если для хранения музыки и фильмов достаточно 10 гигабайт, то это очень круто:)</li><li><strong><a href="http://cloud.mail.ru/">Облако Mail.ru</a></strong> в момент открытия сервиса раздали всем по целому терабайту, который вместит все то, что не загрузилось в другие облака. Здесь, кроме нескольких папочек с музыкой, я храню еще и архивы проектов, так как они занимают очень много места.</li><li>В последние несколько лет заработало множество сервисов по лицензионному прослушиванию музыки: <a href="http://apple.com/music">Apple Music</a>, <a href="http://music.google.com/">Google Music</a>, <a href="http://music.yandex.ru/">Яндекс.Музыка</a>, а так же Boom от ВКонтакте. В области просмотра видео также есть неплохие сервисы. Возможно пора переходить на лицензионную сторону?</li></ul></li></ul><h2 id="Сколько-данных-можно-хранить-в-облаках"><a href="#Сколько-данных-можно-хранить-в-облаках" class="headerlink" title="Сколько данных можно хранить в облаках?"></a>Сколько данных можно хранить в облаках?</h2><ul><li><a href="http://photos.google.com/">Google Фото</a> сохраняет неограниченное число фотографий с небольшим сжатием. При выключенном сжатии, фотографии начинают храниться на Google Диске и тратить запас свободных гигабайт из этого сервиса</li><li><a href="http://drive.google.com/">Google Диск</a> позволяет хранить до 15 гигабайт бесплатно. Кроме несжатых фотографий, место на Диске занимают еще и вложения Gmail.</li><li><a href="/r/ydisk">Яндекс.Диск</a> бесплатно разместит 10 гигабайт любых файлов и неограниченное количество фотографий, загруженных с мобильных устройств. Также Яндекс <a href="https://yandex.ru/support/disk/enlarge/bonus-space.html">сотрудничает</a> с компаниями и проводит <a href="/posts/2013/yandex-disk-32gb/">свои акции</a>, которые добавляют бесплатное место к Диску. Кстати, предоставяется по полгига за каждого приглашенного Вами пользователя.</li><li><a href="/r/drop">Dropbox</a> остался почти нетронутым в нашем обсуждении облачных сервисов, потому что предоставляет всего лишь 2 гигабайта на бесплатном тарифе, но также имеет удобные интерфейсы для работы с документами, фотографиями и простыми файлами. Как и Яндекс.Диск, Dropbox дает по полгига за каждого приглашенного пользователя. </li><li><a href="http://cloud.mail.ru/">Облако Mail.ru</a> как говорилось выше, сюда можно загрузить 1 терабайт данных.</li></ul><p>На самом деле, не обязательно придерживаться всех этих правил, чтобы работать с облачными хранилищами. Я просто рассказал про свой подход, при котором, как мне кажется, можно сохранить больше всего данных в облаках. </p><p>Ну а пост, конечно же, эмоциональный. Уже в третий раз мой ноутбук ломается в самый неподходящий момент, а доступ к данным, которые остались внутри, получить необходимо <del>прямо сейчас</del> внезапно. Благодаря облакам, я могу просто взять другой аппарат, в течение дня развернуть все необходимое, и продолжить заниматься своими делами. Предлагаю и Вам перенести всё в облака и избежать лишних проблем и волнения в экстренных ситуациях:)</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2018/clouds/</id>
    <link href="https://ru.atnartur.dev/posts/2018/clouds/"/>
    <published>2018-01-17T17:19:39.000Z</published>
    <summary>
      <![CDATA[<p>Очень часто бывает так, что о сохранности своих данных мы вспоминает только в тот момент, когда они по какой-то причине пропадают. Сегодня я хочу рассказать Вам про свой подход работы с облачными хранилищами, а также о том, как не бояться за сохранность данных, когда сгорает жесткий диск, а телефон попадает в стиральную машину:)</p>]]>
    </summary>
    <title>Всё в облака!</title>
    <updated>2025-02-01T17:45:09.745Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="git" scheme="https://ru.atnartur.dev/tags/git/"/>
    <category term="Docker" scheme="https://ru.atnartur.dev/tags/Docker/"/>
    <content>
      <![CDATA[<p>Несколько ссылок на мои образовательные материалы по git и Docker.</p><span id="more"></span><p>Не так давно <a href="/posts/2017/univer-ifttt/">вышел материал</a> о том, как мы в университете сделали автоматизацию доставки лекционных материалов до студентов. Сейчас я решил поделиться еще некоторым полезными вещами, которые скопились у меня за последнее время.</p><h2 id="Git"><a href="#Git" class="headerlink" title="Git"></a>Git</h2><p><img src="/content/2013/07/Git-Logo-2Color-300x125.jpg"></p><p>Жизнь разработчика очень трудно представить без работы систем контроля версий. В 2013 году у меня вышла пара статей о том, как работать с git: <a href="/posts/2013/git-1/">об основах</a> и <a href="/posts/2013/git-2/">регистрации на Bitbucket и установке git на Windows</a>. Часть информации в этих статьях устарела, часть остается актуальной по сей день.</p><p>В ноябре 2016 я сделал презентацию по основам git, которая включает в себя все объяснение основных команд git, базовое введение в работу с консолью и работу с ветками. Посмотреть ее можно <a href="https://docs.google.com/presentation/d/1BIpeJf14iSeG3EL8mYtsvDEUeNU2yWVJ440rNWH51o8/edit?usp=sharing">здесь</a>.</p><p>Также повторю ссылку на <a href="https://events.yandex.ru/lib/talks/671/">доклад Сергея Сергеева из Яндекса</a>, который я цитировал в 2013 году. Там очень понятно и с примерами объясняются основные принципы работы систем контроля версий.</p><h2 id="Docker"><a href="#Docker" class="headerlink" title="Docker"></a>Docker</h2><p><img src="/content/2016/10/docker-house/docker.png"></p><p>В последнее время в своих проектах я использую Docker для запуска и деплоя серверных приложений. </p><p>Не так давно у меня вышла статья <a href="/posts/2016/docker-house/">о принципах контейнерного подхода</a>. </p><p><a href="https://gitlab.com/atnartur/docker-docs">Документация по запаковыванию приложений в Docker</a> содержит еще и инструкцию по запуску деплоя контейнеров через Gitlab CI. Также есть <a href="https://youtu.be/w6waxC0snGk">дополненная видеоинструкция по этому поводу</a>, <a href="https://github.com/atnartur/docker-test-project">репозиторий</a> и <a href="https://docs.google.com/presentation/d/1Z7LgtFm3Ke8oatkk5MtzESwtIUwc84fNL4_NbYUQ0kg/edit?usp=sharing">презентация</a> к ней.</p><p>Для того, чтобы запускать Gitlab CI раннеры, я собрал <a href="https://hub.docker.com/r/atnartur/docker/">специальный образ</a>. Также <a href="https://github.com/atnartur/docker-image">в репозиторием с исходниками</a> этого образа есть установочные скрипты для docker и docker-compose для систем на базе Debian.</p><p>В общем-то, на этом все. Если у вас есть желание помочь, то многие из этих материалов являются git-репозиториями, возможность отправки пуллреквестов в которые открыта. Также вы всегда можете связаться со мной по <a href="http://atnartur.dev/">указанным контактам</a> :)</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2017/edu-open-data/</id>
    <link href="https://ru.atnartur.dev/posts/2017/edu-open-data/"/>
    <published>2017-09-04T17:49:47.000Z</published>
    <summary>
      <![CDATA[<p>Несколько ссылок на мои образовательные материалы по git и Docker.</p>]]>
    </summary>
    <title>Образовательные материалы по git и Docker</title>
    <updated>2026-06-14T17:55:57.215Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="Trello" scheme="https://ru.atnartur.dev/tags/Trello/"/>
    <category term="GMail" scheme="https://ru.atnartur.dev/tags/GMail/"/>
    <category term="IFTTT" scheme="https://ru.atnartur.dev/tags/IFTTT/"/>
    <category term="Telegram" scheme="https://ru.atnartur.dev/tags/Telegram/"/>
    <content>
      <![CDATA[<p>На нашем факультете было принято отправлять лекционные материалы и некоторые общие объявления для студентов на так называемую почту потока. Преподавателям очень удобно: написав на один адрес, они сразу же доставляют информацию огромному количеству людей. А вот когда огромное количество этих самых людей пытаются зайти на почтовый сервис для того, чтобы забрать корреспонденцию, начинаются трудности. <span id="more"></span></p><p><img src="/content/2017/09/univer-ifttt/ifttt.jpg"></p><p>Таким образом, наш бедненький Gmail начал часто падать и закрывать доступ к авторизации. Однако, всем по-прежнему хотелось получать информацию вовремя, ведь иногда на лекции бывает трудно записать все термины, о которых говорит преподаватель. Для уверенности в том, что студенты смогут повторить полученный материал, наставники пересылают лекции студентам простым для них способом. В конце семестра на этой самой почте потока также появляются экзаменационные вопросы.</p><p>С первых дней обучения мне понравилось, что никто и никогда не идет против применения информационных технологий в любой сфере жизни на факультете, поэтому нам с однокурсником с легкостью удалось запустить автоматизацию этого процесса.</p><p>Преподаватели  продолжают отправлять информацию на обычную почту - на этом этапе не изменилось ровным счетом ничего. Но вот после того, как письмо попадает в Gmail, почтовый сервис автоматически пересылает письма на специальный адрес, который предоставляет сервис <a href="http://trello.com/">Trello</a> (именно там было решено структурировано хранить всю получаемую информацию).</p><p>Переcылка в Gmail добавляется в настройках на вкладке “Пересылка и POP&#x2F;IMAP”. Специальный адрес электронной почты, который “слушает” Trello и переделывает письма в карточки на доске, можно получить после нажатия на кнопку “Menu”, далее “Next” , затем “Email Settings”.</p><p><img src="/content/2017/09/univer-ifttt/trello.png"></p><p>Информацию собрали, осталось грамотно уведомить поток о данном сервисе. Уже на этом этапе можно отправить ссылку на доску в Trello, ребята могут поставить мобильное приложение и получать уведомления. Но большинство из нас являются активными пользователями мессенджера <a href="http://telegram.org/">Telegram</a>, и поэтому мы решили также сделать специальный канал, куда бот будет отправлять все обновления. </p><p>Автоматизацию с телеграмом настраивать уже немного сложнее.</p><p>После создания Telegram-канала мы поняли, что в этот канал  сторонние боты писать не могут. Таким образом, бот <a href="http://ifttt.com/">IFTTT</a> нам помочь не смог, а сам сервис все-таки сыграл свою важную роль. Здесь мы настроили Applet (в сервисе IFTTT) под названием “<a href="https://ifttt.com/gmail">If any new email in inbox for ...@gmail.com</a>, then make <a href="https://ifttt.com/maker_webhooks">a web request</a>”, который после получения новых писем делает запрос на <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>,  которое в свою очередь публикует сообщение в канал с помощью бота.<br>Для начала нужно зарегистрировать бота, чтобы получить его токен. Делается это с помощью специального служебного бота <a href="http://t.me/botfather">@BotFather</a>.</p><p>Запрос к Bot API выглядит так: </p><ul><li>URL: <code>https://api.telegram.org/botBOTTOKEN/sendMessage?chat_id=CHAT_ID&amp;parse_mode=markdown</code></li><li>Method: <code>POST</code></li><li>Content-type: <code>application/json</code></li><li>Body: <figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;chat_id&quot;</span><span class="hljs-punctuation">:</span><span class="hljs-string">&quot;CHAT_ID&quot;</span><span class="hljs-punctuation">,</span> <br>    <span class="hljs-attr">&quot;parse_mode&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;markdown&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;text&quot;</span><span class="hljs-punctuation">:</span><span class="hljs-string">&quot;*&#123;&#123;Subject&#125;&#125;* от &#123;&#123;FromAddress&#125;&#125; \n</span><br><span class="hljs-string">    &#123;&#123;FirstAttachmentPublicURL&#125;&#125; \n</span><br><span class="hljs-string">    Полная версия на доске https://trello.com/...&quot;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure></li></ul><p>Используемые сокращения:</p><ul><li><code>BOTTOKEN</code> - токен созданного нами на предыдущем шаге бота</li><li><code>CHAT_ID</code> - ID чата, в который надо публиковать сообщения (после добавления бота в этот чат, ID чата можно будет увидеть, открыв следующую ссылку <code>https://api.telegram.org/botBOTОКЕN/getUpdates</code>)</li><li>Вместо переменных подставляются соответствующие поля из электронного письма</li><li><code>trello.com/…</code> - ссылка на нашу доску, для того, чтобы можно было прочесть полную версию письма или ознакомиться с другими документами</li></ul><p>Иногда бывает так, что старосты групп (только они имеют доступ на редактирование доски) добавляют информацию на доску вручную. Для того, чтобы эта информация тоже попадала в телеграм, был настроен еще один IFTTT-Applet “<a href="https://ifttt.com/trello">If card added to the board in Trello</a> make <a href="https://ifttt.com/maker_webhooks">a web request</a>” со следующими параметрами:</p><ul><li>URL, Method, Content-type такие же, как и в предыдущем шаге.</li><li>Body: <figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;chat_id&quot;</span><span class="hljs-punctuation">:</span><span class="hljs-string">&quot;-1001082774373&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;parse_mode&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;markdown&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;text&quot;</span><span class="hljs-punctuation">:</span><span class="hljs-string">&quot;&lt;&lt;&lt;*&#123;&#123;Title&#125;&#125;*&gt;&gt;&gt; в списке &#123;&#123;ListName&#125;&#125; \n</span><br><span class="hljs-string">    &#123;&#123;Description&#125;&#125; \n</span><br><span class="hljs-string">    &lt;&lt;&lt;[Посмотреть полностью](&#123;&#123;CardURL&#125;&#125;)&gt;&gt;&gt;&quot;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure></li></ul><p>Опять-таки, вместо переменных подставляются данные из добавленной карточки из Trello.</p><p>В результате, в канале появлялись сообщения следующего вида: </p><p><img src="/content/2017/09/univer-ifttt/tg.png"></p><p>После выполнения вышеуказанных настроек, происходило тестирование, длившееся достаточно долго. Так как в IFTTT обрабатывается очень много событий, наши запросы иногда выполнялись моментально, иногда около часа, а иногда (крайне редко, конечно) вообще не выполнялись. </p><p>Спасибо <a href="https://vk.com/andrey_romanov11">Андрею Романову</a> за помощь в организации и тестировании данной затеи. В целом, студенты и старосты были довольны нашим трудом. Хочется отметить, что всё, что описано выше, не требует вложений (Trello, IFTTT, Telegram - бесплатные) и  написания кода (все работает через связки API этих сервисов). Однако, если вы захотите что-то улучшить в данной автоматизации, то вам все-таки придется написать своего бота, который будет обрабатывать вебхуки от других API. </p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2017/univer-ifttt/</id>
    <link href="https://ru.atnartur.dev/posts/2017/univer-ifttt/"/>
    <published>2017-09-02T20:21:05.000Z</published>
    <summary>
      <![CDATA[<p>На нашем факультете было принято отправлять лекционные материалы и некоторые общие объявления для студентов на так называемую почту потока. Преподавателям очень удобно: написав на один адрес, они сразу же доставляют информацию огромному количеству людей. А вот когда огромное количество этих самых людей пытаются зайти на почтовый сервис для того, чтобы забрать корреспонденцию, начинаются трудности.]]>
    </summary>
    <title>Автоматизируй это: как мы разгружали почту потока</title>
    <updated>2025-02-01T17:45:09.747Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="Стачка" scheme="https://ru.atnartur.dev/tags/%D0%A1%D1%82%D0%B0%D1%87%D0%BA%D0%B0/"/>
    <category term="конференции" scheme="https://ru.atnartur.dev/tags/%D0%BA%D0%BE%D0%BD%D1%84%D0%B5%D1%80%D0%B5%D0%BD%D1%86%D0%B8%D0%B8/"/>
    <content>
      <![CDATA[<p>После того, как я начал увлекаться разработкой, в привычку вошло смотреть записи докладов с ИТ конференций. Конечно же, вскоре появилось желание побывать на таком мероприятии вживую, почувствовать атмосферу большого ИТ сообщества, получить кучу новой информации и нового опыта.</p><span id="more"></span><p><img src="/content/2017/04/nastachku2017/17966848_741891662656360_3074818954279755585_o.jpg"></p><p>Не могу сказать, что ожидания от Стачки 2017 оправдались полностью, но и говорить, что все было ужасно, не буду, так как это мое первое посещение крупной конференции. Однако, лабиринты Ленинского Мемориала и хаотичное расположение локаций, где проходят доклады, “оценить” получилось. Если еще добавить тот факт, что доклады идут друг за другом без перерыва (особенно если модератор не следит за регламентом), то переходы между лекционными залами становятся просто адом.</p><p><img src="/content/2017/04/nastachku2017/17880394_741892442656282_6381169541146383698_o.jpg"></p><p>Перед конференцией во время просмотра расписания разбегались глаза от количества интересной информации, которая будет подаваться одновременно. По факту получилось так, что многие докладчики пересказывали очевидную информацию или повторяли чье-то выступление. </p><p>Очень хотелось узнать подробнее про машинное обучение, и во второй день “Стачки” это все же удалось сделать на докладе Никиты Жильцова, который рассказал именно про принципы работы нейронных сетей, а не сделал очередной обзор этой области. Стоит отметить, что Никита является выпускником КФУ и основателем Textocat, а я принимал участие в хакатоне Textocat в 2014 и сейчас учусь в КФУ. Приятно видеть знакомые лица!</p><p>Интересно было послушать на решения проблем, с которыми столкнулись недавно мы. Павел Силин предложил упростить архитектуру SPA-приложений на React и использовать сервер на GraphQL как единственный источник правды. </p><p>Во время доклада Ивана Михеева про сложные интеграционные проекты в голове была только одна фраза: “Блин, это же про нас!” Здесь я убедился в том, что правильная, четкая спецификация и грамотное проектирование системы - самое главное.</p><p>Огромное спасибо компании Сергею Крылову и всем ребятам за возможность побывать на Стачке! Несмотря на все недочеты, впечатление от конференции осталось положительным:)</p><p><img src="/content/2017/04/nastachku2017/17990291_741892299322963_7587855555566597705_o.jpg"></p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2017/nastachku2017/</id>
    <link href="https://ru.atnartur.dev/posts/2017/nastachku2017/"/>
    <published>2017-04-16T15:59:33.000Z</published>
    <summary>
      <![CDATA[<p>После того, как я начал увлекаться разработкой, в привычку вошло смотреть записи докладов с ИТ конференций. Конечно же, вскоре появилось желание побывать на таком мероприятии вживую, почувствовать атмосферу большого ИТ сообщества, получить кучу новой информации и нового опыта.</p>]]>
    </summary>
    <title>Стачка 2017 - это советский ИТ антураж повсюду</title>
    <updated>2025-02-01T17:45:09.746Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Софт" scheme="https://ru.atnartur.dev/categories/%D0%A1%D0%BE%D1%84%D1%82/"/>
    <category term="Google" scheme="https://ru.atnartur.dev/tags/Google/"/>
    <category term="Google Drive" scheme="https://ru.atnartur.dev/tags/Google-Drive/"/>
    <content>
      <![CDATA[<p>Google Диск, и Google Таблицы в частности, очень часто используются для доставки очень важной информации, об обновлениях которой необходимо узнавать своевременно. <span id="more"></span></p><p>Сразу к делу. И так, чтобы подписаться на обновления данных в Google Таблице, нужно:</p><ol><li>Авторизоваться в Google аккаунте</li><li>Открыть нужную Google Таблицу</li><li>В верхнем меню нажать на “Инструменты”, затем “Уведомления”</li><li>Выбрать параметры получения уведомлений</li><li>Нажать на кнопку “ОК”.</li></ol><p><img src="/content/2017/02/google-spreadsheet-notifications/main.png"></p><p>По опыту: ежедневная сводка об изменениях приходит каждый день в 15.00. Если Вас не устраивает такой принцип работы, можно выбрать мгновенный способ получения уведомлений. Информация об изменениях будет приходить на почту, которая привязана к текущему аккаунту Google.</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2017/google-spreadsheet-notifications/</id>
    <link href="https://ru.atnartur.dev/posts/2017/google-spreadsheet-notifications/"/>
    <published>2017-02-22T15:22:32.000Z</published>
    <summary>
      <![CDATA[<p>Google Диск, и Google Таблицы в частности, очень часто используются для доставки очень важной информации, об обновлениях которой необходимо узнавать своевременно.]]>
    </summary>
    <title>Подписка на уведомления об изменении Google таблиц</title>
    <updated>2025-02-01T17:45:09.746Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="Проекты" scheme="https://ru.atnartur.dev/tags/%D0%9F%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B/"/>
    <content>
      <![CDATA[<p>Идея написать этот пост пришла ко мне после прочтения <a href="https://vk.com/wall202916188_266">заметки Данила Агафонова</a> об изученных им технологиях. Я решил продолжить серию таких записей, так как считаю, что мой список и подход может быть интересен кому-нибудь еще. <span id="more"></span></p><p>Мой путь изучения технологий начался в 2011 году. Все, что было до этого, не очень интересно. И даже если было бы интересно, то рассказывать там особо не о чем. </p><p>Я не очень люблю долго изучать что-то в теории, а сразу спешу применять как можно больше на практике. Возможно, в некоторых местах внимательные читатели моего блога и те, кто близко со мной знакомы, смогут провести параллели с развитием некоторых проектов, в которых я работал когда-то. И эти люди, безусловно, будут правы, потому что в каждом следующем проекте я всегда пытаюсь взять какую-то новую технологическую высоту, таким образом разрабатывая новые продукты и развивая свои навыки написания хорошего, качественного и современного кода.  </p><p>Особую благодарность хочу выразить проекту <a href="http://itjump.ru/">ITJump</a> и лично <a href="http://mubinov.com/">Алмазу Мубинову</a>, а также <a href="http://life-notes.ru/">Агафонову Данилу</a>. Именно эти люди внесли большой вклад в мои знания.</p><p>И так, начнем. Список разделен по годам. Совпадение сроков изучения технологий со срокам разработки проектов небессмысленны:)</p><h2 id="2011"><a href="#2011" class="headerlink" title="2011"></a>2011</h2><ul><li>HTML, CSS</li><li>начальные навыки верстки сайтов</li><li>PHP </li><li>MySQL</li><li>Apache</li></ul><h2 id="2012"><a href="#2012" class="headerlink" title="2012"></a>2012</h2><ul><li>JavaScript<ul><li>JQuery</li></ul></li><li>Adobe Photoshop</li><li>Кроссбраузерная адаптивная верстка</li><li>SVN</li><li>GIT</li><li>PHP<ul><li>Написание собственного фреймворка</li></ul></li></ul><h2 id="2013"><a href="#2013" class="headerlink" title="2013"></a>2013</h2><ul><li>Расширения для Google Chrome</li><li>npm</li><li>bower</li><li>Grunt</li><li>LESS</li><li>Twitter Bootstrap</li></ul><h2 id="2014"><a href="#2014" class="headerlink" title="2014"></a>2014</h2><ul><li>PHP<ul><li>Symfony 2</li><li>Phalcon 1.34</li><li>PHPMailer</li><li>Composer</li><li>Масштабирование серверных приложений</li><li>Разработка REST API</li></ul></li><li>Linux<ul><li>разворачивание веб-сервера: nginx, php5-fpm, mysql</li><li>настройка FTP-доступа</li><li>системное администрирование сервера</li></ul></li><li>nginx<ul><li>использования балансировщика</li></ul></li><li>NodeJS<ul><li>express</li><li>websocket</li></ul></li><li>Redis</li><li><a href="http://ru.atnartur.dev/posts/2014/php-qiwi/">Первый open source проект</a></li></ul><h2 id="2015"><a href="#2015" class="headerlink" title="2015"></a>2015</h2><ul><li>PHP<ul><li>Phalcon 2</li><li>Phinx (миграции БД)</li></ul></li><li>NodeJS<ul><li>Electron</li><li>nw.js</li><li>mocha</li></ul></li><li>gulp</li><li>Python 3</li><li>Разработка своей системы для Continuous Deployment</li><li>Поддержка распределенной серверной архитектуры: сервера приложений, сервер базы данных, сервер кеширования, сервер-балансировщик</li></ul><h2 id="2016"><a href="#2016" class="headerlink" title="2016"></a>2016</h2><ul><li>PHP<ul><li>Codeception (API&#x2F;unit тестирование)</li><li>PHP7 (+использование статической типизации)</li><li>Phalcon 3</li><li>PHP-FPM - фоновая обработка задач</li></ul></li><li>JavaScript<ul><li>EcmaScript 2015</li><li>Three.JS</li><li>Matreshka.JS</li><li>WebWorkers</li><li>Web Audio API</li><li>React (Redux)</li></ul></li><li>webpack</li><li>Java</li><li>C#</li><li>Docker </li><li>Gitlab CI </li><li>Continuous Integration &amp; Deployment</li><li>PostgreSQL</li></ul><p>Желаю всем в новом году научиться чему-то новому, а также понять, что принципы правильного проектирования и разработки намного важнее количества изученных технологий. </p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2017/my-skills/</id>
    <link href="https://ru.atnartur.dev/posts/2017/my-skills/"/>
    <published>2017-01-28T21:31:01.000Z</published>
    <summary>
      <![CDATA[<p>Идея написать этот пост пришла ко мне после прочтения <a href="https://vk.com/wall202916188_266">заметки Данила Агафонова</a> об изученных им технологиях. Я решил продолжить серию таких записей, так как считаю, что мой список и подход может быть интересен кому-нибудь еще.]]>
    </summary>
    <title>Мой путь изучения технологий</title>
    <updated>2026-06-14T17:54:10.776Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Жизнь" scheme="https://ru.atnartur.dev/categories/%D0%96%D0%B8%D0%B7%D0%BD%D1%8C/"/>
    <category term="Проекты" scheme="https://ru.atnartur.dev/tags/%D0%9F%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B/"/>
    <category term="ClienD" scheme="https://ru.atnartur.dev/tags/ClienD/"/>
    <category term="ClienDDev" scheme="https://ru.atnartur.dev/tags/ClienDDev/"/>
    <content>
      <![CDATA[<p>Решил немного рассказать об истории команды ClienDDev, о команде веселых ребят и энтузиастов своего дела, о команде, которая сделала огромный толчок к саморазвитию каждого из нас.</p><span id="more"></span><p><img src="/content/2016/08/clienddev/itjump.jpg"></p><p>Команда ClienDDev сформировалась на проекте <a href="http://itjump.ru/">ITJump</a> в 2013 году. На тот момент я работал над проектом Cafe4me (закрыт сейчас), а первым проектом был <a href="http://wikitatar.ru/">WikiTatar</a> (работает очень старая версия). Летом 2013 года мне написал <a href="http://timur.clienddev.ru/">Тимур Мустафин</a>, который попросил помочь ему красиво просматривать оценочки в школьном дневнике на <a href="http://edu.tatar.ru/">edu.tatar.ru</a>. Я согласился, да так увлекся, что написал сразу авторизацию и вывод оценок. <a href="https://vk.com/wall-57640526_55">Так начался ClienD</a>.</p><p>За неделю была сделана первая веб-версия, и примерно 30 августа 2013 года она была выложена на <a href="http://cliend.ru/">cliend.ru</a>. 1 сентября все сломалось потому, что на портале Электронного образования пропали данные. К началу октября данные появились, и пошли первые пользователи.<br>Обновления происходили <a href="https://vk.com/wall-57640526_62">достаточно часто</a>: что-то дополняли, исправляли…</p><p><img src="/content/2016/08/clienddev/cliend_v1.jpg"></p><p><a href="https://vk.com/wall-57640526_98">К январю 2014 года</a> было выпущено <a href="http://android.cliend.ru/">Android-приложение ClienD</a>, которое написал Чирков Илья. К этому моменту была переработана… А может хватит вдаваться в технические подроности? Я же про команду рассказываю, а не про архитектуру проекта.</p><p><a href="https://krasovsky.me/">Красовский Савелий</a> присоединился к нашей команде тоже в январе 2014, и он является автором логотипа проекта ClienD и команды ClienDDev. </p><p><a href="http://clienddev.ru/"><img src="/content/2016/08/clienddev/clienddev.png"></a></p><p>Чуть позже к нам присоединился Хабиров Булат, который сделал <a href="http://ios.cliend.ru/">iOS приложение</a>, которое вышло в ноябре 2014 года. </p><p>Летом 2014 года мы принимали участие в <a href="http://ru.atnartur.dev/posts/2014/php-qiwi/">хакатоне от Qiwi</a>, где <a href="https://vk.com/wall-71801221_10">разрабатывали проект “Автоматизированные велопарковки”</a>. Идеей также заинтересовались Закиров Анвар и Габитов Саяр. Затем они тоже вступили в нашу команду.</p><p>Поддерживать сервера команды стало слишком сложно, поэтому к нам присоединился <a href="http://postgred.gitlab.io/">Андрей Алесксандров</a>, который тоже является выпускником проекта ITJump. </p><p>Все течет, все изменяется. И состав нашей команды тоже. Сейчас с нами 6 человек, и все мы работаем в свое удовольствие, разрабатывая свои проекты, а так же проекты на заказ: разработчики мобильных приложений <a href="https://vk.com/findviewbyid">Чирков Илья</a> и <a href="https://vk.com/khabiroff_b">Хабиров Булат</a>, системный администратор <a href="http://postgred.gitlab.io/">Александров Андрей</a> и веб-разработчики <a href="http://timur.clienddev.ru/">Тимур Мустафин</a>, Анвар Закиров и <a href="http://atnartur.dev/">я</a> :) </p><p><img src="/content/2016/08/clienddev/team.png"></p><p>ClienD так же развивается. В минувшем учебном году мы <a href="https://vk.com/wall-57640526_311">пробили планку в 10 000 пользователей ежедневно</a>, а к концу учебного года <a href="https://vk.com/wall-57640526_316">успела присоединиться еще тысяча</a>. Наконец-то вышло в свет <a href="http://ext.cliend.ru/">расширение ClienD</a>, которое упрощает работу школьных системных администраторов и учителей. Совсем скоро планируется большое обновление приложений, следите за новостями в <a href="http://vk.com/cliend">группе проекта - vk.com&#x2F;cliend</a>. </p><p>На хакатоне от Microsoft <a href="https://vk.com/wall-71801221_52">мы разработали игру The Second</a> - хардкорную убивалку времени. Скачать The Second для Android можно <a href="https://play.google.com/store/apps/details?id=ru.clienddev.thesecond">здесь</a>.</p><p>В предыдущем учебном году <a href="https://vk.com/wall-71801221_137">стартовал проект Quantroom</a>, который хочет помочь школьникам одержать победу над ЕГЭ. Совсем скоро состоится обновление сервиса, а пока Вы можете оценить его <a href="http://quantroom.ru/">здесь</a> и <a href="http://vk.com/clienddev">подписаться на обновения группы ClienDDev</a>, чтобы не пропустить обновление.</p><p>Также мы работаем со <a href="http://need4speak.com/">школой английских языков Need4Speak</a>, для которой мы сделали новый сайт и кучу внутренних сервисов. </p><p>Под нашей технической поддержкой находится сайт <a href="http://rvmarket.ru/">RVMarket</a> и серверная часть <a href="https://divany.rvmarket.ru/divan/2788">3D-плеера диванов</a></p><p>Этим летом мы заняли <a href="https://vk.com/wall-71801221_139">первое место на хакатоне Geekday от Geekbrains</a> с проектом <a href="http://guitar4all.ru/">Guitar4All</a>. А недавно этот проект <a href="http://itpark-kazan.ru/ru/node/2764">стал резидентом Бизнес-Инкубатора казанского IT-парка</a>! Поздравляем!</p><p>А также, с этого лета мы начали работать с очень крутой веб-студией под названием <a href="http://uvee.ru/">Uvee</a>!</p><p>Вот так вот из маленького проекта, созданного для решения собственных проблем и саморазвития, появилась целая команда разработчиков. </p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2016/clienddev/</id>
    <link href="https://ru.atnartur.dev/posts/2016/clienddev/"/>
    <published>2016-11-04T20:34:19.000Z</published>
    <summary>
      <![CDATA[<p>Решил немного рассказать об истории команды ClienDDev, о команде веселых ребят и энтузиастов своего дела, о команде, которая сделала огромный толчок к саморазвитию каждого из нас.</p>]]>
    </summary>
    <title>ClienDDev - как все начиналось</title>
    <updated>2026-06-14T17:55:57.208Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="сервер" scheme="https://ru.atnartur.dev/tags/%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80/"/>
    <category term="linux" scheme="https://ru.atnartur.dev/tags/linux/"/>
    <content>
      <![CDATA[<p>Около месяца мы боролись с тем, что на сервере постоянно кончаются inodes (по-русски: иноды или айноды). Это индексные дескрипторы файлов. Одна айнода соответствует одному файлу на сервере. В ней хранится некоторая метаинформация, кроме названия и пути к файлу. Подробнее о нашей проблеме - ниже. </p><span id="more"></span><p>Поиск проблемы осложняется тем, что линукс постоянно заявляет, что <code>no space left on device</code>. Но по факту место на самом деле есть (в нашем случае около 5 гигов), но места все равно нет. Значит надо смотреть айноды.</p><p><code>df -h</code> - посмотреть информацию о занятом пространстве на жестком диске<br><code>df -i</code> - посмотреть информацию об айнодах</p><p>Первоначально я пытался просто удалять некоторые папки, которые содержат очень много файлов. Я считал, что здесь нет никакой проблемы, просто файлов с логами стало слишком много, и их надо немного почистить. Однако освобожденные 55000 айнод забились через 3 дня. </p><p><em>Вечер понедельника</em><br><img src="/content/2016/10/inodes-problems/1.png"></p><p><em>Утро четверга</em><br><img src="/content/2016/10/inodes-problems/2.png"></p><p>Это было очень странно. В результате было принято логичное решение найти в системе такую папку, где очень много непонятных файлов.</p><p>Чтобы построить дерево всех каталогов и файлов, выполняем команду: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">tree / &gt; dirs.txt<br></code></pre></td></tr></table></figure><p>Команду нужно выполнить с root-правами для того, чтобы в выдачу попали все каталоги системы. Если утилита tree не установлена, устаналиваем ее из репозиторией, например так: <code>apt-get install tree</code>.</p><p>В нашем случае сгенерировался файл на 73МБ. После его анализа была найдена папка, в которой скопилось очень много файлов. Для упрощения поиска полного пути до папки, выполняем команду </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">du -h / &gt; dirs.txt<br></code></pre></td></tr></table></figure><p>Она покажет нам вес каждой папки и полный путь до нее. Опять же, команда должна выполняться с root-правами.</p><p>После того, как был найден полный путь до папки, выяснилось, что в ней содержатся <strong>1098902 файлов</strong> на 231 МБ. В получении этих данных помогли команды:</p><p><code>du -h .</code> - подсчет размера текущей папки<br><code>ls -f . | wc -l</code> - подсчет количества файлов в текущей папке</p><p>Теперь начинаем удалять файлы с помощью</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">rm /path/to/dir -rf<br></code></pre></td></tr></table></figure><p>где <code>/path/to/dir</code> - путь до проблемной папки.</p><p>После этих операций количество свободных айнод перевалило за миллион. Победа!<br><img src="/content/2016/10/inodes-problems/3.png"></p><p><em>Спасибо <a href="https://vk.com/romanovandreey">Андрею Романову</a> за помощь в решении проблем!</em></p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2016/inodes-problems/</id>
    <link href="https://ru.atnartur.dev/posts/2016/inodes-problems/"/>
    <published>2016-10-21T18:55:42.000Z</published>
    <summary>
      <![CDATA[<p>Около месяца мы боролись с тем, что на сервере постоянно кончаются inodes (по-русски: иноды или айноды). Это индексные дескрипторы файлов. Одна айнода соответствует одному файлу на сервере. В ней хранится некоторая метаинформация, кроме названия и пути к файлу. Подробнее о нашей проблеме - ниже. </p>]]>
    </summary>
    <title>Решение проблем с ограничением по количеству файлов (inodes)</title>
    <updated>2025-02-01T17:45:09.746Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="Docker" scheme="https://ru.atnartur.dev/tags/Docker/"/>
    <content>
      <![CDATA[<p>Docker - платформа для контейнеризации, которая в последнее время стремительно набирает популярность. На всех популярных ресурсах для разработчиков говорят о контейнерах и образах, но новичку разобраться в этом очень сложно. Далее - рассказ о самых базовых принципах Docker c понятными аналогиями.</p><span id="more"></span><p><img src="/content/2016/10/docker-house/docker.png"></p><p><em>Сказ будет сопровождаться объяснениями в скобках, которые соответствуют определяниям и терминам из реальной жизни.</em></p><p>Представим, что мы построили новый дом (сервер). Внешне он выглядит нормально (64-битная система, оперативки больше гигабайта), но внутри совершенно пустой (нет ни одного размещенного проекта). </p><p>Устраиваем новоселье, и зовем жителей (проекты). Каждый житель хочет устроить для себя комфортные условия. Поэтому мы, как собственники, должны предоставить их. Проводим воду, подключаем электричество, покупаем плиту и холодильник (ставим необходимое ПО для запуска приложений, например: nginx, php, mysql). Экономии ради не заморачиваемся насчет разделения этих приборов. Жителей не много, они нетребовательные и им хватит по одной плите и умывальнику на весь дом.</p><p>Но со временем у жителей появляются новые потребности. Кому-то для начала новой жизни с мурлыкающим соседом требуется отдельная комната с большим холодильником для хранения годового запаса кошачьего корма, А молодой семье с детьми хочется иметь отдельную кухню. (Наши проекты тоже развиваются, требуют новых версий программ, дополнительных расширений и настроек. Одним приложениям это требуются, другим - нет, а третьи вообще могут поломаться из-за обновления версий).</p><p>Случается, что управляющая компания отключает или электричество, или воду, или еще что-нибудь… У всех жителей сразу же пропадает возможность помыть руки или приготовить пищу, поэтому начинается всеобщее негодование. (Ломается программа на сервере - все проекты, которые использовали ее, перестают работать.)</p><p>А бывает так, что кто-то на кухне не убрал за собой после еды. Другим приходится убирать за этим негодяем, что безусловно пользы особой не приносит. (Проекты в обычном окружении имеют намного больше прав, чем должны. А если проект будет взломан, и злоумышленники попытаются запустить вирус на сервере?)</p><p>Как хорошие хозяева, мы принимаем решение построить для каждого жителя (или семьи) отдельную комнату и поставить туда свою раковину, свою плиту и свой холодильник. Да, на это требуется время и деньги. Однако, после того, как эти преобразования будудт сделаны, уровень комфорта в нашем доме намного повысится. Каждый квартирант теперь в состоянии купить модернизированную плиту или поставить не требующий разморозки холодильник. (Каждый проект, находящйися в контейнере, может иметь свои версии программ и свои настройки, которые не будут пересекаться с другими проектами). </p><p>При этом люди советуются с соседями и покупают бытовую технику по их рекомендациям. (Docker-образы могут наследоваться от других образов, получая все настройки родительского образа и добавляя свои.)</p><p>В городе силами активистов совместно с мерией была создана книга рецептов, в которой все жители оставляют свои кулинарные рецепты. Такой своеобразный метод обмена опытом:) (В публичном личном репозитории Docker-образов <a href="http://hub.docker.com/">Docker hub</a> лежат публичные образы Docker, при этом можно посмотреть Dockerfile - “рецепт” создания образа).</p><p>В результате в доме стало немного меньше места (теперь программы отдельные для каждого проекта, а не одни на сервер), но зато теперь каждый житель в состоянии сам решать, что ему нужно и чем он будет пользоваться.  </p><p><em>Статья родилась во время разработки нового проекта с использованием Docker в соавторстве с <a href="http://timur.clienddev.ru/">Тимуром Мустафиным</a>.</em></p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2016/docker-house/</id>
    <link href="https://ru.atnartur.dev/posts/2016/docker-house/"/>
    <published>2016-10-19T06:30:56.000Z</published>
    <summary>
      <![CDATA[<p>Docker - платформа для контейнеризации, которая в последнее время стремительно набирает популярность. На всех популярных ресурсах для разработчиков говорят о контейнерах и образах, но новичку разобраться в этом очень сложно. Далее - рассказ о самых базовых принципах Docker c понятными аналогиями.</p>]]>
    </summary>
    <title>Избушка на docker-ножках или объяснение Docker на примере домов</title>
    <updated>2025-02-01T17:45:09.745Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="git" scheme="https://ru.atnartur.dev/tags/git/"/>
    <content>
      <![CDATA[<p>Несколько лет назад я подсмотрел интересный метод оформления commit-сообщений: в начале каждого сообщения ставится один из символов <code>+ - ! =</code>. В этой статье - небольшие мысли по поводу таких сокращений.</p><span id="more"></span><p><img src="/content/2013/07/Git-Logo-2Color.jpg"></p><p><strong>Вот, собственно, и сами условные обозначения:</strong></p><ul><li><code>+</code> добавлено</li><li><code>-</code> удалено</li><li><code>=</code> изменение в текущем функционале</li><li><code>!</code> исправлено</li><li><code>Х%</code> сделано на Х процентов</li></ul><p>Данная тема неоднократно порождала холиварчики приличного объема среди моих друзей-программистов. Одни считали, что сокращения слов в символы - это очень удобно потому, что можно быстро визуально определить, что было сделано в это коммите. Другие утверждали, что это излишне, и новые разработчики, пришедшие в проект, будут с трудом входить в данный способ изъяснения. (Это далеко не полный список возражений по этому поводу.)</p><p>И данный кусочек содержится в каждом README моих проектов для того, чтобы новый разработчик мог прочитать и понять, что же значит эта “загадочная” “морзянка”. </p><p>И все-таки, в чем трудность запомнить 5 символов и не писать <code>added / deleted/removed / edited / updated / fixed / created / not fully implimented / ...</code>? Да, значения слов повторяются, как они и повторяются в реальной жизни. Если нужно будет выполнить поиск всех добавлений нового функционала, то нужно будет искать, например, <code>created, added...</code> Английский язык не столь многогранен, как русский, но все же…</p><p>Стоп, а если коммит-сообщения еще и на двух языках? Еще больше вариантов надо учесть при поиске. </p><p>Вот, например, <a href="https://github.com/ClienDDev/ClienD-ext/commits/dev">использование этих условных обозначений в проекте ClienD extension</a>. </p><p>Намного проще писать просто один сивмол в начале каждого коммита, который будет охарактеризовать его тип, ведь так?</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2016/commit-symbols/</id>
    <link href="https://ru.atnartur.dev/posts/2016/commit-symbols/"/>
    <published>2016-09-30T20:48:39.000Z</published>
    <summary>
      <![CDATA[<p>Несколько лет назад я подсмотрел интересный метод оформления commit-сообщений: в начале каждого сообщения ставится один из символов <code>+ - ! =</code>. В этой статье - небольшие мысли по поводу таких сокращений.</p>]]>
    </summary>
    <title>Символы в коммитах - польза или зло?</title>
    <updated>2025-02-01T17:45:09.745Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="О разработке" scheme="https://ru.atnartur.dev/categories/%D0%9E-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5/"/>
    <category term="PHP" scheme="https://ru.atnartur.dev/tags/PHP/"/>
    <category term="Phalcon" scheme="https://ru.atnartur.dev/tags/Phalcon/"/>
    <content>
      <![CDATA[<p>Phalcon - молодой PHP-фреймворк, выполненный в виде PHP-расширения. Он работает намного быстрее из-за того, что все основные операции выполняются на системном уровне. Однако, из-за того, что он молодой, иногда встречаются странные вещи. Вот об одном таком баге я сегодня расскажу.</p><span id="more"></span><h2 id="Предыстория"><a href="#Предыстория" class="headerlink" title="Предыстория"></a>Предыстория</h2><p>Летом мы в команде начали разрабатывать проект на Phalcon 2. В этом проекте установка даты рождения пользователя реализована следующим образом: в браузере работает плагин календаря, который в результате вписывал в поле ввода дату в формате <code>ДД.ММ.ГГГГ</code>. </p><p><img src="/content/2016/09/phalcon_magic/calendar.png"></p><p>После отправки формы контроллер отправляет дату в вышеуказанном формате в специальный метод в модели, который превращает строковую дату в timestamp. </p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs php"><span class="hljs-comment">/* ClientsController */</span><br><span class="hljs-variable">$info</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Clients</span>();<br><span class="hljs-variable">$info</span>-&gt;<span class="hljs-title function_ invoke__">setBirthday</span>(<span class="hljs-variable">$this</span>-&gt;request-&gt;<span class="hljs-title function_ invoke__">getPost</span>(<span class="hljs-string">&quot;birthday&quot;</span>));<br><span class="hljs-comment">/* Clients model */</span><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Clients</span></span>&#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setBirthday</span>(<span class="hljs-params"><span class="hljs-variable">$birthday</span></span>)</span>&#123;<br>        <span class="hljs-variable">$res</span> = <span class="hljs-title function_ invoke__">strtotime</span>(<span class="hljs-variable">$birthday</span>);<br>        <span class="hljs-variable language_">$this</span>-&gt;birthday = <span class="hljs-variable">$res</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>Работает этот код хорошо на следующей конфигурации на PHP5.4 и Phalcon 2.0.3. Однако на PHP5.6 и Phalcon 2.0.13 начинаются проблемы. В результате выполнения функции <code>setBirthday()</code>, в поле <code>birthday</code> записывалось значение <code>false</code>. </p><p>В первый раз это произошло у моего коллеги на Fedora с PHP5.6 и Phalcon 2.0.13. Я пытался помочь ему разобраться, но, честно сказать, списывал это на локальные баги окружения, потому что у меня все работало хорошо (Windows 10, OpenServer5.2.2, PHP5.6 и Phalcon 2.0.3). На production сервере все тоже работало хорошо (Phalcon 2.0.3). </p><p><em>Интересная особенность, правда? На 2.0.3 работает нормально, а на 2.0.13 нет. Но об этом далее.</em></p><h2 id="Выявление-проблемы"><a href="#Выявление-проблемы" class="headerlink" title="Выявление проблемы"></a>Выявление проблемы</h2><p>Когда проблема проявилась у меня на машине (PHP5.6, Phalcon 2.0.13) я все-таки решил разобраться в ней. И вот что получилось.</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Clients</span></span>&#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setBirthday</span>(<span class="hljs-params"><span class="hljs-variable">$birthday</span></span>)</span>&#123;<br>        <span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-string">&#x27;---&#x27;</span>);<br>        <span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-string">&#x27;before strtotime&#x27;</span>);<br>        <span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-variable">$birthday</span>); <br>        <span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-variable">$this</span>-&gt;birthday); <br>        <span class="hljs-variable">$res</span> = <span class="hljs-title function_ invoke__">strtotime</span>(<span class="hljs-variable">$birthday</span>);<br>        <span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-variable">$res</span>);<br>        <span class="hljs-variable language_">$this</span>-&gt;birthday = <span class="hljs-variable">$res</span>;<br>        <span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-string">&#x27;after strtotime&#x27;</span>);<br>        <span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-variable">$this</span>-&gt;birthday);<br>        <span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-string">&#x27;///&#x27;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>Расставил отладочные выводы на каждый чих и посмотрел, что происходит в процессе выполнения функции:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">// Начинается выполнение ClientsController-&gt;editAction(). <br>// Доходит до выполнения $info-&gt;setBirthday(). <br>string(18) &quot;before bithday set&quot; <br>string(3) &quot;—&quot; // начало выполнения функции<br>string(16) &quot;before strtotime&quot; // вывод перед вызовом функции приведения строки к числу<br>string(10) &quot;09.09.2016&quot; // вывод из аргумента функции setBirthday<br>string(0) &quot;&quot; // вывод текущего знаения дня рождения ($this-&gt;birthday) <br>int(1473368400) // результат работы функции приведения строки к числу<br>string(3) &quot;—&quot; СНОВА начало выполнения функции<br>string(16) &quot;before strtotime&quot; опять все то же самое<br>int(1473368400) // приняли уже значение числовое, которое до этого получилось<br>string(0) &quot;&quot; дата по прежнему пустая <br>bool(false) // результат работы функции есесьно false,<br>// потому что число к числу привести не возможно<br>string(15) &quot;after strtotime&quot;<br>bool(false) //после выполнения всего такой результат<br>string(3) &quot;///&quot; // завершилась первая<br>string(15) &quot;after strtotime&quot; // <br>bool(false) <br>string(3) &quot;///&quot; // завершилась вторая<br></code></pre></td></tr></table></figure><p>Таким образом, видно, что идет вложенный вызов функции. Контроллер вызывает <code>setBirthday()</code>, она начинает выполняться, потом доходит до установки значения в свойство <code>$this-&gt;birthday = $res;</code> и функция начинает выполняться заново, принимает уже числовое значение из strtotime, а потом не может это числовое значение опять привести к числу, потому что <code>strtotime</code> требует строку. Очевидно, что идет какая-то магия, вызов магического метода <code>setBirthday()</code>.</p><h2 id="Документация-Phalcon"><a href="#Документация-Phalcon" class="headerlink" title="Документация Phalcon"></a>Документация Phalcon</h2><p>В <a href="http://docs.phalconphp.ru/ru/latest/reference/models.html#id3">документации Phalcon 3</a> уже есть блок, описывающий работу магических методов в модели. Но в Phalcon 2 его еще не было. </p><p>Не так давно в версии 2.0.11 был закрыт <a href="https://github.com/phalcon/cphalcon/issues/11286">issue</a>, который решает проблему с getters &amp; setters. Таким образом, с версии 2.0.11 они начинают нормально работать, и поэтому на версии 2.0.13 проявлялся баг. </p><p><img src="/content/2016/09/phalcon_magic/issue.png"></p><p>Ну а решение есть в документации: свойства, которые мы меняем через getters&#x2F;setters должны быть <code>protected</code>.</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2016/phalcon-magic/</id>
    <link href="https://ru.atnartur.dev/posts/2016/phalcon-magic/"/>
    <published>2016-09-09T17:58:30.000Z</published>
    <summary>
      <![CDATA[<p>Phalcon - молодой PHP-фреймворк, выполненный в виде PHP-расширения. Он работает намного быстрее из-за того, что все основные операции выполняются на системном уровне. Однако, из-за того, что он молодой, иногда встречаются странные вещи. Вот об одном таком баге я сегодня расскажу.</p>]]>
    </summary>
    <title>Особенности работы magic methods в моделях Phalcon 2</title>
    <updated>2025-02-01T17:45:09.746Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Софт" scheme="https://ru.atnartur.dev/categories/%D0%A1%D0%BE%D1%84%D1%82/"/>
    <category term="sync" scheme="https://ru.atnartur.dev/tags/sync/"/>
    <category term="IDE" scheme="https://ru.atnartur.dev/tags/IDE/"/>
    <category term="Sublime Text" scheme="https://ru.atnartur.dev/tags/Sublime-Text/"/>
    <content>
      <![CDATA[<p>Среда разработки - программа, в которой программист проводит больше всего времени. Именно поэтому ее выбор очень важен для каждого, кому приходится работать с кодом. Сегодня я расскажу о Sublime Text и о плагинах, которыми я пользуюсь.</p><span id="more"></span><p><img src="/content/2016/07/sublime/logo.png">  </p><h2 id="Путь"><a href="#Путь" class="headerlink" title="Путь"></a>Путь</h2><p>За время работы я перепробовал множество IDE и текстовых редакторов. Началось все с <a href="https://notepad-plus-plus.org/">Notepad++</a>. Когда его возможностей стало не хватать, перешел на <a href="http://www.adobe.com/ru/products/dreamweaver.html">Adobe Dreamviewer</a>, который показался слишком тяжелым. <a href="http://www.mpsoftware.dk/phpdesigner.php">phpDesigner</a> был очень хорош, но там не было сворачивания кода (а на больших файлах оно бывает очень необходимо). Sublime Text 2 не понравился. Философия кучи плагинов тогда меня не впечатлила потому, что не верилось, что все эти дополнения могут покрыть мои потребности. <a href="http://komodoide.com/komodo-edit/">Komodo Edit</a> я использовал очень долго, пока не перешел на <a href="https://www.jetbrains.com/phpstorm/">PHPStorm</a>, который стабильно кушал большую часть оперативной памяти, а многие функции я просто не использовал. </p><h2 id="Sublime-Text-3"><a href="#Sublime-Text-3" class="headerlink" title="Sublime Text 3"></a>Sublime Text 3</h2><p>Для того, чтобы разобраться <a href="https://www.sublimetext.com/3">Sublime Text 3</a> пришлось потратить немного времени. Как оказалось, сейчас есть множество плагинов, которые покрывают большинство потребностей современной разработки.</p><h2 id="Основные-функции"><a href="#Основные-функции" class="headerlink" title="Основные функции"></a>Основные функции</h2><p>Но сначала немного расскажу об основных функциях. Дальше по тексту я буду упоминать некоторые функции, которых нет в обычном Sublime Text, но которые есть у меня, потому что установлены некоторые плагины. О дополнениях рассках пойдет ниже.</p><p><strong>Проект &#x3D; папка</strong>. Так как это в первую очередь текстовый редактор, а не IDE, этот принцип вполне уместен и удобен. Сделал новое окно, перетащил папку - и все. Файлы проекта проиндексированы, автодополнение работает…</p><p><strong>Command Palette</strong> вызывается по <code>CTRL+SHIFT+P</code> и представляет собой каталог со всеми командами в Sublime. Например, здесь можно сделать <code>git push</code> или выполнить gulp-задачу.</p><p><img src="/content/2016/07/sublime/commandpalette.png">  </p><p><img src="/content/2016/07/sublime/commandpalette2.png"></p><p><strong>Быстрый запуск</strong> чем сложнее редактор&#x2F;IDE, тем дольше он запускается. Sublime запускается быстро даже с кучей установленных плагинов.</p><p><a href="http://packagecontrol.io/"><strong>Package Control</strong></a> каталог пакетов (плагинов, тем…). Ставится отдельно <a href="https://packagecontrol.io/installation">с помощью команды</a>. Так же может помочь <a href="https://packagecontrol.io/docs/syncing">сделать синхронизацию</a> не только настроек редактора, но и настроек всех плагинов. </p><h2 id="Плагины"><a href="#Плагины" class="headerlink" title="Плагины"></a>Плагины</h2><p>Полный список плагинов можно увидеть в моем <a href="https://gist.github.com/atnartur/911aef2a774115eb339d6a32c664d7c9">конфигурационном файле Package Control</a>.</p><p>Чтобы устанавливать большинство плагинов в Sublime Text, нужно:</p><ol><li><a href="https://packagecontrol.io/installation">Поставить Package Control</a></li><li>Нажать <code>CTRL+SHIFT+P</code></li><li>Набрать <code>install package</code>, нажать enter</li><li>Набрать название плагина и установить</li></ol><h3 id="Расширение-функциональности"><a href="#Расширение-функциональности" class="headerlink" title="Расширение функциональности"></a>Расширение функциональности</h3><ul><li><a href="https://packagecontrol.io/packages/DocBlockr">DocBlockr</a> - генерация документации для функций. Над объявлением функции пишем <code>/**</code>, нажимаем enter, и плагин разворачивает блок документации:<br><img src="/content/2016/07/sublime/docblockr.png"></li><li><a href="https://packagecontrol.io/packages/Emmet">Emmet</a> - разворачивает аббревиатуры вида <code>div#id_test.class_test*2&gt;h1</code> в</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;id_test&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;class_test&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;id_test&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;class_test&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre></td></tr></table></figure><ul><li><a href="https://packagecontrol.io/packages/FileHeader">FileHeader</a> - в начало файла добавляет информацию об авторе, дате создания и изменения файла</li><li><a href="https://packagecontrol.io/packages/Git">Git</a> - добавляет в Command palette команды <a href="http://ru.atnartur.dev/posts/2013/git-1/">git</a></li><li><a href="https://packagecontrol.io/packages/GitGutter">GitGutter</a> - в начале строки добавляет символы, информирующие об удалении, добавлении или изменении строки. Информацию предоставляет git<br><img src="/content/2016/07/sublime/gitgutter.png"></li><li><a href="https://packagecontrol.io/packages/Gulp">Gulp</a> - запускает gulp-задачи прямо в Sublime Text</li><li><a href="https://packagecontrol.io/packages/HTMLBeautify">HTMLBeautify</a> - форматирует (расставляет отступы) в HTML коде</li><li><a href="https://packagecontrol.io/packages/Indent%20XML">Indent XML</a> - то же самое для XML</li><li><a href="https://packagecontrol.io/packages/Javascript%20Beautify">Javascript Beautify</a> - и JS</li><li><a href="https://packagecontrol.io/packages/Pretty%20JSON">Pretty JSON</a> - то же самое для JSON, но еще и минификация и валидация</li><li><a href="https://packagecontrol.io/packages/JSHint">JSHint</a> - проверка синтаксиса JS</li><li><a href="https://packagecontrol.io/packages/LiveReload">LiveReload</a> - обычно использую LiveReload через gulp, но иногда бывает нужно перезагружать страницу просто по сохранению файла. Так же он умеет вставлять тег <code>&lt;script&gt;</code> с подключением скрипта LiveReload и обновлять страницу по разным триггерам:<br><img src="/content/2016/07/sublime/livereload.png"></li><li><a href="https://packagecontrol.io/packages/SFTP">SFTP</a> - помогает редактировать файлы на удаленных серверах</li></ul><h3 id="Автодополнение"><a href="#Автодополнение" class="headerlink" title="Автодополнение"></a>Автодополнение</h3><ul><li><a href="https://packagecontrol.io/packages/All%20Autocomplete">All Autocomplete</a> - Сканирует файлы в проекте и на основе собранной информации реализует подсказки по коду.</li><li><a href="https://packagecontrol.io/packages/JavaScript%20Completions">JavaScript Completions</a></li><li><a href="https://packagecontrol.io/packages/jQuery">jQuery</a></li><li><a href="https://packagecontrol.io/packages/apiDoc%20Autocompletion">apiDoc Autocompletion</a> - автодополнения для <a href="http://apidocjs.com/">apidoc</a>, инструмента для генерации документации по API</li><li><a href="https://packagecontrol.io/packages/PHP%20Companion">PHP Companion</a></li><li><a href="https://packagecontrol.io/packages/TodoReview">TodoReview</a> - собирает информацию обо всех блоках <code>@TODO</code></li><li><a href="https://packagecontrol.io/packages/WakaTime">WakaTime</a> - “фитнес-трекер” для программиста. <a href="/posts/2016/wakatime/">Подробнее про этот сервис</a></li></ul><h3 id="Поддержка-синтаксиса"><a href="#Поддержка-синтаксиса" class="headerlink" title="Поддержка синтаксиса"></a>Поддержка синтаксиса</h3><ul><li><a href="https://packagecontrol.io/packages/Babel">Babel</a> (JavaScript ES6, React JSX)-</li><li><a href="https://packagecontrol.io/packages/Handlebars">Handlebars</a></li><li><a href="https://packagecontrol.io/packages/LESS">LESS</a></li><li><a href="https://packagecontrol.io/packages/Python%203">Python 3</a></li><li><a href="https://packagecontrol.io/packages/MarkdownEditing">MarkdownEditing</a> - очень удобный плагин для редактирования markdown-файлов с оригинальным дизайном:<br><img src="/content/2016/07/sublime/md.png"></li></ul><h3 id="Темы"><a href="#Темы" class="headerlink" title="Темы"></a>Темы</h3><ul><li><a href="https://packagecontrol.io/packages/Material%20Theme">Material Theme</a> - использую цветовую схему Material Theme Dark из этой темы</li><li><a href="https://packagecontrol.io/packages/Numix%20Theme">Numix Theme</a> - использую тему Numix Theme Dark</li><li><a href="https://packagecontrol.io/packages/Materialize">Materialize</a></li><li><a href="https://packagecontrol.io/packages/Predawn">Predawn</a></li><li><a href="https://packagecontrol.io/packages/Theme%20-%20Asphalt">Theme - Asphalt</a></li><li><a href="https://packagecontrol.io/packages/Theme%20-%20Brogrammer">Theme - Brogrammer</a></li><li><a href="https://packagecontrol.io/packages/Theme%20-%20Flatland">Theme - Flatland</a></li><li><a href="https://packagecontrol.io/packages/Theme%20-%20Glacier">Theme - Glacier</a></li><li><a href="https://packagecontrol.io/packages/Theme%20-%20Piatto">Theme - Piatto</a></li><li><a href="https://packagecontrol.io/packages/Theme%20-%20Soda">Theme - Soda</a></li><li><a href="https://packagecontrol.io/packages/Theme%20-%20Spacegray">Theme - Spacegray</a></li></ul>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2016/sublime/</id>
    <link href="https://ru.atnartur.dev/posts/2016/sublime/"/>
    <published>2016-07-20T09:45:31.000Z</published>
    <summary>
      <![CDATA[<p>Среда разработки - программа, в которой программист проводит больше всего времени. Именно поэтому ее выбор очень важен для каждого, кому приходится работать с кодом. Сегодня я расскажу о Sublime Text и о плагинах, которыми я пользуюсь.</p>]]>
    </summary>
    <title>Мой Sublime Text</title>
    <updated>2026-06-14T17:54:10.762Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Софт" scheme="https://ru.atnartur.dev/categories/%D0%A1%D0%BE%D1%84%D1%82/"/>
    <category term="IDE" scheme="https://ru.atnartur.dev/tags/IDE/"/>
    <content>
      <![CDATA[<p>Без мотивации работа не в радость. А значит - срываются сроки, падает качество кода… Сегодня поговорим о Wakatime - сервис, который собирает статистику работы программиста и мотивирует работать лучше.</p><span id="more"></span><p><img src="/content/2016/07/wakatime/logo.png"></p><p>Принцип работы <a href="/r/wakatime">Wakatime</a> очень прост: в Ваш редактор&#x2F;IDE устанавливается плагин Wakatime (на данный момент поддерживаются 34 редактора), который собирает активность работы в программе. Затем на основе этих данных на сайте Wakatime строятся графики активности по дням, проектам, языкам программирования, редакторам.</p><p><img src="/content/2016/07/wakatime/main.png"></p><p>Также можно посмотреть, распределение времени по проектам за каждый день.</p><p><img src="/content/2016/07/wakatime/day.png"></p><p>Главным мотиватором служит раздел Daily Avarage, который говорит о среднем времени работы в редакторе за последнюю неделю. </p><p><img src="/content/2016/07/wakatime/average.png"></p><p>Стоит отметить, что считается только время, проведенное в редакторе. Нельзя полагаться на Wakatime, если в Вашем проекте почасовая оплата работы над проектом, ведь кроме редактора используется еще множество программ, работу в которых Wakatime не отслеживает.</p><p>Основная работа программиста - писать код. А когда программист пишет код, он работает в среде разработки. Если время работы в редакторе мало, значит в целом день был не очень продуктивен с точки зрения программирования, значит время тратилось не на написание кода, а на что-то другое. </p><p><strong><a href="/r/wakatime">Зарегистрироваться на Wakatime и узнать, сколько времени в день уходит на написание кода можно здесь</a></strong>.</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2016/wakatime/</id>
    <link href="https://ru.atnartur.dev/posts/2016/wakatime/"/>
    <published>2016-07-18T12:50:29.000Z</published>
    <summary>
      <![CDATA[<p>Без мотивации работа не в радость. А значит - срываются сроки, падает качество кода… Сегодня поговорим о Wakatime - сервис, который собирает статистику работы программиста и мотивирует работать лучше.</p>]]>
    </summary>
    <title>Wakatime - &quot;фитнес-трекер&quot; для программиста</title>
    <updated>2025-02-01T17:45:09.747Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Софт" scheme="https://ru.atnartur.dev/categories/%D0%A1%D0%BE%D1%84%D1%82/"/>
    <category term="Школа" scheme="https://ru.atnartur.dev/tags/%D0%A8%D0%BA%D0%BE%D0%BB%D0%B0/"/>
    <category term="Сортировка" scheme="https://ru.atnartur.dev/tags/%D0%A1%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0/"/>
    <content>
      <![CDATA[<p>Как отсортировать файлы и папки в файловом менеджере на Windows вперемешку? Пример: сначала идет папка на букву А, затем файлы на Б, затем снова папки на В. По умолчанию система сначала выводит папки по алфавиту, а затем идут файлы по алфавиту. Так как же сделать так, чтобы файлы и папки сортировались на равне? <span id="more"></span></p><p>Я пробовал копать в сторону проводника Windows, Панели управления… Безуспешно. Пробовал <a href="http://www.softportal.com/software-15503-q-dir.html">Q-Dir</a> и <a href="http://www.ghisler.com/">Total Commander</a> и опять не нашел там решения указанной проблемы (возможно, плохо искал).</p><p>Решение нашлось в программе <a href="http://freecommander.com/ru/%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BA%D0%B8/">Free Commander</a>. Для того, чтобы сортировка папок наравне с файлами включилась, нужно зайти в меню View&#x2F;Sort by и нажать на пункт “Unsorted”. После этого сортировка выключается, и файлы с папками отбражаются в нужном нам порядке.</p><p><img src="/content/2016/05/filemanager-sort-dir-with-files/freecommander.png"></p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2016/filemanager-sort-dir-with-files/</id>
    <link href="https://ru.atnartur.dev/posts/2016/filemanager-sort-dir-with-files/"/>
    <published>2016-05-15T10:46:28.000Z</published>
    <summary>
      <![CDATA[<p>Как отсортировать файлы и папки в файловом менеджере на Windows вперемешку? Пример: сначала идет папка на букву А, затем файлы на Б, затем снова папки на В. По умолчанию система сначала выводит папки по алфавиту, а затем идут файлы по алфавиту. Так как же сделать так, чтобы файлы и папки сортировались на равне?]]>
    </summary>
    <title>Сортировка файлов и папок вперемешку</title>
    <updated>2025-02-01T17:45:09.745Z</updated>
  </entry>
  <entry>
    <author>
      <name>Артур Атнагулов</name>
    </author>
    <category term="Софт" scheme="https://ru.atnartur.dev/categories/%D0%A1%D0%BE%D1%84%D1%82/"/>
    <category term="GIT" scheme="https://ru.atnartur.dev/tags/GIT/"/>
    <category term="windows" scheme="https://ru.atnartur.dev/tags/windows/"/>
    <category term="sync" scheme="https://ru.atnartur.dev/tags/sync/"/>
    <category term="Яндекс Диск" scheme="https://ru.atnartur.dev/tags/%D0%AF%D0%BD%D0%B4%D0%B5%D0%BA%D1%81-%D0%94%D0%B8%D1%81%D0%BA/"/>
    <content>
      <![CDATA[<p>Хотя сейчас присутствуют множество сервисов для хранения и синхронизации файлов в облаке (например, <a href="http://ru.atnartur.dev/r/ydisk">Яндекс.Диск</a> или <a href="http://ru.atnartur.dev/r/drop">Dropbox</a>), случаются ситуации, когда данные решения для синхронизации не подходят. <span id="more"></span></p><p>Не буду рассказывать о моей ситуации, потому что это не так уж и интересно, на самом деле. Просто представим, что в каком-то конкретном месте нет нормального подключения к интернету для работы облачных служб. В таком случае приходит необходимость использовать синхронизацию файлов через флешку.</p><h2 id="GIT"><a href="#GIT" class="headerlink" title="GIT"></a>GIT</h2><p>GIT может быть неплохим решением для синхронизации файлов и папок небольшого размера. Дома при наличии интернета закомитили на компе, скачали на флешке, и все ОК. Однако, нет возможности производить синхронизацию в месте назначения, где подключение к интернету (согласно нашей ситуации) отсутствует.</p><h2 id="Meld"><a href="#Meld" class="headerlink" title="Meld"></a>Meld</h2><p><a href="http://meldmerge.org/">Meld</a> - программа для сравнения файлов и папок. Подробно показывает различия версий между разными сущностями и дает возможность синхронизировать данные в полуручном режиме.</p><p><img src="/content/2016/05/dir-sync/meld.png"></p><p>На скриншоте видно, что в левой папке отсутсвует директория из правой. Кликаем по папке в правой области, нажимаем на “Copy left”, и точно такая же директория появляется в левой области. Все просто. </p><p>Программа устанавливается и работает без подключения к интернету, поэтому она является идеальным решением для данной проблемы.</p><h2 id="Мой-случай"><a href="#Мой-случай" class="headerlink" title="Мой случай"></a>Мой случай</h2><p>Ну теперь, когда вся основная информация сказана, расскажу про мой случай. Как многие уже догадались, речь идет о школе, где стабильное подключение к интернету отсутсвует. Поэтому я, закончив работу с файлами дома, синхронизирую данные с флешкой, а затем через флешку синхронизирую файлы с компьютером в школе. В папке текущего мероприятия может быть больше 2 гигабайт файлов, поэтому GIT такое не выдержит, а облака будут синхронизироваться очень долго.</p>]]>
    </content>
    <id>https://ru.atnartur.dev/posts/2016/dir-sync/</id>
    <link href="https://ru.atnartur.dev/posts/2016/dir-sync/"/>
    <published>2016-05-10T18:53:10.000Z</published>
    <summary>
      <![CDATA[<p>Хотя сейчас присутствуют множество сервисов для хранения и синхронизации файлов в облаке (например, <a href="http://ru.atnartur.dev/r/ydisk">Яндекс.Диск</a> или <a href="http://ru.atnartur.dev/r/drop">Dropbox</a>), случаются ситуации, когда данные решения для синхронизации не подходят.]]>
    </summary>
    <title>Программы для синхронизации директорий без подключения к интернету</title>
    <updated>2026-06-14T17:54:10.781Z</updated>
  </entry>
</feed>
