{
    "version": "https:\/\/jsonfeed.org\/version\/1.1",
    "title": "Ихрь постит в уютный бложик: заметки с тегом devops",
    "_rss_description": "Фрирайтинг, заметки, шитпостинг и все подряд",
    "_rss_language": "ru",
    "_itunes_email": "",
    "_itunes_categories_xml": "",
    "_itunes_image": "",
    "_itunes_explicit": "",
    "home_page_url": "https:\/\/ifedyukin.ru\/blog\/tags\/devops\/",
    "feed_url": "https:\/\/ifedyukin.ru\/blog\/tags\/devops\/json\/",
    "icon": "https:\/\/ifedyukin.ru\/blog\/pictures\/userpic\/userpic@2x.jpg?1737233225",
    "authors": [
        {
            "name": "Игорь Федюкин",
            "url": "https:\/\/ifedyukin.ru\/blog\/",
            "avatar": "https:\/\/ifedyukin.ru\/blog\/pictures\/userpic\/userpic@2x.jpg?1737233225"
        }
    ],
    "items": [
        {
            "id": "74",
            "url": "https:\/\/ifedyukin.ru\/blog\/all\/change-ssh-port-on-ubuntu-22-using-ansible\/",
            "title": "Change SSH port on Ubuntu 22 using Ansible",
            "content_html": "<p>After updating Ubuntu 22.10 SSHd uses socket-based activation.<br \/>\nAs a result, the sshd configuration port does not affect the listening port.<br \/>\nTo change the port, we need to perform the following tasks:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">- name: Create config folder\r\n  file:\r\n    path: \/etc\/systemd\/system\/ssh.socket.d\r\n    state: directory\r\n    recurse: true\r\n\r\n- name: Create config file\r\n  copy:\r\n    dest: \/etc\/systemd\/system\/ssh.socket.d\/listen.conf\r\n    content: |\r\n      [Socket]\r\n      ListenStream={{ custom_ssh_port }}\r\n\r\n- name: Reload systemd manager\r\n  systemd:\r\n    daemon_reload: yes\r\n\r\n- name: restart ssh\r\n  service:\r\n    name: &quot;ssh&quot;\r\n    state: restarted\r\n\r\n- name: Update connection port\r\n  set_fact:\r\n    ansible_ssh_port: &quot;{{ custom_ssh_port }}&quot;<\/code><\/pre>",
            "date_published": "2023-03-29T18:46:00+03:00",
            "date_modified": "2023-03-29T18:45:52+03:00",
            "tags": [
                "development",
                "devops"
            ],
            "_date_published_rfc2822": "Wed, 29 Mar 2023 18:46:00 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "74",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": []
            }
        },
        {
            "id": "62",
            "url": "https:\/\/ifedyukin.ru\/blog\/all\/kak-hostit-mysql-na-malenkih-vps\/",
            "title": "Как хостить MySQL на маленьких VPS",
            "content_html": "<p>Недавно я начал хостить бложик (apache + mysql + nginx + пара сервисов на nodejs) на самом дешевом дроплете от Digital Ocean (надо ж кредитные токены DO куда-то потратить).<\/p>\n<p>Внезапно столкнулся с проблемой, что контейнер MySQL периодически умирает от OOM киллера, MySQL с небольшим количеством данных съедала ну очень много памяти в простое.<\/p>\n<p>Платить больше я не хотел, а воспоминания, что когда-то MySQL крутилась у меня на прямо маленьких VPS никак не давала покоя.<\/p>\n<p>Оказалось, что есть такая штука, как <tt><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/performance-schema.html#:~:text=The%20MySQL%20Performance%20Schema%20is,of%20the%20server%20at%20runtime.\">Performance Schema<\/a><\/tt>.<\/p>\n<blockquote>\n<p>The MySQL Performance Schema is a feature for monitoring MySQL Server execution at a low level.<br \/>\nThe Performance Schema provides a way to inspect internal execution of the server at runtime.<\/p>\n<\/blockquote>\n<p>Дальше я не особо читал, конечно, погуглил чуть, решил поэкспериментировать.<br \/>\nДобавляем в конфиг в секцию <tt>[mysqld]<\/tt>  строчку, чтобы отключить эту фичу (я просто примонтировал новый конфиг), перезагрузил сервис и бах — случилась магия и теперь MySQL кушает гораздо меньше ресурсов. А мониторить мне пока не к чему.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/ifedyukin.ru\/blog\/pictures\/Screenshot-2022-03-22-at-17.08.52.png\" width=\"1966\" height=\"616\" alt=\"\" \/>\n<\/div>\n",
            "date_published": "2022-03-22T17:20:29+03:00",
            "date_modified": "2022-03-22T17:17:38+03:00",
            "tags": [
                "development",
                "devops"
            ],
            "image": "https:\/\/ifedyukin.ru\/blog\/pictures\/Screenshot-2022-03-22-at-17.08.52.png",
            "_date_published_rfc2822": "Tue, 22 Mar 2022 17:20:29 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "62",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": [
                    "https:\/\/ifedyukin.ru\/blog\/pictures\/Screenshot-2022-03-22-at-17.08.52.png"
                ]
            }
        },
        {
            "id": "53",
            "url": "https:\/\/ifedyukin.ru\/blog\/all\/ad-blockers-workaround\/",
            "title": "Обходим блокировку аналитики AD блокерами",
            "content_html": "<p>AD-блокеры — несомненно крутая штука, тем не менее, они блокируют скрипты аналитики, которые не всегда являются злом.<\/p>\n<p>Например, <a href=\"https:\/\/www.cloudflare.com\/web-analytics\/\">аналитика<\/a> от Cloudflare — про приватность и легкость, данных лишних не собирает, на сторону их не продает. Потому обойти ее блокировку различными расширениями не так уже и подло.<\/p>\n<p>Блокировщики работают в несколько этапов: блокируют подгрузку файла скрипта аналитики и блокируют запросы к серверам аналитики, к которым эти скрипты и обращаются.<\/p>\n<p>Для обхода блокировки нам понадобится одна вещь — прокся с возможностью замены части контента в ответе, например, nginx.<\/p>\n<p>Добавляем два локейшена в конфиг:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\"># сам скрипт аналитики\r\nlocation \/analytics-script.js {\r\n  # очень важно, чтоб отрабатывал sub_filter, т.к. если сервер отдаст gzip, то замена не сработает\r\n  proxy_set_header Accept-Encoding &quot;&quot;;\r\n\r\n  # заменяем в скрипте оригинальный адрес на наш, проксированный\r\n  sub_filter 'https:\/\/cloudflareinsights.com\/cdn-cgi\/rum'  'https:\/\/домен\/analytics-api';\r\n  sub_filter_types *;\r\n  sub_filter_once off;\r\n\r\n  proxy_hide_header Content-Security-Policy;\r\n  proxy_pass https:\/\/static.cloudflareinsights.com\/beacon.min.js;\r\n}\r\n\r\n# проксируем вызовы к оригинальному api аналитики\r\nlocation \/analytics-api {\r\n  proxy_pass https:\/\/cloudflareinsights.com\/cdn-cgi\/rum;\r\n}<\/code><\/pre><p>Подключаем аналитику не с домена Cloudflare, а со своего домена.<\/p>\n<p>Таким образом мы загружаем и скрипт аналитики со своего домена, и аналитику он отправляет на наш домен, а внутри уже все проксируется в Cloudflare.<\/p>\n<p>AD блокеры ничего не блокируют, мы получаем аналитику. Запросы куда-то еще уходят неявно.<\/p>\n<div class=\"hr\"><\/div><p>Есть одна небольшая проблема — у всех запросов один IP, IP прокси, почему-то аналитика Cloudflare не учитвает заголовки и не имеет задокументированных способов переопределить IP юзера, как это сделано в Google аналитике.<\/p>\n<p>Закинул в коммьюнити <a href=\"https:\/\/community.cloudflare.com\/t\/web-analytics-ip-override\/246614\">топик<\/a> на эту тему, посмотрим, как решится.<\/p>\n",
            "date_published": "2021-02-22T13:14:12+03:00",
            "date_modified": "2021-02-24T19:39:46+03:00",
            "tags": [
                "development",
                "devops"
            ],
            "_date_published_rfc2822": "Mon, 22 Feb 2021 13:14:12 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "53",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": []
            }
        },
        {
            "id": "29",
            "url": "https:\/\/ifedyukin.ru\/blog\/all\/dockerize-js\/",
            "title": "Docker’изация JavaScript-приложений",
            "content_html": "<p>Поучаствовал в IT-Субботнике, рассказал о Docker’изации JavaScript-приложений.<\/p>\n<div class=\"e2-text-video\">\n<iframe src=\"https:\/\/www.youtube.com\/embed\/xC7-HCPEn6k?enablejsapi=1\" allow=\"autoplay\" frameborder=\"0\" allowfullscreen><\/iframe>\n<\/div>\n",
            "date_published": "2018-11-30T18:03:02+03:00",
            "date_modified": "2021-01-24T18:03:37+03:00",
            "tags": [
                "community",
                "development",
                "devops",
                "javascript"
            ],
            "image": "https:\/\/ifedyukin.ru\/blog\/pictures\/remote\/youtube-xC7-HCPEn6k-cover.jpg",
            "_date_published_rfc2822": "Fri, 30 Nov 2018 18:03:02 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "29",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/jquery\/jquery.js",
                    "system\/library\/media-seek\/media-seek.js"
                ],
                "og_images": [
                    "https:\/\/ifedyukin.ru\/blog\/pictures\/remote\/youtube-xC7-HCPEn6k-cover.jpg"
                ]
            }
        },
        {
            "id": "28",
            "url": "https:\/\/ifedyukin.ru\/blog\/all\/docker-nginx-env\/",
            "title": "Docker + Nginx + Env",
            "content_html": "<p>Иногда бывают ситуации, когда внутри nginx-конфига надо сделать что-то в зависимости от переменной окружения, которая прокинется откуда-то извне (или не прокинется), так помимо этого это всё ещё и в Docker крутится, в который окружение прокидывается от compose или k8s.<\/p>\n<p>С выше изложенными условиями выполнить эту задачу по-простому не получится (ну или я не нашёл как). Решение, представленное ниже, не особо-то и сложное, но, на мой взгляд, несколько нетривиально, ну и, если б я нашёл другой способ, то такой огород городить точно не стал бы.<\/p>\n<p>Итак, ближе к делу, берём и переписываем наш Dockerfile следующим образом.<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">FROM # какой-то образ\r\n\r\nWORKDIR \/usr\/src\/app\r\nCOPY . \/usr\/src\/app\r\n\r\nENTRYPOINT [&quot;\/usr\/src\/app\/start.sh&quot;]\r\n\r\nEXPOSE 80<\/code><\/pre><p>Ключевой момент здесь — ENTRYPOINT, им является какой-то sh-файл (не забудьте ему права на выполнение дать). Этот файл будет преобразовывать наш nginx-конфиг и запускать nginx.<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">#!\/bin\/sh\r\n\r\nexport SEARCH=&quot;${SEARCH_FROM_ENV:-google}&quot;\r\n\r\nenvsubst '${SEARCH}' &lt; \/etc\/nginx\/nginx.conf &gt; \/etc\/nginx\/nginx.conf\r\n\r\nnginx -g 'daemon off;'<\/code><\/pre><p>В строчке экспорта мы “отдаём” переменную SEARCH, которую читаем из переменной окружения “SEARCHFROMENV”.<br \/>\nВ случае, если переменная окружения не задана, присваиваем стандартное значение “google”.<br \/>\nПосле чего обращаемся к системной утилите envsubst, которой конкретно указываем, какие переменные менять, передаём в неё наш конфиг, а результат выполнения пишем в этот же конфиг.<br \/>\nНаконец, запускаем nginx.<br \/>\nВ конфиге nginx изначально указываем где-то выражение следующего вида.<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">if (${SEARCH} = &quot;google&quot;) {\r\n  # что-то делаем\r\n}<\/code><\/pre><p>После обработки envsubst без передачи каких-либо переменных окружения переменная SEARCH заменится на “google”.<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">if (&quot;google&quot; = &quot;google&quot;) {\r\n  # что-то делаем\r\n}<\/code><\/pre><p>Таким образом мы прокидываем какую-то env-переменную в nginx-конфиг, а так же, в случае неопределённого окружения, имеем значение по-умолчанию.<\/p>\n",
            "date_published": "2018-09-06T18:01:56+03:00",
            "date_modified": "2021-01-24T18:02:13+03:00",
            "tags": [
                "development",
                "devops"
            ],
            "_date_published_rfc2822": "Thu, 06 Sep 2018 18:01:56 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "28",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": []
            }
        },
        {
            "id": "18",
            "url": "https:\/\/ifedyukin.ru\/blog\/all\/gitlab-ci-telegram\/",
            "title": "GitLab CI & Telegram",
            "content_html": "<p>Последнее время стал замечать, что всё больше людей юзают GitLab, отмечая, что у него клёвый CI\/CD. Не знаю, как правильно всё это оценивать, но попробовать я решил однозначно.<\/p>\n<p>Во-первых, переместил статические сайтики с GitHub Pages на GitLab Pages. Во-вторых, сделал небольшое ExpressJS-приложение и разместил на GitLab, а так же настроил деплой на Heroku, что, <a href=\"https:\/\/ifedyukin.ru\/blog\/all\/bachelor-work-1\/\">оказалось, очень просто<\/a>.<\/p>\n<p>GitLab CI, конечно, крут, много полезных примеров и доков, из коробки поддерживается множество интеграций и фич, но мне была нужна интеграция с Telegram, т. к. он всегда включен.<\/p>\n<p>Ну, раз нет интеграции из коробки, то сделаем сами, причем на данном этапе нужно всего лишь уведомление о статусе деплоя.<\/p>\n<p>Задача поставлена, сложностей никаких (на самом деле они возникали, но это я доки невнимательно читал). Делаем.<\/p>\n<p>Чтобы взаимодействовать с Telegram создадим бота: пишем <a href=\"https:\/\/t.me\/BotFather\">@BotFather<\/a>, создаём обычного бота, загружаем аватарку и т. п. Всё это делается максимально просто. После создания бота в чатик прилетит token для работы с API.<\/p>\n<p>Бот будет слать уведомления пользователю с нужным ID, но просто так Telegram не позволяет рассылать сообщения от бота любому пользователю, поэтому новосозданному боту нужно отправить хотя бы какое-то сообщение от пользователя, которому должны приходить уведомления.<\/p>\n<p>Сохраняем нужные значения в GitLab:<\/p>\n<ol start=\"1\">\n<li>Settings нужного репозитория;<\/li>\n<li>Пункт CI\/CD;<\/li>\n<li>Secret variables;<\/li>\n<li>Сохранить в переменную TELEGRAM_BOT_TOKEN token, присланный после создания бота через BotFather;<\/li>\n<li>В TELEGRAM_USER_ID сохраняем ID пользователя, которому будут отправляться уведомления.<\/li>\n<\/ol>\n<p>Для удобной отправки уведомлений создадим скрипт в `ci-notify.sh`, в котором будем обращаться к API Telegram и отправлять сообщение нужному пользователю через созданного бота:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">#!\/bin\/bash\r\n\r\nTIME=&quot;10&quot;\r\nURL=&quot;https:\/\/api.telegram.org\/bot$TELEGRAM_BOT_TOKEN\/sendMessage&quot;\r\nTEXT=&quot;Deploy status: $1%0A%0AProject:+$CI_PROJECT_NAME%0AURL:+$CI_PROJECT_URL\/pipelines\/$CI_PIPELINE_ID\/%0ABranch:+$CI_COMMIT_REF_SLUG&quot;\r\n\r\ncurl -s --max-time $TIME -d &quot;chat_id=$TELEGRAM_USER_ID&amp;disable_web_page_preview=1&amp;text=$TEXT&quot; $URL &gt; \/dev\/null<\/code><\/pre><p>В качестве первого параметра для этого скрипта должен передаваться статус деплоя. В .gitlab-ci.yml описываем вызов этого скрипта в нужное время с нужными параметрами:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">stages:\r\n- deploy\r\n- notify\r\n\r\ndeploy:\r\n  image: node:4.2.2\r\n  stage: deploy\r\n\r\n  script:\r\n  #deploy success\r\n  - sh .ci-notify.sh ✅\r\n\r\n  artifacts:\r\n    paths:\r\n    - public\r\n  only:\r\n  - master\r\n\r\nnotify_error:\r\n  stage: notify\r\n  script:\r\n  - sh .ci-notify.sh ❌\r\n  when: on_failure #deploy fail<\/code><\/pre><p>Для обозначения статуса деплоя я использовал Emoji. В случае успешного выполнения деплоя просто вызывается скрипт с нужны параметром. В случае неудачного деплоя “управление переходит” в следующий stage, в котором происходит уведомление о неудачном деплое.<\/p>\n<p>Таким образом, где-то за 10 минут можно настроить простые уведомления в Telegram из GitLab CI и хоть чуть-чуть почувствовать себя DevOps’ом. Если подзаморочиться, то можно расширить возможности всего этого, но мне как-то лень.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/ifedyukin.ru\/blog\/pictures\/tg-ci.png\" width=\"492\" height=\"612\" alt=\"\" \/>\n<\/div>\n",
            "date_published": "2018-01-05T17:22:20+03:00",
            "date_modified": "2023-10-20T11:26:57+03:00",
            "tags": [
                "development",
                "devops"
            ],
            "image": "https:\/\/ifedyukin.ru\/blog\/pictures\/tg-ci.png",
            "_date_published_rfc2822": "Fri, 05 Jan 2018 17:22:20 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "18",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "https:\/\/ifedyukin.ru\/blog\/pictures\/tg-ci.png"
                ]
            }
        },
        {
            "id": "13",
            "url": "https:\/\/ifedyukin.ru\/blog\/all\/travis-ci-chrome\/",
            "title": "Запуск Karma в Chrome — Travis-CI",
            "content_html": "<p>Если вы тестируете свой код и используете GitHub, то скорее всего вы уже используете Travis, однако, при тестировании JavaScript в Travis может возникнуть проблема — Travis не умеет запускать тесты в Chrome, но преодолеть её можно всего несколькими строчками config-файлов.<\/p>\n<p>Для начала нам нужно настроить сам travis, в моём случае `.travis.yml` выглядит следующим образом:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">language: node_js\r\nnode_js:\r\n- '6.2'\r\nbefore_install:\r\n- export CHROME_BIN=chromium-browser\r\n- export DISPLAY=:99.0\r\n- sh -e \/etc\/init.d\/xvfb start\r\n- sudo apt-get update\r\n- sudo apt-get install -y libappindicator1 fonts-liberation\r\n- wget https:\/\/dl.google.com\/linux\/direct\/google-chrome-stable_current_amd64.deb\r\n- sudo dpkg -i google-chrome*.deb\r\nscript:\r\n- npm install\r\n- npm test<\/code><\/pre><p>Внимание здесь стоит обратить на блок `before_install`.<\/p>\n<p>Для Karma тоже нужно несколько изменить файл настроек — создадим лаунчер, который будет использоваться только в окружении Travis:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">module.exports = function (config) {\r\n    var configuration = {\r\n        \/\/ configs\r\n        \r\n        browsers: ['Chrome'],\r\n        customLaunchers: {\r\n            Chrome_travis_ci: {\r\n                base: 'Chrome',\r\n                flags: ['--no-sandbox']\r\n            }\r\n        },\r\n      \r\n        \/\/ configs\r\n    };\r\n\r\n    if (process.env.TRAVIS) {\r\n        configuration.browsers = ['Chrome_travis_ci'];\r\n    }\r\n\r\n    config.set(configuration);\r\n}<\/code><\/pre><p>На этом процедура настройки закончена — пушим изменения на Github и можем наблюдать процесс тестирования в “консоли” Travis соответствующего репозитория.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/ifedyukin.ru\/blog\/pictures\/travisLog.png\" width=\"800\" height=\"367\" alt=\"\" \/>\n<\/div>\n",
            "date_published": "2017-03-27T17:05:37+03:00",
            "date_modified": "2021-01-24T17:06:13+03:00",
            "tags": [
                "development",
                "devops",
                "javascript"
            ],
            "image": "https:\/\/ifedyukin.ru\/blog\/pictures\/travisLog.png",
            "_date_published_rfc2822": "Mon, 27 Mar 2017 17:05:37 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "13",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "https:\/\/ifedyukin.ru\/blog\/pictures\/travisLog.png"
                ]
            }
        }
    ],
    "_e2_version": 4098,
    "_e2_ua_string": "Aegea 11.1 (v4098)"
}