GitHub Actions-ը չի հասնում ձեր VPN-ին՝ սեփական runner, թե թունել
Հոսթինգի runner-ը ձեր ցանցից դուրս գտնվող նոր VM է։ Կա՛մ runner դրեք այդ ցանցի ներսում, կա՛մ թունել բացեք job-ից և սպասեք իրական երթուղու։
Ձեր deploy քայլը կախվում է ներքին hostname-ի վրա, մինչդեռ նույն հրամանը ձեր նոթբուքից անմիջապես պատասխանում է, և client-ի build-ը երկու մեքենաներում նույնական է։ job-ը աշխատում է GitHub-ի հոսթինգի runner-ում՝ վիրտուալ մեքենա, որը ստեղծվել է հենց այդ մեկ job-ի համար GitHub-ի ամպում՝ ձեր մասնավոր հասցեների տարածք երթուղի չկա, ներքին գոտու համար resolver գրառում չկա և ոչ մի վկայական, որը կընդունի մասնավոր endpoint-ը։ Այս սահմանափակմանը տեղավորվում է ընդամենը երկու ձևավորում, և դրանցից ոչ մեկը հնարավոր չէ միացնել ավելի ուշ՝ առանց workflow-ը նորից կառուցելու։ Ընտրեք դրանց միջև YAML-ի առաջին տողից առաջ, որովհետև հետագա տեղափոխումն ավելի թանկ է, քան առաջին փորձը։
Ընտրությունը runner-ի միջև է, որը տեղադրվում է այնտեղ, որտեղ ցանցն արդեն ավարտվում է, և թունելի միջև, որը գոյություն ունի միայն մեկ job-ի ընթացքում։ Երկուսն էլ տարիներ շարունակ կրում են production deploy-ներ և ձախողվում են տարբեր ձևերով։
Որտեղ է իրականում սահմանը
Այն ամենը, ինչ տեղադրում եք նոթբուքում, կանգ է առնում նոթբուքում, իսկ այն ամենը, ինչ տեղադրում եք job-ի ընթացքում, անհետանում է runner-ի հետ։ Չկա մշտական ցանցային namespace, որին client-ը կմնա կապված գործարկումների միջև, ուստի յուրաքանչյուր գործարկում սկսվում է նույն անհասանելի վիճակից։ Azure-ը փաստագրում է այս սահմանը Microsoft-ի հոսթինգի agent-ների համար, որտեղ մասնավոր ցանցեր VPN և ExpressRoute կապերը չեն աջակցվում, իսկ GitHub-ի հոսթինգի runner-ները կրում են նույն ձևի սահմանափակումը, ոչ թե առանձին մեխանիզմ։ Կարդացեք սա որպես արտադրանքի հատկություն, ոչ թե թերություն, որը պետք է շրջանցել։
Ոչ մի պահոցի կարգավորում չի թուլացնում դա։ job-ի պիտակը որոշում է, թե որ pool-ը կվերցնի աշխատանքը, իսկ հոսթինգի պիտակը նշանակում է մեքենա ձեր պարագծից դուրս ամբողջ գործարկման ընթացքում։
Ուստի մասնավոր հասանելիությունը պետք է գա ձեր սեփականությունից։ runner, որը տեղադրված է այնտեղ, որտեղ ցանցն արդեն ավարտվում է, կամ թունել, որը մեկ job է ապրում։ Հոդվածի մնացած մասը վերաբերում է այդ երկուսից մեկի համար գիտակցաբար վճարելուն, փոխարենը production-ում հաշիվը հայտնաբերելու։
Որոշեք գործարկումների հաճախականությամբ, ոչ թե սովորությամբ
Օգտագործեք սեփական runner-ը VPC-ում կամ գրասենյակի ցանցում, երբ deploy-ները օրական մի քանի անգամ են գալիս, երբ job-ը դիպչում է մեկից ավելի մասնավոր պորտի, կամ երբ պլատֆորմի թիմն արդեն պատասխանատու է patch-ների և հավատարմագրերի ռոտացիայի համար։ Այդ մեքենայի վրա job-ը լուծում և երթուղավորում է ինչպես firewall-ի հետևում գտնվող ցանկացած host։ Ցանցային աշխատանքը կատարվում է մեկ անգամ՝ տեղադրման պահին, ոչ թե ամեն գործարկման ժամանակ։
Կառուցեք թունելը job-ի ներսում, երբ հասանելիությունը հազվադեպ է, և դուք չեք ուզում երկար կյանքի agent, որը ձեր ցանցում հավատարմագիր է պահում։ job-ը մի քանի րոպեով դառնում է իր իսկ ցանցային client-ը և հետևում ոչինչ չի թողնում, ինչը հաճախ ամբողջ նպատակն է։
Գնահատեք թունելի արժեքը նախքան այն ընտրելը։ Յուրաքանչյուր գործարկում վճարում է պրոֆիլի ձևավորման, client-ի մեկնարկի, պատրաստվածության և ապամոնտաժման համար։ Թունելը, որը բարձրանում է, բայց երբեք չի ստուգվում, ստեղծում է deploy, որը ձախողվում է կես ճանապարհին և մասնավոր վիճակը թողնում է մի ձևով, որը ոչ ոք չէր պլանավորել։ Դրանից հետո կրկնվող փորձը հազվադեպ է մաքուր լինում, և երկրորդ ձախողումը սովորաբար բարդվում է client-ի տարբերակի վրա։
Պրոֆիլը գրել այնպես, որ քայլը չկարողանա ընդհատվել
Պահեք պրոֆիլներն ու հավատարմագրերը պահոցից դուրս և ձևավորեք դրանք secret-ներից այն քայլում, որը դրանց կարիքն ունի։ Հերթականությունը որոշում է՝ job-ը կդիմանա՞, թե՞ ոչ՝ sudo-ի տակ ստեղծված ֆայլը պատկանում է root-ին, և քայլի արտոնություն չունեցող shell-ն այլևս չի կարող գրել դրա մեջ։
- name: Bring up OpenVPN
run: |
umask 077
profile="$RUNNER_TEMP/client.ovpn"
printf '%s\n' "$VPN_PROFILE" > "$profile"
: > "$RUNNER_TEMP/openvpn.log"
printf '%s\n' "$VPN_CREDENTIALS" > "$RUNNER_TEMP/credentials.txt"
sudo openvpn --daemon openvpn-job \
--config "$profile" \
--auth-user-pass "$RUNNER_TEMP/credentials.txt" \
--writepid "$RUNNER_TEMP/openvpn.pid" \
--log "$RUNNER_TEMP/openvpn.log"Այդ բլոկի երկու մանրամասնություններն էլ նախատեսված են հետագա քայլերի համար։ umask 077 դեպքում պրոֆիլը հասնում է 0600 ռեժիմով՝ runner-ի օգտատիրոջ սեփականության ներքո, sudo-ն դեռ կարող է կարդալ այն, և client-ը լոգը գրում է ֆայլում, որն արդեն պատկանում է runner-ին, ուստի պատրաստվածության ցիկլը կարող է կարդալ այդ լոգը առանց երկրորդ sudo-ի։ --writepid գրանցում է այն PID-ը, որին ուղղվելու է ապամոնտաժումը։
client վկայականով նույնականացվող պրոֆիլը պահանջում է իր բանալին գրված նույն ձևով, իսկ static-challenge պրոֆիլին անհրաժեշտ է համապատասխան auth ռեժիմ։ DNS ընտրանքներ push անող պրոֆիլներին Ubuntu-ում և Debian-ում անհրաժեշտ է openvpn-systemd-resolved, որովհետև push-ը միայնակ բազային իմիջում ոչինչ չի փոխում։
Պատրաստվածությունը զոնդ է, ոչ դադար
Հաստատուն sleep-ը դիմանում է ուղիղ այն պահին, երբ կենտրոնացուցիչը դանդաղում է, և այդ ժամանակ deploy-ի քայլը վազում է մինչև երթուղու գոյությունը։ Ձախողումն այնուհետև ի հայտ է գալիս deploy-ի գործիքի ներսում որպես timeout մասնավոր host-ի նկատմամբ, ինչը ոչինչ չի ասում թունելի մասին։
- name: Await private endpoint
run: |
for attempt in $(seq 1 30); do
if getent hosts private.example.internal > /dev/null && \
timeout 3 bash -c 'exec 3<>/dev/tcp/private.example.internal/8443' 2>/dev/null; then
echo "endpoint answered after $attempt attempts"
exit 0
fi
sleep 2
done
grep -Ei 'error|verify|auth' "$RUNNER_TEMP/openvpn.log" | tail -40
exit 1Ցիկլը դիտում է երկու բան, որովհետև դրանցից ցանկացածը կարող է կոտրված կեսը լինել։ getent hosts ապացուցում է, որ push արված resolver-ը պատասխանում է այդ անվան համար, իսկ socket-ի բացումը ապացուցում է, որ ինչ-որ բան լսում է այն պորտում, որը կօգտագործի deploy-ը՝ միայն երթուղի ունեցող պրոֆիլին պետք է բառացի հասցեն և ջնջված getent տողը։
Երբ ցիկլը սպառվում է, լոգը միակն է, որ բաժանում է երեք տարբեր խափանում՝ մերժված վկայական, սխալ auth ռեժիմ և երթուղի, որը երբեք չեկավ։ tail-ից առաջ զտելը պահում է այդ ելքը բավական կարճ, որպեսզի կարդացվի, մինչ job-ը դեռ ձախողվում է։ Կարդացեք առաջին սխալը, ոչ թե վերջին տողը, որովհետև client-ը, որը չի կարող նույնականանալ, կրկնում է իրեն այնքան, մինչև ցիկլը հանձնվի։ ip -4 route show dev tun0 պատասխանում է ավելի պարզ հարցին՝ արդյոք ընդհանրապես երթուղի push է արվել, և նույն ցիկլում արժե մեկ տող։
Ապամոնտաժում և արտեֆակտի հարցը
Փակեք job-ը if: always() պաշտպանված քայլով, որը կատարում է sudo kill "$(sudo cat "$RUNNER_TEMP/openvpn.pid")" և ոչ այլ բան։ Համօգտագործվող սեփական runner-ում երբեք մի սպանեք ըստ պրոցեսի անվան՝ մեկ այլ job կարող է ունենալ client նույն binary-ով և նման արգումենտներով։ Ձախողումն այն ժամանակ ընկնում է այն job-ի վրա, որը երկրորդն աշխատեց, և նրա լոգը ցույց է տալիս մի workflow, որը ոչինչ սխալ չի արել։
Պաշտպանությունը կարևոր է, որովհետև ձախողված deploy-ը միևնույն է թողնում է կենդանի թունել։ Առանց always() մաքրման քայլը բաց է թողնվում հենց այն պահին, երբ այն պետք է, և հաջորդ գործարկումը սկսվում է արդեն միացված client-ի դեմ։
Ցանկացած ձևավորում ընդունելուց առաջ մնում է մեկ հարց՝ արդյոք build-ի փուլին իսկապես պե՞տք է մասնավոր երթուղի։ deployment agent-ը, որն արդեն նստած է ցանցում, կարող է քաշել հաստատված արտեֆակտ, և դա սովորական build-երը հեռու է պահում մասնավոր ուղուց։ Ավելի քիչ job, որ թունել է պահում, նշանակում է ավելի քիչ տեղ, որտեղ հին երթուղին կարող է թաքնվել։