Blog

  • ELK-стек для чого?

    ELK-стек для чого?

    В цій статті ви знайдете інформацію щодо ELK-стеку (Elasticsearch, Logstash, Kibana).
    Ця добірка фактично стала стандартом для централізованого збереження та аналізу логів.
    Але насправді це значно потужніший інструмент, аніж деякі його представляють. Розберімося що до чого.

    Elasticsearch – це пошукова система, яка є спадкоємиця проєкту Compass, який своєю чергою побудований на базі Apache Lucene.
    Першочергова ціль була: спрощення масштабування системи. Забігаючи наперед скажу що це дійсно вийшло.
    Зараз позиціонується як розподілений, масштабуємий, RESTful пошуковий та аналітичний механізм.
    Який дозволяє побудувати не тільки систему аналізу логів, а і повноцінний повнотекстовий пошук для сайту, або бізнес орієнтовану аналітичну систему.
    Пошукові індекси у складі багатоНОДової системи, легко масштабуються, балансування підтримується “із коробки”, та не потребує додаткової складної конфігурації.
    Починаючи із версії 5.1.1 має підтримку аналізу української мови, що стане в нагоді для побудови повнотекстового пошуку, наприклад у інтернет-магазині.

    Logstash – це система обробки збору та обробки даних для подальшого збереження цих даних, наприклад в Elasticsearch.
    Та за допомогою багатьох плагінів, дозволяє зберігати данні у різні системи, наприклад: AWS CloudWatch, DataDogHQ, Google BigQuery, Graphite, InfluxDB.
    Повний список можна подивитись: https://www.elastic.co/guide/en/logstash/current/output-plugins.html
    “Із коробки” підтримує збір та парсинг типових системних логів Linux, Nginx, MySQL, Redis.
    Повний модулів можна подивитись: https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html
    Також за допомогою плагіну Grok, можливо описати фільтри специфічних для проєкту логів, парсити їх та зберігати до Elasticsearch.
    Парсинг специфічних логів це окрема тема, яку я додам трошки пізніше.

    Kibana – система візуалізації та аналізу даних збережених в Elasticsearch. З коробки має налаштовані інтерфейси для візуалізації логів Linux, Nginx, MySQL, Redis.
    Що дозволяє у дуже короткий час отримати готову систему аналізу логів, типової конфігурації, наприклад LAMP(Linux, Apache2, MySQL, PHP).
    Також за допомогою вбудованих інструментів дозволяє побудувати свій інтерфейс, будувати графіки та таблиці.
    Має два типи можливих видів побудови запитів: Kibana Query Language(KQL) та Lucene query syntax(LQS).
    Насправді KQL використовується виключно для фільтрації даних, та не несе ролі агрегації та сортування. Не вміє працювати із регулярними виразами, або нечіткими запитами.
    Для таких цілей необхідно використовувати Lucene query, але він не підтримує автозаповнення.
    Kibana із вебінтерфейсу також дозволяє описати та сконфігурувати індекси у самому Elasticsearch: описати типи полів, життєвий цикл, кількість реплік.

    Яка практична цінність цієї системи?
    Як ми бачимо кожен з окремо взятих інструментів може жити своїм життям навіть поза ELK-стеком.
    Тому якщо нам необхідно побудувати повнотекстовий пошук: Гнучкість пошукових фільтрів, включаючи нечіткий пошук та мультиарендність, коли в рамках одного об’єкта Elasticsearch дозволяє динамічно організувати декілька різних пошукових систем.

    Або у BigData проєктах – автоматична індексація нових JSON-об’єктів, які завантажуються в базу та одразу стають доступними для пошуку, за рахунок відсутності схеми згідно з типовою NoSQL-концепцією. Це дозволяє прискорити прототипування пошукових рішень Big Data.

    Наявність вбудованих аналізаторів тексту дозволяє Elasticsearch автоматично виконувати токенізацію, лематизацію, стемінг та інші перетворення для вирішення NLP-завдань, пов’язаних з пошуком даних.


    Але ми тут розглядаємо саме ELK-стек, тому основна цінність цієї системи, це збір, візуалізація та пошук по логам, який можливо побудувати при незначних витратах часу.

    Як почати використовувати?
    Тут є декілька варіантів, або із використанням репозиторію, або за допомогою контейнерів Docker.
    Хоча розглянемо обидва варіанти, я не буду розписувати покроково, процес встановлення він дуже простий. Тому одразу дам Bash-скрипт:

    #!/bin/bash
    wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
    sudo apt-get install apt-transport-https
    echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
    sudo apt-get update && sudo apt-get install elasticsearch logstash kibana
    # secure http connect to services 9200 - elastic search, 5601 - kibana port, 5044 - logstash
    iptables -I INPUT ! -s 127.0.0.1/32 -p tcp -m tcp --dport 9200 -j ACCEPT
    iptables -I INPUT ! -s 127.0.0.1/32 -p tcp -m tcp --dport 5601 -j ACCEPT
    iptables -I INPUT ! -s 127.0.0.1/32 -p tcp -m tcp --dport 5044 -j ACCEPT
    
    #Enable services
    sudo systemctl enable elasticsearch && sudo systemctl start elasticsearch
    sudo systemctl enable logstash && sudo systemctl start logstash
    sudo systemctl enable kibana && sudo systemctl start kibana
    
    
    ## Echo key for add kibana to elasticstack
    echo "YOUR KEY FOR ENROLLMENT:"
    /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana --url "https://localhost:9200"

    Таким чином ми отримаємо однонодову інсталяцію ELK-стеку, у базовій комплектації готову для роботи.

    Або мультинодовий кластер на Docker-compose, але тут треба буде мати встановлений Docker engine і docker-compose.

    У будь-якій директорії створюємо файли .env та docker-compose.yml
    Вміст файлу ./.env:

    STACK_VERSION=8.2.2
    ES_PORT=127.0.0.1:9200
    KIBANA_PORT=127.0.0.1:5601
    LOGSTASH_PORT=127.0.0.1:5044
    
    ELASTIC_PASSWORD=<set strong password>
    KIBANA_PASSWORD=<set strong password>

    та файлу docker-compose.yml:

    version: "2.2"
    services:
      setup:
        image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
        volumes:
          - certs:/usr/share/elasticsearch/config/certs
        user: "0"
        command: >
          bash -c '
            if [ x${ELASTIC_PASSWORD} == x ]; then
              echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
              exit 1;
            elif [ x${KIBANA_PASSWORD} == x ]; then
              echo "Set the KIBANA_PASSWORD environment variable in the .env file";
              exit 1;
            fi;
            if [ ! -f config/certs/ca.zip ]; then
              echo "Creating CA";
              bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
              unzip config/certs/ca.zip -d config/certs;
            fi;
            if [ ! -f config/certs/certs.zip ]; then
              echo "Creating certs";
              echo -ne \
              "instances:\n"\
              "  - name: es01\n"\
              "    dns:\n"\
              "      - es01\n"\
              "      - localhost\n"\
              "    ip:\n"\
              "      - 127.0.0.1\n"\
              "  - name: es02\n"\
              "    dns:\n"\
              "      - es02\n"\
              "      - localhost\n"\
              "    ip:\n"\
              "      - 127.0.0.1\n"\
              "  - name: es03\n"\
              "    dns:\n"\
              "      - es03\n"\
              "      - localhost\n"\
              "    ip:\n"\
              "      - 127.0.0.1\n"\
              > config/certs/instances.yml;
              bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key;
              unzip config/certs/certs.zip -d config/certs;
            fi;
            echo "Setting file permissions"
            chown -R root:root config/certs;
            find . -type d -exec chmod 750 \{\} \;;
            find . -type f -exec chmod 640 \{\} \;;
            echo "Waiting for Elasticsearch availability";
            until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
            echo "Setting kibana_system password";
            until curl -s -X POST --cacert config/certs/ca/ca.crt -u elastic:${ELASTIC_PASSWORD} -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
            echo "All done!";
          '
        healthcheck:
          test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"]
          interval: 1s
          timeout: 5s
          retries: 120
    
      es01:
        depends_on:
          setup:
            condition: service_healthy
        image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
        volumes:
          - certs:/usr/share/elasticsearch/config/certs
          - esdata01:/usr/share/elasticsearch/data
        ports:
          - ${ES_PORT}:9200
        environment:
          - node.name=es01
          - cluster.name=${CLUSTER_NAME}
          - cluster.initial_master_nodes=es01,es02,es03
          - discovery.seed_hosts=es02,es03
          - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
          - bootstrap.memory_lock=true
          - xpack.security.enabled=true
          - xpack.security.http.ssl.enabled=true
          - xpack.security.http.ssl.key=certs/es01/es01.key
          - xpack.security.http.ssl.certificate=certs/es01/es01.crt
          - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
          - xpack.security.http.ssl.verification_mode=certificate
          - xpack.security.transport.ssl.enabled=true
          - xpack.security.transport.ssl.key=certs/es01/es01.key
          - xpack.security.transport.ssl.certificate=certs/es01/es01.crt
          - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
          - xpack.security.transport.ssl.verification_mode=certificate
          - xpack.license.self_generated.type=${LICENSE}
        mem_limit: ${MEM_LIMIT}
        ulimits:
          memlock:
            soft: -1
            hard: -1
        healthcheck:
          test:
            [
              "CMD-SHELL",
              "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
            ]
          interval: 10s
          timeout: 10s
          retries: 120
    
      es02:
        depends_on:
          - es01
        image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
        volumes:
          - certs:/usr/share/elasticsearch/config/certs
          - esdata02:/usr/share/elasticsearch/data
        environment:
          - node.name=es02
          - cluster.name=${CLUSTER_NAME}
          - cluster.initial_master_nodes=es01,es02,es03
          - discovery.seed_hosts=es01,es03
          - bootstrap.memory_lock=true
          - xpack.security.enabled=true
          - xpack.security.http.ssl.enabled=true
          - xpack.security.http.ssl.key=certs/es02/es02.key
          - xpack.security.http.ssl.certificate=certs/es02/es02.crt
          - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
          - xpack.security.http.ssl.verification_mode=certificate
          - xpack.security.transport.ssl.enabled=true
          - xpack.security.transport.ssl.key=certs/es02/es02.key
          - xpack.security.transport.ssl.certificate=certs/es02/es02.crt
          - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
          - xpack.security.transport.ssl.verification_mode=certificate
          - xpack.license.self_generated.type=${LICENSE}
        mem_limit: ${MEM_LIMIT}
        ulimits:
          memlock:
            soft: -1
            hard: -1
        healthcheck:
          test:
            [
              "CMD-SHELL",
              "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
            ]
          interval: 10s
          timeout: 10s
          retries: 120
    
      es03:
        depends_on:
          - es02
        image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
        volumes:
          - certs:/usr/share/elasticsearch/config/certs
          - esdata03:/usr/share/elasticsearch/data
        environment:
          - node.name=es03
          - cluster.name=${CLUSTER_NAME}
          - cluster.initial_master_nodes=es01,es02,es03
          - discovery.seed_hosts=es01,es02
          - bootstrap.memory_lock=true
          - xpack.security.enabled=true
          - xpack.security.http.ssl.enabled=true
          - xpack.security.http.ssl.key=certs/es03/es03.key
          - xpack.security.http.ssl.certificate=certs/es03/es03.crt
          - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
          - xpack.security.http.ssl.verification_mode=certificate
          - xpack.security.transport.ssl.enabled=true
          - xpack.security.transport.ssl.key=certs/es03/es03.key
          - xpack.security.transport.ssl.certificate=certs/es03/es03.crt
          - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
          - xpack.security.transport.ssl.verification_mode=certificate
          - xpack.license.self_generated.type=${LICENSE}
        mem_limit: ${MEM_LIMIT}
        ulimits:
          memlock:
            soft: -1
            hard: -1
        healthcheck:
          test:
            [
              "CMD-SHELL",
              "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
            ]
          interval: 10s
          timeout: 10s
          retries: 120
    
      kibana:
        depends_on:
          es01:
            condition: service_healthy
          es02:
            condition: service_healthy
          es03:
            condition: service_healthy
        image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
        volumes:
          - certs:/usr/share/kibana/config/certs
          - kibanadata:/usr/share/kibana/data
        ports:
          - ${KIBANA_PORT}:5601
        environment:
          - SERVERNAME=kibana
          - ELASTICSEARCH_HOSTS=https://es01:9200
          - ELASTICSEARCH_USERNAME=kibana_system
          - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
          - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt
        mem_limit: ${MEM_LIMIT}
        healthcheck:
          test:
            [
              "CMD-SHELL",
              "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
            ]
          interval: 10s
          timeout: 10s
          retries: 120
    
      logstash:
        image: docker.elastic.co/logstash/logstash:${STACK_VERSION}
        ports:
          - ${LOGSTASH_PORT}:5044
          - "5000:5000/tcp"
          - "5000:5000/udp"
          - "9600:9600"
        environment:
          LS_JAVA_OPTS: -Xms256m -Xmx256m
    	depends_on:
          es01:
            condition: service_healthy
          es02:
            condition: service_healthy
          es03:
            condition: service_healthy
    	
    volumes:
      certs:
        driver: local
      esdata01:
        driver: local
      esdata02:
        driver: local
      esdata03:
        driver: local
      kibanadata:
        driver: local

    І запустити для чого виконати команду:

    docker-compose up -d

    Після запуску кластера можна під’єднатися до Kibana http://localhost:5601
    використавши логін elastic та пароль зазначений в ELASTIC_PASSWORD

    Далі вже необхідно буде встановити збір логів із серверів. Якщо ми хочемо збирати стандартні логи, то для цього навіть не потрібен Logstash, замість нього достатньо встановити на сервері із якого необхідно збирати логи, Filebeat.


    Filebeat – це легкий агент для збору та надсилання файлів журналювання, який може відправляти данні, або напряму в Elasticsearch, або для подальшого опрацювання до Logstash.

    Для встановлення Filebeat необхідно виконати декілька дій на сервері із Elasticsearch та сервері із якого збираємо логи.
    На сервері звідки беремо логи:

    #!/bin/bash
    curl -L -O --output-dir /tmp https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-8.2.2-amd64.deb 
    sudo dpkg -i /tmp/filebeat-8.2.2-amd64.deb
    sudo rm /tmp/filebeat-8.2.2-amd64.deb
    filebeat modules enable nginx

    Після чого змінюємо конфігурацію модуля /etc/filebeat/modules.d/nginx.yml:

    - module: nginx
      access:
        enabled: true
        var.paths: ["/var/log/nginx/access.log*"]

    Необхідно створити юзера для запису, на сервері з Elasticsearch виконуємо:

    ./bin/elasticsearch-users useradd filebeat

    Вигадуємо пароль, та вводимо його, changeme!23

    Створюємо групу із достатніми правами для інсталяції згідно з офіційною документацією

    curl -k -u "elastic:<ELASTIC_PASSWORD>" -X PUT "https://localhost:9200/_security/role/filebeat_write?pretty" -H 'Content-Type: application/json' -d'
    {
      "cluster": [
        "monitor",
        "read_pipeline",
        "read_ilm",
        "manage"
      ],
      "indices": [
        {
          "names": [
            "filebeat-*"
          ],
          "privileges": [
            "create_doc",
            "view_index_metadata",
            "create_index"
          ]
        },
        {
          "names": [
            "filebeat-*",
            ".ds-filebeat-*"
          ],
          "privileges": [
            "manage",
            "manage_follow_index",
            "all"
          ]
        }
      ]
    }
    '

    Додаємо до груп:

    ./bin/elasticsearch-users roles filebeat -a kibana_admin
    ./bin/elasticsearch-users roles filebeat -a filebeat_writer

    У файлі конфігурації на сервері із Filebeat змінити рядки:

    output.elasticsearch:
      hosts: ["https://myEShost:9200"]
      username: "filebeat" 
      password: "changeme!23"
      
    setup.kibana:
        host: "myKIBANAhostIP:5601" 
        username: "filebeat"  
        password: "changeme!23"

    Причому у блоці із setup.kibana, юзер і пароль, опціональні, Якщо їх не вказати будуть використані із блоку output.elasticsearch.

    Після всіх підготовчих робіт виконуємо команду, яка створить робочі столи і візуалізації для вибраних модулів(в нашому випадку Nginx).

    filebeat setup -e

    Також хочу зазначити що група kibana_admin, необхідна одноразово для створення базових робочих столів із візуалізацією. Можна потім відізвати цю роль:

    ./bin/elasticsearch-users roles filebeat -r kibana_admin

    На цьому базова конфігурація закінчена і можна перейти до Kibana. Та подивитись логи Nginx.


    Додаткові сервери додаються так само, але вже без всіх цих маніпуляцій зі створенням групи й додаванням юзера до неї.
    Або із використанням Ansible розгорнути типові конфігурації Filebeat, на всіх необхідних серверах.

  • Що таке DevOps? Та як стати DevOps Інженером? ч.2

    Що таке DevOps? Та як стати DevOps Інженером? ч.2

    Продовжуємо першу частину статті “Що таке DevOps? Та як стати DevOps Інженером?“.

    Сьогодні більшість компаній перейшли від обслуговування своїх серверів до перенесення своєї інфраструктури до “хмари”.
    Що таке хмара?
    Хмара – це набір сервісів та серверів, які постачальник хмарних технологій здає в оренду замовнику.
    Ще називають Infrastructure as a Service(IaaS) – Інфраструктура як сервіс.
    Такий підхід дозволяє зменшити витрати на обслуговування інфраструктури, зменшити час на зміну конфігурації, вирішити проблеми зі зберіганням бекапів тощо.
    Існує багато різних постачальників хмарних рішень: Amazon Web Services(AWS), Azure, Google Cloud Provider(GCP), DigitalOcean.

    Всі вони мають безліч сервісів: такі як замовлення віртуального сервера, або специфічних, як сервіс черг.
    Безумовним лідером у хмарних технологіях є AWS, тому я рекомендую почати вивчення саме з нього. Станом на 2022 рік вони надають доступ до більш ніж 200+ сервісів.
    Але не лякайтесь на справді нема потреби знати всі ці сервіси, достатньо розумітися на декількох самих необхідних, наприклад: EC2, S3, RDS, EKS, CloudFront.

    Автоматизація

    Безумовно, спочатку, зайти до веб-консолі та запустити один два-три-десять віртуальних серверів, буде не складно.
    Але із часом така рутинна робота, почне напрягати та і де тут автоматизація?
    Тут нам на допомогу приходять різні системи автоматизації, такі як: Ansible, Puppet, Chef.
    Дуже легким у розумінні та обслуговуванні є:
    Ansible – це система яка дозволяє автоматизувати розгортання та обслуговування типових систем. За допомогою великої кількості готових модулів, та можливістю створювати свої.
    Також відмічу систему побудови інфраструктури:
    Terraform – ця система Infrastructure as a Code(IaC) дозволяє описати типову конфігурацію серверів у вигляді коду, які необхідно створити у хмарі, та у декілька команд, розгорнути готову інфраструктуру, або так само легко змінювати її конфігурацію.
    Має багато додаткових модулів, які покривають більшість наявних IaaS постачальників.

    Оркестрація

    Скоріш за все ви зіткнетеся із необхідністю розгортання великої кількості Docker контейнерів, тут вам знадобиться системи оркестрації контейнерів, наприклад: Kubernetes, Docker Swarm, Openshift Container Platform, Rancher та інші.
    Велика кількість проєктів використовує саме Kubernetes, як систему оркестрації. Тому рекомендую звернути увагу на нього у першу чергу.

    Логування

    Після того як ви описали та побудували свою інфраструктуру, особливо якщо вона досить велика, вам необхідно налаштувати централізований збір метрик систем та іх логування.
    Систем моніторингу також дуже багато. Скоріш за все, ви зустрінете на проектах: Zabbix, Nagios, Munin, тощо.
    Zabbix – це інструмент для моніторингу ІТ-інфраструктури, такої як мережі, сервери, віртуальні машини та хмарні сервіси. Збирає та відображає основні показники системи.
    Останнім часом дуже популярним стає Prometheus та система візуалізації метрик Grafana.
    Велике значення у концепції DevOps має логування. Необхідно зберігати як системі логи, так і логи аплікейшену(ПЗ). Особливо це важко стає коли у вас десятки серверів.
    Тут на допомогу приходять системи централізованого збору та агрегації логів: ELK, Graylog, Sentry.
    ELK-stack – це абревіатура трьох популярних сервісів, системи повнотекстового пошуку ElasticSearch, системи збору логів Logstash, та Kibana системи візуалізації та аналізу даних.
    Ця підбірка систем, дозволяє збирати та аналізувати велику кількість логів систем. Та із деякими маніпуляціями навіть специфічні до вашого проєкту логи.

    Скриптинг

    І останнім по рахунку, але не останнім по важливості, знанням для DevOps інженеру – є вміння писати скрипти. Тому треба звернути увагу на BASH скриптинг, знати на непоганому рівні Python, або із нещодавнього Golang.

    Контроль версій

    Також хочу звернути вашу увагу, на те що більшість інструментів що я згадав, мають у своїй основі: конфігураційні файли, файли опису інфраструктури.
    Ці файли, або вихідні коди ПЗ, зберігаються у системі контролю версій, самий популярний наразі – це Git.
    Систем управління контролем версій також багато: Bitbucket, GitHub, GitLab. Вони всі працюють однаково, але більшість open-source проектів використовують GitHub.

    Післямова

    Дуже багато систем і інструментів у кожному із напрямків і це здається дуже важким, як це все знати та пам’ятати. Не лякайтесь, достатньо знати хоча б один із представлених, щоб розуміти як це працює.
    Ці знання дадуть вам змогу претендувати на позицію Junior DevOps.
    Беззаперечно, вся ця інформація хоч і має на своїй меті огляд всіх можливих варіантів, але я міг щось забути. Якщо ви помітили неточність, будь ласка напишіть коментар – я обов’язково виправлю.

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

  • Що таке DevOps? Та як стати DevOps Інженером?

    Що таке DevOps? Та як стати DevOps Інженером?

    В цій статті я хочу познайомити вас із напрямком DevOps та як встати на цей шлях і що необхідно знати.
    Описати основні концепції та інструменти.
    Ця сторінка буде модифікуватися, та будуть додаватися лінки із посиланнями на більш поглиблене ознайомлення із кожним інструментом, або концепцією.

    Що таке DevOps?

    DevOps – це філософія підходу до розборки програмного забезпечення та набір практик.
    Полягає вона в об’єднанні двох напрямків Development(Розробка) та Operations(Обслуговування).
    Історично склалося, що код пишуть – розробники, а встановлюють і обслуговують – адміністратори систем.
    При такому підході часто створюються ситуації, на кшталт: “В мене на комп’ютері все працює, то щось на сервері”.
    DevOps покликаний вирішити такі проблеми та за допомогою яких інструментів це можливо зробити.

    Програмне забезпечення (ПЗ) – це результат роботи програмістів, у вигляді вихідних кодів(source code).
    Ці вихідні коди зберігаються у системі контролю версій, популярний наразі – Git.

    Як стати DevOps інженером у 2022 році? Що потрібно знати?

    Зважаючи на те що DevOps інженер, об’єднує декілька напрямків, він повинен розуміти основні концепції як що працює, наприклад:

    Але це все лірика.
    Основна задача DevOps інженеру – це супроводження проєкту, вихідні коди повинні бути скомпільовані(якщо це необхідно) та встановлені на сервер.
    Я рекомендую розібратися із:

    Також, необхідно знати як працює мережа:

    Тут вже, мабуть, йде голова обертом, щось дуже багато треба знати й розуміти.
    Для початку необхідно знати концепцію, а не бути абсолютним професіоналом у кожному із напрямків.
    На великих проєктах, існують команди спеціалістів: системних адміністраторів, мережевих адміністраторів, адміністраторів безпеки.
    Ваша задача: підготувати ПЗ та сервер до роботи, а не повністю його обслуговувати. Але і таке буває.

    Сьогодні контейнеризація – стала новим стандартом.
    Вивчаємо:

    Одним із таких систем є Docker. Тому це треба обов’язково вивчити й розуміти як працює.

    Але нащо це все, ви запитаєте мене, і де тут DevOps?

    Далі цікавіше.Одними з основних концепцій DevOps є CI/CD та автоматизація.


    Continuous Integration(CI) – “Безперервна інтеграція”, це коли код який потрапив до репозиторію від різних розробників, автоматично збирається на білд сервері та проходить етапи тестування і перевірок.
    На етапі CI можуть створюватись якісь “Артефакти”. Для того, щоб кожного разу не перебудовувати та пришвидшити наступні типові побудови – використовують репозиторій зберігання артефактів. Таким є NEXUS artifact repository.
    Артефакт – це скомпільовані файли, бібліотеки, які використовуються для роботи програми, та з’явились у процесі побудови.
    Також на етапі побудови можуть з’явитися Docker Images які також необхідно десь зберігати. Наприклад DockerHub.

    Continuous Delivery(CD) – “Безперервне доставлення” є розширенням безперервної інтеграції, оскільки вона автоматично розгортає всі зміни коду в тестовому та/або предпродуктовому(Staging) середовищі після етапу збірки. А доставляння та розгортання у продуктовій середі виконуєтся власноруч.
    Окремо відмічу:
    Continious Deployment(CD) – Це фактично заміна або перезагрузка “безперервного доставляння”. Завдяки цій практиці кожна зміна вихідних кодів, яка проходить усі етапи вашого виробничого конвеєра, буде доступна вашим клієнтам. Немає ніякого втручання людини, і лише невдалий тест запобіжить впровадженню нових змін у продуктовій середі.
    Для автоматизації цих процесів використовують системи автоматизації: GitHub Actions, GitLab CI/CD, Jenkins, TeamCity.
    Найбільш використовуваним наразі є Jenkins. Тому це кандидат на вивчення у першу чергу.

    Тут я хочу зупинитися, та продовжу у далі у наступній частині.
    Де ми розглянемо постачальників хмарних технологій, інструменти оркестрації, моніторинг та логування.

  • Як пришвидшити роботу із plain index у Manticore Search?

    Як пришвидшити роботу із plain index у Manticore Search?

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

    Проблема пов’язана із тим, що “проходження”, по одному “простому індексу”(plain index) виконується із використанням одного ядра, у той час, коли інші ядра “простоюють”.
    У офіційній документації знаходимо що існує “розподілений індекс”(distributed index), який утилізує стільки ядер, скільки вказано у директиві dist_threads.
    UPD. Директива dist_threads від 2020 року застаріла, на заміну пришла директива threads, яка за замоченням відповідає кількості ядер процесора.

    Перейдемо до демонстрації, як це працює.
    Наприклад ви маєте “простий індекс” такого плану:

    source src_products:connect_database
    {
            sql_query_pre = SET CHARACTER_SET_RESULTS=utf8
            sql_query_pre   = SET GROUP_CONCAT_MAX_LEN = 2000
            sql_range_step      = 1000;
    		sql_query_range     = SELECT min(id), max(id) 
    		
    		sql_query               = \
                    SELECT t1.id AS product_id, \
                t2.id AS supplier_id, \
                count(distinct t1.id) as product_count, \
                t1.product_count as total_product_count, \
                unix_timestamp(t2.supplier_start_date) AS date_sale_start, \
                unix_timestamp(t2.date_modified) AS date_modified, \
                unix_timestamp(t2.sale_end_date) AS date_end_sale, \
                unix_timestamp(t2.created_at) AS created_at, \
                t1.title AS product_title, \
                t1.description AS product_description, \
                t1.title_en AS product_title_en, \
                t1.description_en AS product_description_en, \
                t2.title, \
                t2.description, \
                t2.title_en, \
                t2.description_en, \
                t2.status_id as status_id, \
                t2.identifier AS supplier_identifier, \
                t2.name AS supplier_name, \
                t2.id AS supplier_id, \
                    FROM products t1 \
                inner join supplier t2 on t2.id = t1.supplier_id \
            WHERE t1.id >= \$start AND t1.id <= \$end AND t2.enabled = 1 \
            GROUP BY t1.id
    		
    		sql_field_string = product_title
    		sql_field_string = product_description
    		sql_attr_uint = product_id
    		sql_attr_uint = supplier_id
    		sql_attr_multi = uint code_id from query; \
    			select t1.id as id, t3.codes_id as code_id \
    			from products t1 \
    			inner join items t2 on t1.product_id = t2.product_id \
    			inner join codes_item t3 on t2.id = t3.item_id
    }
    
    index products
    {
        source   = src_products
        path   = /var/lib/manticore/data/src_products
        min_stemming_len    = 3
        min_word_len        = 3
        phrase_boundary     = ., ?, !, ^, U+2026
        min_infix_len = 3
    }

    Рішення проблеми:

    Нам необхідно розбити один великий “простий індекс” на декілька(бажано за кількістю ядер) та об’єднати їх у “розподілений індекс”.
    Можемо взяти сумарну кількість записів розділити порівну й описати декілька сорсів із якимось офсетом.
    Але що робити якщо таблиця постійно наповнюється та кількість записів змінюється? А ще записи можуть видалятися.
    Тому цю проблему ми віддамо на рішення самої БД, щоб вона завжди нам віддавала рівні частини звідки починати й де закінчувати для кожного шматочка “простого індексу”.
    Тут на допомогу приходить модифікація нашого sql_query_range, який вказує звідки починати й де закінчувати вибірку для побудови індексу.

    Наприклад, щоб поділити на 3 рівні частини:

    sql_query_range     = SELECT min(S.ID), max(S.ID) FROM \
    						  ( \
    							SELECT L.ID, \
    							  @RN:=@RN+1 ROWNUMBER, \
    							  (SELECT ROUND(COUNT(*)*0) FROM products) startN, \          # вказуємо звідки починаемо, для другого індексу буде: COUNT(*)*0.334. Для останнього: COUNT(*)*0.668
    							  (SELECT ROUND(COUNT(*)*0.334) FROM products) stopN \ 		  # де закінчуємо, для другого індексу буде: COUNT(*)*0.668. Для останнього: COUNT(*)
    							FROM products L \
    							  CROSS JOIN (SELECT @RN:=0) R \
    							ORDER BY L.ID \
    						  ) S \
    						WHERE S.ROWNUMBER >= S.startN and S.ROWNUMBER <= S.stopN

    Далі розмножуємо опис джерела(source), де змінюємо назву, та змінюємо sql_query_range.

    source src_products2:connect_database
    {
            sql_query_pre = SET CHARACTER_SET_RESULTS=utf8
            sql_query_pre   = SET GROUP_CONCAT_MAX_LEN = 2000
            sql_range_step      = 1000;
            sql_query_range     = SELECT min(S.ID), max(S.ID) FROM \
    								  ( \
    									SELECT L.ID, \
    									  @RN:=@RN+1 ROWNUMBER, \
    									  (SELECT ROUND(COUNT(*)*0.334) FROM products) startN, \
    									  (SELECT ROUND(COUNT(*)*0.668) FROM products) stopN \
    									FROM products L \
    									  CROSS JOIN (SELECT @RN:=0) R \
    									ORDER BY L.ID \
    								  ) S \
    								WHERE S.ROWNUMBER >= S.startN and S.ROWNUMBER <= S.stopN
    		...
        ...
        ...		
    }

    Розмножуємо опис index:

    index products2 
    {
    	source   = src_products2
        path   = /var/lib/manticore/data/src_products2
        min_stemming_len    = 3
        min_word_len        = 3
        phrase_boundary     = ., ?, !, ^, U+2026
        min_infix_len = 3
    }

    І накінець описуємо “розподілений індекс”(distributed index):

    index products_distributed
    {
        type = distributed
        local = products
        local = products2
        local = products3
    }

    Перезапускаємо індексацію, перезагружаємо сервіс Manticore Search, перевіряємо чи всі данні проіндексувались вірно.
    Ну і також необхідно буде змінити у конфігах вашого аплікейшену назву індексу: products -> products_distributed.

    Післямова

    Таким чином ми розділили один звичайний індекс, на декілька, та об’єднали їх до розподіленого індексу.
    Що дозволить ядру Manticore Search, використовувати більше ядер для пошуку по індексу.

    Безумовно ця історія створює одну неприємну річ, це обслуговування джерел(source) для індексів, при зміні одного, необхідно виконати зміни у кожному джерелі.
    Але приріст продуктивності коштує цього. Також для зміни конфігурації можливо використовувати різні інструменти, наприклад Ansible.
    Або створити Docker образ, конфігурацію manticore закинути до Git, та перебудовувати образ в процесі вашого CI/CD пайплайну.