Գնա՞լ: Բաշ! Հանդիպեք shell-օպերատորին (վերանայեք և վիդեո զեկույց KubeCon EU'2020-ից)
Այս տարի եվրոպական Kubernetes-ի գլխավոր համաժողովը՝ KubeCon + CloudNativeCon Europe 2020, վիրտուալ էր: Սակայն ձևաչափի նման փոփոխությունը մեզ չխանգարեց ներկայացնելու մեր վաղուց ծրագրված «Գնացե՞լ» զեկույցը։ Բաշ! Meet the Shell-operator»-ը նվիրված մեր բաց կոդով նախագծին shell-օպերատոր.
Այս հոդվածը, որը ոգեշնչված է ելույթից, ներկայացնում է Kubernetes-ի համար օպերատորների ստեղծման գործընթացը պարզեցնելու մոտեցումը և ցույց է տալիս, թե ինչպես կարող եք ձեր սեփականը ստեղծել նվազագույն ջանքերով՝ օգտագործելով shell-օպերատոր:
Ներկայացնելով զեկույցի տեսանյութը (~23 րոպե անգլերեն, նկատելիորեն ավելի տեղեկատվական, քան հոդվածը) և դրանից հիմնական քաղվածքը տեքստային տեսքով: Գնա՛
Flant-ում մենք մշտապես օպտիմիզացնում և ավտոմատացնում ենք ամեն ինչ: Այսօր մենք կխոսենք մեկ այլ հետաքրքիր հայեցակարգի մասին: Հանդիպում. cloud-native shell scripting!
Այնուամենայնիվ, եկեք սկսենք համատեքստից, որում տեղի է ունենում այս ամենը. Kubernetes:
Kubernetes API և կարգավորիչներ
Kubernetes-ում API-ն կարող է ներկայացվել որպես մի տեսակ ֆայլի սերվեր՝ դիրեկտորիաներով յուրաքանչյուր տեսակի օբյեկտի համար: Այս սերվերի օբյեկտները (ռեսուրսները) ներկայացված են YAML ֆայլերով: Բացի այդ, սերվերն ունի հիմնական API, որը թույլ է տալիս անել երեք բան.
ստանալու համար ռեսուրսը իր տեսակի և անվանմամբ.
փոփոխություն ռեսուրս (այս դեպքում սերվերը պահպանում է միայն «ճիշտ» օբյեկտները. բոլոր սխալ ձևավորված կամ այլ դիրեկտորիաների համար նախատեսված օբյեկտները մերժվում են);
հետեւեք ռեսուրսի համար (այս դեպքում օգտվողն անմիջապես ստանում է իր ընթացիկ/թարմացված տարբերակը):
Այսպիսով, Kubernetes-ը գործում է որպես մի տեսակ ֆայլի սերվեր (YAML մանիֆեստների համար) երեք հիմնական մեթոդներով (այո, իրականում կան ուրիշներ, բայց մենք առայժմ դրանք բաց կթողնենք):
Խնդիրն այն է, որ սերվերը կարող է պահել միայն տեղեկատվություն: Այն աշխատեցնելու համար ձեզ հարկավոր է հատուկ արարիչ սարք - երկրորդ ամենակարևոր և հիմնարար հայեցակարգը Kubernetes աշխարհում:
Կարգավորիչների երկու հիմնական տեսակ կա. Առաջինը վերցնում է տեղեկատվությունը Kubernetes-ից, մշակում այն ըստ nested տրամաբանության և վերադարձնում այն K8-ներին։ Երկրորդը տեղեկատվություն է վերցնում Kubernetes-ից, բայց, ի տարբերություն առաջին տեսակի, փոխում է որոշ արտաքին ռեսուրսների վիճակը։
Եկեք ավելի սերտ նայենք Kubernetes-ում տեղակայման ստեղծման գործընթացին.
Տեղակայման վերահսկիչ (ներառված է kube-controller-manager) տեղեկատվություն է ստանում տեղակայման մասին և ստեղծում ReplicaSet:
ReplicaSet-ը ստեղծում է երկու կրկնօրինակներ (երկու պատիճ)՝ հիմնվելով այս տեղեկատվության վրա, բայց այս պատյանները դեռ նախատեսված չեն:
Ժամանակացույցը պլանավորում է պատիճներ և ավելացնում հանգույցների տեղեկատվությունը նրանց YAML-ներին:
Kubelets-ը փոփոխություններ է կատարում արտաքին ռեսուրսի մեջ (ասենք Docker):
Այնուհետև այս ամբողջ հաջորդականությունը կրկնվում է հակառակ հերթականությամբ. kubelet-ը ստուգում է բեռնարկղերը, հաշվարկում պատի կարգավիճակը և այն հետ ուղարկում: ReplicaSet կարգավորիչը ստանում է կարգավիճակը և թարմացնում է կրկնօրինակների հավաքածուի վիճակը: Նույնը տեղի է ունենում Deployment Controller-ի հետ, և օգտատերը վերջապես ստանում է թարմացված (ընթացիկ) կարգավիճակը:
Shell-օպերատոր
Պարզվում է, որ Kubernetes-ը հիմնված է տարբեր կարգավարների համատեղ աշխատանքի վրա (Kubernetes օպերատորները նույնպես վերահսկիչներ են)։ Հարց է առաջանում՝ ինչպե՞ս ստեղծել սեփական օպերատորը նվազագույն ջանքերով։ Եվ ահա մեր մշակածը օգնության է հասնում shell-օպերատոր. Այն թույլ է տալիս համակարգի ադմինիստրատորներին ստեղծել իրենց սեփական հայտարարությունները՝ օգտագործելով ծանոթ մեթոդներ:
Պարզ օրինակ՝ գաղտնիքների պատճենում
Եկեք նայենք մի պարզ օրինակի.
Ենթադրենք, մենք ունենք Kubernetes կլաստեր: Այն ունի անվանատարածք default ինչ-որ գաղտնիքով mysecret. Բացի այդ, կլաստերում կան այլ անունների տարածքներ: Դրանցից որոշների վրա կցված է հատուկ պիտակ: Մեր նպատակն է պատճենել Secret-ը պիտակով անվանատարածքներում:
Խնդիրը բարդանում է նրանով, որ կլաստերում կարող են հայտնվել նոր անվանատարածքներ, և դրանցից ոմանք կարող են ունենալ այս պիտակը։ Մյուս կողմից, երբ պիտակը ջնջվում է, գաղտնիքը նույնպես պետք է ջնջվի: Բացի սրանից, Գաղտնիքն ինքը կարող է նաև փոխվել. այս դեպքում նոր Գաղտնիքը պետք է պատճենվի պիտակներով բոլոր անվանատարածքներում: Եթե գաղտնիքը պատահաբար ջնջվի որևէ անվանատարածքում, մեր օպերատորը պետք է անմիջապես վերականգնի այն:
Այժմ, երբ առաջադրանքը ձևակերպված է, ժամանակն է սկսել այն իրականացնել shell-օպերատորի միջոցով: Բայց նախ արժե մի քանի խոսք ասել հենց shell-օպերատորի մասին:
Ինչպես է աշխատում shell-օպերատորը
Ինչպես Kubernetes-ի մյուս բեռները, shell-օպերատորն աշխատում է իր սեփական պատի մեջ: Այս պատիճում գրացուցակում /hooks գործարկվող ֆայլերը պահվում են: Սրանք կարող են լինել սցենարներ Bash, Python, Ruby և այլն: Նման գործարկվող ֆայլերը մենք անվանում ենք կեռիկներ (կեռիկներ).
Shell-օպերատորը բաժանորդագրվում է Kubernetes-ի իրադարձություններին և գործարկում է այս կեռիկները՝ ի պատասխան այն իրադարձությունների, որոնք մեզ անհրաժեշտ են:
Ինչպես է shell-օպերատորը գիտի, թե որ կեռիկը գործարկել և երբ: Բանն այն է, որ յուրաքանչյուր կեռիկ ունի երկու փուլ։ Գործարկման ընթացքում shell-օպերատորը գործարկում է բոլոր կեռիկները վեճով --config Սա կազմաձևման փուլն է: Եվ դրանից հետո կեռիկները գործարկվում են սովորական ձևով՝ ի պատասխան այն իրադարձությունների, որոնց դրանք կապված են: Վերջին դեպքում կեռիկը ստանում է պարտադիր համատեքստ (պարտադիր համատեքստ) - JSON ձևաչափով տվյալներ, որոնց մասին ավելի մանրամասն կխոսենք ստորև։
Բաշում օպերատորի պատրաստում
Այժմ մենք պատրաստ ենք իրականացմանը։ Դա անելու համար մենք պետք է գրենք երկու գործառույթ (ի դեպ, խորհուրդ ենք տալիս գրադարան shell_lib, ինչը մեծապես հեշտացնում է Bash-ում կեռիկներ գրելը):
առաջինը անհրաժեշտ է կազմաձևման փուլի համար. այն ցուցադրում է պարտադիր համատեքստը.
երկրորդը պարունակում է կեռիկի հիմնական տրամաբանությունը:
#!/bin/bash
source /shell_lib.sh
function __config__() {
cat << EOF
configVersion: v1
# BINDING CONFIGURATION
EOF
}
function __main__() {
# THE LOGIC
}
hook::run "$@"
Հաջորդ քայլը որոշելն է, թե ինչ առարկաներ են մեզ անհրաժեշտ: Մեր դեպքում մենք պետք է հետևենք.
փոփոխությունների աղբյուրի գաղտնիքը;
կլաստերի բոլոր անվանատարածքները, որպեսզի իմանաք, թե որոնք են դրանց վրա կցված պիտակ.
թիրախային գաղտնիքները ապահովելու համար, որ դրանք բոլորը համաժամանակյա են աղբյուրի գաղտնիքի հետ:
Բաժանորդագրվեք գաղտնի աղբյուրին
Դրա համար պարտադիր կազմաձևումը բավականին պարզ է: Նշում ենք, որ մեզ հետաքրքրում է Secret անունով mysecret անունների տարածքում default:
Ինչպես տեսնում եք, կոնֆիգուրացիայի մեջ նոր դաշտ է հայտնվել անունով jqFilter. Ինչպես հուշում է նրա անունը, jqFilter զտում է բոլոր ավելորդ տեղեկությունները և ստեղծում նոր JSON օբյեկտ՝ մեզ հետաքրքրող դաշտերով: Նմանատիպ կոնֆիգուրացիայով կեռիկը կստանա հետևյալ պարտադիր համատեքստը.
Այն պարունակում է զանգված filterResults կլաստերի յուրաքանչյուր անվանատարածքի համար: Բուլյան փոփոխական hasLabel ցույց է տալիս, թե արդյոք պիտակը կցված է տվյալ անվանատարածքին: Ընտրիչ keepFullObjectsInMemory: false ցույց է տալիս, որ ամբողջական առարկաները հիշողության մեջ պահելու կարիք չկա:
Հետևել թիրախային գաղտնիքներին
Մենք բաժանորդագրվում ենք բոլոր Գաղտնիքներին, որոնցում նշված է անոտացիա managed-secret: "yes" (դրանք մեր թիրախն են dst_secrets):
Այս դեպքում jqFilter զտում է բոլոր տեղեկությունները, բացառությամբ անվանատարածքի և պարամետրի resourceVersion. Գաղտնիքը ստեղծելիս վերջին պարամետրը փոխանցվել է անոտացիային. այն թույլ է տալիս համեմատել գաղտնիքների տարբերակները և թարմացնել դրանք:
Այս կերպ կազմաձևված կեռիկը, երբ գործարկվի, կստանա վերը նկարագրված երեք պարտադիր համատեքստերը: Դրանք կարելի է դիտարկել որպես մի տեսակ ակնթարթային նկար (վայրկյանական լուսանկար) կլաստեր.
Այս ամբողջ տեղեկատվության հիման վրա կարելի է մշակել հիմնական ալգորիթմ: Այն կրկնվում է բոլոր անվանատարածքների վրա և.
եթե hasLabel հարցեր true ներկայիս անվանատարածքի համար՝
Համաշխարհային գաղտնիքը համեմատում է տեղականի հետ.
եթե դրանք նույնն են, դա ոչինչ չի անում.
եթե դրանք տարբերվում են - կատարում է kubectl replace կամ create;
եթե hasLabel հարցեր false ներկայիս անվանատարածքի համար՝
համոզվում է, որ գաղտնիքը տվյալ անվանատարածքում չէ.
եթե առկա է տեղական Գաղտնիքը, ջնջեք այն օգտագործելով kubectl delete;
եթե տեղական Գաղտնիքը չի հայտնաբերվել, այն ոչինչ չի անում:
Ահա թե ինչպես մենք կարողացանք ստեղծել պարզ Kubernetes կարգավորիչ՝ օգտագործելով YAML կոնֆիգուրացիայի 35 տող և մոտավորապես նույնքան Bash կոդ: Shell-օպերատորի գործն է դրանք միմյանց կապել:
Այնուամենայնիվ, գաղտնիքները պատճենելը կոմունալ ծառայության միակ ոլորտը չէ: Ահա ևս մի քանի օրինակ՝ ցույց տալու համար, թե ինչի է նա ընդունակ։
Օրինակ 1. ConfigMap-ում փոփոխություններ կատարելը
Եկեք նայենք տեղակայմանը, որը բաղկացած է երեք պատյաններից: Փոդներն օգտագործում են ConfigMap՝ որոշ կոնֆիգուրացիաներ պահելու համար: Երբ փոդերը գործարկվեցին, ConfigMap-ը որոշակի վիճակում էր (եկեք այն անվանենք v.1): Համապատասխանաբար, բոլոր պատիճներն օգտագործում են ConfigMap-ի այս կոնկրետ տարբերակը:
Հիմա ենթադրենք, որ ConfigMap-ը փոխվել է (v.2): Այնուամենայնիվ, պատիճները կօգտագործեն ConfigMap-ի նախորդ տարբերակը (v.1):
Ինչպե՞ս կարող եմ ստիպել նրանց անցնել նոր ConfigMap-ին (v.2): Պատասխանը պարզ է՝ օգտագործեք ձևանմուշ։ Եկեք բաժնին ավելացնենք ստուգիչ գումարի նշում template Տեղակայման կոնֆիգուրացիաներ.
Արդյունքում, այս ստուգման գումարը կգրանցվի բոլոր pods-ում, և այն կլինի նույնը, ինչ Deployment-ը: Այժմ դուք պարզապես պետք է թարմացնեք անոտացիան, երբ փոխվում է ConfigMap-ը: Իսկ shell-օպերատորը այս դեպքում հարմար է: Ձեզ անհրաժեշտ է միայն ծրագրավորել կեռիկ, որը կբաժանորդագրվի ConfigMap-ին և կթարմացնի ստուգման գումարը.
Եթե օգտագործողը փոփոխություններ կատարի ConfigMap-ում, ապա shell-օպերատորը կնկատի դրանք և կվերահաշվարկի ստուգիչ գումարը: Որից հետո կխաղա Կուբերնետեսի կախարդանքը. նվագախումբը կսպանի պատիճը, կստեղծի նորը, կսպասի, որ այն դառնա Ready, և անցնում հաջորդին։ Արդյունքում, Deployment-ը կհամաժամացվի և կանցնի ConfigMap-ի նոր տարբերակին:
Օրինակ 2. Աշխատանք հատուկ ռեսուրսների սահմանումների հետ
Ինչպես գիտեք, Kubernetes-ը թույլ է տալիս ստեղծել անհատական տեսակի օբյեկտներ։ Օրինակ, դուք կարող եք ստեղծել բարի MysqlDatabase. Ենթադրենք, այս տեսակն ունի երկու մետատվյալների պարամետր. name и namespace.
apiVersion: example.com/v1alpha1
kind: MysqlDatabase
metadata:
name: foo
namespace: bar
Մենք ունենք Kubernetes կլաստեր՝ տարբեր անվանատարածքներով, որտեղ կարող ենք ստեղծել MySQL տվյալների բազաներ։ Այս դեպքում shell-օպերատորը կարող է օգտագործվել ռեսուրսները հետևելու համար MysqlDatabase, դրանք միացնելով MySQL սերվերին և համաժամացնելով կլաստերի ցանկալի և դիտարկված վիճակները։
Օրինակ 3. Կլաստերային ցանցի մոնիտորինգ
Ինչպես գիտեք, ping-ի օգտագործումը ցանցը վերահսկելու ամենապարզ միջոցն է: Այս օրինակում մենք ցույց կտանք, թե ինչպես կարելի է իրականացնել նման մոնիտորինգ՝ օգտագործելով shell-operator:
Առաջին հերթին, դուք պետք է բաժանորդագրվեք հանգույցներին: Shell օպերատորին անհրաժեշտ է յուրաքանչյուր հանգույցի անունը և IP հասցեն: Նրանց օգնությամբ նա կպինգի այս հանգույցները։
Parameter executeHookOnEvent: [] կանխում է կեռիկը գործարկելու ցանկացած իրադարձության պատասխան (այսինքն՝ ի պատասխան հանգույցների փոփոխման, ավելացման, ջնջման): Այնուամենայնիվ, նա կվազի (և թարմացնել հանգույցների ցանկը) Պլանավորված - ամեն րոպե, ինչպես սահմանված է դաշտում schedule.
Այժմ հարց է առաջանում, թե կոնկրետ ինչպե՞ս ենք մենք իմանում այնպիսի խնդիրների մասին, ինչպիսին է փաթեթների կորուստը: Եկեք նայենք կոդը.
function __main__() {
for i in $(seq 0 "$(context::jq -r '(.snapshots.nodes | length) - 1')"); do
node_name="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.name')"
node_ip="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.ip')"
packets_lost=0
if ! ping -c 1 "$node_ip" -t 1 ; then
packets_lost=1
fi
cat >> "$METRICS_PATH" <<END
{
"name": "node_packets_lost",
"add": $packets_lost,
"labels": {
"node": "$node_name"
}
}
END
done
}
Մենք կրկնում ենք հանգույցների ցանկը, ստանում նրանց անուններն ու IP հասցեները, ping ենք անում և արդյունքներն ուղարկում Պրոմեթևսին: Shell-օպերատորը կարող է չափումներ արտահանել Պրոմեթևս, դրանք պահելով շրջակա միջավայրի փոփոխականում նշված ուղու համաձայն տեղակայված ֆայլում $METRICS_PATH.
Ահա այստեղ դուք կարող եք ստեղծել օպերատոր ցանցի պարզ մոնիտորինգի համար կլաստերի մեջ:
Հերթի մեխանիզմ
Այս հոդվածը թերի կլիներ առանց նկարագրելու մեկ այլ կարևոր մեխանիզմ, որը ներկառուցված է shell-օպերատորի մեջ: Պատկերացրեք, որ այն կատարում է ինչ-որ կեռիկ՝ ի պատասխան կլաստերի իրադարձության:
Ի՞նչ կլինի, եթե միևնույն ժամանակ ինչ-որ բան տեղի ունենա կլաստերում: ևս մեկ բան իրադարձություն?
Արդյո՞ք shell-օպերատորը կգործարկի կեռիկի մեկ այլ օրինակ:
Իսկ եթե, ասենք, կլաստերում միանգամից հինգ իրադարձություն տեղի ունենան։
Արդյո՞ք shell-օպերատորը դրանք կմշակի զուգահեռ:
Ինչ վերաբերում է սպառված ռեսուրսներին, ինչպիսիք են հիշողությունը և պրոցեսորը:
Բարեբախտաբար, shell-օպերատորն ունի ներկառուցված հերթերի մեխանիզմ: Բոլոր իրադարձությունները հերթագրվում և մշակվում են հաջորդաբար:
Սա բացատրենք օրինակներով։ Ենթադրենք, մենք ունենք երկու կեռիկ: Առաջին իրադարձությունը գնում է առաջին մանգաղին: Երբ դրա մշակումն ավարտվի, հերթը առաջ է շարժվում: Հաջորդ երեք իրադարձությունները վերահղվում են երկրորդ կեռիկին՝ դրանք հանվում են հերթից և մտնում դրա մեջ «փաթեթով»: Այն է մանգաղը ստանում է իրադարձությունների զանգված — կամ, ավելի ճիշտ, պարտադիր համատեքստերի զանգված:
Նաև սրանք իրադարձությունները կարելի է միավորել մեկ մեծ. Դրա համար պատասխանատու է պարամետրը group պարտադիր կոնֆիգուրացիայի մեջ:
Դուք կարող եք ստեղծել ցանկացած թվով հերթեր/կեռիկներ և դրանց տարբեր համակցություններ: Օրինակ, մեկ հերթ կարող է աշխատել երկու կեռիկով, կամ հակառակը:
Ձեզ անհրաժեշտ է միայն համապատասխանաբար կարգավորել դաշտը queue պարտադիր կոնֆիգուրացիայի մեջ: Եթե հերթի անունը նշված չէ, ապա կեռիկը աշխատում է լռելյայն հերթի վրա (default) Հերթի այս մեխանիզմը թույլ է տալիս ամբողջությամբ լուծել ռեսուրսների կառավարման բոլոր խնդիրները՝ կեռիկների հետ աշխատելիս:
Ամփոփում
Մենք բացատրեցինք, թե ինչ է shell-օպերատորը, ցույց տվեցինք, թե ինչպես կարելի է այն օգտագործել արագ և առանց ջանքերի Kubernetes օպերատորներ ստեղծելու համար և բերեցինք դրա օգտագործման մի քանի օրինակ:
Shell-օպերատորի մասին մանրամասն տեղեկատվություն, ինչպես նաև դրա օգտագործման արագ ձեռնարկը հասանելի է համապատասխան էջում։ պահոցներ GitHub-ում. Մի հապաղեք կապվել մեզ հետ հարցերով. կարող եք դրանք քննարկել հատուկ ձևով Telegram խումբ (ռուսերեն) կամ լեզվով այս ֆորումը (անգլերեն):
Եվ եթե ձեզ դուր եկավ, մենք միշտ ուրախ ենք տեսնել նոր թողարկումներ/PR/աստղեր GitHub-ում, որտեղ, ի դեպ, կարող եք գտնել այլոց հետաքրքիր նախագծեր. Դրանցից արժե առանձնացնել հավելում-օպերատոր, որը shell-operator-ի մեծ եղբայրն է. Այս օգտակար ծրագիրը օգտագործում է Helm գծապատկերները հավելումներ տեղադրելու համար, կարող է թարմացումներ մատուցել և վերահսկել գծապատկերների տարբեր պարամետրեր/արժեքներ, վերահսկում է գծապատկերների տեղադրման գործընթացը և կարող է նաև փոփոխել դրանք՝ ի պատասխան կլաստերի իրադարձությունների: