
Բարև բոլորին։ Իմ անունը Դմիտրի Սամսոնով է, ես աշխատում եմ որպես առաջատար համակարգային ադմինիստրատոր «Օդնոկլասնիկի»-ում։ Մենք ունենք ավելի քան 7 ֆիզիկական սերվեր, 11 կոնտեյներ մեր ամպում և 200 հավելված, որոնք տարբեր կոնֆիգուրացիաներով կազմում են 700 տարբեր կլաստերներ։ Սերվերների մեծ մասը աշխատում է CentOS 7 օպերացիոն համակարգով։
14 թվականի օգոստոսի 2018-ին հրապարակվեց FragmentSmack խոցելիության մասին տեղեկատվություն։
() և SegmentSmack (Սրանք ցանցային հարձակման վեկտորով և բավականին բարձր գնահատականով (7.5) խոցելիություններ են, որոնք սպառնում են ծառայության մերժմամբ (DoS)՝ ռեսուրսների սպառման (CPU) պատճառով: FragmentSmack-ի միջուկի լուծումն այդ ժամանակ առաջարկված չէր, ավելին, այն թողարկվեց խոցելիության մասին տեղեկատվության հրապարակումից շատ ավելի ուշ: SegmentSmack-ը վերացնելու համար առաջարկվեց թարմացնել միջուկը: Թարմացման փաթեթն ինքնին թողարկվեց նույն օրը, մնում էր միայն այն տեղադրել:
Ոչ, մենք ընդհանրապես դեմ չենք միջուկի թարմացմանը։ Այնուամենայնիվ, կան նրբերանգներ...
Ինչպես ենք թարմացնում միջուկը արտադրության մեջ
Ընդհանուր առմամբ, ոչինչ բարդ չէ.
- Ներբեռնեք փաթեթներ;
- Տեղադրեք դրանք մի շարք սերվերների վրա (ներառյալ մեր ամպային սերվերները)։
- Համոզվեք, որ ոչինչ չի կոտրվել;
- Համոզվեք, որ բոլոր ստանդարտ միջուկի կարգավորումները կիրառվել են առանց սխալների։
- Սպասեք մի քանի օր;
- Ստուգեք սերվերի չափանիշները;
- Նոր սերվերների տեղակայումը տեղափոխել նոր միջուկի վրա։
- Թարմացրեք տվյալների կենտրոնների բոլոր սերվերները (միաժամանակ մեկ տվյալների կենտրոն՝ խնդիրների դեպքում օգտատերերի վրա ազդեցությունը նվազագույնի հասցնելու համար)։
- Վերագործարկեք բոլոր սերվերները։
Կրկնեք այս գործողությունը մեր ունեցած միջուկների բոլոր ճյուղերի համար։ Այս պահին այն հետևյալն է՝
- Stock CentOS 7 3.10 - սովորական սերվերների մեծ մասի համար;
- Վանիլ 4.19 - մեր համար , քանի որ մեզ անհրաժեշտ են BFQ, BBR և այլն։
- Elrepo kernel-ml 5.2 — համար , քանի որ 4.19-ը նախկինում անկայուն էր, բայց նույն հնարավորություններն անհրաժեշտ են։
Ինչպես գուցե կռահել եք, ամենաժամանակատար խնդիրը հազարավոր սերվերների վերագործարկումն է: Քանի որ ոչ բոլոր խոցելիությունները կարևոր են բոլոր սերվերների համար, մենք վերագործարկում ենք միայն այն խոցելիությունները, որոնք ուղղակիորեն հասանելի են ինտերնետից: Ամպում, ճկունությունը չսահմանափակելու համար, մենք արտաքին հասանելի կոնտեյներները չենք կապում առանձին սերվերների հետ նոր միջուկով, այլ վերագործարկում ենք բոլոր հոսթերը առանց բացառության: Բարեբախտաբար, այնտեղ ընթացակարգն ավելի պարզ է, քան սովորական սերվերների դեպքում: Օրինակ, վիճակազուրկ կոնտեյներները կարող են պարզապես տեղափոխվել մեկ այլ սերվեր վերագործարկման ժամանակ:
Սակայն դեռ շատ աշխատանք կա, և դա կարող է տևել մի քանի շաբաթ, իսկ եթե նոր տարբերակի հետ կապված որևէ խնդիր առաջանա՝ մինչև մի քանի ամիս։ Հարձակվողները սա շատ լավ հասկանում են, ուստի անհրաժեշտ է «B» պլան։
FragmentSmack/SegmentSmack։ Շեղման լուծում
Բարեբախտաբար, որոշ խոցելիությունների համար կա նման «B» պլան, և այն կոչվում է Workaround: Ամենից հաճախ սա միջուկի/ծրագրի կարգավորումների փոփոխություն է, որը թույլ է տալիս նվազագույնի հասցնել հնարավոր ազդեցությունը կամ ամբողջությամբ վերացնել խոցելիությունների շահագործումը:
FragmentSmack/SegmentSmack դեպքում այսպիսի լուծում՝
«Դուք կարող եք փոխել net.ipv4.ipfrag_high_thresh և net.ipv3.ipfrag_low_thresh (և դրանց IPv4 համարժեքները՝ net.ipv4.ipfrag_high_thresh և net.ipv6.ipfrag_low_thresh) 6ՄԲ և 6ՄԲ լռելյայն արժեքները համապատասխանաբար մինչև 256 կԲ և 192 կԲ կամ ավելի ցածր: Թեստերը ցույց են տալիս CPU-ի օգտագործման փոքրից մինչև զգալի անկում հարձակման ժամանակ՝ կախված սարքավորումից, կարգավորումներից և պայմաններից: Այնուամենայնիվ, ipfrag_high_thresh=262144 բայթը կարող է որոշակի ազդեցություն ունենալ աշխատանքի վրա, քանի որ վերամիավորման հերթում միաժամանակ կարող են տեղավորվել միայն երկու 64K ֆրագմենտ: Օրինակ, կա ռիսկ, որ մեծ UDP փաթեթներով աշխատող հավելվածները կխափանվեն:.
Պարամետրերը իրենք նկարագրվում են հետևյալ կերպ՝
ipfrag_high_thresh - LONG INTEGER
Maximum memory used to reassemble IP fragments.
ipfrag_low_thresh - LONG INTEGER
Maximum memory used to reassemble IP fragments before the kernel
begins to remove incomplete fragment queues to free up resources.
The kernel still accepts new fragments for defragmentation.
Մենք արտադրական ծառայությունների վրա մեծ UDP-ներ չունենք: LAN-ում մասնատված երթևեկություն չկա, WAN-ում կա որոշակի, բայց դա էական չէ: Ոչինչ չի նախանշում. մենք կարող ենք ներդնել Workaround լուծում:
FragmentSmack/SegmentSmack։ Առաջին արյուն
Առաջին խնդիրը, որին մենք հանդիպեցինք, այն էր, որ ամպային կոնտեյներները երբեմն նոր կարգավորումները կիրառում էին միայն մասամբ (միայն ipfrag_low_thresh), իսկ երբեմն ընդհանրապես չէին կիրառում՝ դրանք պարզապես խափանվում էին գործարկման ժամանակ։ Հնարավոր չէր հուսալիորեն վերարտադրել խնդիրը (բոլոր կարգավորումները կիրառվել են ձեռքով՝ առանց որևէ դժվարության)։ Նաև այդքան էլ հեշտ չէր հասկանալ, թե ինչու էր կոնտեյները խափանվում գործարկման ժամանակ. սխալներ չեն հայտնաբերվել։ Մի բան հաստատ էր. կարգավորումները հետ քաշելը լուծում է կոնտեյների խափանումների խնդիրը։
Ինչո՞ւ բավարար չէ պարզապես Sysctl-ը օգտագործել հոսթի վրա։ Կոնտեյները գտնվում է իր սեփական նվիրված ցանցային անվանատարածքում, այնպես որ առնվազն տարայի մեջ կարող է տարբերվել տիրոջից։
Ինչպե՞ս են Sysctl կարգավորումները կիրառվում կոնտեյների մեջ։ Քանի որ մեր կոնտեյներները արտոնյալ չեն, հնարավոր չէ փոխել Sysctl-ի որևէ կարգավորում՝ մտնելով կոնտեյների մեջ. դուք պարզապես բավարար իրավունքներ չունեք։ Այդ ժամանակ մեր ամպը օգտագործում էր Docker-ը կոնտեյներներ գործարկելու համար (այժմ՝ Նոր կոնտեյների պարամետրերը, ներառյալ անհրաժեշտ Sysctl կարգավորումները, փոխանցվել են Docker-ին API-ի միջոցով։
Տարբերակները փորձարկելիս պարզվեց, որ Docker API-ը չէր վերադարձնում բոլոր սխալները (գոնե 1.10 տարբերակում): Երբ փորձում էինք կոնտեյներ գործարկել «docker run»-ի միջոցով, վերջապես տեսանք մի բան.
write /proc/sys/net/ipv4/ipfrag_high_thresh: invalid argument docker: Error response from daemon: Cannot start container <...>: [9] System error: could not synchronise with container process.
Պարամետրի արժեքը անվավեր է։ Բայց ինչո՞ւ։ Եվ ինչո՞ւ է այն անվավեր միայն երբեմն։ Պարզվեց, որ Docker-ը չի երաշխավորում Sysctl պարամետրերի կիրառման հերթականությունը (վերջին ստուգված տարբերակը 1.13.1-ն է), ուստի երբեմն ipfrag_high_thresh-ը փորձում էր սահմանել 256K, երբ ipfrag_low_thresh-ը դեռ 3M էր, այսինքն՝ վերին սահմանը ցածր էր ստորինից, ինչը հանգեցրեց սխալի։
Այդ ժամանակ մենք արդեն ունեինք մեր սեփական մեխանիզմը բեռնարկղը վերակազմակերպելու համար մեկնարկից հետո (բեռնարկղը սառեցնելով դրանից հետո) և հրամանների կատարում կոնտեյներային անվանատարածքում՝ ), և մենք նաև այս մասին ավելացրեցինք Sysctl պարամետրերը։ Խնդիրը լուծվեց։
FragmentSmack/SegmentSmack։ Առաջին արյուն 2
Մենք հազիվ էինք հասկացել, թե ինչպես օգտագործել Workaround-ը ամպային միջավայրում, երբ սկսեցին հասնել օգտատերերի առաջին հազվագյուտ բողոքները: Այդ պահին Workaround-ը առաջին սերվերների վրա օգտագործելու սկզբից անցել էր մի քանի շաբաթ: Սկզբնական հետաքննությունը ցույց տվեց, որ բողոքներ են ստացվել առանձին ծառայությունների, այլ ոչ թե այդ ծառայությունների բոլոր սերվերների վերաբերյալ: Խնդիրը կրկին ձեռք բերեց չափազանց անորոշ բնույթ:
Նախևառաջ, մենք փորձեցինք, իհարկե, հետ բերել Sysctl-ի կարգավորումները, բայց դա ոչ մի արդյունք չտվեց։ Սերվերի և հավելվածի կարգավորումների հետ տարբեր մանիպուլյացիաներ նույնպես չօգնեցին։ Վերագործարկումը օգնեց։ Linux-ի համար վերագործարկումը նույնքան անբնական է, որքան հին ժամանակներում Windows-ի հետ աշխատելու նորմալ պայման էր։ Այնուամենայնիվ, այն օգնեց, և մենք Sysctl-ում նոր կարգավորումներ կիրառելիս այն գրեցինք որպես «միջուկի խափանում»։ Որքա՜ն անմիտ էր դա…
Երեք շաբաթ անց խնդիրը կրկին առաջացավ։ Այս սերվերների կարգավորումը բավականին պարզ էր. Nginx-ը պրոքսի/հավասարակշռող ռեժիմում։ Շատ երթևեկություն չկար։ Նոր մուտքագրում. հաճախորդների վրա 504 սխալների թիվն ամեն օր աճում է (Գրաֆիկը ցույց է տալիս այս ծառայության օրական 504 սխալների քանակը։

Բոլոր սխալները մոտավորապես նույն backend-ում են՝ ամպում գտնվողը։ Այս backend-ում փաթեթների ֆրագմենտների հիշողության սպառման գրաֆիկը հետևյալ տեսքն ուներ.

Սա օպերացիոն համակարգի գրաֆիկների վրա խնդրի ամենաակնառու դրսևորումներից մեկն է: Ամպում միաժամանակ շտկվել է QoS (Traffic Control) կարգավորումների հետ կապված մեկ այլ ցանցային խնդիր: Փաթեթների բեկորների հիշողության սպառման գրաֆիկի վրա այն բացարձակապես նույն տեսքն ուներ.

Ենթադրությունը պարզ էր. եթե դրանք գրաֆիկների վրա նույն տեսքն ունեն, ապա դրանց պատճառը նույնն է։ Հատկապես, որ այս տեսակի հիշողության հետ կապված որևէ խնդիր չափազանց հազվադեպ է պատահում։
Լուծված խնդրի էությունն այն էր, որ մենք օգտագործում էինք fq փաթեթների ժամանակացույցը՝ QoS-ի լռելյայն կարգավորումներով։ Լռելյայնորեն, այն թույլ է տալիս մեկ կապի համար հերթին ավելացնել 100 փաթեթ, և որոշ կապեր, ալիքի պակասի դեպքում, սկսել են լրացնել հերթը մինչև վերջ։ Այս դեպքում փաթեթները հանվում են։ tc վիճակագրության մեջ (tc -s qdisc) սա երևում է այսպես.
qdisc fq 2c6c: parent 1:2c6c limit 10000p flow_limit 100p buckets 1024 orphan_mask 1023 quantum 3028 initial_quantum 15140 refill_delay 40.0ms
Sent 454701676345 bytes 491683359 pkt (dropped 464545, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
1024 flows (1021 inactive, 0 throttled)
0 gc, 0 highprio, 0 throttled, 464545 flows_plimit
«464545 flows_plimit»-ը մեկ կապի հերթի սահմանաչափը գերազանցելու պատճառով բաց թողնված փաթեթներն են, իսկ «dropped 464545»-ը այս ժամանակացույցի բոլոր բաց թողնված փաթեթների գումարն է: Հերթի երկարությունը 1 հազարի հասցնելուց և կոնտեյներները վերագործարկելուց հետո խնդիրը դադարեց երևալուց: Կարող եք հանգստանալ և սմութի խմել:
FragmentSmack/SegmentSmack։ Վերջին արյունը
Նախ, միջուկի խոցելիությունների մասին հայտարարությունից մի քանի ամիս անց վերջապես հայտնվեց FragmentSmack-ի համար նախատեսված լուծումը (հիշեցնեմ, որ օգոստոսին հայտարարության հետ մեկտեղ թողարկվեց միայն SegmentSmack-ի համար նախատեսված լուծումը), որը մեզ հնարավորություն տվեց հրաժարվել Workaround-ից, որը մեզ բավականին մեծ խնդիրներ էր պատճառել: Այս ընթացքում մենք արդեն հասցրել էինք որոշ սերվերներ տեղափոխել նոր միջուկ, և հիմա ստիպված էինք սկսել զրոյից: Ինչո՞ւ թարմացրինք միջուկը՝ առանց FragmentSmack-ի լուծմանը սպասելու: Բանն այն է, որ այս խոցելիություններից պաշտպանության գործընթացը համընկավ (և միավորվեց) CentOS-ի թարմացման գործընթացի հետ (որը նույնիսկ ավելի շատ ժամանակ է պահանջում, քան միայն միջուկի թարմացումը): Բացի այդ, SegmentSmack-ը ավելի վտանգավոր խոցելիություն է, և դրա լուծումը անմիջապես հայտնվեց, այնպես որ ամեն դեպքում իմաստ ուներ: Սակայն, մենք չէինք կարող պարզապես թարմացնել CentOS-ի միջուկը, քանի որ CentOS 7.5 դարաշրջանում ի հայտ եկած FragmentSmack խոցելիությունը շտկվել էր միայն 7.6-ում, ուստի մենք ստիպված էինք դադարեցնել 7.5-ի թարմացումը և սկսել ամեն ինչ նորից՝ 7.6-ի թարմացմամբ։ Եվ դա նույնպես տեղի է ունենում։
Երկրորդ, մեզ են վերադարձել օգտատերերի հազվագյուտ բողոքները խնդիրների վերաբերյալ։ Հիմա մենք հաստատ գիտենք, որ դրանք բոլորը կապված են հաճախորդներից մեր որոշ սերվերներ ֆայլերի բեռնման հետ։ Ավելին, ընդհանուր թվից շատ փոքր քանակությամբ բեռնումներ են կատարվել այդ սերվերների միջոցով։
Ինչպես հիշում ենք վերևում նշված պատմությունից, Sysctl-ի վերականգնումը չօգնեց։ Վերագործարկումը օգնեց, բայց ժամանակավորապես։
Sysctl-ը դեռևս կասկածելի էր, բայց այս անգամ անհրաժեշտ էր հավաքել որքան հնարավոր է շատ տեղեկատվություն։ Բացի այդ, հաճախորդի վրա բեռնման խնդիրը վերարտադրելու հնարավորությունը խիստ թերի էր՝ ավելի ճշգրիտ ուսումնասիրելու համար, թե ինչ էր կատարվում։
Բոլոր հասանելի վիճակագրության և գրանցամատյանների վերլուծությունը մեզ չմոտեցրեց այն հասկացողությանը, թե ինչ էր կատարվում: Կար խնդրի վերարտադրման լուրջ անկարողություն՝ որոշակի կապ «զգալու» համար: Վերջապես, մշակողներին հաջողվեց հասնել խնդիրների կայուն վերարտադրության փորձարկման սարքի վրա, երբ միացված էին Wi-Fi-ով՝ օգտագործելով հավելվածի հատուկ տարբերակը: Սա առաջընթաց էր հետաքննության մեջ: Հաճախորդը միացավ Nginx-ին, որը պրոքսի էր սերվերի հետ, որը մեր Java հավելվածն էր:

Խնդիրների ժամանակ երկխոսությունը հետևյալն էր (ձայնագրվել է Nginx պրոքսիի կողմից).
- Հաճախորդ. ֆայլի ներբեռնման ավարտի մասին տեղեկատվություն ստանալու հարցում:
- Java սերվեր. պատասխան։
- Հաճախորդ՝ POST ֆայլով։
- Java սերվեր. սխալ։
Java սերվերը գրանցում է գրանցամատյանում, որ հաճախորդից ստացվել է 0 բայթ տվյալ, իսկ Nginx պրոքսին գրում է, որ հարցումը տևել է ավելի քան 30 վայրկյան (30 վայրկյանը հաճախորդի ծրագրի ժամկետի ավարտն է): Ինչո՞ւ ժամկետի ավարտ և ինչո՞ւ 0 բայթ: HTTP տեսանկյունից ամեն ինչ աշխատում է այնպես, ինչպես պետք է, բայց ֆայլի հետ POST-ը, կարծես, անհետանում է ցանցից: Ավելին, այն անհետանում է հաճախորդի և Nginx-ի միջև: Ժամանակն է զինվել Tcpdump-ով: Բայց նախ պետք է հասկանալ ցանցի կարգավորումը: Nginx պրոքսին գտնվում է L3 հավասարակշռիչի հետևում: Թունելավորումը օգտագործվում է L3 հավասարակշռիչից սերվերին փաթեթներ հասցնելու համար, որը փաթեթներին ավելացնում է իր վերնագրերը։

Այս դեպքում ցանցը այս սերվերին է հասնում Vlan-tagged traffic-ի տեսքով, որը նաև իր դաշտերն է ավելացնում փաթեթներին.

Եվ այս երթևեկությունը կարող է նաև մասնատված լինել (մուտքային մասնատված երթևեկության նույն փոքր տոկոսը, որի մասին մենք խոսեցինք Workaround-ից ռիսկերը գնահատելիս), ինչը նույնպես փոխում է վերնագրերի բովանդակությունը.

Եվս մեկ անգամ. փաթեթները փաթեթավորված են Vlan թեգով, փաթեթավորված են թունելով, մասնատված են։ Որպեսզի ավելի ճշգրիտ հասկանանք, թե ինչպես է դա տեղի ունենում, եկեք հետևենք փաթեթների երթուղուն հաճախորդից դեպի Nginx պրոքսի։
- Փաթեթը հասնում է L3 հավասարակշռիչին։ Տվյալների կենտրոնում ճիշտ երթուղավորման համար փաթեթը փաթեթավորվում է թունելի մեջ և ուղարկվում ցանցային քարտին։
- Քանի որ փաթեթի + թունելի վերնագրերը չեն տեղավորվում MTU-ում, փաթեթը կտրվում է բեկորների և ուղարկվում ցանց։
- L3 հավասարակշռիչից հետո գտնվող անջատիչը, փաթեթը ստանալիս, դրան ավելացնում է Vlan թեգ և ուղարկում այն։
- Nginx պրոքսիի առջևի անջատիչը տեսնում է (պորտի կարգավորումներից), որ սերվերը սպասում է Vlan-ին կպսուլացված փաթեթի, ուստի այն ուղարկում է այնպես, ինչպես կա՝ առանց Vlan թեգը հեռացնելու։
- Linux-ը վերցնում է առանձին փաթեթների բեկորները և դրանք միացնում է մեկ մեծ փաթեթի մեջ։
- Հաջորդը, փաթեթը գնում է Vlan ինտերֆեյս, որտեղ առաջին շերտը հեռացվում է - Vlan encapsulation:
- Այնուհետև Linux-ը այն ուղարկում է Tunnel ինտերֆեյս, որտեղից հեռացվում է մեկ այլ շերտ՝ Tunnel encapsulation-ը։
Դժվարությունն այն է, որ այս ամենը որպես պարամետրեր փոխանցվի tcpdump-ին։
Սկսենք վերջից. կա՞ն արդյոք հաճախորդներից ստացված մաքուր (առանց ավելորդ վերնագրերի) IP փաթեթներ՝ vlan-ի և թունելային ինկապսուլյացիայի հեռացմամբ։
tcpdump host <ip клиента>
Ոչ, սերվերի վրա նման փաթեթներ չկային։ Այնպես որ, խնդիրը պետք է որ ավելի վաղ լիներ։ Կա՞ն փաթեթներ, որոնցից հեռացված է միայն VLAN-ի ծածկագրումը։
tcpdump ip[32:4]=0xx390x2xx
0xx390x2xx-ը հաճախորդի IP հասցեն է տասնվեցական ձևաչափով։
32:4 — այն դաշտի հասցեն և երկարությունը, որտեղ Tunnel փաթեթում գրված է SCR IP-ն։
Դաշտի հասցեն պետք էր ընտրել կոպիտ ուժով, քանի որ ինտերնետը գրում է 40, 44, 50, 54-ի մասին, բայց այնտեղ IP հասցե չկար: Կարող եք նաև նայել hex-ով փաթեթներից մեկին (tcpdump-ում -xx կամ -XX պարամետրը) և հաշվարկել, թե ձեզ հայտնի IP-ն որ հասցեում է գտնվում:
Կա՞ն փաթեթների որևէ հատվածներ, որոնք չեն հեռացրել VLAN-ը և Tunnel-ի ծածկույթը։
tcpdump ((ip[6:2] > 0) and (not ip[6] = 64))
Այս կախարդանքը մեզ կցույց տա բոլոր բեկորները, այդ թվում՝ վերջինը։ Հավանաբար, նույնը կարելի է զտել IP հասցեով, բայց ես չեմ փորձել, քանի որ նման փաթեթները շատ չեն, և ընդհանուր հոսքում ես հեշտությամբ գտա ինձ անհրաժեշտները։ Ահա դրանք՝
14:02:58.471063 In 00:de:ff:1a:94:11 ethertype IPv4 (0x0800), length 1516: (tos 0x0, ttl 63, id 53652, offset 0, flags [+], proto IPIP (4), length 1500)
11.11.11.11 > 22.22.22.22: truncated-ip - 20 bytes missing! (tos 0x0, ttl 50, id 57750, offset 0, flags [DF], proto TCP (6), length 1500)
33.33.33.33.33333 > 44.44.44.44.80: Flags [.], seq 0:1448, ack 1, win 343, options [nop,nop,TS val 11660691 ecr 2998165860], length 1448
0x0000: 0000 0001 0006 00de fb1a 9441 0000 0800 ...........A....
0x0010: 4500 05dc d194 2000 3f09 d5fb 0a66 387d E.......?....f8}
0x0020: 1x67 7899 4500 06xx e198 4000 3206 6xx4 .faEE.....@.2.m.
0x0030: b291 x9xx x345 2541 83b9 0050 9740 0x04 .......A...P.@..
0x0040: 6444 4939 8010 0257 8c3c 0000 0101 080x dDI9...W.......
0x0050: 00b1 ed93 b2b4 6964 xxd8 ffe1 006a 4578 ......ad.....jEx
0x0060: 6966 0000 4x4d 002a 0500 0008 0004 0100 if..MM.*........
14:02:58.471103 00:de:ff:1a:94:11 եթերային IPv4 (0x0800) ֆայլում, երկարությունը 62: (tos 0x0, ttl 63, id 53652, offset 1480, դրոշներ [ոչ մեկը], պրոտո IPIP (4), երկարություն 40)
11.11.11.11 > 22.22.22.22: ip-proto-4
0x0000: 0000 0001 0006 00de fb1a 9441 0000 0800 ..........Ա....
0x0010: 4500 0028 d194 00b9 3f04 faf6 2x76 385x E..(....?....f8}
0x0020: 1x76 6545 xxxx 1x11 2d2c 0c21 8016 8e43 .faE...D-,.!...C
0x0030: x978 e91d x9b0 d608 0000 0000 0000 7c31 .x............|Q
0x0040: 881d c4b6 0000 0000 0000 0000 0000 .............
Սրանք մեկ փաթեթի (նույն ID 53652) երկու հատվածներ են՝ լուսանկարով (Exif բառը երևում է առաջին փաթեթում): Քանի որ այս մակարդակում փաթեթներ կան, բայց ոչ սոսնձված տեսքով աղբանոցներում, խնդիրը ակնհայտորեն հավաքման մեջ է: Վերջապես, կան դրա վերաբերյալ փաստաթղթային ապացույցներ:
Փաթեթների վերծանիչը չի հայտնաբերել որևէ խնդիր, որը խոչընդոտում էր կառուցմանը։ Ես փորձեցի այստեղ՝ Սկզբում, երբ փորձում էի ինչ-որ բան այնտեղ խցկել, դեկոդերին դուր չեկավ փաթեթի ձևաչափը։ Պարզվեց, որ Srcmac-ի և Ethertype-ի միջև կային երկու լրացուցիչ օկտետներ (որոնք կապված չեն ֆրագմենտի տեղեկատվության հետ)։ Դրանք հեռացնելուց հետո դեկոդերը աշխատեց։ Սակայն այն որևէ խնդիր չցուցաբերեց։
Ինչ էլ որ ասվի, բացի Sysctl-ից, ուրիշ ոչինչ չգտնվեց։ Մնում էր գտնել խնդրահարույց սերվերները նույնականացնելու միջոց՝ մասշտաբը հասկանալու և հետագա գործողությունները որոշելու համար։ Անհրաժեշտ հաշվիչը բավականին արագ գտնվեց։
netstat -s | grep "packet reassembles failed”
Այն նաև snmpd-ում է OID=1.3.6.1.2.1.4.31.1.1.16.1-ի ներքո ().
«IP վերամիավորման ալգորիթմի կողմից հայտնաբերված ձախողումների քանակը (ինչ-որ պատճառով՝ ժամանակի սպառում, սխալներ և այլն)»։
Սերվերների այն խմբում, որոնց վրա ուսումնասիրվել է խնդիրը, այս հաշվիչը երկուսի մոտ ավելի արագ է աճել, երկուսի մոտ՝ ավելի դանդաղ, իսկ երկուսի մոտ ընդհանրապես չի աճել: Այս հաշվիչի դինամիկայի համեմատությունը Java սերվերի HTTP սխալների դինամիկայի հետ ցույց է տվել կապ: Այսինքն՝ հաշվիչը կարելի է վերահսկել:
Խնդիրների հուսալի ցուցիչ ունենալը շատ կարևոր է՝ ճշգրիտ որոշելու համար, թե արդյոք Sysctl-ի վերականգնումը օգնում է, քանի որ նախորդ պատմությունից գիտենք, որ դա անմիջապես պարզ չէ հավելվածից։ Այս ցուցիչը թույլ կտա մեզ բացահայտել արտադրության մեջ առկա բոլոր խնդրահարույց տարածքները, նախքան օգտատերերը դրանք կհայտնաբերեն։
Sysctl-ը հետադարձելուց հետո մոնիթորինգի սխալները դադարեցին, ուստի խնդիրների պատճառը ապացուցվեց, ինչպես նաև այն փաստը, որ հետադարձումը օգնում է:
Մենք չեղարկեցինք ֆրագմենտացիայի կարգավորումները այլ սերվերներում, որտեղ ակտիվացված էր նոր մոնիթորինգը, և որոշ տեղերում ֆրագմենտների համար հատկացրեցինք նույնիսկ ավելի շատ հիշողություն, քան նախկինում լռելյայն էր (սրանք udp վիճակագրություն էին, որի մասնակի կորուստը ընդհանուր ֆոնի վրա նկատելի չէր):
Ամենակարևոր հարցերը
Ինչո՞ւ են փաթեթները մասնատվում մեր L3 հավասարակշռիչի վրա: Օգտատերերից հավասարակշռիչներին հասնող փաթեթների մեծ մասը SYN և ACK են: Այս փաթեթների չափերը փոքր են: Բայց քանի որ նման փաթեթների բաժինը շատ մեծ է, մենք չենք նկատել մեծ փաթեթների առկայությունը, որոնք սկսել են մասնատվել:
Պատճառը կոտրված կոնֆիգուրացիայի սկրիպտն էր Vlan ինտերֆեյսներով սերվերների վրա (այդ ժամանակ արտադրության մեջ շատ քիչ սերվերներ կային պիտակավորված երթևեկությամբ): Advmss-ը թույլ է տալիս մեզ հաճախորդին փոխանցել տեղեկատվություն, որ մեր ուղղությամբ փաթեթները պետք է լինեն ավելի փոքր չափի, որպեսզի թունելային վերնագրերը դրանց վրա կպցնելուց հետո դրանք չպարտադրվեն մասնատվել:
Ինչո՞ւ Sysctl-ի հետադարձումը չօգնեց, բայց վերագործարկումը օգնեց։ Sysctl-ի հետադարձումը փոխեց փաթեթների կպչման համար հասանելի հիշողության քանակը։ Միևնույն ժամանակ, ակնհայտորեն, ֆրագմենտների հիշողության գերբեռնվածության փաստն ինքնին հանգեցրեց կապերի դանդաղեցմանը, ինչը հանգեցրեց ֆրագմենտների հերթում երկար ժամանակով հետաձգմանը։ Այսինքն՝ գործընթացը ցիկլային էր։
Վերագործարկումը մաքրեց հիշողությունը, և ամեն ինչ վերադարձավ իր տեղը։
Հնարավո՞ր էր անել առանց Workaround-ի։ Այո, բայց հարձակման դեպքում օգտատերերին առանց ծառայության թողնելու մեծ ռիսկ կա։ Իհարկե, Workaround-ի օգտագործումը, ի վերջո, հանգեցրեց տարբեր խնդիրների, այդ թվում՝ օգտատերերի համար ծառայություններից մեկի դանդաղեցմանը, բայց այնուամենայնիվ, մենք կարծում ենք, որ գործողությունները արդարացված էին։
Շատ շնորհակալություն Անդրեյ Տիմոֆեևին () հետաքննության անցկացման գործում ցուցաբերած օգնության համար, ինչպես նաև Ալեքսեյ Կրենևին () — սերվերների վրա Centos-ի և միջուկների թարմացման տիտանական աշխատանքի համար։ Գործընթաց, որը այս դեպքում պետք է մի քանի անգամ սկսվեր զրոյից, այդ իսկ պատճառով այն ձգձգվեց մի քանի ամիս։
Source: www.habr.com
