Ձեր 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, որ թունել է պահում, նշանակում է ավելի քիչ տեղ, որտեղ հին երթուղին կարող է թաքնվել։

Առնչվող հոդվածներ