Вашиот чекор за деплој виси на внатрешно име на хост, додека истата команда одговара веднаш од лаптопот, а клиентскиот build на двете машини е идентичен. Job се извршува на runner хостиран од GitHub: виртуелна машина создадена за точно тој еден job во облакот на GitHub, без рута до вашиот приватен адресен простор, без resolver запис за внатрешната зона и без сертификат што приватниот endpoint ќе го прифати. Само два дизајна се вклопуваат во тоа ограничување и ниту еден не може да се вклучи подоцна без повторно градење на workflow. Изберете меѓу нив пред првиот ред YAML, бидејќи миграцијата подоцна чини повеќе од првиот обид.

Изборот е меѓу runner поставен таму каде што мрежата веќе завршува и тунел што живее колку еден job. И двете со години носат продукциски деплои и паѓаат на различен начин.

Каде всушност лежи границата

Сѐ што инсталирате на лаптопот запира на лаптопот, а сѐ што инсталирате за време на job исчезнува со runner. Нема постојан мрежен namespace на кој клиентот би останал прикачен меѓу пуштањата, па секое пуштање почнува во истото недостапно состојба. Azure ја документира таа граница за агентите хостирани од Microsoft, каде VPN и ExpressRoute врски во приватни мрежи не се поддржани, а runner хостирани од GitHub носат иста форма на ограничување наместо посебен механизам. Читајте го тоа како својство на производот, не како дефект што треба да се заобиколи.

Ниту едно поставување на репозиториумот сѐ уште не го олеснува тоа. Ознаката на job одлучува кој pool ќе ја преземе работата, а хостирана ознака значи машина надвор од вашиот периметар за целото пуштање.

Приватната достапност затоа мора да дојде од нешто што го поседувате. Runner поставен таму каде што мрежата веќе завршува, или тунел што живее еден job. Останатото во статијата е за тоа свесно да платите за едно од двете, наместо сметката да ја откриете во продукција.

Одлучувајте по фреквенција на пуштања, не по познатост

Посегнете по сопствен runner во VPC или канцелариската мрежа кога деплоите пристигнуваат неколку пати дневно, кога job допира повеќе од еден приватен порт или кога тимот за платформа веќе се грижи за печови и ротација на акредитиви. Job на таа машина резолвира и рутира како секој друг хост зад firewall. Мрежната работа се случува еднаш, при инсталацијата, наместо при секое пуштање.

Изградете го тунелот внатре во job кога пристапот е повремен и не сакате долговечен агент што држи акредитив во вашата мрежа. Job станува сопствен мрежен клиент за неколку минути и не остава ништо зад себе, што често е целата поента.

Проценете ја цената на тунелот пред да го изберете. Секое пуштање плаќа за материјализирање на профилот, стартување на клиентот, подготвеност и демонтажа. Тунел што се крева но никогаш не се проверува дава деплој што паѓа на половина пат и остава приватна состојба во форма што никој не ја планирал. Повторувањето потоа ретко е чисто, а втората грешка обично се припишува на верзијата на клиентот.

Пишување на профилот така што чекорот не може да прекине

Држете ги профилите и акредитивите надвор од репозиториумот и материјализирајте ги од тајни во чекорот што ги бара. Редоследот одлучува дали job ќе преживее: датотека создадена под sudo му припаѓа на root, а непривилегираната обвивка на чекорот тогаш не може да пишува во неа.

- 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 сѐ уште може да го чита, а клиентот го пишува својот лог во датотека што runner веќе ја поседува, па циклусот за подготвеност може да го чита тој лог без втор sudo. --writepid го запишува PID на кој ќе цели демонтажата.

Профил што се автентицира со клиентски сертификат бара својот клуч напишан на ист начин, а профил со статичен предизвик бара соодветен режим на автентикација. Профилите што туркаат DNS опции бараат openvpn-systemd-resolved на Ubuntu и Debian, бидејќи самото туркање ништо не менува на основна слика.

Подготвеноста е сонда, не пауза

Фиксен sleep издржува точно до моментот кога концентраторот ќе забави, а тогаш чекорот за деплој тргнува пред рутата да постои. Грешката потоа излегува внатре во алатката за деплој како timeout кон приватниот хост, што ништо не кажува за тунелот.

- 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 докажува дека турнатиот resolver одговара за тоа име, а отворањето на сокетот докажува дека нешто слуша на портот што деплојот ќе го користи; профил само со рута бара буквалната адреса и избришаниот ред getent.

Кога циклусот ќе истече, логот е единственото што раздвојува три различни дефекти: одбиен сертификат, погрешен режим на автентикација и рута што никогаш не пристигнала. Филтрирањето пред tail го држи тој излез доволно краток за читање додека job сѐ уште паѓа. Читајте ја првата грешка наместо последниот ред, бидејќи клиент што не може да се автентицира се повторува додека циклусот не се откаже. ip -4 route show dev tun0 одговара на поедноставното прашање дали рута воопшто била турната и чини еден ред во истиот циклус.

Демонтажа и прашањето за артефактот

Затворете го job со чекор заштитен со if: always() што извршува sudo kill "$(sudo cat "$RUNNER_TEMP/openvpn.pid")" и ништо друго. На споделен сопствен runner никогаш не убивајте по име на процес: друг job може да поседува клиент со истиот бинарен фајл и слични аргументи. Грешката тогаш паѓа на job што работел втор, а неговиот лог покажува на workflow што не згрешил ништо.

Заштитата е важна бидејќи неуспешен деплој сепак остава жив тунел. Без always() чекорот за чистење се прескокнува точно кога е потребен, а следното пуштање стартува кон веќе поврзан клиент.

Останува едно прашање пред да усвоите кој било од двата дизајна: дали фазата на build воопшто бара приватна рута? Агент за деплој што веќе седи во мрежата може да повлече одобрен артефакт, што ги држи обичните build-ови подалеку од приватната патека. Помалку job-ови што држат тунел значи помалку места каде застарена рута може да се скрие.

Поврзани статии