Какво е повторяем билд?
Детерминистичният билд или възпроизводимият билд са малко по-различни, но могат да се разберат като едно и също от тази статия.
Възпроизводими билдове се отнасят доМногократни изпълнения на процеса на изграждане с една и съща входна и строителна среда могат да дадат абсолютно еднакви резултати。 Тази технология е важна за разработката, разпространението и валидирането на сигурността на софтуер.
Билд е възпроизводим, ако дава абсолютно същия изход, независимо кога и къде се изпълнява. Без значение на кой компютър работиш, по кое време на деня и какви външни услуги използваш в мрежата, възпроизводимите билдове произвеждат един и същ изход байт по байт. Това е страхотно както за разработка (защото възпроизводимите билдове са лесни за споделяне между различни устройства на разработчиците), така и за продукция (защото е лесно да се увериш, че резултатите от възпроизводимите билдове не са били манипулирани – просто пусни билда отново на собствената си машина и провери дали резултатите са последователни!). са много полезни.
Трите стълба на повторяемите билдове
Стълб 1: Повторяеми билдове
Повторяемостта на билд се отнася до това, което се случва с самата машина за сглобяване. Ако приемем, че нашите билд входове са налични и нищо не се променя в света около нас, дали нашата конфигурация дава същия изход при повторение?
Детерминистичен план за инсталация
Първото, най-простото и най-очевидно изискване при повторяема конфигурация е детерминистичен план за инсталиране на зависимости.
В повечето езици е толкова просто, колкото да провериш заключен файл. Съвременните инструменти за сглобяване често позволяват на проекти да изразяват директни изисквания за зависимости като ограничения и след това да ги разрешават, за да генерират план за инсталация (списък с имена на зависимости и двойки версии за инсталиране). Много от тези инструменти също генерират заключващи файлове за сериализирани инсталационни планове. Разработчиците могат да подават тези заключващи файлове към контрол на версиите, така че бъдещите билдове да използват същите имена и версии на зависимости.
Имайте предвид, че ни трябва и детерминистичен в самата конфигурация на зависимости (не само при избора на версия), а детерминистичният план за инсталация не ни позволява да постигнем това!
Детерминирана конструкция
След като знаем какво да построим, самият ни билд (включително нашия собствен код и билд на зависимост) трябва да бъде детерминиран.
Това всъщност може да не е проблем за проекти без стъпка на компилация! Например, проект Node с всички зависимости е чист JavaScript и не е необходима допълнителна работа за постигане на ефективна детерминираност.
За проекти, които включват стъпки по компилация или превод (компилация от източник към източник), осигуряването на детерминизъм е най-трудната част от изграждането на възпроизводима конфигурация. Процесът на компилация може имплицитно да въведе недетерминизъм по различни начини, включително:
- Скриптовете за изграждане на програми с Тюринг могат да променят компилирания изход по желание.
- Скриптове след инсталацията, които разчитат на изпълними файлови системи или мрежови повиквания.
- C свързване към пакет, инсталиран от системата, където връзките на различни системи с различни заглавия могат да дават различни изходи.
- Стъпки за създаване на файл, който чете извън контрола на версиите.
- Изградете стъпки за генериране на времеви печати чрез системно време.
- Стъпки за изграждане на зависимости, които не са изразени в плана за инсталиране на мрежово изтегляне (например, изтегляне на NPM зависимост от GitHub за кеширана бинарна конфигурация, която е с C-bound).
- Променете поведението според текущо зададената променлива на средата, но не подавайте билд с конфигурацията на променливата на средата.
Не всички от тези поведения непременно създават несигурност, когато са правилно настроени, но правилното конфигуриране на процеса на изграждане може да бъде сложно и трудно. Например, можете да прочетете този блог пост за несигурността при Chromium билдове. Много от тези проблеми могат да бъдат смекчени чрез контролиране на местната строителна среда, което ще обсъдим в следващия раздел.
Стълб 2: Неизменна среда
Дори при повторяеми билдове, трябва да сме сигурни, че входните данни за билд не се променят. Често това означава, че искаме да се уверим, че надграждаме върху непроменима снимка на обкръжението ни.
Неизменна локална среда
Както обсъдихме по-горе, често срещан източник на несигурност при билдовете е разчитането на "зависимости", които не се улавят от инструмента за билд. Системните библиотеки, ограничени към C, са най-често срещаните примери, но и други локални фактори на околната среда като настройки на променливи в околната среда и файлове извън обхвата на контрола на версиите, също могат да повлияят на билда.
Лесен начин да се смекчи този проблем е да се стартира билдът в известен, неизменим контейнер. Например, контейнерно време за изпълнение като Docker помага да се гарантира, че всички използват едни и същи системни зависимости, едни и същи променливи на околната среда и работят на една и съща файлова система. Освен това е лесно да се провери, че съдържанието на контейнера съвпада с известен добър build контейнер, и ако е необходимо, контейнерът може лесно да бъде напълно премахнат от известния добър образ и възстановен.
Обърнете внимание, че сме много ясни относно известните контейнери или известните изображения на контейнери. Не е достатъчно просто да подадеш Dockerfile! Защо? Защото самият Dockerfile не описва напълно възпроизводим процес на изграждане на Docker образи, тъй като те не работят в неизменна глобална среда.
Неизменна глобална среда
Системите за изграждане често взаимодействат с външни услуги, за да изпълняват задачи като разрешаване на версии и изтегляне на зависимости. Но външните услуги се променят често.
Ако пуснете apt install nodejs днес, ще получите различни резултати от миналата година, а вероятно и следващата година ще получите различни. Затова самите Dockerfiles не могат да опишат възпроизвеждаеми билдове – стартирането на един и същ Dockerfile в различни моменти ще даде различни изходи на билдове!
Простото решение тук е да конфигурирате билда, когато е възможно, като се специфицира точна версия (идеално и точен хеш на съдържанието), така че бъдещите версии да използват същата версия като текущата. Но външните услуги също могат неочаквано да променят поведението си – наистина песимистична възпроизводима конфигурация управлява вътрешен образ с възможно най-много мрежови ресурси.
Стълб 3: Наличност на ресурси
Да кажем, че нашият билд е повтарящ се и светът под краката ни не се променя. Всичко, което ни трябва сега, е достъп до входа за билд. Изглежда просто, нали? Добре......
Регистърът понякога се проваля
Повечето Node разработчици са имали поне един NPM прекъсване, по време на който строителният процес без кеширане или огледално използване на NPM пакети е нарушен. Много разработчици на Node също са претърпили премахване на ляв панел и фалшиви устройства, което сериозно увреди на NPM екосистемата и ефективно доведе до прекъсване.
Единственият надежден начин да се избегнат такива прекъсвания в билдовете е да използвате собствено огледало на регистъра на пакети. Когато външни услуги не са налични, изображението може да остане онлайн; Когато официалният регистър изтрие стария пакет, огледалото може да продължи да предоставя услуги. Същият принцип важи и за други отдалечени услуги: освен ако не стартирате собствен образ, наличността на build pipeline е сравнима само с наличността на неговите услуги.
Изборът да управляваш образ на услугата винаги е деликатен компромис. От една страна, регистри като NPM имат специализирани инженерни и оперативни екипи, които имат експертизата да поддържат тези системи онлайн. От друга страна, много по-лесно е да се стартира малък образ за малък набор от зависимости, отколкото всички NPM изображения. Трябва да вземате огледални решения, базирайки се на спецификите на всяка услуга, като вземете предвид надеждността на историческите външни услуги и наличността на вашия екип и нуждите от персонал.
Доставчиците гарантират максимална наличност
Лесен начин да осигурите максимална достъпност на зависимостите на вашия проект е да ги добавите към вашия доставчик. Повечето мениджъри на пакети поддържат някаква форма на "vendoring", което означава, че вместо да разчитаме на изтегляния от външни услуги, съхраняваме изходния код на зависимостта в контрол на версиите, съжителствайки с нашия изходен код. Например, в Node това може да изглежда като ангажиране на node_modules към контрол на версиите.
Въпреки че това решение не е перфектно (в зависимост от това как е настроен доставчикът и проектът, което може да натовари много контрола на версиите), често е най-простото и най-лесно решение за максимална наличност.
Препратка:
Входът към хиперлинк е видим.
Входът към хиперлинк е видим. |