Docker в роботі погляд на його використання в Badoo (рік)

Антон Турецький (Badoo
Антон Турецький

Сьогодні я запрошу вас на таку внутрішню кухню Badoo розповім про те, чи потрібен Docker нам. Ви спробуєте зробити висновки для себе, чи потрібен він вам. Цієї інформації на просторах Інтернету, відповідно, немає, тому що вся вона ось така – в нашому тісному вузькому колі.



Протягом доповіді я розповім про саму значиму річ, яка стосується того, з чого треба починати виконання будь-якої задачі. Треба вирішити, навіщо ви її робите, навіщо ви за це беретеся?

Для себе ми на ці питання відповіли, без проблем у нас не було б ніякого впровадження. Якусь частину проблем ми вирішуємо. Я виділив основні з них, я розповім вам про них і про те, як ми з ними впоралися. В кінці я порекламирую нас, які ми чудові, як ми любимо всякі-різні нові велосипеди, як ми їх робимо, дивимося, винаходимо. Я вам їх покажу, про них розповім, ви складете якусь свою думку. Отже, поїхали!



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



Основна наша ключова одиниця – це сервіс. Якщо не працює сервіс, те, навіщо ми всі тут зібралися? У віддаленому поданні сервіс виглядає приблизно таким чином. Це шматок інтелектуальної власності програміста, який пише якусь бізнес-логіку і хоче щось отримати. Там шарами знаходяться якісь мережеві налаштування на машині, купа якихось RPM-пакетів, пов'язані і не пов'язані з сервісом директорії. У загальному і цілому, це є такий клубок, який служба експлуатації отримує до себе, і стікет системи, або як ще здійснюється передача сервісу від розробника до продакшну. Наше основне завдання полягає в тому, що нам треба взяти один великий і потужний сервер і напхати на нього якихось там сервісів.



Все виглядає відмінно. За останні кілька років ситуація з цим ніяк не змінилася. Сервіси ми як викочували у форматі клубка, так і викочуємо.



Проблема, яка у нас виникає – це коли нам потрібно взяти і з якоїсь причини розселити наші сервіси, тому що машина не справляється, нове залізо приїхало, ми хочемо просто взяти його і смигрировать. І перше, що ми отримуємо при міграції, якщо ми не користуємося, наприклад, Docker'ом – це те, що ми сервіс перетягуємо на чисту машину, на чистій новій машині у нас все прекрасно, чудово, директорії підтягнулися, але одна проблема… Хто використовує систему управління конфігураціями типу Chef і Puppet, і хто викочується ними? А хто пише, зворотні маніфести за забиранию всього і вся? У залі – всього три людини. Відповідно, всі, хто цього не пише, я думаю, знають про те, що у вашому сервері в кінцевому підсумку залишаються якісь дірки і шматки спадщини того, що там живе, і в залежності від частоти цих міграцій сервер рано чи пізно росте і перетворюється в якусь смітник. Якщо ми беремо Docker і займаємося тієї ж самої міграцією, то ми як цеглинка вийняли і поклали в інше місце, і нічого старого там не залишилося. Чудово.



Таким чином, перша причина, по якій ми, взагалі, почали дивитися на Docker з боку експлуатації – це завдання по отвязыванию application, по збору його в клубок, від операційної системи.

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



І оскільки ми розміщуємо своє обладнання у виділених дата-центрах, знімаючи якісь кейджі, в певний момент часу ми починаємо думати про те, що обладнання необхідно змінювати. По ряду причин – обладнання виходить більш нове, воно більше видає нам якихось папуг і одиничок у продуктивності, рано чи пізно нам треба міняти.



Відповідно, перша проблема, пов'язана з capacity planning, точніше, не проблема, а навіть завдання, полягає в тому, що нам необхідно максимально раціонально використовувати місце в наших стійках, тому що стійка – це якась віддача від неї, це оренда скриньки, це вартість електроенергії і в першу чергу, це гроші.



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



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



Але якщо ми говоримо про те, що ми сервіси кучкуем на сервері більш потужному, то ми отримуємо наступний пункт, який я позначив «±» – ми приходимо до ситуації, коли, начебто, абстраговано у нас з'являється більше можливостей, більше єдиних точок відмови. Коли у вас один сервіс живе на одній фізичній машині, як би все добре, швидше за все, ми менше втрачаємо, якщо у нас сервер вийде з ладу. В даному випадку, це занадто філософське питання, тому що якщо ми тут говоримо про заміну старого обладнання на нове, нове, швидше за все, з ладу вийде з меншою ймовірністю, ніж старе. І багато чули, скільки проблем виникає в процесі деплоя. Тобто за фактом, за статистикою можна сказати, що отримання цього Single point of failure, воно набагато менше роялит на ваш продакшн, ніж крива якась викладка.



Третій момент, навіщо і чому ми почали дивитися на Docker – це проблема хвилює і гостра, це посадити ваш додаток від моменту його написання програмістом до першого польоту у продакшн, не змінюючи нічого, на що додаток може зав'язуватися. У разі використання Docker, т. до. ми спочатку свої додатки підсаджуємо в певне оточення з якимись либами, пакетами, ще з чим-то воно у нас посаджено з процесом складання умовно team city. Далі воно у нас в такому ж первозданному вигляді їде в процес Q&A тестування, після цього у нас через staging, development наше готове додаток в контейнері вже може їхати на продакшн. Тобто ми на цьому етапі відкидаємо в сторону якісь зміни… Якщо у вас виникла проблема з додатком, та різне оточення цього додатка на development, на staging і на Q&A тестуванні, то досить важко зрозуміти, що ж, взагалі, впливає на наш додаток, що воно не працює, тобто цю задачу ми тут вирішили на 100%.



І останній пункт, який я відзначив як пункт із зірочкою, тому що спочатку, коли я побачив Dockerfile, мені здалося, що це якесь повернення років на 10 назад. Незрозумілий бэш, ні бэш, чого-то треба писати, коли є puppet, навіщо писати ці txt файлики? Але насправді плюс цього полягає в тому, що завжди, дивлячись на config сусіда, ти можеш бачити, що і навіщо чоловік робив, про що він, можливо, думав, і знаючи про те, як працює Docker і дивлячись на такі Docker файли, ти інколи можеш цей файлик міняти, щоб собі оптимізувати процес накладання шарів, виконання чогось, використання кешування. Тобто за фактом, Dockerfile є гарною постійно підтримується в актуальному вигляді документацією до вашого додатком.

Отже, ми відповіли на питання, навіщо ж ми хотіли Docker і ми, впроваджуючи це, завжди повертаємося до цих буквах і дивимося: вирішили, не вирішили, належить, не належить?

Далі цікавий момент, який почався з процесу впровадження Docker у нас. Звичайно, ми стикалися з проблемами. Частина цих проблем обговорювалася на Github Docker і т. п. Частина вирішена, частину ми ще не спіймали. Я думаю, що вони прийдуть до нас слідом.



Перша цікава проблема полягала в тому, що ми хочемо сбирать логи нашого додатка, хоч якось централізовано. Перед нами постало завдання: «зробіть нам якийсь syslog, програмісти хочуть писати в divlog, далі ми його будемо кудись посилати, обробляти і т. д.».



Ми посиділи, подумали: як так взяти в контейнер запхати другий сервіс? Початкова ідея: один контейнер – одна служба, яка працює в ньому. Неправильно. Дилема виникає. Що робити?



Це виходить +1 сервіс, який треба пхати в кожен контейнер, гарантувати його роботу, тому що з нас спитають, якщо у нас там логи не доставились. Це підпункт, який виправдовує лінь, що треба чогось брати і робити. І завдання, яке, як звичайно, нам потрібна вчора. Нам треба вже збирати логи з боку програмістів. Готова частина обробки цих логів, нам треба тільки взяти і зробити. Відповідно, перше, що було придумано і що ми почали використовувати – це ми взяли і смапили сокет dev/log всередину контейнера і почали писати туди. Круто!



Вирішили, домовилися, викотили, відмінно працює. Повідомлення ходять. До першої проблеми, коли нам треба було на хостах поміняти config'і syslog'а й зробити йому reload. Т. о. контейнери продовжують тримати старий сокет, писати щось туди, повідомлення нікуди не йдуть, вони залишаються там. Що робити?



Ця проблема – хороший кейс, який говорить про те, що ви не забувайте про перші накиди ваших рішень, які були на першому етапі. В даному випадку нам довелося повернутися назад, повернутися до ідеї про те, що «давайте зробимо syslog всередині контейнера, бог з нею, з цією ідеєю, що один сервіс – один контейнер». І нам же ніхто не говорив фізично про один сервіс, тобто, може бути, там працює функціонал одного сервісу, а процесів більше. Ми запхали syslog в контейнер, config цього syslog'а не змінюється, за фактом підтримувати, крім актуальної версії, нам нічого не потрібно, бо з syslog'а ми шолом на локальний хост тієї машини, де запускається, а далі ми вже з syslog'а цієї машини відправляємо дані в якийсь наш центральний репозиторій з логами.



Наступна цікава проблема, яка стосувалася того, що в контейнер нам потрібно якусь директорію, умовно кажучи, додати або якесь блоковий пристрій. Начебто все просто, є ключик -v, який ти прописувати, додаєш, у тебе все працює. І є в нашій компанії особливість така, що ми використовуємо якісь loop-девайси для роздачі свого коду, і потім його монтуємо з -o loop. У нас з'являється абстрактне блоковий пристрій, ми запускаємо контейнером, мапим якийсь пристрій там під директорію з цього loop-девайса. Все працює відмінно і за рахунок особливості Docker'а, що кожну директорію, кожен файлик, яку він намагається замапить до себе всередину, він йде по всій ланцюжку всіх точок монтування і тягне весь proc/mount, який потрібен йому зараз для запуску в тому стейте, про який ви говорите, весь proc/mount тягне за собою.

Далі сама суть проблеми, я думаю, для всіх стає зрозумілою і очевидною, що ми хочемо це loop-пристрій відмонтувати, щоб у нас там не було 12-20-50 на машині, нам старий код, тижневий, не потрібен. І що ми тут отримуємо? Ми отримуємо ситуацію, коли у нас якийсь процес тримає блоковий пристрій, а відмонтувати ми його не можемо. Для цього нам треба піти, зайти в контейнер і спробувати відмонтувати там. Але оскільки контейнер ми запускаємо в режимі «без привілеїв», зробити umount з хост системи ми там не можемо.



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



Відповідно, перше рішення, яке може підійти, воно в принципі працює – це взяти і запустити контейнер в привілейованому режимі. Ми так не робимо.

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



Тобто це один з тих кейсів, коли просто потрібно сісти і подумати про те, що можливо зробити. Якісь структурні внутрішні домовленості і зміни, які вирішать цю проблему. Тобто битися про проблему з технічної позиції в даному випадку сенсу не мало. Ми в разі Docker контейнерів просто перестали використовувати такі динамічні loop-девайси.



Наступні цікаві граблі пов'язані з iptables, з nf_conntrack. Що нам дає Docker, коли ми про нього читаємо? Він говорить про те, що ми можемо запустити скільки завгодно контейнерів, ми можемо юзати величезна кількість портів, ми можемо влаштовувати зв'язку між контейнерами всередині, у нас виходить якась ізоляція. Всі говорять про те, що це буде, звичайно, через iptables, є такі правила, які ви можете прописати, якщо ви забули при запуску в контейнері їх вказати. Але ніхто не говорить про одне – про те, що в цьому випадку ми зобов'язані використовувати линуксовый модуль nf_conntrack, який сам по собі я швидким не назвав.



Є два шляхи, по яким нам можна вирішувати дану проблему.

Перший шлях, якщо у вас програма не дуже навантажене по мережі – це жити, як є. Все відмінно, все працює, до того моменту, поки ви не зіштовхнулися з переповненням таблиці nf_conntrack. Що ми можемо зробити, щоб продовжити собі життя? Збільшити таблицю nf_conntrack.

Далі, це така причинно-наслідковий зв'язок – якщо ми збільшуємо саму таблицю conntrack, то ми повинні збільшити хеш-таблицю з'єднань, щоб їх було більше, ніж їх є за умовчанням в ядрі linux.

Варто пам'ятати про третій пункт, який у нас за замовчуванням в лінуксі зберігає близько 10-ти хвилин всі з'єднання встановлені, вже відпрацьовані. За фактом, для сучасного сервісу, я думаю, що більше 30 сек. після створення з'єднання, якщо щось не сталося, то або з сервісом погано, або коннекшн просто не потрібен. Тому 10 хв – це реальний оверкил.



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



Як ми вирішили для себе цю проблему? Перше, що хочу сказати, це те, що ми не використовуємо conntrack, ми намагаємося його не використовувати на продакшн оточенні, на наших сервісах. Ми його використовуємо в частині девела, ми його використовуємо на стейжинге, тому що там кількість мережевого обміну сильно менше.

З того, на що я запропонував би подивитися – це чудовий проект Weave, який дозволяє вам будувати мережеве взаємодія в обхід мережевого устаткування, тобто це більш софтовое рішення між вашими Docker-хостами. Найпростіше рішення – це використовувати дефолотовые bridge, які при запуску Docker може генерувати. Також можна використовувати вбудований линуксовый bridge-конфігуратор. І, якщо хочеться красивості і трохи більше гнучкості, є чудова жарт – Open vSwitch. На даний момент у списку плагінів для Docker ніякої управлялки для Open vSwitch немає, що шкода, вони обіцяли у минулому році.



Як я сказав раніше, для себе ми вирішили це таким чином, що ми використовуємо практично при запуску всіх наших контейнерів перекидання мережевого пристрою з хост-системи в контейнер і від conntrack ми позбавляємося. Штампика, що problem solved я не поставив, бо це така проблема, над якою варто думати. Для себе ми її вирішили так, хтось, можливо, вирішить її інакше.



Наступний цікавий момент, з яким зіткнулися більшість, хто з Docker мав хоч якусь справу – це вибір Storage Driver, для того щоб файлики зберігати. Тому що у кожного є плюси, у кожного є мінуси. Є AuFS, з якою вони вийшли на ринок і запропонували, але який в mainline не увійшов і не ввійде ніколи. Є класне рішення проблеми, коли ви берете логічні пристрої, займаєтеся їх монтуванням, тобто це Device Mapper плюс якась вбудована файлова система, умовно Х3. Ви займаєтеся цим монтуванням – перемонтированием… Все це виростає в таку, взагалі, величезну ланцюжок цих шарів і логічних пристроїв. Є BTRFS, який половину функціоналу, потрібного для Docker, підтримує by default саме як закладений функціонал файлової системи. І є ще кілька інших штук, які реалізовані якимись плагінами, деякі кльові, деякі не дуже. Тут я зазначив ті три речі, з якими доводилося працювати, і серед яких доводилося вибирати.



І також дивимося на проблеми, які може спричинити за собою BTRFS. BTRFS зобов'язує вас тримати якесь блоковий пристрій, яке буде rootdir'ом для Docker на BTRFS. Не в кожній таблиці розділів розбиття на нашому сервері був присутній BTRFS, відповідно, це нас зобов'язувало займатися ресетапом або попиливанием LWM і виділенням цього розділу.

Друге – ніхто не приховував того, що BTRFS ніколи не робить ресинк відразу на диск, він ніколи не пише безпосередньо на блочний пристрій, він спочатку веде якийсь свій журнал, пише інформацію в журнал, потім запускається ядерний процес самого BTRFS, який дивиться на цю чергу, робить якісь рухи тіла і кудись щось записує. На практиці за нашими вимірами performance від використання BTRFS вийшов. Десь є у нас блок девайс на 10-му рейді, і ми отримали від нього десь поділена навпіл – це у нас видавав BTRFS.

Дуже актуальна проблема для служби моніторингу, відділу експлуатації – це, взагалі, зрозуміти, скільки місця зайнято на BTRFS, скільки вільно? І найбільша проблема, коли у тебе, начебто, місце є, а ніби його й немає – куди воно поділося, і ти розумієш, що в тебе мета-дата розпухла, файлів включена, начебто, трохи, і тобі потрібно запустити ребаланс, а у тебе близько до піку і IO і так страждає, а ще ребаланс. Печаль.



З плюсів BTRFS за останні рік-два – це єдиний storage driver, який нормально і, принаймні, очікувано працював з Docker. Ніби як, проблему вирішили, BTRFS – наш вибір. Докупимо SSD, будемо жити, бог з ним з перформансом, розберемося.



І тут не так давно ми подивилися в бік нового Storage Driver – OverlayFS. Забігаючи вперед, скажу, що ми займаємося його впровадженням, етап тестування ми вже з ним пройшли, якісь тести отримали, тести досить хороші.

Чому я FS сіреньким залишив, а Overlay красненьким зафарбував? З версії 3.18 він увійшов в ядро під іншою назвою, він увійшов в ядро під назвою модуля просто Overlay. Хто-небудь знає, чим займається вбудований вже давно в ядро модуль OverlayFS? Я теж не знаю. Нових слайдів про Overlay не буде, я скажу інформацію, яка була готова буквально на минулому тижні. За нашими тестами швидкість роботи, час відповіді з використанням OverlayFS на базі EX4, у нас в процентному співвідношенні такий overhead від Docker вийшов в межах похибки до 3-5%, не більше, тобто для себе ми розглядаємо його наступною точкою і наступним моментом, куди ми почнемо мігрувати і робити. Єдиний мінус, який це на себе накладає – це знову ж таки ядро 3.18, мінімум, тобто треба оновлюватися, треба йти вперед.

Overlay FS працює щодо BTRFS для того, щоб влаштовувати ці шари тільки для читання, він використовує хард-лінки. Тут є ще один плюс у порівнянні з BTRFS, який полягає в тому, що ваша операційна система в оперативний пам'яті може використовувати файловий кеш. Але якщо ви використовуєте BTRFS, т. к. там є сабвольюмы і навіть, якщо файлик, ніби як, дедуплицирован і, ніби як, ти розумієш, що файлик – це один файлик, з точки зору ОС Лінукс файлик виходить інший. І якщо з одного контейнера і з іншого контейнера ми будемо запитувати, ніби як, один і той же файл, то в кеші він, швидше за все, не залишиться чи з'явиться два різних кешу для цього файлу. Overlay нас від цього рятує. Як я сказав, performance виходить на читання/запис дуже схожим на можливості рідної файлової системи, чому ні? Та для експлуатації, та для моніторингу, і для всіх продовжує працювати старий DF. Ми знаємо, скільки у нас там зайнято, скільки вільно. Круто. Тобто немає цієї загадки, не потрібен ребаланс, відмінно! Я вважаю, що це наше майбутнє рішення проблеми і ті, хто не пробував, закликаю просто хоча б подивитися на цей драйвер.



Далі узагальнені не виділені проблеми, з якими зіткнулися. Полягають вони в тому, що ми відійшли від ідеї того, що в одному контейнері може бути запущений тільки один сервіс або одна служба. Але тут постало питання вибору того, як запускати більше одного, тобто є cmd, але які важелі у нас ще є для того, щоб що-небудь ще запустити?

Перше, що ми вирішили – це написати милиця для Entrypoint, де ми запускаємо якісь крони і т. п., готуємо наш контейнер перед запуском, запускаємо фонові якісь служби і після цього передаємо управління вже в інструкцію Docker cmd і запускаємо наш основний сервіс.



Т. к. це все було схоже на якийсь ну зовсім милицю, і його страшно підтримувати, а ще entrypoint кожна людина з команди експлуатації писав самостійно – вони були дуже різними в залежності від настрою. Потрібно було шукати якийсь більш уніфікований підхід. І з того, що подивилися, вибір припав на S6, і зараз ми його використовуємо. Я залишив координати проекту, це якийсь init-менеджер для роботи всередині Docker-контейнерів. Він дозволяє всі базові штуки, які, як мінімум, нам потрібні, тобто він дозволяє зробити старт, зробити стоп. Залежно від стопа ти можеш завершити контейнер чи ні, ти можеш виконати якийсь набір команд перед стартом контейнера і перед його остановом. Все досить непогано. Тим, кого цікавлять завдання і проблеми запуску однієї служби в контейнері, я рекомендую подивитися на це.

Така підлозі-проблема, напів-немає, і питання, теж виникає – чи можна використовувати кілька From в Dockerfile. Відповідь на це питання протягом року змінювався тричі. Причому кардинально. Так-ні-так – залежно від версії Docker. З нашого досвіду я можу сказати, що можна використовувати до першої проблеми, коли у вас ієрархія верств одного образу не перетнеться з ієрархією іншого, і ви втратите раптово якийсь файлик. Тобто ви будете думати, що він там є, а його там немає. І втратите якусь кількість часу в пошуках, чому його там немає, а він повинен бути. Тобто я б порекомендував не використовувати.

Ще цікава проблема, з якою ми досить недавно зіткнулися, нещодавно про неї дізналися. Це такий кльовий функціонал в Docker з версії 1.6, по-моєму. Вони взяли і зробили команду docker.exec, де ти можеш взяти і виконати що-небудь, выгружение контейнера з хост-системи, і подивитися на результат. Все круто, але не так давно з'ясувалося, що цей exec зроблений, взагалі, для налагодження, девелопмент-мода, тобто він не припускав, що ти можеш з хост-системи запускати якісь моніторингові штуки, які там find відпрацював що-небудь таке, пошукав, cut зробив. І проблема полягала в тому, що в Docker inspect'е, якщо дивитися в name-space конкретного контейнера, всі exec'і всіх їх до и, за яким можна було подивитися на їх результат, вони жили до тих пір, поки ти контейнер не перезагрузишь. Починаючи то з 1.7, але швидше з версії 1.8, вони зробили якийсь garbage collector, який дивиться по-старому, чи не залишилося там мертвих непотрібних exec'ів, і їх чистить. Це було дійсно великою проблемою, тому що якщо дивитися на споживання пам'яті, то з плином днів, тижнів пам'ять просто підростала і підростала, незрозуміло чому.



Загальна рада, якщо у вас є якась проблема, спробуйте запитати де-небудь, та знайти рішення цієї проблеми, тому як Docker, сам по собі, дуже швидко розвивається, і там багато чого змінюється. Буває таке, коли ви намагаєтеся вирішити те, що вже вирішили інші тиждень назад або в попередньому релізі, чи просто у майстер ще щось не увійшло.



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



Як ми за ним стежимо зараз у себе? Нам необхідно перевіряти, що всі пакети, потрібні патч-сети для нашого базового образу присутні, щоб нам заздалегідь відому дірку не викотити, наприклад, на продакшн. Як ми вирішуємо цю проблему? Ми беремо і по якомусь крону або в якийсь час запускаємо в інтерактивному режимі наш контейнер і, використовуючи zypper або що ще можна придумати для якоїсь іншої ОС, ми просто чекаем по всім нашим репозиториям наявність яких-небудь update ів і, якщо вони є, ми передаємо цю інформацію у вигляді тікета на команду експлуатаційників, які приймають рішення про те, оновлювати його далі або не оновлювати. Якщо оновлювати, ми оновлюємо базовий образ і викочуємо його туди, де ми займаємось складанням. Якщо є необхідність, і патч якийсь серйозний, потрібен фікс, ми йдемо, оновлюємо потрібний нам контейнер.



І перед тим, як нам, взагалі, котити наш application на production, ми робимо порівняння нашого, скажімо так, актуального RPM-списку пакетів і їх версій, які повинні бути з тим, що присутня в нашому новоствореному контейнері. Якщо щось не сходиться, ми вирушаємо назад по гілці на момент складання, тестування і т. д. тобто поки ми не отримаємо тут відповідність того, що ми згодні з таким набором версій на продакшн, ми його приймаємо.



Для того щоб наші аппликухи якось збирати, зрозуміло, що ми руками не будемо писати кожен раз і Dockerfile міняти, версію нашого базового образу. Нам потрібна якась автоматика. Автоматику ми це зробили, вдавшись до використання puppet. Назвали ми її просто docker_build.



Які вимоги ми накладали на цю нашу автоматику? Ми хотіли генерувати структуру директорій і конфіги для конкретного сервісу. Ми хотіли навчити і навчили його робити інструкцію Dockerfile'а в автоматичному режимі. Ми хотіли доставляти/витягувати з наших систем, збірок наших сервісів виконувані файли програми і класти їх поруч. Ми хотіли, звичайно ж, щоб він виконував збірку. Відправляти результат у Registry, причому з можливістю або відправляти чи не відправляти, бо там щось може піти не так, ми просто хотіли локально подивитися, що він там збере. І необхідним функціоналом було те, що ми хотіли дозволити тому, хто збирає контейнер, виконувати це руками.



Сервіси у нас викочуються наступним чином. До нас в нашу тікет-систему в JIRA надходить завдання про те, що наші розробники вирішили, що зібрали нову версію, і вони хочуть її на продакшн. Вони ставлять нам завдання. Контейнер автоматично на той момент збирається, він говорить, що статус «зібраний», все добре. У цьому інтерфейсі у нас є функціонал, якщо ми поміняли в puppet'е якісь config'і, ми хочемо зробити rebuild. Тобто якщо нам потрібен контейнер, не той, що нас зібрали, прислали, а з іншими config'ами, ми можемо це зробити тут. Наступним, другим кроком (на слайді) можемо переконатися в тому, що команда на перезбирання спрямована, і якщо все пройшло добре, ми отримуємо якийсь success з адресою і посиланням нашого нового контейнера в registry, звідки ми його можемо забрати.



Docker хост і контейнер, що працює на ньому, є необхідність моніторити. Для вирішення цієї проблеми ми в даному випадку запилили перший велосипед, який є образом контейнера на кожному docker хості, в якому ми коллектим якусь інформацію. Загальна схема виглядає так, т. е. ми збираємо на контейнері інформацію, засилає її раз на якийсь час у Graphite, а вже там малюємо красивий інтерфейс.



Як у нас виглядає цей моніторинговий контейнер? У нас є Docker CLI всередині, тому що нам потрібно збирати статистику через docker stats, на CPU і пам'яті кожного контейнеру. І ми за замовчуванням використовуємо на наших серверах SAR і запускаємо наш моніторинговий контейнер. Ми діру SAR'ом підтягуємо. Раз на якийсь час наш docker контейнер моніторинговий запускає агрегатор статки, якого необхідно відправити в Graphite. Засилає її туди. Не вийшло відіслати? Наступний прохід. Він перевірить, якщо залишилися якісь зліпки на FS, зашлет другим прогоном. Після чого ми це малюємо на Graphite. Сам контейнер, що він запущений, що він, взагалі, є на Docker хості, ми моніторимо Zabbix'ом.



У загальному і цілому, графіки виходять приблизно такі. Їх багато, вони не дуже читають, але, я думаю, графаны бачили всі. Ось так ми бачимо інформацію по хосту пам'яті, ми можемо дивитися на кожного контейнеру та інформацію, в загальному, про хості за CPU.



Можемо дивитися використання мережі і т. д…



На даному слайді картинка вже про те, що відбувається з самим контейнером, отвязываясь від самого хоста.



Те ж саме можна сказати про CPU.



І останній момент, якого я б хотів торкнутися – це винахід ще одного велосипеда по оркестрации управління нашими контейнерами, бо ми, обираючи, не вибрали нічого з готових рішень. Я не буду зупинятися на тих рішеннях, які ми розглянули, якщо комусь буде цікаво, пізніше це запитає. Я скажу, який набір вимог ми висловлювали до нашого baDocker, щоб його почати розробляти.



  • Перше, що мені не сподобалося, коли ми дивилися на готові рішення – це присутність ще одного контейнера, який потрібно запускати на кожному хості,
    який щось там збирати і через нього займатися управлінням. Перша фішка – це Clientless. Нам не потрібен жоден клієнт, у нас Docker API, навіщо
    нам ще щось?
  • Нам необхідно управління Registry двох версій – першої і другої. Тому що у нас є старі спадщини на версіях 1 і 2, і нам потрібен старий Registry. І
    у нас є нові версії Docker, де ми використовуємо Registry v2.
  • Як зазвичай, ми хочемо мати всю-всю інформацію про хостах, про контейнерах (я просто її до купи зібрав). Всю інформацію, яку можна було отримати, хочемо
    мати.
  • Також хочемо мати якийсь планувальник завдань, який буде отримувати якийсь набір інструкцій про те, що «ці образи туди вилий, звідти їх забери, бо
    що вони не потрібні, сервіс вже поїхав». Поки що це таке кодова назва, нехай це буде Планувальник.
  • І останнє, що хотілося б бачити – це Dashboard, який скаже про те, що «хостів у тебе стільки, там можливі проблеми, там неможливі, якщо
    потрібно, піди, подивися».


На поточний момент часу це виглядає приблизно таким чином. Тобто ми можемо подивитися Summary за нашими хостам, по кожному майданчику, скільки пам'яті там все, скільки зайнято, скільки контейнерів всього, скільки застопанно, скільки працює.



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



Ми можемо зайти з цього інтерфейсу на будь-який контейнер, чого-то там написати, отримати якийсь висновок, наприклад, «не коннектись через shell нікуди»…



За нашим сервісам ми можемо побачити, які типи даного сервісу є, які є версії, на яких серверах працюють ті чи інші версії даного сервісу.



І можемо використовувати такі підказки у вигляді темплейту, як же нам запустити контейнер для цього сервісу, тобто ми натискаємо на потрібний нам тип і бачимо – ось підказка: «піди, запусти ось так, якщо тобі це потрібно зробити руками».



На закінчення хотілося б сказати наступне – використовуючи будь-який інструмент, в даному випадку Docker, я б рекомендував використовувати якийсь свій підхід, дивитися нестандартно на деякі речі і, роблячи таке, взяти до уваги проблеми інших людей, які ці проблеми вирішили або радять, як їх вирішити. І, звичайно ж, я хотів сказати про те, що винаходити велосипеди – це не соромно, це кльово і цікаво, якщо тільки колеса не квадратні у вашого велосипеда.

Контакти
a.turetsky@corp.badoo.com
twitter
Блог компанії Badoo

Ця доповідь — розшифровка одного з кращих виступів на конференції розробників високонавантажених систем HighLoad++. Зараз ми активно готуємо конференцію 2016 року — у цьому році HighLoad++ пройде в Сколково, 7 і 8 листопада.

На HighLoad++ 2016 буде величезна секція з DevOps, ось деякі з доповідей за docker'у, раптом вам буде цікаво:


Також деякі з цих матеріалів використовуються нами в навчальному онлайн-курс по розробці високонавантажених систем HighLoad.Guide — це ланцюжок спеціально підібраних листів, статей, матеріалів, відео. Вже зараз у нашому підручнику понад 30 унікальних матеріалів. Підключайтеся!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.