Nakabitin ang deploy step mo sa isang internal hostname habang ang parehong command ay agad sumasagot mula sa laptop mo, at ang client build sa dalawang makina ay magkapareho. Tumatakbo ang job sa runner na hosted ng GitHub: isang virtual machine na ginawa para sa isang job na iyon sa loob ng cloud ng GitHub, walang ruta papasok sa private address space mo, walang resolver entry para sa internal zone at walang certificate na tatanggapin ng private endpoint. Dalawa lang ang disenyong kasya sa hadlang na iyon, at wala sa kanila ang maisi-switch on mamaya nang hindi binubuo muli ang workflow. Pumili bago ang unang linya ng YAML, dahil mas mahal ang paglipat pagkatapos kaysa sa unang pagtatangka.

Ang pagpili ay nasa pagitan ng runner na inilagay kung saan nagtatapos na ang network at ng tunnel na nabubuhay hanggang isang job lang. Pareho silang nagdala ng production deploy nang maraming taon, at nabibigo sila sa magkaibang paraan.

Saan talaga ang hangganan

Lahat ng ini-install mo sa laptop ay humihinto sa laptop, at lahat ng ini-install mo habang may job ay nawawala kasama ng runner. Walang permanenteng network namespace na makakapitan ng client sa pagitan ng mga run, kaya bawat run ay nagsisimula sa parehong hindi maabot na estado. Dokumentado ng Azure ang hangganang ito para sa mga agent na hosted ng Microsoft, kung saan hindi sinusuportahan ang VPN at ExpressRoute links papasok sa private network, at ang mga runner na hosted ng GitHub ay may parehong anyo ng paghihigpit, hindi hiwalay na mekanismo. Basahin ito bilang katangian ng produkto, hindi bilang depektong dapat lusutan.

Walang setting ng repository ang nagpapaluwag nito. Ang label ng job ang nagpapasya kung aling pool ang kukuha ng trabaho, at ang hosted na label ay nangangahulugang makinang labas ng perimeter mo sa buong run.

Kaya ang private reachability ay dapat magmula sa isang bagay na pag-aari mo. Runner na inilagay kung saan nagtatapos na ang network, o tunnel na nabubuhay nang isang job. Ang natitirang bahagi ng artikulo ay tungkol sa sadyang pagbabayad para sa isa sa dalawa, sa halip na madiskubre ang bill sa production.

Magpasya ayon sa dalas ng run, hindi sa pagiging pamilyar

Gumamit ng sariling runner sa loob ng VPC o network ng opisina kapag ilang beses sa isang araw ang deploy, kapag higit sa isang private port ang hinahawakan ng job, o kapag ang platform team na ang may hawak ng patching at credential rotation. Ang job sa makinang iyon ay nagre-resolve at nagru-route tulad ng anumang host sa likod ng firewall. Ang gawaing network ay nangyayari isang beses, sa pag-install, hindi sa bawat run.

Itayo ang tunnel sa loob ng job kapag paminsan-minsan lang ang access at ayaw mo ng mahabang buhay na agent na may hawak ng credential papasok sa network mo. Nagiging sariling network client ang job nang ilang minuto at walang naiiwan, na kadalasan ang buong punto.

Tantiyahin ang gastos ng tunnel bago ito piliin. Bawat run ay nagbabayad para sa pagbuo ng profile, pag-start ng client, paghihintay ng kahandaan at pagtatanggal. Ang tunnel na umaangat pero hindi kailanman na-verify ay gumagawa ng deploy na nabibigo sa kalagitnaan at nag-iiwan ng private state sa anyong walang nagplano. Bihirang malinis ang muling pagtatangka pagkatapos nito, at ang ikalawang kabiguan ay kadalasang isinasisi sa bersyon ng client.

Isulat ang profile para hindi maputol ang step

Itago ang profile at credential sa labas ng repository at buuin sila mula sa secret sa step na nangangailangan. Ang pagkakasunod-sunod ang nagpapasya kung mabubuhay ang job: ang file na ginawa sa ilalim ng sudo ay pag-aari ng root, at hindi na makakasulat dito ang shell ng step na walang pribilehiyo.

- 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"

Ang dalawang detalye sa bloke na iyon ay para sa mga susunod na step. Sa umask 077 dumarating ang profile sa mode 0600 na pag-aari ng runner user, nababasa pa rin ito ng sudo, at isinusulat ng client ang log nito sa file na pag-aari na ng runner, kaya nababasa ng readiness loop ang log na iyon nang walang ikalawang sudo. Itinatala ng --writepid ang PID na pupuntiryahin ng pagtatanggal.

Ang profile na nagpapatunay gamit ang client certificate ay kailangang nakasulat ang key sa parehong paraan, at ang static-challenge profile ay kailangan ang tugmang auth mode. Ang mga profile na nagtutulak ng DNS option ay kailangan ng openvpn-systemd-resolved sa Ubuntu at Debian, dahil ang push lang ay walang binabago sa base image.

Ang kahandaan ay probe, hindi paghinto

Ang fixed sleep ay tumatagal hanggang sa maging mabagal ang concentrator, at pagkatapos ay tumatakbo ang deploy step bago pa umiral ang ruta. Ang kabiguan ay lumilitaw pagkatapos sa loob ng deploy tool bilang timeout sa private host, na walang sinasabi tungkol sa tunnel.

- 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

Dalawang bagay ang binabantayan ng loop dahil alinman sa kanila ang maaaring sira. Pinatutunayan ng getent hosts na sumasagot ang pushed resolver para sa pangalang iyon, at pinatutunayan ng pagbukas ng socket na may nakikinig sa port na gagamitin ng deploy; ang profile na ruta lang ay kailangan ang literal na address at tanggalin ang linyang getent.

Kapag nag-expire ang loop, ang log ang tanging bagay na naghihiwalay sa tatlong magkaibang sira: tinanggihang certificate, maling auth mode at rutang hindi dumating. Ang pagsala bago ang tail ay nagpapanatili sa output na sapat na maikli para mabasa habang nabibigo pa ang job. Basahin ang unang error sa halip na ang huling linya, dahil ang client na hindi makapagpatunay ay umuulit hanggang sumuko ang loop. Sinasagot ng ip -4 route show dev tun0 ang mas simpleng tanong kung may naipush na ruta, at isang linya lang ang halaga nito sa parehong loop.

Pagtatanggal, at ang tanong tungkol sa artifact

Isara ang job sa isang step na pinoprotektahan ng if: always() at nagpapatakbo lang ng sudo kill "$(sudo cat "$RUNNER_TEMP/openvpn.pid")". Sa shared na sariling runner, huwag kailanman pumatay ayon sa pangalan ng proseso: maaaring may client ang ibang job na pareho ang binary at katulad ang argumento. Ang kabiguan ay babagsak sa job na tumakbo pangalawa, at ang log nito ay tumuturo sa workflow na walang ginawang mali.

Mahalaga ang proteksyon dahil ang bigong deploy ay nag-iiwan pa rin ng buhay na tunnel. Kung wala ang always(), ang cleanup step ay nilalaktawan sa mismong sandaling kailangan ito, at ang susunod na run ay magsisimula laban sa client na nakakonekta na.

May isang tanong pa bago tanggapin ang alinman sa dalawang disenyo: kailangan ba talaga ng private route ng build stage? Ang deployment agent na nakaupo na sa loob ng network ay makakakuha ng aprubadong artifact, at inilalayo nito ang ordinaryong build sa private path. Kaunting job na may hawak ng tunnel ay kaunting lugar kung saan makakapagtago ang lumang ruta.

Kaugnay na mga artikulo