Via deploja paŝo pendas ĉe interna hostnomo dum la sama komando respondas tuj de via tekkomputilo, kaj la klienta konstruo estas identa sur ambaŭ maŝinoj. La job ruliĝas sur GitHub-gastigita runner: virtuala maŝino kreita por ĝuste tiu unu job ene de la nubo de GitHub, sen itinero en vian privatan adresspacon, sen resolver-enskribo por la interna zono kaj sen atestilo, kiun la privata finpunkto akceptus. Nur du dezajnoj konformas al tiu limigo, kaj neniu el ili povas esti ŝaltita poste sen rekonstrui la workflow. Elektu inter ili antaŭ la unua linio de YAML, ĉar migri poste kostas pli ol la unua provo.

La elekto estas inter runner lokita tie, kie la reto jam finiĝas, kaj tunelo, kiu ekzistas dum la daŭro de unu job. Ambaŭ portas produktadajn deplojojn jam de jaroj, kaj ili malsukcesas laŭ malsamaj manieroj.

Kie la limo vere troviĝas

Ĉio, kion vi instalas sur via tekkomputilo, haltas ĉe via tekkomputilo, kaj ĉio, kion vi instalas dum job, malaperas kun la runner. Ne ekzistas persista retnoma spaco, al kiu kliento povus resti ligita inter ruloj, do ĉiu rulo komenciĝas en la sama neatingebla stato. Azure dokumentas tiun limon por Microsoft-gastigitaj agentoj, kie VPN- kaj ExpressRoute-ligoj en privatajn retojn ne estas subtenataj, kaj GitHub-gastigitaj runner'oj portas tiun saman formon de limigo anstataŭ apartan mekanismon. Legu tion kiel produkta eco, ne kiel difekto, kiun oni devus ĉirkaŭiri.

Neniu repository-agordo malstreĉas tion. La job-etikedo decidas, kiu pool prenas la laboron, kaj gastigita etikedo signifas maŝinon ekster via perimetro dum la tuta rulo.

Privata atingeblo do devas veni de io, kion vi posedas. Runner lokita tie, kie la reto jam finiĝas, aŭ tunelo, kiu vivas unu job. La resto de la artikolo temas pri pagi por unu el tiuj du intence, anstataŭ malkovri la fakturon en produktado.

Decidu laŭ rula ofteco, ne laŭ kutimo

Uzu propran runner en la VPC aŭ la oficeja reto, kiam deplojoj alvenas plurfoje tage, kiam la job tuŝas pli ol unu privatan pordon, aŭ kiam platforma teamo jam prizorgas flikadon kaj rotacion de akreditaĵoj. Job sur tiu maŝino solvas kaj enkursigas kiel ajna alia hosto malantaŭ la fajroŝirmilo. La reta laboro okazas unufoje, ĉe instalado, anstataŭ ĉe ĉiu rulo.

Konstruu la tunelon ene de la job, kiam la aliro estas okaza kaj vi ne volas longdaŭran agenton teni akreditaĵon en vian reton. La job iĝas sia propra reta kliento por kelkaj minutoj kaj lasas nenion malantaŭe, kio ofte estas la tuta celo.

Taksku la koston de la tunelo antaŭ ol elekti ĝin. Ĉiu rulo pagas por materialigo de la profilo, klienta starto, preteco kaj malmuntado. Tunelo, kiu leviĝas sed neniam estas kontrolita, produktas deplojon, kiu malsukcesas duonvoje kaj lasas la privatan staton en formo, kiun neniu planis. La ripeto poste malofte estas pura, kaj la dua fiasko kutime estas atribuata al la klienta versio.

Skribi la profilon tiel, ke la paŝo ne povu aborti

Tenu profilojn kaj akreditaĵojn ekster la repository kaj materialigu ilin el sekretoj en la paŝo, kiu bezonas ilin. La ordo decidas, ĉu la job pluvivas: dosiero kreita sub sudo apartenas al root, kaj la senprivilegia ŝelo de la paŝo tiam ne plu povas skribi en ĝin.

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

Ambaŭ detaloj en tiu bloko ekzistas por postaj paŝoj. Kun umask 077 la profilo alvenas en reĝimo 0600 posedata de la runner-uzanto, sudo ankoraŭ povas legi ĝin, kaj la kliento skribas sian protokolon en dosieron, kiun la runner jam posedas, do la preteca buklo povas legi tiun protokolon sen dua sudo. --writepid registras la PID, kiun la malmuntado celos.

Profilo, kiu aŭtentikigas per klienta atestilo, bezonas sian ŝlosilon skribitan same, kaj static-challenge-profilo bezonas la konforman aŭtentikigan reĝimon. Profiloj, kiuj puŝas DNS-opciojn, bezonas openvpn-systemd-resolved sur Ubuntu kaj Debian, ĉar la puŝo sola ŝanĝas nenion sur baza bildo.

Preteco estas sondilo, ne paŭzo

Fiksa sleep teniĝas ĝuste ĝis la koncentrilo malrapidiĝas, kaj tiam la deploja paŝo ruliĝas antaŭ ol ekzistas la itinero. La fiasko tiam aperas ene de la deploja ilo kiel timeout kontraŭ la privata hosto, kio diras nenion pri la tunelo.

- 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

La buklo observas du aferojn, ĉar ĉiu el ili povas esti la rompita duono. getent hosts pruvas, ke la puŝita resolver respondas por tiu nomo, kaj la malfermo de la soketo pruvas, ke io aŭskultas sur la pordo, kiun la deplojo uzos; profilo nur kun itinero bezonas la literalan adreson kaj la forigitan linion getent.

Kiam la buklo eksvalidiĝas, la protokolo estas la sola afero, kiu apartigas tri malsamajn fuŝaĵojn: malakceptitan atestilon, malĝustan aŭtentikigan reĝimon kaj itineron, kiu neniam alvenis. Filtrado antaŭ la tail tenas tiun eligon sufiĉe mallonga por legi, dum la job ankoraŭ fiaskas. Legu la unuan eraron anstataŭ la lastan linion, ĉar kliento, kiu ne povas aŭtentikigi, ripetas sin ĝis la buklo rezignas. ip -4 route show dev tun0 respondas la pli simplan demandon, ĉu itinero estis puŝita entute, kaj ĝi kostas unu linion ene de la sama buklo.

Malmuntado kaj la demando pri la artefakto

Fermu la job per paŝo gardata de if: always(), kiu rulas sudo kill "$(sudo cat "$RUNNER_TEMP/openvpn.pid")" kaj nenion alian. Sur komuna propra runner, neniam mortigu laŭ proceza nomo: alia job povas posedi klienton kun la sama binaraĵo kaj similaj argumentoj. La fiasko tiam falas sur tiun job, kiu ruliĝis due, kaj ĝia protokolo montras al workflow, kiu faris nenion malĝustan.

La gardo gravas, ĉar malsukcesinta deplojo tamen lasas vivan tunelon. Sen always() la puriga paŝo estas preterlasata ĝuste kiam ĝi necesas, kaj la sekva rulo startas kontraŭ kliento jam konektita.

Unu demando restas antaŭ ol adopti iun el la du dezajnoj: ĉu la konstrua stadio entute bezonas privatan itineron? Deploja agento, kiu jam sidas ene de la reto, povas elpreni aprobitan artefakton, kio tenas ordinarajn konstruojn for de la privata vojo. Malpli da joboj tenantaj tunelon signifas malpli da lokoj, kie malnova itinero povas kaŝiĝi.

Rilataj artikoloj