update help elma
This commit is contained in:
@ -1,20 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Рекомендации по разработке микросервисов для переносимых сервисов</title>
|
||||
<title>Guidelines for developing microservices for portable services</title>
|
||||
<meta name="generator" content="Help+Manual" />
|
||||
<meta name="keywords" content="" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Настройки сервиса Инициализация и работа микросервиса может подразумевать наличие некоторых настроек. Бывают ситуации, когда потребители микросервиса должны уметь..." />
|
||||
<meta name="description" content="Service settings Initialization and operation of a microservice may require certain settings to be available. Sometimes a microservice needs to be adjusted to fit the needs of..." />
|
||||
<meta name="picture" content="" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="Cправка по Low-code платформе ELMA365" />
|
||||
<meta property="og:url" content="https://elma365.com/ru/help" />
|
||||
<meta property="og:image" content="" />
|
||||
<meta property="og:site_name" content="ELMA365" />
|
||||
<meta property="og:title" content="Full documentation for BRIX365 platform. Low-code developer guide. User guide. Admin guide. Developer guide." />
|
||||
<meta property="og:url" content="https://brix365.com/en/help" />
|
||||
<meta property="og:image" content="" />
|
||||
<link rel="icon" href="favicon.png" type="image/png" />
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
@ -24,7 +23,6 @@
|
||||
<link rel="stylesheet" href="./article.css" />
|
||||
<link rel="stylesheet" href="./glossary.css" />
|
||||
<link rel="stylesheet" href="./theme.css" />
|
||||
|
||||
<script type="text/javascript" src="jquery.js"></script>
|
||||
<script type="text/javascript" src="helpman_settings.js"></script>
|
||||
<script type="text/javascript" src="helpman_topicinit.js"></script>
|
||||
@ -36,16 +34,16 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-M6ETBEC1R9"></script><script>window.dataLayer=window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date()); gtag('config', 'G-M6ETBEC1R9');</script>
|
||||
|
||||
<script>!function(e,t,c,n,r,a,m){e.ym=e.ym||function(){(e.ym.a=e.ym.a||[]).push(arguments)},e.ym.l=1*new Date;for(var s=0;s<document.scripts.length;s++)if(document.scripts[s].src===n)return;a=t.createElement(c),m=t.getElementsByTagName(c)[0],a.async=1,a.src=n,m.parentNode.insertBefore(a,m)}(window,document,"script","https://mc.yandex.ru/metrika/tag.js"),ym(83179930,"init",{clickmap:!0,trackLinks:!0,accurateTrackBounce:!0,webvisor:!0})</script><noscript><div><img alt=""src=https://mc.yandex.ru/watch/83179930 style=position:absolute;left:-9999px></div></noscript>
|
||||
|
||||
<header class="header elma-365">
|
||||
<div class="container">
|
||||
<a class="header__logo" href="https://elma365.com/ru/help">
|
||||
<img src="./logo.svg" alt="header logo">
|
||||
<a class="header__logo" href="https://brix365.com/en/help">
|
||||
<img src="./logo-en.svg" alt="header logo">
|
||||
</a>
|
||||
<!-- <div class="hero__search-form" id="search-panel">
|
||||
<form class="search-form" onsubmit="ym(83179930,'reachGoal','poisk')">
|
||||
<form class="search-form" onsubmit="ym(83180416,'reachGoal','poisk')">
|
||||
<label class="search-form__label">
|
||||
<span id="reset-search" class="search__icon"></span>
|
||||
<input class="search-form__input" type="text">
|
||||
@ -65,7 +63,7 @@
|
||||
</div>
|
||||
<div class="header__navi">
|
||||
|
||||
<ul class="header__list"><li><span class="solution-select"><span class="solution-select__selected"></span><svg width="7" height="4" viewBox="0 0 7 4" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1L3.5 3.5L6 1" stroke="white" stroke-linecap="round" stroke-linejoin="round"/></svg><ul class="solution-select__list"><li><a class="project-link" href="https://elma365.com/ru/help/platform/get-trial.html">Платформа</a></li><li><a class="project-link" href="https://elma365.com/ru/help/ecm/ecm-functions.html">ECM</a></li><li><a class="project-link" href="https://elma365.com/ru/help/crm/crm_overview.html">CRM</a></li><li><a class="project-link" href="https://elma365.com/ru/help/service/service-functions.html">Service</a></li><li><a class="project-link" href="https://elma365.com/ru/help/projects/projects-functions.html">Проекты</a></li><li><a class="project-link" href="https://elma365.com/ru/help/business_solutions/-elma365-store.html">Бизнес-решения</a></li></ul></span></li><li><a href="https://api.elma365.com/ru/"target="_blank">API</a></li><li><a href="https://tssdk.elma365.com/"target="_blank">SDK</a></li><li><a href="https://community.elma365.com/" target="_blank">Community</a></li><li><a href="https://elma-academy.com/ru/" target="_blank">Академия</a></li><li><a href="https://elma365.com/ru/" target="_blank">Сайт ELMA365</a></li></ul>
|
||||
<ul class="header__list"><li><span class="solution-select"><span class="solution-select__selected"></span><svg width="7" height="4" viewBox="0 0 7 4" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1L3.5 3.5L6 1" stroke="white" stroke-linecap="round" stroke-linejoin="round"/></svg><ul class="solution-select__list"><li><a class="project-link" href="https://brix365.com/en/help/platform/get-trial.html">Platform</a></li><li><a class="project-link" href="https://brix365.com/en/help/ecm/ecm-functions.html">ECM</a></li><li><a class="project-link" href="https://brix365.com/en/help/crm/crm_overview.html">CRM</a></li><li><a class="project-link" href="https://brix365.com/en/help/service/service-functions.html">Service</a></li><li><a class="project-link" href="https://brix365.com/en/help/projects/projects-functions.html">Projects</a></li><li><a class="project-link" href="https://brix365.com/en/help/business_solutions/-elma365-store.html">Business Solutions</a></li></ul></span></li><li><a href="https://api.brix365.com/en/" target="_blank">API</a></li><li><a href="https://tssdk.brix365.com/" target="_blank">SDK</a></li></ul>
|
||||
|
||||
|
||||
</div>
|
||||
@ -80,8 +78,8 @@
|
||||
|
||||
<aside class="sidebar" id="sidebar">
|
||||
<div class="sidebar__header">
|
||||
<a class="header__logo" href="https://elma365.com/ru/help">
|
||||
<img src="./logo-light.svg">
|
||||
<a class="header__logo" href="https://brix365.com/en/help">
|
||||
<img src="./logo-light-en.svg">
|
||||
</a>
|
||||
<span class="sidebar__close elma-365-close" id="close"></span>
|
||||
</div>
|
||||
@ -94,15 +92,15 @@
|
||||
<div class="article-inner">
|
||||
<div class="content">
|
||||
<header class="article__header">
|
||||
<div class="article__bread" style="display:flex; gap:10px;">
|
||||
<div class="article__bread" style="display:flex; gap:10px;">
|
||||
<span id="subcategory" class="search-res__item-category search-res__item-category_subcategory subcategory article__badge"></span>
|
||||
|
||||
<div class="topic__breadcrumbs">
|
||||
<p><a href="solutions-building.html">Разработка решений на платформе ELMA365</a> > <a href="portable-microservices.html">Переносимые сервисы в модулях</a> / Рекомендации по разработке микросервисов для переносимых сервисов</p>
|
||||
<p><a href="solutions-building.html">Build solutions in BRIX</a> > <a href="portable-microservices.html">Portable services in modules</a> / Guidelines for developing microservices for portable services</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="topic__title"><h1 class="p_Heading1"><span class="f_Heading1">Рекомендации по разработке микросервисов для переносимых сервисов</span></h1>
|
||||
<div class="topic__title"><h1 class="p_Heading1"><span class="f_Heading1">Guidelines for developing microservices for portable services</span></h1>
|
||||
</div>
|
||||
|
||||
</header>
|
||||
@ -111,93 +109,93 @@
|
||||
<a href="#h1-article" class="scroll-top"></a>
|
||||
</div>
|
||||
<!-- Placeholder for topic body. -->
|
||||
<h2 class="p_Heading2"><a id="service-settings" class="hmanchor"></a><span class="f_Heading2">Настройки сервиса</span></h2>
|
||||
<p class="p_Normal">Инициализация и работа микросервиса может подразумевать наличие некоторых настроек. Бывают ситуации, когда потребители микросервиса должны уметь сконфигурировать его для себя. Например, задать строку подключения к какому‑то источнику данных. В этом случае следует добавить возможность получения настроек. У разработчика модуля имеется возможность передавать значения переменных окружения в поднимаемый контейнер. За счёт получения данных из них можно реализовать конфигурирование своего микросервиса.</p>
|
||||
<p class="p_Normal">В зависимости от выбранного для разработки фреймфворка это может быть либо прямое считывание значений из переменных окружения, либо конфигурирование через специальный провайдер, как например в <span style="font-weight: bold;">.NET</span>.</p>
|
||||
<p class="p_Normal">При таком подходе разработчик модуля сможет подать свои данные для микросервиса или даже предоставить эту возможность конечному пользователю.</p>
|
||||
<p class="p_Normal">Следует учитывать, что переменные среды задаются при инициализации контейнера, и изменения, вносимые конечным потребителем модуля на лету, не будут применяться.</p>
|
||||
<h2 class="p_Heading2"><a id="provided-api" class="hmanchor"></a><span class="f_Heading2">Предоставляемый API</span></h2>
|
||||
<p class="p_Normal">C микросервисом можно взаимодействовать путём отправки HTTP-запросов из скриптов, например, в процессах или виджетах. Чтобы эта возможность стала доступна, на стороне микросервиса нужно проработать и реализовать web API. Об оптимальных подходах и инструментах создания web API читайте в статьях: </p>
|
||||
<h2 class="p_Heading2"><span class="f_Heading2">Service settings</span></h2>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Initialization and operation of a microservice may require certain settings to be available. Sometimes a microservice needs to be adjusted to fit the needs of the consumer. For example, it may need to be possible to set a connection string for a data source. For this purpose, the developer of the microservice should add an opportunity to retrieve its settings. The developer of the module can pass the values of environment variables into the container that is to be deployed. Data from them can be used for the configuration of the microservice.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Depending on the framework chosen for development, this can be either direct reading of values from the environment variables or configuring the microservice via a separate provider, for instance as in </span><span style="font-family: Inter; font-weight: bold;">.NET</span><span style="font-family: Inter;">.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">With this approach, the module developer can submit their data to the microservice or even give this opportunity to the end user.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Note that environment variables are set then the container is initialized, and the changes made by the end user in the module when working with it will not be applied.</span></p>
|
||||
<h2 class="p_Heading2"><span class="f_Heading2">Provided API</span></h2>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Module developers can interact with the microservice by sending HTTP requests from scripts, for example, in processes or widgets. To make it possible, a web API has to be developed and implemented for the microservice. Read about the best approaches and tools for creating web APIs in the following articles:</span></p>
|
||||
<ul style="list-style-type:disc">
|
||||
<li class="p_Normal"><a href="https://docs.microsoft.com/ru-ru/azure/architecture/best-practices/api-design" class="weblink">«Проектирование веб-API RESTFUL»</a>;</li><li class="p_Normal"><a href="https://habr.com/ru/post/181988/" class="weblink">«Разработка web API»</a>.</li></ul>
|
||||
<p class="p_Normal">Предоставляемый API следует задокументировать для удобства его использования при дальнейшей работе с модулем.</p>
|
||||
<h3 class="p_Heading3"><a id="communication-between-services" class="hmanchor"></a><span class="f_Heading3">Организация взаимодействия между двумя переносимыми сервисами</span></h3>
|
||||
<p style="line-height: 1.28; margin: 0 0 11px 0;"><span style="font-size: 15px; font-family: Inter;">В поставках ELMA365 On‑Premises и SaaS Enterprise можно настроить взаимодействие между двумя микросервисами в рамках одного модуля, используя переменные окружения.</span></p>
|
||||
<p style="line-height: 1.28; margin: 0 0 11px 0;"><span style="font-size: 15px; font-family: Inter;">Для этого:</span></p>
|
||||
<li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;"><span style="font-family: Inter;"><a href="https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design" target="_blank" class="weblink">RESTful web API design</a>.</span></li><li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;"><span style="font-family: Inter;"><a href="https://pages.apigee.com/rs/apigee/images/api-design-ebook-2012-03.pdf" target="_blank" class="weblink">Web API Design</a>.</span></li></ul>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">The provided API has to be documented in order to make it easier to use in the future.</span></p>
|
||||
<h3 class="p_Heading3"><a id="communication-between-services" class="hmanchor"></a><span class="f_Heading3">Communication between two portable services</span></h3>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">In BRIX On-Premises and SaaS Enterprise editions, you can configure the interaction between two microservices within a single module using environment variables.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">To do this:</span></p>
|
||||
<ol style="list-style-type:upper-roman">
|
||||
<li value="1" style="line-height: 1.28; margin-top: 0; margin-right: 0; margin-bottom: 11px;"><span style="font-size: 15px; font-family: Inter;">Добавьте для сервиса 1 переменную окружения, в которой будет храниться адрес сервиса 2. Для этого откройте модуль, перейдите к редактированию переносимого сервиса и создайте переменную в </span><span style="font-size: 15px; font-family: Inter; color: #0563c1;"><a href="portable-services.html#main-tab" class="topiclink">настройках Docker‑контейнера</a></span><span style="font-size: 15px; font-family: Inter;">.</span></li></ol>
|
||||
<p style="line-height: 1.28; margin: 0 0 11px 34px;"><span style="font-size: 15px; font-family: Inter;">Допустим, для переменной окружения с адресом сервиса 2 можно задать название </span><code><b>serv2url</b></code><span style="font-size: 15px; font-family: Inter;">. Eсли уникальное имя сервиса 2 — </span><code><b>serv2</b></code><span style="font-size: 15px; font-family: Inter;">, то в качестве значения переменной нужно записать </span><code><b>{$_srv_serv2}</b></code><span style="font-size: 15px; font-family: Inter;">.</span></p>
|
||||
<li value="1" style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;">Add an environment variable for service 1 to store the address of service 2. To do this, open the module, go to the editing page of the portable service and create the variable in the <span style="color: #0563c1;"><a href="portable-services.html#main-tab" class="topiclink">Docker container settings</a></span>.</li></ol>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 34px;">Let’s say that an environment variable with the service address 2 can be named <code><b>serv2url</b></code>. If the unique name of service 2 is <code><b>serv2</b></code>, then <code><b>{$_srv_serv2}</b></code> should be written as the value of the variable.</p>
|
||||
<ol style="list-style-type:upper-roman">
|
||||
<li value="2" style="line-height: 1.28; margin-top: 0; margin-right: 0; margin-bottom: 11px;"><span style="font-size: 15px; font-family: Inter;">После этого на стороне переносимого сервиса 1 с помощью переменной можно настроить получение URL‑адреса сервиса 2:</span></li></ol>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">serv2url, exists := os.LookupEnv("serv2Url")</span></p>
|
||||
<ol style="list-style-type:upper-roman">
|
||||
<li value="3" style="line-height: 1.28; margin-top: 0; margin-right: 0; margin-bottom: 11px;"><span style="font-size: 15px; font-family: Inter;">Используя полученный адрес, можно отправить HTTP-запрос:</span></li></ol>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">resp, err := http.Get(serv2url + "/hello")</span></p>
|
||||
<p style="line-height: 1.28; margin: 0 0 11px 0;"><span style="font-size: 15px; font-family: Inter;">Предоставляемую возможность следует задокументировать, чтобы в дальнейшем ей можно было воспользоваться при работе с модулем.</span></p>
|
||||
<h3 class="p_Heading3"><a id="ports" class="hmanchor"></a><span class="f_Heading3">Порты</span></h3>
|
||||
<p class="p_Normal">По умолчанию переносимый сервис настроен на работу с портом 3000. Если порт микросервиса будет отличаться от этого значения, следует указать это в документации модуля.</p>
|
||||
<h3 class="p_Heading3"><a id="microservice-state" class="hmanchor"></a><span class="f_Heading3">Состояние микросервиса</span></h3>
|
||||
<p class="p_Normal">На данный момент переносимые сервисы не поддерживают хранение состояния микросервисов. Это значит, что при остановке микросервиса данные, циркулируемые в нём, будут утеряны.</p>
|
||||
<p class="p_Normal">Поэтому не следует использовать файловую систему для долгосрочного хранения данных микросервиса. </p>
|
||||
<p class="p_Normal"><span style="font-weight: bold;">Пример:</span></p>
|
||||
<p class="p_Normal">Нельзя: принимать на постоянное хранение на диске файлы картинок, чтобы потом доставать их по запросу пользователей.</p>
|
||||
<p class="p_Normal">Можно: временно скидывать на диск файлы картинок, чтобы сразу же обработать и вернуть их пользователю.</p>
|
||||
<h2 class="p_Heading2"><a id="logging" class="hmanchor"></a><span class="f_Heading2">Логирование</span></h2>
|
||||
<p class="p_Normal">При разработке микросервиса учтите, что на данный момент у пользователей не будет возможности получать логи через файловую систему или консоль. Если требуется добавить логи, их нужно возвращать либо через специально разработанный под них API, либо отправлять в какую-либо внешнюю систему, куда у пользователя будет доступ.</p>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">Начало внимание</span></p>
|
||||
<p class="p_Normal">При разработке учитывайте, что микросервис не сохраняет свое состояние, поэтому логи, хранимые в файлах, будут утеряны при выключении сервиса.</p>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">Конец внимание</span></p>
|
||||
<h2 class="p_Heading2"><a id="example-on-go" class="hmanchor"></a><span class="f_Heading2">Пример на Go</span></h2>
|
||||
<p class="p_Normal">Стандартным потоком вывода в Go является объект <code><b>os.Stdout</b></code>, который фактически представляет консоль. В самом простом случае мы можем вывести в этот поток данные, импортировав модуль <span style="font-weight: bold;">log</span>:</p>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">log.Print("Hello log")</span><br />
|
||||
<span class="f_CodeExample">log.Printf("Hello log - %s", "hi yourself")</span></p>
|
||||
<p class="p_Normal">Кроме того, можно использовать специализированные библиотеки логирования, обладающие широкими функциональными возможностями, включая уровни логирования.</p>
|
||||
<p class="p_Normal">Примером такой библиотеки может быть <a href="https://github.com/uber-go/zap" class="weblink">zap</a>. Ниже приведён пример вывода логов, выводимых в формате, соответствующем<span style="font-weight: bold;"> .e365</span>: </p>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">// Инициализация</span><br />
|
||||
<li value="2" style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;">After that, in portable service 1, you can get the URL of service 2 using the variable:</li></ol>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">serv2url, exists := os.LookupEnv("serv2Url")</span></p>
|
||||
<ol style="list-style-type:upper-roman" start="3">
|
||||
<li value="3" style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;"><span style="font-family: Inter;">Using the received address, you can send an HTTP request:</span></li></ol>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">resp, err := http.Get(serv2url + "/hello")</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">This feature should be documented so that it can be used when working with the module.</span></p>
|
||||
<h3 class="p_Heading3"><span class="f_Heading3">Ports</span></h3>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">By default, a portable service is configured to use port 3000. If the microservice uses a different port, this should be specified in the module’s documentation.</span></p>
|
||||
<h3 class="p_Heading3"><span class="f_Heading3">Microservice state</span></h3>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">At the moment, storing the state of microservices is not supported in portable services. This means that when a microservice stops, data from it will be lost.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">That’s why you shouldn’t use the file system to store microservice data permanently.</span></p>
|
||||
<h4 class="p_Heading4"><span class="f_Heading4">Example:</span></h4>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">You cannot: permanently store image files on the disk to access them upon users’ requests.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">You can: temporarily upload image files to the disk to process them instantly and send them back to the user.</span></p>
|
||||
<h2 class="p_Heading2"><span class="f_Heading2">Logs</span></h2>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">When you develop your microservice, note that at the moment users cannot view logs in the file system or console. If you need to add logs, return them either using a separately developed API or send them to an external system that users will have access to.</span></p>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">начало внимание</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Note that the microservice does not save its state, so logs stored in files will be lost when the service is shut down.</span></p>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">конец внимание</span></p>
|
||||
<h2 class="p_Heading2"><span class="f_Heading2">Example with Go</span></h2>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">The standard output for Go is the </span><code><b>os.Stdout</b></code><span style="font-family: Inter; font-weight: bold;"> </span><span style="font-family: Inter;">object that is practically a console. In the simplest case you can display data in this object by importing the </span><code><b>log</b></code><span style="font-family: Inter;"> module:</span></p>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">log.Print("Hello log")</span><br />
|
||||
<span class="f_CodeExample">log.Printf("Hello log - %s", "hi yourself")</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Moreover, you can use specialized logging libraries that have rich functionality, including log levels.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">One example of such libraries is <a href="https://github.com/uber-go/zap" target="_blank" class="weblink">zap</a>. Below you can see an example of logs showed in a format that corresponds to </span><span style="font-family: Inter; font-weight: bold;">.e365</span><span style="font-family: Inter;">:</span></p>
|
||||
<p class="p_CodeExample" style="page-break-inside: avoid;"><span class="f_CodeExample">// Initialization</span><br />
|
||||
<span class="f_CodeExample">{</span><br />
|
||||
<span class="f_CodeExample">enc := zap.NewProductionEncoderConfig()</span><br />
|
||||
<span class="f_CodeExample">enc.TimeKey = "timestamp"</span><br />
|
||||
<span class="f_CodeExample">enc.EncodeTime = zapcore.ISO8601TimeEncoder</span><br />
|
||||
<span class="f_CodeExample">enc := zap.NewProductionEncoderConfig()</span><br />
|
||||
<span class="f_CodeExample">enc.TimeKey = "timestamp"</span><br />
|
||||
<span class="f_CodeExample">enc.EncodeTime = zapcore.ISO8601TimeEncoder</span><br />
|
||||
<span class="f_CodeExample"> </span><br />
|
||||
<span class="f_CodeExample">opts := []zap.Option{</span><br />
|
||||
<span class="f_CodeExample">opts := []zap.Option{</span><br />
|
||||
<span class="f_CodeExample">zap.AddCaller(),</span><br />
|
||||
<span class="f_CodeExample">}</span><br />
|
||||
<span class="f_CodeExample"> </span><br />
|
||||
<span class="f_CodeExample">logger := zap.New(zapcore.NewCore(</span><br />
|
||||
<span class="f_CodeExample">logger := zap.New(zapcore.NewCore(</span><br />
|
||||
<span class="f_CodeExample">zapcore.NewJSONEncoder(enc),</span><br />
|
||||
<span class="f_CodeExample">zapcore.Lock(os.Stderr),</span><br />
|
||||
<span class="f_CodeExample">zapcore.InfoLevel, // Требуемый уровень логирования</span><br />
|
||||
<span class="f_CodeExample">), opts...)</span><br />
|
||||
<span class="f_CodeExample">logger = l.Named("MyService1")</span><br />
|
||||
<span class="f_CodeExample">zapcore.InfoLevel, // Required log level</span><br />
|
||||
<span class="f_CodeExample">), opts...)</span><br />
|
||||
<span class="f_CodeExample">logger = l.Named("MyService1")</span><br />
|
||||
<span class="f_CodeExample"> </span><br />
|
||||
<span class="f_CodeExample">zap.ReplaceGlobals(logger)</span><br />
|
||||
<span class="f_CodeExample">}</span><br />
|
||||
<span class="f_CodeExample">// Получение логера</span><br />
|
||||
<span class="f_CodeExample">// Getting logger</span><br />
|
||||
<span class="f_CodeExample">{</span><br />
|
||||
<span class="f_CodeExample">l = zap.L()</span><br />
|
||||
<span class="f_CodeExample">l = zap.L()</span><br />
|
||||
<span class="f_CodeExample">}</span><br />
|
||||
<span class="f_CodeExample">// Вывод лога</span><br />
|
||||
<span class="f_CodeExample">// Showing log</span><br />
|
||||
<span class="f_CodeExample">{</span><br />
|
||||
<span class="f_CodeExample">l.Info("Hello zap-log")</span><br />
|
||||
<span class="f_CodeExample">l.Info("Hello zap-log")</span><br />
|
||||
<span class="f_CodeExample">}</span></p>
|
||||
<h2 class="p_Heading2"><a id="samples" class="hmanchor"></a><span class="f_Heading2">Пробы</span></h2>
|
||||
<p class="p_Normal">При использовании микросервисов бывают ситуации, когда к ним предъявляются повышенные требования к работоспособности, надёжности и актуальности. Для таких случаев Kubernetes предоставляет специальную опцию, которую называют пробами. Разработчик микросервиса проектирует и реализует точки, при обращении к которым можно получить однозначно интерпретируюмую информацию о работоспособности микросервиса. При разворачивании микросервисов к ним будут обращаться пробы, чтобы запросить информацию о текущем состоянии микросервиса.</p>
|
||||
<p class="p_Normal">Переносимые сервисы поддерживают работу Readiness и Liveness проб. Они могут быть использованы по отдельности или одновременно для одного микросервиса. Их использование может обеспечить отсутствие траффика, пока микросервис не готов для этого, и требуется перезапустить его в случае поломки.</p>
|
||||
<p class="p_Normal">Рекомендуется дополнительно ознакомиться с <a href="https://kubernetes.io/ru/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/" class="weblink">Официальной документацией</a> и <a href="https://habr.com/ru/company/flant/blog/470958/" class="weblink">рекомендациями по работе с пробами</a>.</p>
|
||||
<p class="p_Normal">Следует максимально подробно и чётко описывать в документации способы обращения к пробам. Неверное описание или использование описания разработчиком модуля приведёт к неработоспособности микросервиса или даже всего модуля. Также крайне важно корректно реализовать точки входа проб на стороне микросервиса.</p>
|
||||
<p class="p_Normal">Если вы не предусмотрели наличие пробы, сообщите об этом в документации. Самостоятельный подбор проб разработчиком модуля крайне нежелателен.</p>
|
||||
<h3 class="p_Heading3"><a id="liveness" class="hmanchor"></a><span class="f_Heading3">Liveness</span></h3>
|
||||
<p class="p_Normal">При работе микросервиса может возникнуть ситуация, когда он придёт в состояние, в котором будет неспособен полноценно обеспечивать свою функциональность. И решением может послужить перезапуск сервиса. Для выполнения проверки на наличие такой ситуации существуют Liveness-пробы. Они выполняют некоторые действия, результатом которых будет определение статуса работоспособности. </p>
|
||||
<p class="p_Normal">При желании можно использовать HTTP-запрос, открытие TCP-соединения или команду в контейнере.</p>
|
||||
<h2 class="p_Heading2"><span class="f_Heading2">Probes</span></h2>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Sometimes higher standards of availability, reliability, and innovation are demanded from microservices. For these cases Kubernetes provides a special feature called probes. A microservice developer designs and implements endpoints that can be accessed to get accurate information about a microservice’s health. When the microservices are deployed, the probes request information about their current state.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Portable services support Readiness and Liveness probes. They can be used separately or at the same time for one microservice. This probes ensure the microservice receives traffic only when it’s ready to accept it. In case of a failure, the microservice is restarted.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">We also recommend you to read the <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/" target="_blank" class="weblink">official documentation</a> and <a href="https://srcco.de/posts/kubernetes-liveness-probes-are-dangerous.html" target="_blank" class="weblink">guidelines to working with probes</a>.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">You should describe the possible ways to perform checks with probes in the documentation clearly and in detail. If the description is wrong or the module developer uses it in a wrong way, the microservice or even the whole module will become inoperable. It is also critical to implement proper endpoints for the probes in the microservice.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">If you didn’t account for probes, specify this in the documentation. It is highly undesirable for the module developer to assign probes on their own.</span></p>
|
||||
<h3 class="p_Heading3"><span class="f_Heading3">Liveness probe</span></h3>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">During the operation of a microservice, it can fall into a state that prevents it from functioning properly. The solution can be restarting the service. To check whether the microservice is in such a state, Liveness probes are used. They perform certain actions to confirm that your microservice is working as intended.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">You can use an HTTP request, open a TCP connection, or run a command in your container.</span></p>
|
||||
<ul style="list-style-type:disc">
|
||||
<li class="p_Normal">для проведения HTTP-пробы Kubernetes отправляет HTTP GET-запрос микросервису. Если обработчик пути микросервиса, указанного в настройке пробы переносимого сервиса, возвращает код успеха, то микросервис рассматривается как живой. Если обработчик возвращает код ошибки, микросервис перезапускается. Любой код, больший или равный 200 и меньший 400, означает успех. Любой другой код интерпретируется как ошибка;</li><li class="p_Normal">для проведения TCP-пробы Kubernetes использует TCP-сокет. По конфигурации, указанной в настройке пробы переносимого сервиса, проводится попытка открыть сокет. Если удаётся установить соединение, контейнер будет считаться живым. Если нет, то микросервис перезапускается;</li><li class="p_Normal">для проведения пробы-команды в контейнере микросервиса исполняется команда, указанная в настройке пробы переносимого сервиса. Если команда выполнена успешна, она возвращает 0, и микросервис считается живым. Если команда возвращает ненулевое значение, то микросервис перезапускается.</li></ul>
|
||||
<h3 class="p_Heading3"><a id="readiness" class="hmanchor"></a><span class="f_Heading3">Readiness</span></h3>
|
||||
<p class="p_Normal">Есть ситуации, когда микросервис не готов обслуживать запросы. Например, пока проводится предварительная инициализация при старте или есть некоторая зависимость от других внешних микросервисов. В таких случаях нужно дождаться перехода в готовое состояние. Однако в это же время микросервис не должен обслуживать обращения к нему. Для обработки таких ситуаций можно использовать Readiness-пробы. Это позволяет микросервису сообщить, что он в данный момент не готов, и запрос будет отправлен на другой экземпляр микросервиса, если он имеется.</p>
|
||||
<p class="p_Normal">Настройки и поведения Readiness-проб аналогичны Liveness-пробам.</p>
|
||||
<h2 class="p_Heading2"><a id="documenting" class="hmanchor"></a><span class="f_Heading2">Документация для микросервиса</span></h2>
|
||||
<p class="p_Normal">При разработке микросервиса следует задокументировать:</p>
|
||||
<li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 7px;"><span style="font-family: Inter;">To perform an HTTP probe, Kubernetes sends an HTTP GET request to the microservice. If the handler for the microservice’s path returns a success code, Kubernetes considers the microservice to be alive and healthy. If the handler returns a failure code, the microservice is restarted. Any code greater than or equal to 200 and less than 400 indicates success. Any other code indicates a failure.</span></li><li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 7px;"><span style="font-family: Inter;">A TCP probe uses a TCP socket. With the configuration of the portable service probe, Kubernetes attempts to open a socket. If it can establish a connection, the container is considered healthy. If it can’t, it is restarted.</span></li><li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 7px;"><span style="font-family: Inter;">In a command-based probe, a command specified in the portable service probe settings is performed. If the command runs successfully, it returns 0, and the microservice is considered healthy. If it returns a value other than 0, the microservice is restarted. </span></li></ul>
|
||||
<h3 class="p_Heading3"><span class="f_Heading3">Readiness probe</span></h3>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Sometimes a microservice may not be ready to accept requests. For example, while preparatory initialization is performed at the startup or there is a dependency on other external microservices. In this cases, Kubernetes needs to wait until the microservice becomes ready. During this time, the microservice shouldn’t process requests sent to it. To handle situations like these, you can use Readiness probes. This allows the microservice to signal that it is not ready at the moment, and the request should be sent to another instance if there is one.</span></p>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">Readiness probes are configured in the same way as Liveness probes.</span></p>
|
||||
<h2 class="p_Heading2"><span class="f_Heading2">Microservice documentation</span></h2>
|
||||
<p style="line-height: 1.20; margin: 7px 0 16px 0;"><span style="font-family: Inter;">While developing a microservice, describe the following in the documentation:</span></p>
|
||||
<ul style="list-style-type:disc">
|
||||
<li class="p_Normal">предоставляемый API;</li><li class="p_Normal">порт, через который следует обращаться к микросервису;</li><li class="p_Normal">список переменных окружения, через которые можно конфигурировать микросервис;</li><li class="p_Normal">наличие и атрибуты Liveness и Readiness;</li><li class="p_Normal"><a href="portable-services.html#add-portable-microservices" class="topiclink">уникальное имя переносимого сервиса</a> для настройки развёртывания микросервиса с помощью <a href="configmap.html" class="topiclink">ConfigMap</a>.</li></ul>
|
||||
<li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;"><span style="font-family: Inter;">Provided API.</span></li><li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;"><span style="font-family: Inter;">The port that is used to access the microservice.</span></li><li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;"><span style="font-family: Inter;">The list of environment variables that the microservice can be configured with.</span></li><li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;"><span style="font-family: Inter;">Whether Liveness and Readiness probes are set up and how to use them.</span></li><li style="line-height: 1.20; margin-top: 7px; margin-right: 0; margin-bottom: 16px;"><span style="font-family: Inter;"><a href="portable-services.html#add-service" class="topiclink">The unique name of the portable service</a> to configure the microservice deployment using <a href="configmap.html" class="topiclink">ConfigMap</a>.</span></li></ul>
|
||||
|
||||
<div class="bottom-nav">
|
||||
|
||||
@ -214,7 +212,7 @@
|
||||
|
||||
</div>
|
||||
<!-- добавляет на страницу строку блок Была ли статья полезной? -->
|
||||
<div class="feedback-wrap"><div class="feedback" id="feedback"><span><b>Была ли статья полезной?</b></span><form action="" method="POST" class="feedback-form" id="feedback-form"><div class="feedback__popup feedback__popup-response" id="feedback__popup_thx">Спасибо за ваш отзыв!</div><div id="feedback-success-popup"><div class="wrap"><button type="button" class="feedback-popup-close">×</button><svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_212_2187)"><path d="M22 0.6875C10.2294 0.6875 0.6875 10.2294 0.6875 22C0.6875 33.7706 10.2294 43.3125 22 43.3125C33.7706 43.3125 43.3125 33.7706 43.3125 22C43.3125 10.2294 33.7706 0.6875 22 0.6875ZM22 40.5625C11.8023 40.5625 3.4375 32.3078 3.4375 22C3.4375 11.8024 11.6922 3.4375 22 3.4375C32.1977 3.4375 40.5625 11.6922 40.5625 22C40.5625 32.1976 32.3078 40.5625 22 40.5625ZM34.1713 16.933L18.6613 32.3186C18.257 32.7197 17.604 32.7171 17.203 32.3128L9.82283 24.873C9.42176 24.4686 9.42434 23.8157 9.82867 23.4146L10.5609 22.6884C10.9652 22.2873 11.6181 22.2899 12.0192 22.6942L17.9468 28.6697L31.9926 14.7366C32.3969 14.3356 33.0498 14.3382 33.4509 14.7425L34.1772 15.4747C34.5783 15.879 34.5757 16.532 34.1713 16.933Z" fill="#27AE60"></path></g><defs><clipPath id="clip0_212_2187"><rect width="44" height="44" fill="white"></rect></clipPath></defs></svg><p>Ваш отзыв успешно отправлен!</p><span>Спасибо за обратную связь.</span></div></div><div class="feedback__popup" id="feedback__popup_why"><button type="button" class="feedback-popup-close">×</button><div class="feedback__popup-header">Уточните, почему:</div><input type="radio" name="category" id="bad_recommendation" value="bad_recommendation"><label for="bad_recommendation">Рекомендации не помогли</label><input type="radio" name="category" id="difficult_text" value="difficult_text"><label for="difficult_text">Текст трудно понять</label><input type="radio" name="category" id="no_answer" value="no_answer"><label for="no_answer">Нет ответа на мой вопрос</label><input type="radio" name="category" id="bad_header" value="bad_header"><label for="bad_header">Содержание статьи не соответствует заголовку</label><input type="radio" name="category" id="other_reason" value="other_reason"><label for="other_reason">Другая причина</label></div><div class="feedback__popup" id="feedback__popup-other"><button type="button" class="feedback-popup-close">×</button> <div class="feedback__popup-header">Расскажите, что вам не понравилось в статье:</div><textarea class="feedback__textarea" name="other" id=""></textarea><input type="submit" class="feedback__other-btn" value="Отправить"></div><div class="feedback-form__btn-group"><input type="radio" name="useful" id="feedback__useful_yes" value="true"><label for="feedback__useful_yes"><img src="like.svg"/><span class="feedback-form__btn-group_yes-btn">Да</span></label><input type="radio" name="useful" id="feedback__useful_no" value="false"><label for="feedback__useful_no"><img src="dislike.svg"/><span class="feedback-form__btn-group_no-btn">Нет</span></label></div><select name="category"><option disabled>Выберите вариант</option><option value="bad_recommendation" selected>Рекомендации не помогли</option><option value="difficult_text">Текст трудно понять</option><option value="no_answer">Нет ответа на мой вопрос</option><option value="bad_header">Содержание статьи не соответствует заголовку</option><option value="other_reason">Другая причина</option></select><input type="submit"></form></div></div>
|
||||
<div class="feedback" id="feedback"><div class="feedback-help"><span><b>Was this helpful?</b></span><form action="" method="POST" class="feedback-form" id="feedback-form"><div class="feedback__popup feedback__popup-response" id="feedback__popup_thx" style="display: none;">Thanks for your feedback!</div><div class="feedback__popup" id="feedback__popup_why" style="display: none;"><div class="feedback__popup-header">Please specify why:</div><input type="radio" name="category" id="bad_recommendation" value="bad_recommendation"><label for="bad_recommendation">Recommendations did not help me</label><input type="radio" name="category" id="difficult_text" value="difficult_text"><label for="difficult_text">Article is hard to understand</label><input type="radio" name="category" id="no_answer" value="no_answer"><label for="no_answer">Didn`t answer my question</label><input type="radio" name="category" id="bad_header" value="bad_header"><label for="bad_header">Content does not match the topic</label><input type="radio" name="category" id="other_reason" value="other_reason"><label for="other_reason">Other</label></div><div class="feedback__popup" id="feedback__popup-other" style="display: none;"><div class="feedback__popup-header">How we can improve it?</div><textarea class="feedback__textarea" name="other" id=""></textarea><input type="submit" class="feedback__other-btn" value="Submit"></div><div class="feedback-form__btn-group"><input type="radio" name="useful" id="feedback__useful_yes" value="true"><label for="feedback__useful_yes"><img src="like.svg" class="small-img" alt="like"><spanclass="feedback-form__btn-group_yes-btn">Yes</spanclass="feedback-form__btn-group_yes-btn"></label><input type="radio" name="useful" id="feedback__useful_no" value="false"><label for="feedback__useful_no"><img src="dislike.svg" class="small-img" alt="dislike"><spanclass="feedback-form__btn-group_no-btn">No</spanclass="feedback-form__btn-group_no-btn"></label></div><select name="category"><option disabled="">Please specify why</option><option value="bad_recommendation" selected="">Recommendations did not help me</option><option value="difficult_text">Article is hard to understand</option><option value="no_answer">Didn`t answer my question</option><option value="bad_header">Content does not match the topic</option><option value="other_reason">Other</option></select><input type="submit"></form></div><div class="found_typo"><p style="margin: 0px; margin-top: 16px !important;"><span><b>Found a typo?</b></span> Select it and press <i>Ctrl+Enter</i> to send us feedback</p></div></div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
@ -222,7 +220,7 @@
|
||||
<input type="checkbox" />
|
||||
<div class="article__arrow"></div>
|
||||
<div class="table-of-contents elma365-right" id="toc2Content">
|
||||
<h3 class="h3-toc">В этой статье</h3>
|
||||
<h3 class="h3-toc">In this topic</h3>
|
||||
<nav id="toc2"></nav>
|
||||
</div>
|
||||
</aside>
|
||||
@ -233,63 +231,30 @@
|
||||
<div class="footer-container">
|
||||
<div class="footer-mobile">
|
||||
|
||||
<ul class="footer-mobile__list"><li><a href="https://api.elma365.com/ru/" target="_blank">API</a></li><li><a href="https://tssdk.elma365.com/" target="_blank">TS SDK</a></li><li><a href="https://community.elma365.com/" target="_blank">Community</a></li><li><a href="https://elma-academy.com/ru/elma365" target="_blank">Академия</a></li></ul><ul class="footer-mobile__list"><li><a href="https://elma365.com/ru/help/platform/get-trial.html">Платформа</a></li><li><a href="https://elma365.com/ru/help/ecm/ecm-functions.html">ECM</a></li><li><a href="https://elma365.com/ru/help/service/service-functions.html">Service</a></li><li><a href="https://elma365.com/ru/help/projects/projects-functions.html">Проекты</a></li></ul>
|
||||
<ul class="footer-mobile__list"><li><a href="https://brix365.com/en/" target="_blank">BRIX</a></li><li><a href="https://tssdk.brix365.com/en/latest/" target="_blank">SDK</a></li><li><a href="https://api.brix365.com/en/" target="_blank">API</a></li></ul><ul class="footer-mobile__list"><li><a href="https://brix365.com/en/help/platform/get-trial.html">Platform</a></li><li><a href="https://brix365.com/en/help/ecm/ecm-functions.html">ECM</a></li><li><a href="https://brix365.com/en/help/service/service-functions.html">Service</a></li><li><a href="https://brix365.com/en/help/projects/projects-functions.html">Projects</a></li></ul>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="footer-wrap">
|
||||
|
||||
<div><span class="mobile-question-popup">Отправить фидбэк</span><form method="POST" action class="question__popup question-xs" id="question__popup"><div class="question-wrap"><span class="close"></span><span class="title">Задать вопрос</span><label for="help_question" style="display: none;"></label><textarea name="help_question" id="help_question"></textarea><input type="submit" value="Отправить"></div></form><div class="hidden fade-in question-success-xs">Ваш фидбэк отправлен.</div></div>
|
||||
|
||||
<div class="footer-flex-b">
|
||||
<div class="footer-top">
|
||||
<span class="footer-copy">© 2025
|
||||
ELMA365
|
||||
|
||||
|
||||
</span>
|
||||
|
||||
<a href="https://navigator.sk.ru/orn/1122971" target="_blank">
|
||||
<img src="sk-resident.svg" alt="sk icon" class="footer-img" width="117" height="34">
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div class="footer-line">
|
||||
|
||||
<div class="footer-line-copy">
|
||||
<span class="footer-copy">© 2025
|
||||
ELMA365
|
||||
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul class="footer-list">
|
||||
|
||||
<li class="footer-item"><a href="https://elma365.com/ru/" target="_blank" class="footer-link" style="color: #0D4A75;"><img src="browse.svg" alt="browse icon" class="footer-img">elma365.com</a></li><li class="footer-item"><a href="https://www.youtube.com/user/ELMABPM" target="_blank" class="footer-link"><img src="yt.svg" alt="youtube icon" class="footer-img"></a></li><li class="footer-item"><a href="https://vk.com/elma_bpm" target="_blank" class="footer-link"><img src="vk.svg" alt="vk icon" class="footer-img"></a></li><li class="footer-item"><a href="https://t.me/elmaday" target="_blank" class="footer-link"><img src="tg.svg" alt="telegram icon" class="footer-img"></a></li><li class="footer-item"><a href="https://dzen.ru/elma" target="_blank" class="footer-link"><img src="dzen.svg" alt="dzen icon" class="footer-img"></a></li>
|
||||
|
||||
|
||||
<li class="footer-item">
|
||||
<a href="https://navigator.sk.ru/orn/1122971" target="_blank">
|
||||
<img src="sk-resident.svg" alt="sk icon" class="footer-img" width="117" height="34">
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-wrap">
|
||||
|
||||
<div><span class="mobile-question-popup">Send feedback</span><form method="POST" action class="question__popup question-xs" id="question__popup"><div class="question-wrap"><span class="close"></span><span class="title">Ask a question</span><label for="help_question" style="display: none;"></label><textarea name="help_question" id="help_question"></textarea><input type="submit" value="Send"></div></form><div class="hidden fade-in question-success-xs">Sent</div></div>
|
||||
|
||||
<div class="footer-flex-b">
|
||||
<span class="footer-copy">© 2025 BRIX</span>
|
||||
<ul class="footer-list">
|
||||
|
||||
<li class="footer-item">
|
||||
<a href="#" class="arrow-top" style="display: block;"></a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="arrow-top"></a>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
<!-- <script type="text/javascript" src="jquery1.min.js"></script>-->
|
||||
<iframe name="hmnavigation" style="display:none!important"></iframe>
|
||||
<!--<script src="./jquery-ui.js"></script> -->
|
||||
<script src="./jquery-ui.min.js"></script>
|
||||
<script src="./jquery-ui.js"></script>
|
||||
<!--script src="//cdn.jsdelivr.net/npm/featherlight@1.7.14/release/featherlight.min.js" type="text/javascript" charset="utf-8"></script-->
|
||||
<script src="./jquery.tocify.min.js"></script>
|
||||
<script src="./TypoReporter.min.js"></script>
|
||||
|
Reference in New Issue
Block a user