Что понадобится: требования и выбор площадки
В обзорной статье серии я рассказывал, почему мы в ITfresh перевели учёт техники клиентов с Excel на Snipe-IT. Сегодня — практика: наш внутренний регламент развёртывания, по которому инженер поднимает систему с нуля за один рабочий день. Не «за 15 минут», как обещают ролики на YouTube, — за день, но зато с HTTPS, русским интерфейсом, заполненными справочниками, наклейками и первым подписанным актом выдачи. Разница примерно как между «машина завелась» и «машина поставлена на учёт и застрахована».
Начнём с железа, и здесь хорошая новость: Snipe-IT — одно из самых нетребовательных приложений в нашем стеке. Это PHP-приложение на Laravel, которое бо́льшую часть времени просто ждёт запросов. Для компании до 50 рабочих мест — а это сотни две-три активов с лицензиями и расходниками — за глаза хватает виртуалки с 2 vCPU и 2–4 ГБ RAM. Диска считайте 20–30 ГБ: сама система с базой занимает копейки, место со временем съедают фотографии активов и сканы документов, которые сотрудники прикладывают к карточкам. У нас есть инсталляции, которые третий год живут на минимальном тарифе VPS и ни разу не упирались в ресурсы.
Где размещать — вопрос политики, а не техники. Мы предлагаем клиентам два варианта. Первый — виртуалка на наших серверах в дата-центре МТС: мы отвечаем за гипервизор, бэкапы и мониторинг, клиент получает готовый сервис. Второй — VPS или виртуалка самого клиента, если у него есть принципиальная позиция «все данные у нас». Snipe-IT одинаково хорошо живёт в обоих сценариях; важно лишь, чтобы площадка была под чьим-то присмотром, потому что реестр всей техники компании — не тот сервис, который бросают без бэкапов.
До начала работ готовим три вещи, из-за которых чаще всего теряется время посреди внедрения:
- DNS-запись. Заранее заводим A-запись вида
assets.company.ruна IP будущего сервера. Пока TTL истекает и запись разъезжается по резолверам, мы спокойно ставим Docker — к моменту выпуска сертификата всё уже работает. - Почтовый ящик для уведомлений. Snipe-IT шлёт письма при выдаче техники, о истекающих гарантиях и лицензиях. Нужен SMTP-доступ: отдельный ящик вида
assets@company.ruна корпоративной почте. Просить у клиента пароль от общего ящика «в последний момент» — верный способ потерять полдня. - ОС на сервере. Мы ставим Ubuntu 24.04 LTS или Debian 12 — без разницы, Docker уравнивает всё. Из пакетов нужны только
dockerс плагиномcompose,nginxиcertbot.
snipe/snipe-it, и это единственный образ, который поддерживают сами разработчики. Сторонние сборки с Docker Hub мы в боевых внедрениях не используем принципиально.Docker Compose: разбираем стек по контейнерам
Наш типовой стек — три контейнера: само приложение, MariaDB и Redis. База — понятно зачем; Redis держит кэш и сессии, чтобы не гонять их через файлы. Вот docker-compose.yml, с которого мы начинаем каждое внедрение (лежит в /opt/snipeit вместе с .env):
services:
snipeit:
image: snipe/snipe-it:v8.6.3
restart: unless-stopped
ports:
- "127.0.0.1:8000:80"
env_file: .env
volumes:
- snipe-data:/var/lib/snipeit
depends_on:
- db
- redis
db:
image: mariadb:11.4
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "смените-меня-тоже"
MYSQL_DATABASE: snipeit
MYSQL_USER: snipeit
MYSQL_PASSWORD: "смените-меня"
volumes:
- snipe-db:/var/lib/mysql
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
snipe-data:
snipe-db:
Три решения в этом файле — не случайность, а выводы из наших граблей:
- Версия образа прибита гвоздями (
v8.6.3, а неlatest). Релизы у проекта выходят каждые несколько недель, и «плавающий» тег означает, что случайныйdocker compose pullпосреди рабочего дня обновит вам систему без спроса. Обновляемся мы только осознанно — об этом будет отдельная статья про эксплуатацию. - Порт слушает только 127.0.0.1. Наружу смотрит nginx с TLS, а голый HTTP приложения из интернета недоступен. Забытая публикация порта на 0.0.0.0 — классика, из-за которой «внутренние» панели находятся поисковиками Shodan.
- Именованные тома вместо bind-mount. В
snipe-dataживут загруженные файлы и ключи, вsnipe-db— база. Это ровно то, что попадает в бэкап.
Теперь .env — файл, в котором сосредоточены все настройки приложения. Полный шаблон есть в документации, я покажу критичные строки:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://assets.company.ru
APP_TIMEZONE=Europe/Moscow
APP_KEY=base64:... # см. ниже
MYSQL_PORT_3306_TCP_ADDR=db
MYSQL_PORT_3306_TCP_PORT=3306
MYSQL_DATABASE=snipeit
MYSQL_USER=snipeit
MYSQL_PASSWORD=смените-меня
REDIS_HOST=redis
REDIS_PORT=6379
CACHE_DRIVER=redis
SESSION_DRIVER=redis
Про APP_KEY скажу отдельно и громко, потому что это грабля номер один всех самостоятельных внедрений. Этим ключом Laravel шифрует чувствительные данные в базе — в том числе значения зашифрованных кастомных полей и остатки сессий. Генерируется он один раз, перед первым запуском:
docker compose run --rm snipeit php artisan key:generate --show
Команда печатает строку вида base64:xxxx... — её вписываем в .env и тут же кладём копию в парольный менеджер. У нас это Vaultwarden, запись заводится в момент генерации, а не «потом». Потеряете ключ — база останется при вас, но всё зашифрованное в ней превратится в тыкву, и никакой бэкап не спасёт: бэкап без APP_KEY для шифрованных полей бесполезен.
.env нужен именно docker compose up -d (пересоздание), а не docker compose restart. Рестарт перечитывать файл не будет — вы поменяли пароль SMTP, перезапустили контейнер, а письма всё равно не идут, и полчаса жизни уходят на «почему».Запускаем: docker compose up -d. Через минуту-полторы (первый старт накатывает миграции базы) по адресу http://127.0.0.1:8000 на сервере уже отвечает приложение. Наружу его пока не видно — и это правильно, сначала TLS.
TLS и reverse-proxy: nginx впереди контейнера
Вариант «опубликовать порт 8000 наружу и жить так» мы не рассматриваем: реестр техники со всеми серийниками, локациями и фамилиями — это карта вашей инфраструктуры, и гулять по интернету в открытом виде она не должна. Впереди контейнера встаёт nginx, сертификат — бесплатный Let's Encrypt. Конфиг тривиальный:
server {
server_name assets.company.ru;
listen 80;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 20m;
}
}
Дальше certbot --nginx -d assets.company.ru — и certbot сам дописывает секцию с TLS и редиректом с 80-го порта. Обратите внимание на client_max_body_size: по умолчанию nginx режет загрузки крупнее мегабайта, а сотрудники будут прикладывать к карточкам фотографии техники с телефона по 5–8 МБ.
Чтобы приложение корректно жило за прокси, в .env добавляем две строки и пересоздаём контейнер:
APP_TRUSTED_PROXIES=REMOTE_ADDR
SECURE_COOKIES=true
И главная грабля этого этапа, на которую мы наступили в одном из первых внедрений: APP_URL обязан до буквы совпадать с внешним адресом, включая схему https. Laravel строит все абсолютные ссылки от этой переменной. Если в APP_URL остался http:// или внутренний адрес, симптомы будут странные и разрозненные: ссылки в письмах ведут «не туда», после логина выбрасывает на голый IP, браузер ругается на смешанный контент. Мы тогда минут сорок искали «проблему с nginx», а проблема была в одной букве s.
certbot renew --dry-run и убеждаемся, что продление отработает.На этом инфраструктурная часть закончена: https://assets.company.ru открывается, замочек зелёный. По времени — часа полтора-два от чистого сервера, включая кофе. Дальше начинается настройка самого приложения.
Первый вход и включение русского интерфейса
При первом заходе Snipe-IT встречает страницей Pre-Flight check — самопроверкой окружения: версия PHP, расширения, права на каталоги, доступность базы. В Docker-варианте она проходит зелёной практически всегда, потому что образ собран правильно; если что-то красное — вы что-то поменяли в томах или правах руками. Затем мастер просит создать первого администратора и назвать сайт. Логин админа мы заводим по нашему стандарту именования, пароль генерируем в парольный менеджер сразу — тот же принцип, что с APP_KEY: секрет рождается одновременно с записью в хранилище.
Следующие пять минут — то, ради чего многие наши клиенты вообще выбирали Snipe-IT: русский интерфейс. Включается он в два счёта: Admin → Settings → Localization, в поле языка выбираем «Russian / Русский», сохраняем — и весь интерфейс, включая меню, формы и статусы, переключается на русский. Здесь же ставим формат дат и валюту (рубль в списке есть). Часовой пояс приложение уже взяло из APP_TIMEZONE.
Важная деталь для смешанных команд: язык в Snipe-IT — настройка не только глобальная, но и персональная. Глобальный параметр задаёт язык по умолчанию для всех и для писем-уведомлений, но каждый пользователь может выбрать свой в профиле. У нас есть клиент, где бухгалтерия и офис работают в русском интерфейсе, а два инженера-экспата — в английском, и все довольны. Проверьте после включения: профиль пользователя → Language.
Теперь честно про качество перевода, потому что я обещал не рекламный буклет. Перевод Snipe-IT ведётся сообществом через Crowdin, покрытие русского — высокое: весь основной интерфейс, справочники, формы и письма переведены нормально. Но проект выпускает релизы каждые несколько недель, и строки из самых свежих фич приезжают на английском — переводчики догоняют их через релиз-другой. Плюс местами встречаются шероховатости дословного перевода. На практике это выглядит так: в русском интерфейсе изредка мелькает английская кнопка из новой функции. Рабочему процессу это не мешает совсем, но предупредить клиента стоит заранее — тогда вопрос «а почему тут не по-русски» не превращается в претензию.
d.m.Y, валюта — RUB, часовой пояс — Europe/Moscow, у админа включена двухфакторка (Settings → Security → Two-Factor), демо-данные не загружали (галочку с сидированием примеров при установке мы всегда снимаем — вычищать потом «Тестовый ноутбук #4» из боевой базы неинтересно).Скелет справочников — 80% успеха внедрения
Вот здесь решается, приживётся система или нет. Snipe-IT из коробки — пустой конструктор, и если начать заводить активы «как попало», через месяц внутри будет тот же Excel, только с веб-мордой: три написания одного производителя, категории «Ноутбуки», «Ноуты» и «Laptop» одновременно. Поэтому по нашему регламенту сначала строится скелет справочников — сверху вниз, от структуры компании к моделям техники, — и только потом появляется первый актив.
Companies, Locations, Departments — оргструктура
Начинаем с зеркала оргструктуры клиента. Companies — юрлица: у половины наших клиентов их два-три, и техника «ООО Ромашка» не должна смешиваться с техникой «ИП Иванов» хотя бы ради бухгалтерии. Locations — физические площадки: офис на Щёлковской, склад, «удалёнка» (да, мы заводим виртуальную локацию для раздатки удалённым сотрудникам — иначе их техника повисает в воздухе). Departments — отделы, они потом пригодятся в отчётах «сколько техники висит на продажах». На компанию до 50 человек весь блок занимает минут двадцать.
Categories и Manufacturers — типовой набор
Категории — то место, где самодеятельность плодит хаос, поэтому мы приносим готовый список из 12 категорий, отточенный на десятках внедрений: ноутбуки, системные блоки, мониторы, смартфоны, планшеты, принтеры и МФУ, сетевое оборудование, серверы, ИБП, телевизоры и панели, периферия (категория для аксессуаров), расходники. Этого хватает офису до 50 РМ с запасом; экзотику вроде «кассовые аппараты» добавляем по факту. Производителей заводим только тех, что реально есть у клиента — обычно 8–10: Lenovo, HP, Dell, Apple, Kyocera, MikroTik, D-Link, APC и пара местных. Ключевое правило: справочники пополняет админ, а не каждый пользователь на лету — это настройка прав, и мы её фиксируем сразу.
Status Labels — жизненный цикл техники
Статусы в Snipe-IT делятся на типы: deployable (можно выдавать), pending (в подвешенном состоянии), undeployable и archived. Наш типовой набор — «Готов к выдаче» (deployable), «Выдан» (метка присваивается автоматически при checkout), «В ремонте» (pending), «На диагностике» (pending), «Списан» (archived). Больше пяти-шести статусов заводить не советую: каждый лишний статус — это вопрос «а какой ставить?» у офис-менеджера и бардак в отчётах. Списанную технику, забегая вперёд, никогда не удаляем — только архивный статус: история выдач нужна бухгалтерии дольше, чем живёт сам ноутбук.
Models и Fieldsets — почему сначала модели
Частая ошибка новичков — лететь заводить активы, минуя модели. В Snipe-IT актив обязан ссылаться на модель («ThinkPad E14 Gen 5»), а модель — на категорию и производителя. Именно модель определяет, какие кастомные поля будут у актива, через привязанный Fieldset — набор полей. Наш стандартный fieldset для компьютерной техники: процессор, объём RAM, объём диска, MAC-адрес и — обязательное поле по опыту — инвентарный номер бухгалтерии. Это отдельная сущность, не совпадающая с asset tag системы: у бухгалтерии своя нумерация основных средств, и если её некуда записать, сверка при инвентаризации превращается в ад с распечатками. Одно текстовое поле решает проблему навсегда. Заведёте модели правильно — дальше каждый новый ноутбук добавляется за тридцать секунд: выбрал модель, вбил серийник, готово.
Asset Tags, QR-наклейки и печать
Инвентарная наклейка — это то, что превращает базу данных в работающий учёт. Пока на ноутбуке нет наклейки, связь «физический предмет — запись в системе» живёт в чьей-то голове; с наклейкой любой человек со смартфоном за две секунды открывает карточку актива.
Сначала формат тегов. В Settings → Asset Tags включаем автогенерацию: префикс + автоинкремент с ведущими нулями. Наш стандарт — ITF-0001, ITF-0002 (для клиентов — их аббревиатура). Четыре цифры с запасом хватает на любой офис; главное — включить автоинкремент до заведения первого актива, чтобы не было ручной нумерации «кто во что горазд». Тег — это идентификатор навсегда: он не переиспользуется после списания и не меняется при передаче техники.
Дальше наклейки. В Snipe-IT встроен генератор этикеток (Settings → Labels) с гибкой настройкой: размер листа и самой этикетки, поля, что печатать — QR-код, штрих-код, тег, название, серийник, логотип компании. QR-код кодирует прямую ссылку на карточку актива, поэтому сканируется обычной камерой смартфона без всяких приложений — навёл камеру, тапнул по ссылке, открылась карточка. Это и есть механика будущих инвентаризаций: обход офиса с телефоном вместо распечаток.
Практика печати, выстраданная на внедрениях:
- Шаблон подгоняется под конкретный принтер и конкретные этикетки. Мы чаще всего печатаем на обычном офисном лазернике на листах самоклейки A4 (типовой раскрой 65×25 мм) либо на термопринтере этикеток, если он у клиента есть. Перед боевой печатью — тестовый лист на обычной бумаге, прикладываем к самоклейке на просвет: экономит и этикетки, и нервы.
- Тест сканирования до массовой печати. Печатаем одну наклейку, клеим на ноутбук, сканируем тремя разными смартфонами. Если QR меньше сантиметра или принтер «замыливает» края — код читается через раз, и вся затея девальвируется.
- Материал наклеек имеет значение. Бумажная самоклейка на ноутбуке разъездного менеджера превращается в грязный обмылок за полгода. Для техники, которая ездит, берём полиэстеровые (виниловые) этикетки для лазерной печати — они переживают три года в рюкзаке, не выцветая. Для стационарных мониторов и системников достаточно обычной бумажной.
- Куда клеить — тоже стандарт: на ноутбуках — нижняя крышка у петли (не на съёмный аккумулятор), на мониторах — задняя панель у стойки, на системниках — правый бок сверху. Единое место наклейки экономит секунды тысячу раз в год.
Почта, EULA и первый акт выдачи
Финальный технический блок дня — почта. Без неё Snipe-IT нем: не отправляет сотрудникам уведомления о выдаче и не присылает админу алерты об истекающих гарантиях. Настраивается всё в .env (помним: после правки — docker compose up -d, не restart):
MAIL_MAILER=smtp
MAIL_PORT_587_TCP_ADDR=mail.company.ru
MAIL_PORT_587_TCP_PORT=587
MAIL_ENV_ENCRYPTION=tls
MAIL_ENV_USERNAME=assets@company.ru
MAIL_ENV_PASSWORD=пароль-из-хранилища
MAIL_ENV_FROM_ADDR=assets@company.ru
MAIL_ENV_FROM_NAME="Учёт техники"
Проверяем кнопкой тестового письма в настройках уведомлений — и обязательно смотрим, что письмо пришло во «Входящие», а не в спам. Если корпоративная почта у клиента настроена по-взрослому (SPF/DKIM в порядке, а слать мы ходим через её же SMTP), проблем не бывает.
Теперь моя любимая часть — EULA и подпись при выдаче. В настройках категории (или глобально по умолчанию) заводится текст соглашения в Markdown — по-русски это обычно «Акт приёма-передачи оборудования»: сотрудник получает такую-то технику, обязуется вернуть при увольнении, о поломках сообщает в IT. Для категорий с EULA включаем два флажка: требовать принятия и требовать подпись. Дальше происходит магия, ради которой директора нам это внедрение и заказывают.
Делаем первый боевой checkout: берём реальный ноутбук, который сегодня же завели в систему и оклеили, и выдаём его реальному сотруднику. Сотруднику падает письмо: «вам выдан ноутбук ThinkPad, серийный номер такой-то, подтвердите получение». Он открывает ссылку, читает акт, расписывается пальцем или мышкой прямо в браузере — и подписанный документ с датой, временем и картинкой подписи навсегда прикрепляется к карточке актива. Через год, при увольнении, спор «я ничего не получал» заканчивается за десять секунд: открываем карточку, показываем акт с подписью.
Чек-лист сдачи внедрения и типовые ошибки
Внедрение мы закрываем не «вроде работает», а прогоном по чек-листу из 15 пунктов. Вот он целиком — пользуйтесь:
- HTTPS работает, сертификат валиден,
certbot renew --dry-runпроходит. APP_URLсовпадает с внешним адресом до буквы.APP_KEYсохранён в парольном менеджере (проверить, что запись реально открывается!).- Пароли БД и админа — в парольном менеджере, не в чате и не на стикере.
- Порт приложения слушает только 127.0.0.1, наружу — только nginx.
- Русский язык включён, дата/валюта/часовой пояс — российские.
- 2FA включена у всех учёток с админскими правами.
- Справочники заполнены: компании, локации, отделы, 12 категорий, производители, статусы.
- Модели заведены с fieldset'ом, включая поле «инвентарный номер бухгалтерии».
- Автогенерация asset tag включена, формат согласован с клиентом.
- Шаблон этикеток подогнан, тестовая наклейка сканируется тремя телефонами.
- SMTP работает, тестовое письмо дошло во «Входящие».
- EULA заведена, тестовый checkout с подписью проведён на реальном сотруднике.
- Бэкап включён и хотя бы один архив реально создан и скачан.
- Права ролей настроены: справочники правит только админ, офис-менеджер — только выдачи.
И топ-5 ошибок самостоятельных внедрений — рейтинг составлен по инсталляциям, которые нам приносили «доделать после самих себя»:
| # | Ошибка | Чем оборачивается |
|---|---|---|
| 1 | APP_KEY нигде не сохранён | При переезде или восстановлении из бэкапа зашифрованные поля потеряны безвозвратно |
| 2 | Тег latest в compose | Случайный pull обновляет систему в разгар рабочего дня, иногда — с сюрпризами мажорной версии |
| 3 | Активы заведены до справочников | Три написания каждого производителя, категории-дубли; чистка занимает больше времени, чем внедрение |
| 4 | Панель торчит в интернет по HTTP или с портом 8000 на 0.0.0.0 | Реестр всей техники компании индексируется сканерами; в лучшем случае — неприятный разговор про безопасность |
| 5 | Бэкап «настроим потом» | «Потом» наступает после первого сбоя диска; базу с двумя сотнями активов набивают заново руками |
По времени весь регламент — действительно один рабочий день: утром чистый VPS, к обеду HTTPS и русский интерфейс, после обеда справочники и наклейки, вечером первый подписанный акт. В следующей статье серии займёмся тем, что превращает свежую систему в живую: синхронизация сотрудников из Active Directory, миграция исторического Excel-реестра через CSV-импорт и автоматизации через REST API. А если возиться самим не хочется — мы делаем это внедрение под ключ, со всеми граблями, уже собранными за вас.
Оставить комментарий