10 жніўня ў Слёрм стартаваў
У гэтым артыкуле пагаворым аб гісторыі з'яўлення Docker і яго асноўных абстракцыях: Image, Cli, Dockerfile. Лекцыя разлічана на пачаткоўцаў, таму ці наўрад будзе цікавая дасведчаным карыстачам. Тут не будзе крыві, апендыкса і глыбокага апускання. Самыя асновы.
Што такое Docker
Паглядзім на вызначэнне Docker з Вікіпедыі.
Docker - гэта праграмнае забеспячэнне для аўтаматызацыі разгортвання і кіравання праграмамі ў асяроддзях з падтрымкай кантэйнерызацыі.
З гэтага азначэння нічога незразумела. Асабліва незразумела, што значыць "у асяроддзях з падтрымкай кантэйнерызацыі". Каб разабрацца, вернемся ў мінулае. Пачнём з эпохі, якую я ўмоўна называю «Маналітнай эрай».
Маналітная эра
Маналітная эра - гэта пачатак 2000-х, калі ўсе прыкладанні былі маналітнымі, з кучай залежнасцяў. Распрацоўка ішла доўга. Пры гэтым сервераў было не так шмат, мы ўсе іх ведалі па імёнах і маніторылі. Ёсць такое пацешнае параўнанне:
Pets - гэта хатнія жывёлы. У маналітнай эры мы ставіліся да сваіх сервераў, як да хатніх жывёл, песцілі і песцілі, пылінкі здзімалі. А для лепшага кіравання рэсурсамі выкарыстоўвалі віртуалізацыю: бралі сервер і пілавалі на некалькі віртуальных машын, тым самым забяспечваючы ізаляцыю асяроддзя.
Сістэмы віртуалізацыі на базе гіпервізара
Пра сістэмы віртуалізацыі напэўна ўсё чулі: VMware, VirtualBox, Hyper-V, Qemu KVM і т. д. Яны забяспечваюць ізаляцыю прыкладанняў і кіраванне рэсурсамі, але ў іх ёсць і мінусы. Каб зрабіць віртуалізацыю, патрэбен гіпервізор. А гіпервізор - гэта аверхед рэсурсаў. Ды і сама віртуальная машына звычайна цэлая махіна – цяжкая выява, на ім аперацыйная сістэма, Nginx, Apache, магчыма і MySQL. Выява вялікай, віртуальнай машынай няёмка аперыраваць. Як следства, праца з віртуалкамі можа быць маруднай. Каб вырашыць гэтую праблему, стварылі сістэмы віртуалізацыі на ўзроўні ядра.
Сістэмы віртуалізацыі на ўзроўні ядра
Віртуалізацыю на ўзроўні ядра падтрымліваюць сістэмы OpenVZ, Systemd-nspawn, LXC. Яркі прыклад такой віртуалізацыі – LXC (Linux Containers).
LXC - сістэма віртуалізацыі на ўзроўні аперацыйнай сістэмы для запуску некалькіх ізаляваных асобнікаў аперацыйнай сістэмы Linux на адным вузле. LXC не выкарыстоўвае віртуальныя машыны, а стварае віртуальнае асяроддзе з уласнай прасторай працэсаў і сеткавым стэкам.
Па сутнасці LXC стварае кантэйнеры. У чым розніца паміж віртуальнымі машынамі і кантэйнерамі?
Кантэйнер не падыходзіць для ізалявання працэсаў: у сістэмах віртуалізацыі на ўзроўні ядра знаходзяць уразлівасці, якія дазваляюць вылезці з кантэйнера на хост. Таму калі вам трэба нешта ізаляваць, то лепш выкарыстоўваць віртуалку.
Адрозненні паміж віртуалізацыяй і кантэйнерызацыі можна ўбачыць на схеме.
Бываюць апаратныя гіпервізары, гіпервізары па-над АС і кантэйнеры.
"Жалезныя" гіпервізары - гэта крутая штука, калі вы сапраўды хочаце нешта ізаляваць. Бо там ёсць магчымасць ізаляваць на ўзроўні старонак памяці, працэсараў.
Ёсць гіпервізары як праграма, і ёсць кантэйнеры, пра іх мы і будзем казаць далей. У сістэмах кантэйнерызацыі гіпервізара няма, але ёсць Container Engine, які стварае кантэйнеры і кіруе імі. Штука гэта больш легкаважная, таму за кошт працы з ядром оверхед менш, ці яго няма зусім.
Што выкарыстоўваецца для кантэйнерызацыі на ўзроўні ядра
Асноўныя тэхналогіі, якія дазваляюць ствараць ізаляваны ад іншых працэсаў кантэйнер, - гэта Namespaces і Control Groups.
Namespaces: PID, Networking, Mount і User. Ёсць яшчэ, але для прастаты разумення спынімся на гэтых.
PID Namespace абмяжоўвае працэсы. Калі мы, напрыклад, ствараем PID Namespace, змяшчаем туды працэс, то ён становіцца з PID 1. Звычайна ў сістэмах PID 1 - гэта systemd або init. Адпаведна, калі мы змяшчаем працэс у новы namespace, ён таксама атрымлівае PID 1.
Networking Namespace дазваляе абмежаваць/ізаляваць сетку і ўсярэдзіне ўжо размяшчаць свае інтэрфейсы. Mount - гэта абмежаванне па файлавай сістэме. User – абмежаванне па карыстачах.
Control Groups: Memory, CPU, IOPS, Network – усяго каля 12 налад. Інакш іх яшчэ называюць Cgroups («Cі-групы»).
Control Groups кіруюць рэсурсамі для кантэйнера. Праз Control Groups мы можам сказаць, што кантэйнер не павінен спажываць больш нейкай колькасці рэсурсаў.
Каб кантэйнерызацыя паўнавартасна працавала, выкарыстоўваюцца дадатковыя тэхналогіі: Capabilities, Copy-on-write і іншыя.
Capabilities - гэта калі мы кажам працэсу, што ён можа рабіць, а чаго не можа. На ўзроўні ядра гэта проста бітавыя карты са мноствам параметраў. Напрыклад, карыстач root мае поўныя прывілеі, можа рабіць усё. Сервер часу можа змяняць сістэмны час: у яго есць capabilities на Time Capsule, і ўсё. З дапамогай прывілеяў можна гнутка наладзіць абмежаванні для працэсаў, і тым самым засцерагчы сябе.
Сістэма Copy-on-write дазваляе нам працаваць з выявамі Docker, выкарыстоўваць іх больш эфектыўна.
На дадзены момант Docker мае праблемы з сумяшчальнасцю Cgroups v2, таму ў артыкуле разглядаюцца менавіта Cgroups v1.
Але вернемся да гісторыі.
Калі з'явіліся сістэмы віртуалізацыі на ўзроўні ядра, іх пачалі актыўна прымяняць. Оверхед на гіпервізор знік, але некаторыя праблемы засталіся:
- вялікія выявы: у тую ж OpenVZ штурхаюць аперацыёнку, бібліятэкі, кучу рознага софту, і ў выніку выява ўсё роўна атрымліваецца немаленькім;
- няма звычайнага стандарту пакавання і дастаўкі, таму застаецца праблема залежнасцяў. Бываюць сітуацыі, калі два кавалкі кода выкарыстоўваюць адну бібліятэку, але з рознымі версіямі. Паміж імі магчымы канфлікт.
Каб усе гэтыя праблемы вырашыць, надышла наступная эра.
Эра кантэйнераў
Калі наступіла Эра кантэйнераў, змянілася філасофія працы з імі:
- Адзін працэс - адзін кантэйнер.
- Усе неабходныя працэсу залежнасці дастаўляем у яго кантэйнер. Гэта патрабуе распілоўваць маналіты на мікрасэрвісы.
- Чым менш вобраз, тым лепш менш магчымых уразлівасцяў, хутчэй раскочваецца і гэтак далей.
- Інстансы становяцца эфемернымі.
Памятайце, я казаў пра pets vs cattle? Раней інстансы былі падобныя да свойскіх жывёл, а цяпер сталі як cattle - быдла. Раней быў маналіт - адно прыкладанне. Цяпер гэта 100 мікрасэрвісаў, 100 кантэйнераў. У нейкіх кантэйнераў можа быць па 2-3 рэплікі. Нам становіцца не гэтак важна кантраляваць кожны кантэйнер. Нам хутчэй важная даступнасць самага сэрвісу: таго, што робіць гэты набор кантэйнераў. Гэта мяняе падыходы ў маніторынгу.
У 2014-2015 гадах здарыўся росквіт Docker – той тэхналогіі, пра якую мы і будзем зараз казаць.
Docker змяніў філасофію і стандартызаваў ўпакоўку прыкладання. З дапамогай Docker мы можам спакаваць прыкладанне, адправіць яго ў рэпазітар, спампаваць адтуль, разгарнуць.
У Docker-кантэйнер мы закладваем усё неабходнае, таму вырашаецца праблема залежнасцяў. Docker гарантуе ўзнаўляльнасць. Я думаю, многія сутыкаліся з невоспроизводимостью: у цябе ўсё працуе, пушыш на прадакшэн, там гэта перастае працаваць. З Docker гэтая праблема сыходзіць. Калі твой Docker-кантэйнер запускаецца і робіць тое, што патрабуецца рабіць, то з вялікай дзеллю верагоднасці ён запусціцца на прадакшэне і там зробіць тое ж самае.
Адступленне пра оверхед
З нагоды оверхед увесь час ідуць спрэчкі. Хтосьці лічыць, што Docker не нясе дадатковую нагрузку, бо выкарыстоўвае ядро Linux і ўсе яго працэсы, неабходныя для кантэйнерызацыі. Маўляў, "калі вы кажаце, што Docker - гэта оверхед, то тады і ядро Linux оверхед".
З іншага боку, калі паглыбіцца, то ў Docker і праўда ёсць некалькі рэчаў, пра якія з нацяжкай можна сказаць, што гэта оверхед.
Першае - гэта PID namespace. Калі мы ў namespace змяшчаем нейкі працэс, яму прысвойваецца PID 1. У той жа час у гэтага працэсу ёсць яшчэ адзін PID, які знаходзіцца на хаставым namespace, за межамі кантэйнера. Напрыклад, мы запусцілі ў кантэйнеры Nginx, ён стаў PID 1 (майстар-працэс). А на хасце ў яго PID 12623. І складана сказаць, наколькі гэта оверхед.
Другая штука – гэта Cgroups. Возьмем Cgroups па памяці, гэта значыць магчымасць абмяжоўваць кантэйнеру памяць. Пры яе ўключэнні актывуюцца лічыльнікі, memory accounting: ядру трэба разумець, колькі старонак выдзелена, а колькі яшчэ свабодна для гэтага кантэйнера. Гэта магчыма оверхед, але дакладных даследаванняў аб тым, як ён уплывае на прадукцыйнасць, я не сустракаў. І сам не заўважаў, што прыкладанне, запушчанае ў Docker, раптам рэзка губляла ў прадукцыйнасці.
І яшчэ адна заўвага аб прадукцыйнасці. Некаторыя параметры ядра пракідваюцца з хаста ў кантэйнер. У прыватнасці, некаторыя сеткавыя параметры. Таму калі вы жадаеце запусціць у Docker нешта высокапрадукцыйнае, напрыклад тое, што будзе актыўна выкарыстоўваць сетку, то вам, прынамсі, трэба гэтыя параметры падправіць. Які-небудзь nf_conntrack, напрыклад.
Аб канцэпцыі Docker
Docker складаецца з некалькіх кампанентаў:
- Docker Daemon - тое самае Container Engine; запускае кантэйнеры.
- Docker CII - утыліта па кіраванні Docker.
- Dockerfile - інструкцыя па тым, як збіраць выяву.
- Image - выява, з якога раскочваецца кантэйнер.
- Кантэйнер.
- Docker registry - сховішча выяў.
Схематычна гэта выглядае прыкладна вось так:
На Docker_host працуе Docker daemon, запускае кантэйнеры. Ёсць Client, які перадае каманды: зьбяры выяву, запампуй выяву, запусці кантэйнер. Docker daemon ходзіць у registry і выконвае іх. Docker-кліент можа звяртацца і лакальна (да юнікс-сокету), і па TCP з выдаленага хаста.
Пройдзем па кожным кампаненце.
Docker daemon (дэман) - Гэта серверная частка, яна працуе на хост-машыне: спампоўвае вобразы і запускае з іх кантэйнеры, стварае сетку паміж кантэйнерамі, збірае логі. Калі мы кажам "ствары вобраз", гэтым таксама займаецца дэман.
Докер CLI - кліенцкая частка Docker, кансольная ўтыліта для працы з дэманам. Паўтару, яна можа працаваць не толькі лакальна, але і па сетцы.
Базавыя каманды:
docker ps - паказаць кантэйнеры, якія зараз запушчаныя на Docker-хасце.
docker images - паказаць выявы, запампаваныя лакальна.
docker search <> - пошук выявы ў registry.
docker pull <> - спампаваць выяву з registry на машыну.
docker build < > - Сабраць выяву.
docker run <> - запуск кантэйнер.
docker rm <> - выдаліць кантэйнер.
docker logs <> - логі кантэйнера
docker start/stop/restart <> — праца з кантэйнерам
Калі вы асвоіце гэтыя каманды і будзеце ўпэўнена імі карыстацца, то лічыце, што на 70% асвоілі Docker на ўзроўні карыстача.
Докер-файл - інструкцыя для стварэння выявы. Амаль кожная каманда інструкцыі - новы пласт. Паглядзім на прыкладзе.
Прыкладна так выглядае Dockerfile: злева каманды, справа - аргументы. Кожная каманда, што тут ёсць (і ўвогуле пішацца ў Dockerfile), стварае новы пласт у Image.
Нават гледзячы на левую частку можна прыкладна зразумець, што адбываецца. Мы кажам: "ствары нам тэчку" - гэта адзін пласт. "Зрабі тэчку працоўнай" - гэта яшчэ адзін пласт, і гэтак далей. Плаццёны пірог спрашчае жыццё. Калі я ствару яшчэ адзін Dockerfile і ў апошнім радку штосьці зменю - запушчу не "python" "main.py", а што-небудзь іншае, або ўсталюю залежнасці з іншага файла - то папярэднія пласты будуць перавыкарыстаныя, як кэш.
малюнак - Гэта ўпакоўка кантэйнера, з выявы запускаюцца кантэйнеры. Калі глядзець на Docker з пункта гледжання пакетнага мэнэджара (як быццам мы працуем з deb або rpm-пакетамі), то image – гэта ў сутнасці rpm-пакет. Праз yum install мы можам паставіць дадатак, выдаліць яго, знайсці ў рэпазітары, спампаваць. Тут прыкладна тое ж самае: з выявы запускаюцца кантэйнеры, яны захоўваюцца ў Docker registry (па аналогіі з yum, у рэпазітары), і кожны image мае хэш SHA-256, імя і тэг.
Image збіраецца па інструкцыі з Dockerfile. Кожная інструкцыя з Dockerfile стварае новы пласт. Пласты могуць выкарыстоўвацца паўторна.
Docker registry - Гэта рэпазітар вобразаў Docker. Па аналогіі з АС, у Docker ёсць агульнадаступны стандартны рэестр – dockerhub. Але можна сабраць свой рэпазітар, свой Docker registry.
Кантэйнер - тое, што запускаецца з выявы. Па інструкцыі з Dockerfile сабралі выяву, затым мы яго з гэтай выявы запускаем. Гэты кантэйнер ізаляваны ад астатніх кантэйнераў, ён павінен утрымоўваць у сабе ўсё неабходнае для працы прыкладання. Пры гэтым адзін кантэйнер - адзін працэс. Здараецца, што даводзіцца рабіць два працэсы, але гэта некалькі супярэчыць ідэалогіі Docker.
Патрабаванне "адзін кантэйнер - адзін працэс" звязана з PID Namespace. Калі ў Namespace запускаецца працэс з PID 1, калі ён раптам памрэ, то ўвесь кантэйнер таксама памірае. Калі ж там запушчана два працэсы: адзін жыве, а другі памёр, то кантэйнер усё роўна працягне жыць. Але гэта да пытання Best Practices, мы пра іх пагаворым у іншых матэрыялах.
Больш дэталёва вывучыць асаблівасці і поўную праграму курса можна па спасылцы: «
Аўтар: Марсэль Ібраеў, сертыфікаваны адміністратар Kubernetes, практыкуючы інжынер у кампаніі Southbridge, спікер і распрацоўшчык курсаў Слёрм.
Крыніца: habr.com