[Системное администрирование, DevOps] Системный подход к переменным в Ansible
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
ansible devops codestyle
Hey! Меня зовут Денис Калюжный я работаю инженером в отделе автоматизации
процессов разработки. Каждый день новые сборки приложений раскатываются на сотнях
серверов кампании. И в этой статье я делюсь опытом использования Ansible для
этих целей.
Этот гайд предлагает способ организации переменных в деплое. Рассчитан данный
гайд на тех кто уже использует роли в своих плейбуках и читалBest
Practices, но сталкивается с подобными проблемами:
- Найдя переменную в коде, невозможно сходу понять за что она отвечает;
- Есть несколько ролей, и переменные нужно связать одним значением, но никак
не получается;
- Возникают трудности в объяснении другим, как устроена логика переменных в
ваших плейбуках
С этими проблемами мы столкнулись на проектах в нашей компании, в следствие чего
мы пришли к правилам оформления переменных в наших плейбуках, которые в какой-то
мере решили эти проблемы.
Переменные в ролях
Роль — это отдельный Объект системы деплоя. Как и любой объект системы, он
должен иметь интерфейс взаимодействия с остальной системой. Таким интерфейсом
являются переменные роли.
Возьмём, для примера, роль api, которая устанавливает Java приложение на
сервер. Какие переменные у неё могут быть?
Переменные роли можно разделить на 2 вида по типу:
1. Свойства
a) независимые от среды
б) зависимые от среды
2. Связи
a) слушатели
б) запросы внутри системы
в) запросы в среду
Переменные свойства — это переменные, которые определяют поведение роли.
Переменные запроса — это переменные, значение которых используется для
обозначения внешних, по отношению к роли, ресурсов.
Переменные слушатели — это переменные, значение которых, используется для
формирования переменных запроса.
С другой стороны, 1а, 2а, 2б — это переменные, которые не зависят от среды
(железо, внешние ресурсы и т.д.) и могут быть заполнены дефолтными значениями в
defaults роли. Однако переменные типа 1.б и 2.в заполнить кроме как 'example'
значениями невозможно, так как они будут меняться от стенда к стенду в
зависимости от окружения.
Code style
- Название переменной обязательно должно начинаться с названия роли. Это позволит
в дальнейшем легко разобраться, из какой роли переменная и за что она отвечает.
- При использовании переменных в ролях вы должны обязательно следовать принципу
инкапсуляции и использовать переменные определённые либо в самой роли, либо
в ролях, от которых текущая зависит.
- Старайтесь не использовать словари для переменных. Ansible не позволяет
удобно переопределять отдельные значения в словаре.
Пример плохой переменной:
myrole_user:
login: admin
password: admin
Здесь login — средонезависимая переменная, а password — зависимая. Но
поскольку они объединены в словарь, вам придётся задавать её полностью
всегда. Что очень неудобно. Лучше так:
myrole_user_login: admin
myrole_user_password: admin
Переменные в плейбуках деплоя
При составлении плейбука деплоя (далее плейбук), мы придерживаемся правила, что
он должен размещаться в отдельном репозитории. Так же, как и роли: каждая в своем
git репозитории. Это позволяет осозновать, что роли и плейбук — это разные
независимые объекты системы деплоя, и изменения в одном объекте не должны влиять
на работу другой. Достигается это изменением дефолтных значений переменных.
При составлении плейбука, если обобщить, существует возможность переопределять
дефолтные значения переменных роли в двух местах: в переменных плейбука и в
переменных инвентори.
mydeploy # Каталог деплоя
├── deploy.yml # Плейбук деплоя
├── group_vars # Каталог переменных плейбука
│ ├── all.yml # Файл для переменных связи всей системы
│ └── myapi.yml # Файл переменных свойств группы myapi
└── inventories #
└── prod # Каталог окружения prod
├── prod.ini # Инвентори файл
└── group_vars # Каталог для переменных инвентори
└── myapi #
├── vars.yml # Средозависимые переменные группы myapi
└── vault.yml # Секреты (всегда средозависимы) *
* — Variables and Vaults
Разница в том, что переменные плейбука используются всегда при вызове
плейбуков, расположенных с ним на одном уровне. А значит, эти переменные отлично
подходят для изменения дефолтных значений переменных, не зависящих от среды. И,
наоборот, переменные инвентори будут использоваться только для конкретного
окружения, что идеально для переменных зависящих от среды.
Важно отметить, что приоритет переменных не позволит вам переопределить
переменные сначала в переменных плейбука, а потом отдельно в одном инвентори.
Это означает, что уже на этом этапе нужно определиться является ли переменная
средозависимой или нет и разместить ее в положенном месте.
Например, в одном проекте переменная, отвечающая за включение SSL долго была
средозависимой, поскольку мы не могли включить SSL по независящим от нас
причинам на одном из стендов. После того, как мы устранили эту проблему, она
стала средонезависимой и переместилась в переменные плейбука.
Переменные свойств для групп
Расширим нашу модель на рисунке 1, добавив 2 группы серверов с другим Java
приложением, но с разными настройками.
Представим, как будет выглядить плейбук в этом случае:
- hosts: myapi
roles:
- api
- hosts: bbauth
roles:
- auth
- hosts: ghauth
roles:
- auth
У нас есть три группы в плейбуке, поэтому сразу рекомендуется создать столько же
файлов групп в group_vars переменных инвентори, и переменных плейбука. Один
файл группы в этом случае, является описанием одной компоненты вышего приложения
в плейбуке. Открывая файл группы в переменных плейбука вы сразу видите все
отличия от дефолтного поведения ролей установленных на группу. В переменных
инвентори: отличия поведения группы от стенда к стенду.
Code Style
- Старайтесь вообще не использовать host_vars переменные, поскольку они не
описывают систему, а только частный случай, что в перспективе приведёт к
вопросам: "А почему этот хост отличается от остальных?", ответ на который не
всегда легко найти.
Переменные связи
Однако это то, что касается переменных свойств, но как быть с переменными связи?
Их отличие в том, что они должны иметь одинаковое значение в разных группах.
По началу была идея использовать монструозную конструкцию вида:
hostvars[groups['bbauth'][0]]['auth_bind_port'], но от неё сразу отказались
поскольку она имеет недостатки. Во-первых, громоздкость. Во-вторых, зависимость
от определенного хоста в группе. В-третьих, необходимо перед началом деплоя
собрать факты со всех хостов, если мы не хотим получить ошибку неопределённой
переменной.
В итоге решено было использовать переменные связи.
Переменные связи — это переменные, которые принадлежат плейбуку, и нужны для
связи объектов системы.
Переменные связи заполняются в общих переменных системы group_vars/all/vars и
образуются путём выноса всех переменных слушателей из каждой группы, и
добавлением в начало переменной название группы откуда слушатель был вынесен.
Таким образом обеспечивается однотипность и непересекаемость имён.
Попробуем связать переменные из примера выше:
Представим, что у нас есть переменные, которые друг от друга зависят:
# roles/api/defaults:
# Переменная запроса
api_auth1_address: "http://example.com:80"
api_auth2_address: "http://example2.com:80"
# roles/auth/defaults:
# Переменная слушатель
auth_bind_port: "20000"
Вынесем в общие переменные group_vars/all/vars всех слушателей, и добавим в
название имя группы:
# group_vars/all/vars
bbauth_auth_bind_port: "20000"
ghauth_auth_bind_port: "30000"
# group_vars/bbauth/vars
auth_bind_port: "{{ bbauth_auth_bind_port }}"
# group_vars/ghauth/vars
auth_bind_port: "{{ ghauth_auth_bind_port }}"
# group_vars/myapi/vars
api_auth1_address: "http://{{ bbauth_auth_service_name }}:{{ bbauth_auth_bind_port }}"
api_auth2_address: "http://{{ ghauth_auth_service_name }}:{{ ghauth_auth_bind_port }}"
Теперь, меняя значение коннектора, мы будем уверены, что запрос будет обращаться
туда же, где расположен порт.
Code Style
- Поскольку роли и группы это разные объекты системы, нужно чтобы они имели
разные названия, тогда переменные связи будут точно показывать, что они
принадлежат конкретной группе серверов, а не роли в системе.
Средозависимые файлы
В ролях могут использоваться файлы, которые отличаются от среды к среде.
Примером таких файлов можно назвать SSL-сертификаты. Хранить их в текстовом виде
в переменной не очень удобно. Зато удобно хранить путь до них внутри переменной.
Например, используем переменную api_ssl_key_file: "/path/to/file".
Поскольку очевидно, что сертификат ключа будет меняться от среды к среде, то это
средозависимая переменная, а значит она должна расположиться в файле
group_vars/myapi/vars инвентори переменных, и содержать значение 'для примера'.
Удобнее всего в этом случае положить файл ключа в репозиторий плейбука по пути
files/prod/certs/myapi.key, тогда значение переменной будет:
api_ssl_key_file: "prod/certs/myapi.key". Удобство же заключается в том, что
люди отвечающие за разворачивание системы на конкретном стенде, так же имеют
своё выделенное место в репозитории для хранения своих файлов. В то же время
остаётся возможность указать абсолютный путь до сертификата на сервере, на
случай если сертификаты поставляются другой системой.
Несколько стендов в одной среде
Часто возникает потребность развернуть несколько практически идентичных стендов
в одной среде с минимальными различиями. В этом случае мы делим средозависимые
переменные на те, что не меняются в рамках этой среды и те, что меняются. И
выносим последние непосредственно в сами инвентори файлы. После этой манипуляции
становится возможным создать ещё один инвентори прямо в каталоге окружения.
Он будет переиспользовать инвентори group_vars, а также иметь возможность
переопределить некоторые переменные непосредственно под себя.
Окончательная структура каталогов для проекта деплоя:
mydeploy # Каталог деплоя
├── deploy.yml # Плейбук деплоя
├── files # Каталог для файлов деплоя
│ ├── prod # Католог для средозависимых файлов стенда prod
│ │ └── certs #
│ │ └── myapi.key #
│ └── test1 # Каталог для средозависимых файлов стенда test1
├── group_vars # Каталог переменных плейбука
│ ├── all.yml # Файл для переменных связи всей системы
│ ├── myapi.yml # Файл переменных свойств группы myapi
│ ├── bbauth.yml #
│ └── ghauth.yml #
└── inventories #
├── prod # Каталог окружения prod
│ ├── group_vars # Каталог для переменных инвентори
│ │ ├── myapi #
│ │ │ ├── vars.yml # Средозависимые переменные группы myapi
│ │ │ └── vault.yml # Секреты (всегда средозависимы)
│ │ ├── bbauth #
│ │ │ ├── vars.yml #
│ │ │ └── vault.yml #
│ │ └── ghauth #
│ │ ├── vars.yml #
│ │ └── vault.yml #
│ └── prod.ini # Инвентори стенда prod
└── test # Каталог окружения test
├── group_vars #
│ ├── myapi #
│ │ ├── vars.yml #
│ │ └── vault.yml #
│ ├── bbauth #
│ │ ├── vars.yml #
│ │ └── vault.yml #
│ └── ghauth #
│ ├── vars.yml #
│ └── vault.yml #
├── test1.ini # Инвентори стенда test1 в среде test
└── test2.ini # Инвентори стенда test2 в среде test
Подведение итога
После организации переменных в соответствии со статьёй: каждый файл с
переменными отвечает за определённую задачу. А раз у файла есть определённые
задачи, то стало возможным назначить ответственного за правильность каждого
файла. Например, за правильность заполнения переменных плейбука ответственным
становится разработчик деплоя системы, в то время как за заполнение инвентори
переменных отвечает непосредственно администратор, стенд которого описан в
инвентори.
Роли стали самостоятельной единицей разработки с собственным интерфейсом, что
позволило разработчику роли разрабатывать возможности, а не подстраивать роль
под систему. Особенно эта проблема касалась общих ролей для всех систем в
кампании.
Администраторам систем больше не требуется разбираться в коде деплоя. Все что
от них требуется для успешного деплоя, это заполнить файлы средозависимых
переменных.
Литература
Автор
Калюжный Денис Александрович
===========
Источник:
habr.com
===========
Похожие новости:
- [Open source, Системное администрирование, JavaScript, IT-инфраструктура] Решаем практические задачи в Zabbix с помощью JavaScript
- [Конференции, DevOps, Kubernetes] Приглашаем на DINS DevOps EVENING (online): эксплуатация TICK-стека и автоскейлинг в Kubernetes
- [Системное администрирование, Серверное администрирование, Администрирование баз данных] Зачем нужно держать клетки в зоопарке закрытыми (перевод)
- [Системное администрирование, Серверное администрирование, DevOps, Kubernetes] Требования к разработке приложения в Kubernetes
- [Системное администрирование, IT-инфраструктура, Серверное администрирование, DevOps] С чего начать DevOps?
- [Open source, DevOps, Микросервисы, Kubernetes] Open Service Mesh — новая service mesh для Kubernetes от Microsoft
- [Системное администрирование, Софт, IT-компании] Microsoft Defender начал помечать файл hosts как зловредный, если там блокируется сбор телеметрии Windows 10
- [IT-инфраструктура, Тестирование веб-сервисов, DevOps, Kubernetes] Canary деплой с Jenkins-X, Istio и Flagger (перевод)
- [Программирование, DevOps, Облачные сервисы] Проблемы в процессах непрерывной доставки и развертывании программного продукта
- [Системное администрирование, Управление разработкой, Конференции, DevOps] DevOps с человеческим лицом
Теги для поиска: #_sistemnoe_administrirovanie (Системное администрирование), #_devops, #_nspk (нспк), #_mir_plat.form (мир plat.form), #_ansible, #_devops, #_codestyle, #_blog_kompanii_mir_plat.form_(natsionalnaja_sistema_platezhnyh_kart) (
Блог компании Мир Plat.form (Национальная система платежных карт)
), #_sistemnoe_administrirovanie (
Системное администрирование
), #_devops
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 18:34
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
ansible devops codestyle Hey! Меня зовут Денис Калюжный я работаю инженером в отделе автоматизации процессов разработки. Каждый день новые сборки приложений раскатываются на сотнях серверов кампании. И в этой статье я делюсь опытом использования Ansible для этих целей. Этот гайд предлагает способ организации переменных в деплое. Рассчитан данный гайд на тех кто уже использует роли в своих плейбуках и читалBest Practices, но сталкивается с подобными проблемами:
С этими проблемами мы столкнулись на проектах в нашей компании, в следствие чего мы пришли к правилам оформления переменных в наших плейбуках, которые в какой-то мере решили эти проблемы. Переменные в ролях Роль — это отдельный Объект системы деплоя. Как и любой объект системы, он должен иметь интерфейс взаимодействия с остальной системой. Таким интерфейсом являются переменные роли. Возьмём, для примера, роль api, которая устанавливает Java приложение на сервер. Какие переменные у неё могут быть? Переменные роли можно разделить на 2 вида по типу: 1. Свойства
a) независимые от среды б) зависимые от среды 2. Связи a) слушатели б) запросы внутри системы в) запросы в среду Переменные свойства — это переменные, которые определяют поведение роли. Переменные запроса — это переменные, значение которых используется для обозначения внешних, по отношению к роли, ресурсов. Переменные слушатели — это переменные, значение которых, используется для формирования переменных запроса. С другой стороны, 1а, 2а, 2б — это переменные, которые не зависят от среды (железо, внешние ресурсы и т.д.) и могут быть заполнены дефолтными значениями в defaults роли. Однако переменные типа 1.б и 2.в заполнить кроме как 'example' значениями невозможно, так как они будут меняться от стенда к стенду в зависимости от окружения. Code style
Переменные в плейбуках деплоя При составлении плейбука деплоя (далее плейбук), мы придерживаемся правила, что он должен размещаться в отдельном репозитории. Так же, как и роли: каждая в своем git репозитории. Это позволяет осозновать, что роли и плейбук — это разные независимые объекты системы деплоя, и изменения в одном объекте не должны влиять на работу другой. Достигается это изменением дефолтных значений переменных. При составлении плейбука, если обобщить, существует возможность переопределять дефолтные значения переменных роли в двух местах: в переменных плейбука и в переменных инвентори. mydeploy # Каталог деплоя
├── deploy.yml # Плейбук деплоя ├── group_vars # Каталог переменных плейбука │ ├── all.yml # Файл для переменных связи всей системы │ └── myapi.yml # Файл переменных свойств группы myapi └── inventories # └── prod # Каталог окружения prod ├── prod.ini # Инвентори файл └── group_vars # Каталог для переменных инвентори └── myapi # ├── vars.yml # Средозависимые переменные группы myapi └── vault.yml # Секреты (всегда средозависимы) * * — Variables and Vaults Разница в том, что переменные плейбука используются всегда при вызове плейбуков, расположенных с ним на одном уровне. А значит, эти переменные отлично подходят для изменения дефолтных значений переменных, не зависящих от среды. И, наоборот, переменные инвентори будут использоваться только для конкретного окружения, что идеально для переменных зависящих от среды. Важно отметить, что приоритет переменных не позволит вам переопределить переменные сначала в переменных плейбука, а потом отдельно в одном инвентори. Это означает, что уже на этом этапе нужно определиться является ли переменная средозависимой или нет и разместить ее в положенном месте. Например, в одном проекте переменная, отвечающая за включение SSL долго была средозависимой, поскольку мы не могли включить SSL по независящим от нас причинам на одном из стендов. После того, как мы устранили эту проблему, она стала средонезависимой и переместилась в переменные плейбука. Переменные свойств для групп Расширим нашу модель на рисунке 1, добавив 2 группы серверов с другим Java приложением, но с разными настройками. Представим, как будет выглядить плейбук в этом случае: - hosts: myapi
roles: - api - hosts: bbauth roles: - auth - hosts: ghauth roles: - auth У нас есть три группы в плейбуке, поэтому сразу рекомендуется создать столько же файлов групп в group_vars переменных инвентори, и переменных плейбука. Один файл группы в этом случае, является описанием одной компоненты вышего приложения в плейбуке. Открывая файл группы в переменных плейбука вы сразу видите все отличия от дефолтного поведения ролей установленных на группу. В переменных инвентори: отличия поведения группы от стенда к стенду. Code Style
Переменные связи Однако это то, что касается переменных свойств, но как быть с переменными связи? Их отличие в том, что они должны иметь одинаковое значение в разных группах. По началу была идея использовать монструозную конструкцию вида: hostvars[groups['bbauth'][0]]['auth_bind_port'], но от неё сразу отказались поскольку она имеет недостатки. Во-первых, громоздкость. Во-вторых, зависимость от определенного хоста в группе. В-третьих, необходимо перед началом деплоя собрать факты со всех хостов, если мы не хотим получить ошибку неопределённой переменной. В итоге решено было использовать переменные связи. Переменные связи — это переменные, которые принадлежат плейбуку, и нужны для связи объектов системы. Переменные связи заполняются в общих переменных системы group_vars/all/vars и образуются путём выноса всех переменных слушателей из каждой группы, и добавлением в начало переменной название группы откуда слушатель был вынесен. Таким образом обеспечивается однотипность и непересекаемость имён. Попробуем связать переменные из примера выше: Представим, что у нас есть переменные, которые друг от друга зависят: # roles/api/defaults:
# Переменная запроса api_auth1_address: "http://example.com:80" api_auth2_address: "http://example2.com:80" # roles/auth/defaults: # Переменная слушатель auth_bind_port: "20000" Вынесем в общие переменные group_vars/all/vars всех слушателей, и добавим в название имя группы: # group_vars/all/vars
bbauth_auth_bind_port: "20000" ghauth_auth_bind_port: "30000" # group_vars/bbauth/vars auth_bind_port: "{{ bbauth_auth_bind_port }}" # group_vars/ghauth/vars auth_bind_port: "{{ ghauth_auth_bind_port }}" # group_vars/myapi/vars api_auth1_address: "http://{{ bbauth_auth_service_name }}:{{ bbauth_auth_bind_port }}" api_auth2_address: "http://{{ ghauth_auth_service_name }}:{{ ghauth_auth_bind_port }}" Теперь, меняя значение коннектора, мы будем уверены, что запрос будет обращаться туда же, где расположен порт. Code Style
Средозависимые файлы В ролях могут использоваться файлы, которые отличаются от среды к среде. Примером таких файлов можно назвать SSL-сертификаты. Хранить их в текстовом виде в переменной не очень удобно. Зато удобно хранить путь до них внутри переменной. Например, используем переменную api_ssl_key_file: "/path/to/file". Поскольку очевидно, что сертификат ключа будет меняться от среды к среде, то это средозависимая переменная, а значит она должна расположиться в файле group_vars/myapi/vars инвентори переменных, и содержать значение 'для примера'. Удобнее всего в этом случае положить файл ключа в репозиторий плейбука по пути files/prod/certs/myapi.key, тогда значение переменной будет: api_ssl_key_file: "prod/certs/myapi.key". Удобство же заключается в том, что люди отвечающие за разворачивание системы на конкретном стенде, так же имеют своё выделенное место в репозитории для хранения своих файлов. В то же время остаётся возможность указать абсолютный путь до сертификата на сервере, на случай если сертификаты поставляются другой системой. Несколько стендов в одной среде Часто возникает потребность развернуть несколько практически идентичных стендов в одной среде с минимальными различиями. В этом случае мы делим средозависимые переменные на те, что не меняются в рамках этой среды и те, что меняются. И выносим последние непосредственно в сами инвентори файлы. После этой манипуляции становится возможным создать ещё один инвентори прямо в каталоге окружения. Он будет переиспользовать инвентори group_vars, а также иметь возможность переопределить некоторые переменные непосредственно под себя. Окончательная структура каталогов для проекта деплоя: mydeploy # Каталог деплоя
├── deploy.yml # Плейбук деплоя ├── files # Каталог для файлов деплоя │ ├── prod # Католог для средозависимых файлов стенда prod │ │ └── certs # │ │ └── myapi.key # │ └── test1 # Каталог для средозависимых файлов стенда test1 ├── group_vars # Каталог переменных плейбука │ ├── all.yml # Файл для переменных связи всей системы │ ├── myapi.yml # Файл переменных свойств группы myapi │ ├── bbauth.yml # │ └── ghauth.yml # └── inventories # ├── prod # Каталог окружения prod │ ├── group_vars # Каталог для переменных инвентори │ │ ├── myapi # │ │ │ ├── vars.yml # Средозависимые переменные группы myapi │ │ │ └── vault.yml # Секреты (всегда средозависимы) │ │ ├── bbauth # │ │ │ ├── vars.yml # │ │ │ └── vault.yml # │ │ └── ghauth # │ │ ├── vars.yml # │ │ └── vault.yml # │ └── prod.ini # Инвентори стенда prod └── test # Каталог окружения test ├── group_vars # │ ├── myapi # │ │ ├── vars.yml # │ │ └── vault.yml # │ ├── bbauth # │ │ ├── vars.yml # │ │ └── vault.yml # │ └── ghauth # │ ├── vars.yml # │ └── vault.yml # ├── test1.ini # Инвентори стенда test1 в среде test └── test2.ini # Инвентори стенда test2 в среде test Подведение итога После организации переменных в соответствии со статьёй: каждый файл с переменными отвечает за определённую задачу. А раз у файла есть определённые задачи, то стало возможным назначить ответственного за правильность каждого файла. Например, за правильность заполнения переменных плейбука ответственным становится разработчик деплоя системы, в то время как за заполнение инвентори переменных отвечает непосредственно администратор, стенд которого описан в инвентори. Роли стали самостоятельной единицей разработки с собственным интерфейсом, что позволило разработчику роли разрабатывать возможности, а не подстраивать роль под систему. Особенно эта проблема касалась общих ролей для всех систем в кампании. Администраторам систем больше не требуется разбираться в коде деплоя. Все что от них требуется для успешного деплоя, это заполнить файлы средозависимых переменных. Литература Автор Калюжный Денис Александрович =========== Источник: habr.com =========== Похожие новости:
Блог компании Мир Plat.form (Национальная система платежных карт) ), #_sistemnoe_administrirovanie ( Системное администрирование ), #_devops |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 18:34
Часовой пояс: UTC + 5