GitHub Actions nuk e arrin VPN-në tuaj: runner vetjak apo tunel
Një runner i strehuar është një VM e re jashtë rrjetit tuaj. Ose vendos një runner brenda atij rrjeti, ose hap një tunel nga job-i dhe prit një rrugë të vërtetë.
Hapi yt i deploy varet në një hostname të brendshëm, ndërsa e njëjta komandë përgjigjet menjëherë nga laptopi, dhe build-i i klientit në të dy makinat është identik. Job-i ekzekutohet në një runner të strehuar nga GitHub: një makinë virtuale e krijuar për atë job të vetëm në renë e GitHub, pa rrugë drejt hapësirës tënde private të adresave, pa regjistrim resolver për zonën e brendshme dhe pa certifikatë që do të pranonte endpoint-i privat. Vetëm dy dizajne përshtaten me këtë kufizim, dhe asnjëri nuk mund të aktivizohet më vonë pa rindërtuar workflow-in. Zgjidh mes tyre përpara rreshtit të parë YAML, sepse migrimi më pas kushton më shumë se përpjekja e parë.
Zgjedhja është mes një runner të vendosur aty ku rrjeti tashmë mbaron dhe një tuneli që jeton vetëm një job. Të dy kanë mbajtur deploy-e prodhimi për vite, dhe dështojnë në mënyra të ndryshme.
Ku ndodhet në të vërtetë kufiri
Çdo gjë që instalon në laptop ndalon në laptop, dhe çdo gjë që instalon gjatë një job zhduket bashkë me runner-in. Nuk ka namespace rrjeti të përhershëm ku klienti të mbetet i lidhur mes ekzekutimeve, prandaj çdo ekzekutim nis nga i njëjti gjendje e paarritshme. Azure e dokumenton këtë kufi për agjentët e strehuar nga Microsoft, ku lidhjet VPN dhe ExpressRoute drejt rrjeteve private nuk mbështeten, dhe runner-ët e strehuar nga GitHub mbajnë të njëjtën formë kufizimi dhe jo një mekanizëm të veçantë. Lexoje si veti e produktit, jo si defekt për t'u anashkaluar.
Asnjë rregullim i repository-t nuk e lehtëson këtë. Etiketa e job-it vendos cili pool e merr punën, dhe një etiketë e strehuar do të thotë makinë jashtë perimetrit tënd për gjithë ekzekutimin.
Prandaj arritshmëria private duhet të vijë nga diçka që zotëron ti. Një runner i vendosur aty ku rrjeti tashmë mbaron, ose një tunel që jeton një job. Pjesa tjetër e artikullit flet për të paguar me qëllim për njërën prej tyre, në vend që të zbulohesh faturën në prodhim.
Vendos sipas shpeshtësisë së ekzekutimeve, jo sipas njohjes
Përdor një runner vetjak brenda VPC-së ose rrjetit të zyrës kur deploy-et vijnë disa herë në ditë, kur job-i prek më shumë se një port privat, ose kur një ekip platforme tashmë kujdeset për patch-et dhe rotacionin e kredencialeve. Një job në atë makinë zgjidh dhe rrugëton si çdo host tjetër pas firewall-it. Puna e rrjetit bëhet një herë, në instalim, dhe jo në çdo ekzekutim.
Ndërto tunelin brenda job-it kur aksesi është i rastësishëm dhe nuk dëshiron një agjent jetëgjatë që mban një kredencial në rrjetin tënd. Job-i bëhet klienti i vetë rrjetit për disa minuta dhe nuk lë asgjë pas, që shpesh është i gjithë qëllimi.
Vlerëso koston e tunelit përpara se ta zgjedhësh. Çdo ekzekutim paguan për materializimin e profilit, nisjen e klientit, gatishmërinë dhe çmontimin. Një tunel që ngrihet por nuk verifikohet kurrë prodhon një deploy që dështon në gjysmë rrugë dhe lë gjendjen private në një formë që askush nuk e planifikoi. Riprovimi pas kësaj rrallë është i pastër, dhe dështimi i dytë zakonisht i faturohet versionit të klientit.
Ta shkruash profilin që hapi të mos ndërpritet
Mbaj profilet dhe kredencialet jashtë repository-t dhe materializoji nga secret në hapin që i nevojiten. Rendi vendos nëse job-i mbijeton: një skedar i krijuar nën sudo i përket root, dhe shell-i i hapit pa privilegje nuk mund të shkruajë më në të.
- 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"Të dy detajet në atë bllok janë për hapat e mëvonshëm. Me umask 077 profili zbret në modalitetin 0600 dhe i përket përdoruesit të runner-it, sudo ende mund ta lexojë, dhe klienti e shkruan logun e vet në një skedar që runner-i e zotëron tashmë, kështu që cikli i gatishmërisë mund ta lexojë atë log pa një sudo të dytë. --writepid regjistron PID-in që do të synojë çmontimi.
Një profil që vërtetohet me certifikatë klienti ka nevojë për çelësin e vet të shkruar në të njëjtën mënyrë, dhe një profil static-challenge ka nevojë për modalitetin auth që përputhet. Profilet që shtyjnë opsione DNS kanë nevojë për openvpn-systemd-resolved në Ubuntu dhe Debian, sepse shtytja e vetme nuk ndryshon asgjë në një imazh bazë.
Gatishmëria është një sondë, jo një pauzë
Një sleep fiks mban deri në momentin kur koncentratori ngadalësohet, dhe atëherë hapi i deploy ekzekutohet përpara se rruga të ekzistojë. Dështimi pastaj shfaqet brenda mjetit të deploy si timeout drejt host-it privat, që nuk thotë asgjë për tunelin.
- 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 1Cikli vëzhgon dy gjëra sepse secila mund të jetë gjysma e prishur. getent hosts vërteton që resolver-i i shtyrë përgjigjet për atë emër, dhe hapja e socket-it vërteton që diçka dëgjon në portin që do të përdorë deploy-i; një profil vetëm me rrugë ka nevojë për adresën e mirëfilltë dhe rreshtin getent të fshirë.
Kur cikli skadon, logu është e vetmja që ndan tre defekte të ndryshme: një certifikatë e refuzuar, një modalitet auth i gabuar dhe një rrugë që nuk arriti kurrë. Filtrimi përpara tail-it e mban atë dalje mjaft të shkurtër për t'u lexuar ndërsa job-i vazhdon të dështojë. Lexo gabimin e parë dhe jo rreshtin e fundit, sepse klienti që nuk mund të vërtetohet përsëritet derisa cikli të dorëzohet. ip -4 route show dev tun0 përgjigjet pyetjes më të thjeshtë nëse një rrugë u shty fare, dhe kushton një rresht brenda të njëjtit cikël.
Çmontimi dhe pyetja e artefaktit
Mbyll job-in me një hap të mbrojtur nga if: always() që ekzekuton sudo kill "$(sudo cat "$RUNNER_TEMP/openvpn.pid")" dhe asgjë tjetër. Në një runner vetjak të përbashkët mos vrit kurrë sipas emrit të procesit: një job tjetër mund të zotërojë një klient me të njëjtin binary dhe argumente të ngjashme. Dështimi atëherë bie mbi job-in që ekzekutoi i dyti, dhe logu i tij tregon një workflow që nuk bëri asgjë të gabuar.
Mbrojtja ka rëndësi sepse një deploy i dështuar lë gjithsesi një tunel të gjallë. Pa always() hapi i pastrimit kapërcehet pikërisht kur nevojitet, dhe ekzekutimi tjetër nis kundër një klienti tashmë të lidhur.
Mbetet një pyetje përpara se të miratosh cilindo dizajn: a i nevojitet fare fazës build një rrugë private? Një agjent deployment që tashmë rri brenda rrjetit mund të tërheqë një artefakt të miratuar, dhe kjo i mban build-et e zakonshme larg rrugës private. Më pak job-e që mbajnë një tunel do të thotë më pak vende ku një rrugë e vjetër mund të fshihet.