Серверы
  • Готовые серверы
  • Конфигуратор
  • Серверы с 1CPU
  • Серверы с 2CPU
  • 4 поколение AMD EPYC и Intel Xeоn
  • Серверы с AMD Ryzen и Intel Core i9
  • Серверы для хранения данных
  • Cерверы с портом 10 Гбит/c
  • GPU
  • Распродажа
  • VPS
    GPU
  • Выделенные серверы с GPU
  • Виртуальные серверы с GPU
  • Распродажа
    Маркетплейс
    Colocation
  • Размещение серверов в дата-центре в Москве
  • Обслуживание серверов в других ЦОД
  • Кластеры
    Прокат
    Услуги
  • Аренда сетевого оборудования
  • Защита L3-L4 от DDoS атак
  • IPV4 и IPV6 адреса
  • Администрирование серверов
  • Уровни технической поддержки
  • Мониторинг сервера
  • BYOIP
  • USB диск
  • IP-KVM
  • Трафик
  • Коммутация серверов
  • О нас
  • Работа в HOSTKEY
  • Панель управления серверами и API
  • Дата-центры
  • Сеть
  • Тест скорости
  • Специальные предложения
  • Отдел продаж
  • Для реселлеров
  • Гранты для специалистов по Data Science
  • Гранты для научных проектов и стартапов
  • Документация и Частые вопросы
  • Новости
  • Блог
  • Оплата
  • Документы
  • Сообщите о нарушении
  • Looking Glass
  • 04.01.2023

    Сбор логов при помощи Go

    server one
    HOSTKEY
    Арендуйте выделенные и виртуальные серверы с моментальным деплоем в надежных дата-центрах класса TIER III в Москве и Нидерландах. Принимаем оплату за услуги HOSTKEY в Нидерландах в рублях на счет российской компании. Оплата с помощью банковских карт, в том числе и картой МИР, банковского перевода и электронных денег.

    Автор: Александр Тряпкин, DevOps компании Hostkey

    Здравствуйте, уважаемые читатели! В этой статье я хочу поделиться своим опытом решения задачи сбора логов при помощи Go. Как начинающий DevOps, я выбрал для изучения и решения рабочих задач язык программирования Go. Для отправки syslog-логов доступна библиотeка syslog, но увы, она нам не подходит, поскольку данный пакет недоступен на Windows, а задача — сделать мультиплатформенный отправщик логов установки системы на удаленный syslog-сервер. Дополнительно есть потребность отправлять логи в кастомном формате, а именно — в json, для упрощения их последующей обработки. При этом важно, чтобы программа выполнялась одинаково на Linux и на Windows, не требовала установки, выполняла свою задачу и удалялась из системы, поэтому придется изобрести небольшой велосипед. Приступим.

    В качестве принимающей стороны мы будем использовать syslog-ng. Рассмотрим параметры, которые нам интересны в части сбора логов — от специфики параметров зависит, как мы будем их отправлять.

    Сначала указываем новый source для приема логов с удаленных серверов, и тут есть варианты — в зависимости от наших потребностей можно собирать логи по UDP, TCP, а также использовать TLS для шифрования и аутентификации. Наиболее интересным вариантом является TLS, но мы рассмотрим и другие методы — от простого к более сложному.

    1) UDP. Для сбора логов по UDP потребуется следующие параметры в конфигурации syslog-ng:

    source s_network {
    	network( ip("0.0.0.0") #IP, на котором принимать логи, 0.0.0.0 - на всех
    						 transport("udp")  );       };

    Порт по умолчанию — 514/UDP, документация предупреждает о необходимости увеличить UDP-буфер при высокой интенсивности отправки логов, иначе возможны потери сообщений. В случае потери пакетов логи также будут потеряны,так что это не оптимальный вариант.

    2) TCP. Вариант лишен вышеуказанных проблем и, согласно документации, применяется по умолчанию. Примерный конфиг следующий:

    source s_network {
    	network( ip("0.0.0.0") ); };

    3) TLS. Для использования этого протокола необходимо настроить сервер, в официальной документации есть достаточно подробная пошаговая инструкция. Пример:

    source s_remote_tls { 
    network ( ip ("0.0.0.0") port(6514) 
    transport("tls") 
    tls( key-file("/etc/syslog-ng/cert.d/serverkey.pem") cert-file("/etc/syslog-ng/cert.d/servercert.pem")
    ca-dir("/etc/syslog-ng/ca.d")
    peer-verify(yes)) ); };

    При таком варианте настройки мы будем принимать логи только от клиентов, прошедших аутентификацию. Иначе говоря, клиент использует действующий сертификат и логи приходят с IP-адреса или доменного имени, под который сертификат выпущен. Если нет задачи аутентифицировать пользователей, то можно указать peer-verify (no) и получить только шифрование.

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

    Для начала разберемся, как отправить syslog сообщение серверу так, чтобы он его принял и обработал. Из документации мы видим, что принимаемые сообщения должны соответствовать протоколу RFC3164 или RFC5424. Но поскольку это не окончательный вариант, попробуем отправить лог, используя RFC3164, который выглядит следующим образом:

    <30>Dec 25 21:55:36 19202.example.ru systemd[1]: Starting Cleanup of Temporary Directories...

    Теперь разберемся, что значит каждая из частей сообщения:

    • <30> — заголовок, содержащий информацию о severity и facility. Закодированную информацию можно расшифровать с помощью таблицы, в данном случае там содержится facility — system и severity — info.
    • Dec 25 21:55:36 — timestamp.
    • 19202.example.ru — hostname.
    • Systemd[1]: — тег сообщения, указывающий, какой программой было отправлено сообщение.
    • Starting Cleanup of Temporary Directories… — само сообщение.

    Попробуем отправить сообщение в таком формате в наш тестовый сервер syslog-ng, настроенный на прием логов по UDP. Для этого мы используем библиотеку net:

    	logsrv, err := net.ResolveUDPAddr("udp4", "141.105.70.24:514")
    	if err != nil {
    		log.Fatal(err)
    	}
    	logwriter, err := net.DialUDP("udp4", nil, logsrv)
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer logwriter.Close()
    	_, err = logwriter.Write([]byte("<30>Dec 25 21:55:36 test-host go-logger: Hello Habr!"))
    	_, err = logwriter.Write([]byte("<30>Dec 25 21:55:36 test-host go-logger: This is test go-logger!"))
    	if err != nil {
    		log.Fatal(err)
    	}
    

    Выполнив код, мы видим, что сервер получил наши сообщения и обработал. Сообщения записаны в указанный файл:

    [root@19181 ~]# cat /var/log/test 
    Dec 25 21:55:36 test-host go-logger: Hello Habr! 
    Dec 25 21:55:36 test-host go-logger: This is test go-logger!

    Теперь отправим логи по TCP. Перенастраиваем сервер на получение логов по TCP и пробуем:

    tcpAddr, err := net.ResolveTCPAddr("tcp", "141.105.70.24:514")
    	if err != nil {
    		log.Fatal(err)
    	}
    	logwriter, err := net.DialTCP("tcp", nil, tcpAddr)
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer logwriter.Close()
    	_, err = logwriter.Write([]byte("<30>Dec 25 21:55:36 test-host go-logger: Hello Habr!"))
    	_, err = logwriter.Write([]byte("<30>Dec 25 21:55:36 test-host go-logger: This is test go-logger!"))
    	if err != nil {
    		log.Fatal(err)
    	}
    
    Dec 25 21:55:36 test-host go-logger: Hello Habr!<30>Dec 25 21:55:36 test-host go-logger: This is test go-logger!

    Но что мы видим: что-то пошло не так, два сообщения соединены в одно, и второе сообщение не распарсилось. Когда мы отправляли логи по UDP, данная проблема не возникала, поскольку каждое сообщение уходит в своем пакете и обрабатывается отдельно. Решение на самом деле простое — я упустил, что каждое сообщение должно заканчиваться переносом строки \n. Правим и пробуем:

    	_, err = logwriter.Write([]byte("<30>Dec 25 21:55:36 test-host go-logger: Hello Habr!\n"))
    	_, err = logwriter.Write([]byte("<30>Dec 25 21:55:36 test-host go-logger: This is test go-logger!\n"))
    [root@19181 ~]# cat /var/log/test 
    Dec 25 21:55:36 test-host go-logger: Hello Habr! 
    Dec 25 21:55:36 test-host go-logger: This is test go-logger!

    Теперь все ок!

    Мы разобрались как отправить сообщение, пришло время применить это знание. Следует учитывать, что, если мы хотим отправить сообщения в формате json (в дальнейшем это сильно облегчит задачу по обработке логов), нам необходимо отключить парсинг в syslog-ng. Для этого достаточно добавить flags (no-parse) в source. Далее будем пробовать отправить логи по протоколу TLS и в json-формате уже в виде полноценной программы:

    package main
    
    import (
    	"bufio"
    	"crypto/tls"
    	"crypto/x509"
    	"encoding/json"
    	"fmt"
    	"io/ioutil"
    	"log"
    	"os"
    	"time"
    
    	"github.com/pborman/getopt/v2"
    )
    	
    // Задаем структуру нашего сообщения, тут мы не ограничены  протоколом syslog, отправляем только то, что нам необходимо или, наоборот, добавляем
    type message struct {
    	Time     string `json:"timestamp"`
    	Hostname string `json:"host"`
    	Programm string `json:"programm"`
    	Body     string `json:"message"`
    }
    	
    func main() { 
    	// Нужные параметры мы будем передавать в нашу программу посредством ключей, в этом нам поможет библиотека getopt
    	optSyslogSrv := getopt.StringLong("dest", 'd', "", "Remote syslog server with port ip:port, required")
    	optReadFromFile := getopt.StringLong("file", 'f', "", "Read log from file")
    	optProg := getopt.StringLong("prog", 'p', "go-logger", "Programm tag, optional,  default - go-logger")
    	optHost := getopt.StringLong("host", 'H', "", "Host override")
    	optHelp := getopt.BoolLong("help", 'h', "Display usage")
    	optVerb := getopt.BoolLong("verbose", 'v', "Display outgoing msgs")
    	optCa := getopt.StringLong("ca", 'c', "cacert.pem", "CA")
    	optCert := getopt.StringLong("cert", 'C', "clientcert.pem", "Cert")
    	optKey := getopt.StringLong("key", 'K', "", "clientkey.pem", "Key")
    
    	getopt.Parse()
    	// Если программа запущена с ключем -h --help или не задан необходимый параметр, выводим подсказку по использованию
    	if *optHelp || len(*optSyslogSrv) == 0 {
    		getopt.Usage()
    		os.Exit(0)
    	}
    
    	var hostname string
    	var scanner *bufio.Scanner
    
    	if len(*optHost) != 0 { // Если hostname указан ключем, берем информацию оттуда
    		hostname = *optHost
    	} else { // Иначе получаем из системы
    		hostname, _ = os.Hostname()
    	}
    	// Логи наша программа может брать либо из stdin будучи запущеной в pipeline “anyscript.sh | go-logger -d 127.0.0.1:514”, либо из файла. Если указан параметр, то берем из файла
    	if len(*optReadFromFile) != 0 {
    		file, err := os.Open(*optReadFromFile) //Открываем файл
    		if err != nil {
    			log.Fatal(err)
    		}
    		defer file.Close() //Запланируем закрытие файла по окончании
    		scanner = bufio.NewScanner(file) //Читаем файл 
    	} else {
    		scanner = bufio.NewScanner(os.Stdin) //Читаем stdin
    	}
    
    	msg := message{Hostname: hostname, Programm: *optProg} //Вносим данные в структуру
    	//TLS-часть отправки наших логов
    	caCert, _ := ioutil.ReadFile(*optCa) //Подгружаем CA сервера из файла
    	caCertPool := x509.NewCertPool()
    	caCertPool.AppendCertsFromPEM(caCert)
    
    	cert, err := tls.LoadX509KeyPair(*optCert, *optKey) //Подгружаем сертификат и закрытый ключ клиента из файлов
    	if err != nil {
    		log.Fatal(err)
    	}
    	tlsConf := &tls.Config{ //Создаем конфигурацию TLS
    		RootCAs:      caCertPool,
    		Certificates: []tls.Certificate{cert},
    	}
    
    	logwriter, err := tls.Dial("tcp", *optSyslogSrv, tlsConf) // Устанавливаем TLS-соединение
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer logwriter.Close() //Запланируем закрытие соединения по окончании
    
    	for scanner.Scan() { //Обрабатываем каждое полученное сканером сообщение
    		sendMsg := message{
    			Time:     time.Now().Format("2006-01-02T15:04:05.00-07:00"), //Время в нужном нам формате
    			Body:     scanner.Text(),                                    //Сообщение
    			Hostname: msg.Hostname,
    			Programm: msg.Programm,
    		}
    		data, err := json.Marshal(sendMsg) //Маршалим наш json
    		if err != nil {
    			log.Fatal(err)
    		}
    		if *optVerb { //Если задан параметр -v, то печатаем отправляемое сообщение
    			fmt.Println(string(data))
    		}
    		_, err = logwriter.Write(append(data, "\n"...)) //Отправляем сообщение добавив перенос строки
    		if err != nil {
    			log.Fatal(err)
    		}
    	}
    }

    Пробуем выполнить, передав в программу все те же две строки, и сервер получает наши логи:

    {"timestamp":"2022-12-26T00:19:23.54+03:00","host":"test-go-logger","programm":"go-logger","message":"Hello habr!"}
    {"timestamp":"2022-12-26T00:19:23.54+03:00","host":"test-go-logger","programm":"go-logger","message":"Lets test!"}

    Эта программа — одна из первых, написанных мною на Go. В процессе ее создания я разобрался, как работает протокол syslog, и освоил азы нового для меня языка программирования. Программа позволила унифицировать отправку логов на разных операционных системах, независимо от семейства, в тех местах, где нет возможности пользоваться syslog-ng. В настоящее время мы адаптируем созданную программу для дальнейшего применения в инфраструктуре нашей компании.

    Арендуйте выделенные и виртуальные серверы с моментальным деплоем в надежных дата-центрах класса TIER III в Москве и Нидерландах. Принимаем оплату за услуги HOSTKEY в Нидерландах в рублях на счет российской компании. Оплата с помощью банковских карт, в том числе и картой МИР, банковского перевода и электронных денег.

    Другие статьи

    14.07.2024

    Как смотреть видео на YouTube при замедлении сервиса в России?

    Мы протестировали скорость работы YouTube и качество видео при подключении к серверам сервиса в Европе и США.

    12.07.2024

    Создаем чат-бота техподдержки с RAG из документации компании в OpenWebUI | блог компании HOSTKEY

    Рассказываем о нашем опыте создания чат-бота технической поддержки, который призван помогать отделу фронтлайна отвечать на вопросы пользователей (а в будущем — стать частью нашей команды)

    17.06.2024

    Slack блокируют! Переходим на Rocket.Chat?!

    Есть ли жизнь после блокировки Slack? Рассказываем о плюсах альтернативного корпоративного мессенджера Rocket.Chat.

    06.06.2024

    ТОП-10 трендов хостинга, на которые следует обратить внимание в 2024 году

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

    13.05.2024

    Могут ли LLM-агенты взламывать сайты и эксплуатировать уязвимости?

    Рассказываем про исследование в рамках которого тестировалась способность LLM-агентов взламывать сайты.

    HOSTKEY Выделенные серверы в Европе, России и США Готовые решения и индивидуальные конфигурации серверов на базе процессоров AMD, Intel, карт GPU, Бесплатной защитой от DDoS-атак и безлимитный соединением на скорости 1 Гбит/с 30
    4.3 48 48
    Upload