[{"data":1,"prerenderedAt":3757},["ShallowReactive",2],{"content:\u002F07-infrastructure\u002F05-workloads-deploy":3},{"title":4,"description":5,"path":6,"body":7},"Workloads и стратегии деплоя","Kubernetes предоставляет несколько контроллеров для управления жизненным циклом приложений. Каждый из них решает свою задачу: ReplicaSet гарантирует нужное количество реплик, Deployment добавляет стратегии обновления и откатов, а StatefulSet обеспечивает стабильную идентичность для stateful-приложений. Понимание этих абстракций и их взаимосвязей --- ключ к надёжной эксплуатации сервисов в production.","\u002F07-infrastructure\u002F05-workloads-deploy",{"type":8,"value":9,"toc":3695},"minimark",[10,15,18,21,26,29,34,37,48,55,59,284,298,302,309,367,374,399,403,406,493,497,508,511,515,518,520,524,527,533,536,540,599,602,814,816,820,823,827,853,856,872,878,881,885,932,935,949,955,959,965,1046,1049,1053,1060,1066,1069,1073,1079,1089,1091,1094,1097,1149,1152,1172,1176,1183,1234,1240,1246,1248,1252,1297,1300,1314,1318,1383,1386,1446,1448,1452,1455,1459,1462,1465,1471,1474,1478,1481,1487,1490,1494,1497,1503,1506,1510,1599,1601,1605,1608,1612,1726,1729,2031,2035,2038,2044,2047,2051,2054,2160,2163,2184,2190,2193,2223,2227,2230,2233,2274,2278,2291,2297,2301,2344,2351,2353,2357,2361,2364,2370,2374,2401,2404,2471,2475,2518,2524,2527,2547,2550,2553,2556,2601,2604,2606,2610,2614,2617,2638,2642,2645,2659,2665,2669,2741,2748,2752,2806,2808,2812,3144,3146,3150,3155,3158,3163,3175,3180,3183,3188,3194,3199,3206,3211,3214,3219,3225,3227,3231,3235,3238,3249,3256,3260,3263,3274,3277,3281,3284,3315,3317,3321,3350,3528,3691],[11,12,14],"h1",{"id":13},"workloads-и-стратегии-деплоя-в-kubernetes","Workloads и стратегии деплоя в Kubernetes",[16,17,5],"p",{},[19,20],"hr",{},[22,23,25],"h2",{"id":24},"replicaset-гарантия-n-реплик","ReplicaSet --- гарантия N реплик",[16,27,28],{},"ReplicaSet --- контроллер, который гарантирует, что в кластере всегда запущено ровно N подов с заданными labels. Это базовый строительный блок, на котором основан Deployment.",[30,31,33],"h3",{"id":32},"reconciliation-loop","Reconciliation loop",[16,35,36],{},"В основе работы ReplicaSet лежит бесконечный цикл согласования (reconciliation loop). Контроллер постоянно наблюдает за подами, сравнивает фактическое количество с желаемым и реагирует:",[38,39,44],"pre",{"className":40,"code":42,"language":43},[41],"language-text","                ┌──────────────────────────┐\n                │    ReplicaSet Controller  │\n                └──────────┬───────────────┘\n                           │\n          ┌────────────────▼────────────────┐\n          │  Watch: ReplicaSet + Pod objects │\n          └────────────────┬────────────────┘\n                           │\n          ┌────────────────▼────────────────┐\n          │  Count pods matching selector   │\n          └────────────────┬────────────────┘\n                           │\n            ┌──────────────┼──────────────┐\n            ▼              ▼              ▼\n      actual \u003C desired  actual == desired  actual > desired\n            │              │              │\n      Create pods     Do nothing     Delete excess pods\n      from template                  (по правилам приоритета)\n","text",[45,46,42],"code",{"__ignoreMap":47},"",[16,49,50,51,54],{},"Контроллер работает постоянно и реагирует на любые изменения: если под упал, был удалён вручную или нода вышла из строя --- ReplicaSet создаст новые поды до достижения ",[45,52,53],{},"replicas",".",[30,56,58],{"id":57},"манифест","Манифест",[38,60,64],{"className":61,"code":62,"language":63,"meta":47,"style":47},"language-yaml shiki shiki-themes github-dark","apiVersion: apps\u002Fv1\nkind: ReplicaSet\nmetadata:\n  name: kiada\nspec:\n  replicas: 3                    # желаемое количество подов\n  selector:                      # какие поды принадлежат этому RS\n    matchLabels:\n      app: kiada\n      rel: stable\n  template:                      # шаблон для создания подов\n    metadata:\n      labels:\n        app: kiada               # ДОЛЖНЫ совпадать с selector\n        rel: stable\n        ver: \"0.5\"               # могут быть дополнительные labels\n    spec:\n      containers:\n      - name: kiada\n        image: luksa\u002Fkiada:0.5\n","yaml",[45,65,66,83,94,103,114,122,138,150,158,168,179,190,198,206,220,230,244,252,260,273],{"__ignoreMap":47},[67,68,71,75,79],"span",{"class":69,"line":70},"line",1,[67,72,74],{"class":73},"s4JwU","apiVersion",[67,76,78],{"class":77},"s95oV",": ",[67,80,82],{"class":81},"sU2Wk","apps\u002Fv1\n",[67,84,86,89,91],{"class":69,"line":85},2,[67,87,88],{"class":73},"kind",[67,90,78],{"class":77},[67,92,93],{"class":81},"ReplicaSet\n",[67,95,97,100],{"class":69,"line":96},3,[67,98,99],{"class":73},"metadata",[67,101,102],{"class":77},":\n",[67,104,106,109,111],{"class":69,"line":105},4,[67,107,108],{"class":73},"  name",[67,110,78],{"class":77},[67,112,113],{"class":81},"kiada\n",[67,115,117,120],{"class":69,"line":116},5,[67,118,119],{"class":73},"spec",[67,121,102],{"class":77},[67,123,125,128,130,134],{"class":69,"line":124},6,[67,126,127],{"class":73},"  replicas",[67,129,78],{"class":77},[67,131,133],{"class":132},"sDLfK","3",[67,135,137],{"class":136},"sAwPA","                    # желаемое количество подов\n",[67,139,141,144,147],{"class":69,"line":140},7,[67,142,143],{"class":73},"  selector",[67,145,146],{"class":77},":                      ",[67,148,149],{"class":136},"# какие поды принадлежат этому RS\n",[67,151,153,156],{"class":69,"line":152},8,[67,154,155],{"class":73},"    matchLabels",[67,157,102],{"class":77},[67,159,161,164,166],{"class":69,"line":160},9,[67,162,163],{"class":73},"      app",[67,165,78],{"class":77},[67,167,113],{"class":81},[67,169,171,174,176],{"class":69,"line":170},10,[67,172,173],{"class":73},"      rel",[67,175,78],{"class":77},[67,177,178],{"class":81},"stable\n",[67,180,182,185,187],{"class":69,"line":181},11,[67,183,184],{"class":73},"  template",[67,186,146],{"class":77},[67,188,189],{"class":136},"# шаблон для создания подов\n",[67,191,193,196],{"class":69,"line":192},12,[67,194,195],{"class":73},"    metadata",[67,197,102],{"class":77},[67,199,201,204],{"class":69,"line":200},13,[67,202,203],{"class":73},"      labels",[67,205,102],{"class":77},[67,207,209,212,214,217],{"class":69,"line":208},14,[67,210,211],{"class":73},"        app",[67,213,78],{"class":77},[67,215,216],{"class":81},"kiada",[67,218,219],{"class":136},"               # ДОЛЖНЫ совпадать с selector\n",[67,221,223,226,228],{"class":69,"line":222},15,[67,224,225],{"class":73},"        rel",[67,227,78],{"class":77},[67,229,178],{"class":81},[67,231,233,236,238,241],{"class":69,"line":232},16,[67,234,235],{"class":73},"        ver",[67,237,78],{"class":77},[67,239,240],{"class":81},"\"0.5\"",[67,242,243],{"class":136},"               # могут быть дополнительные labels\n",[67,245,247,250],{"class":69,"line":246},17,[67,248,249],{"class":73},"    spec",[67,251,102],{"class":77},[67,253,255,258],{"class":69,"line":254},18,[67,256,257],{"class":73},"      containers",[67,259,102],{"class":77},[67,261,263,266,269,271],{"class":69,"line":262},19,[67,264,265],{"class":77},"      - ",[67,267,268],{"class":73},"name",[67,270,78],{"class":77},[67,272,113],{"class":81},[67,274,276,279,281],{"class":69,"line":275},20,[67,277,278],{"class":73},"        image",[67,280,78],{"class":77},[67,282,283],{"class":81},"luksa\u002Fkiada:0.5\n",[16,285,286,287,289,290,293,294,297],{},"Три ключевых поля в spec: ",[45,288,53],{}," (сколько подов нужно), ",[45,291,292],{},"selector"," (какие поды считать своими), ",[45,295,296],{},"template"," (шаблон для новых подов). Labels в template обязаны совпадать с selector --- иначе ReplicaSet не сможет найти свои поды.",[30,299,301],{"id":300},"ownerreferences-и-принадлежность-подов","ownerReferences и принадлежность подов",[16,303,304,305,308],{},"Каждый под, созданный ReplicaSet, содержит в metadata поле ",[45,306,307],{},"ownerReferences",", указывающее на владельца:",[38,310,312],{"className":61,"code":311,"language":63,"meta":47,"style":47},"ownerReferences:\n- apiVersion: apps\u002Fv1\n  kind: ReplicaSet\n  name: kiada\n  controller: true\n  blockOwnerDeletion: true\n",[45,313,314,320,331,340,348,358],{"__ignoreMap":47},[67,315,316,318],{"class":69,"line":70},[67,317,307],{"class":73},[67,319,102],{"class":77},[67,321,322,325,327,329],{"class":69,"line":85},[67,323,324],{"class":77},"- ",[67,326,74],{"class":73},[67,328,78],{"class":77},[67,330,82],{"class":81},[67,332,333,336,338],{"class":69,"line":96},[67,334,335],{"class":73},"  kind",[67,337,78],{"class":77},[67,339,93],{"class":81},[67,341,342,344,346],{"class":69,"line":105},[67,343,108],{"class":73},[67,345,78],{"class":77},[67,347,113],{"class":81},[67,349,350,353,355],{"class":69,"line":116},[67,351,352],{"class":73},"  controller",[67,354,78],{"class":77},[67,356,357],{"class":132},"true\n",[67,359,360,363,365],{"class":69,"line":124},[67,361,362],{"class":73},"  blockOwnerDeletion",[67,364,78],{"class":77},[67,366,357],{"class":132},[16,368,369,370,373],{},"При удалении ReplicaSet garbage collector автоматически удаляет все принадлежащие ему поды. Если нужно сохранить поды при удалении контроллера, используйте ",[45,371,372],{},"--cascade=orphan",":",[38,375,379],{"className":376,"code":377,"language":378,"meta":47,"style":47},"language-bash shiki shiki-themes github-dark","kubectl delete rs kiada --cascade=orphan\n","bash",[45,380,381],{"__ignoreMap":47},[67,382,383,387,390,393,396],{"class":69,"line":70},[67,384,386],{"class":385},"svObZ","kubectl",[67,388,389],{"class":81}," delete",[67,391,392],{"class":81}," rs",[67,394,395],{"class":81}," kiada",[67,397,398],{"class":132}," --cascade=orphan\n",[30,400,402],{"id":401},"порядок-удаления-при-scale-down","Порядок удаления при scale down",[16,404,405],{},"При уменьшении количества реплик контроллер выбирает, какие поды удалить первыми, по приоритету:",[407,408,409,422],"table",{},[410,411,412],"thead",{},[413,414,415,419],"tr",{},[416,417,418],"th",{},"Приоритет",[416,420,421],{},"Какие поды удаляются первыми",[423,424,425,434,442,449,461,469,477,485],"tbody",{},[413,426,427,431],{},[428,429,430],"td",{},"1",[428,432,433],{},"Поды без назначенной ноды",[413,435,436,439],{},[428,437,438],{},"2",[428,440,441],{},"Поды со статусом Unknown",[413,443,444,446],{},[428,445,133],{},[428,447,448],{},"Поды, которые не ready",[413,450,451,454],{},[428,452,453],{},"4",[428,455,456,457,460],{},"Поды с меньшим deletion cost (annotation ",[45,458,459],{},"controller.kubernetes.io\u002Fpod-deletion-cost",")",[413,462,463,466],{},[428,464,465],{},"5",[428,467,468],{},"Поды на нодах с большим количеством реплик (выравнивание)",[413,470,471,474],{},[428,472,473],{},"6",[428,475,476],{},"Поды, которые были ready меньше времени",[413,478,479,482],{},[428,480,481],{},"7",[428,483,484],{},"Поды с большим количеством restarts",[413,486,487,490],{},[428,488,489],{},"8",[428,491,492],{},"Поды, созданные позже",[30,494,496],{"id":495},"важное-ограничение-обновление-template","Важное ограничение: обновление template",[16,498,499,500,502,503,507],{},"Изменение ",[45,501,296],{}," в ReplicaSet ",[504,505,506],"strong",{},"не обновляет"," существующие поды. Только новые поды (при scale up или замене упавших) создаются по новому шаблону. RS работает как \"формочка для печенья\" --- если изменить форму, уже вырезанное печенье не изменится.",[16,509,510],{},"Именно поэтому ReplicaSet не используют напрямую. Для автоматического обновления подов нужен Deployment.",[30,512,514],{"id":513},"rs-не-гарантирует-healthy-поды","RS не гарантирует healthy-поды",[16,516,517],{},"ReplicaSet гарантирует только количество: actual count == desired count. Если под crash-loop-ит или проваливает readiness probe, RS не удалит и не заменит его --- с точки зрения контроллера, под существует, значит всё в порядке. Диагностика и починка --- ответственность оператора.",[19,519],{},[22,521,523],{"id":522},"deployment-обёртка-над-replicaset","Deployment --- обёртка над ReplicaSet",[16,525,526],{},"Deployment --- основной способ запуска stateless-приложений в Kubernetes. Он управляет ReplicaSet, который в свою очередь управляет подами. Цепочка владения выглядит так:",[38,528,531],{"className":529,"code":530,"language":43},[41],"Deployment → ReplicaSet → Pods\n",[45,532,530],{"__ignoreMap":47},[16,534,535],{},"Вы работаете только с Deployment, остальное создаётся и управляется автоматически.",[30,537,539],{"id":538},"deployment-vs-replicaset","Deployment vs ReplicaSet",[407,541,542,554],{},[410,543,544],{},[413,545,546,548,551],{},[416,547],{},[416,549,550],{},"ReplicaSet",[416,552,553],{},"Deployment",[423,555,556,567,578,589],{},[413,557,558,561,564],{},[428,559,560],{},"Управляет",[428,562,563],{},"подами",[428,565,566],{},"ReplicaSet-ами",[413,568,569,572,575],{},[428,570,571],{},"Обновление template",[428,573,574],{},"ничего не происходит",[428,576,577],{},"rolling update",[413,579,580,583,586],{},[428,581,582],{},"Rollback",[428,584,585],{},"нет",[428,587,588],{},"через revision history",[413,590,591,594,596],{},[428,592,593],{},"Используй напрямую?",[428,595,585],{},[428,597,598],{},"да, для stateless workloads",[30,600,58],{"id":601},"манифест-1",[38,603,605],{"className":61,"code":604,"language":63,"meta":47,"style":47},"apiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: kiada\nspec:\n  replicas: 3\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxSurge: 1\n      maxUnavailable: 0\n  minReadySeconds: 10\n  selector:\n    matchLabels:\n      app: kiada\n      rel: stable\n  template:\n    metadata:\n      labels:\n        app: kiada\n        rel: stable\n        ver: \"0.5\"\n    spec:\n      containers:\n      - name: kiada\n        image: luksa\u002Fkiada:0.5\n",[45,606,607,615,624,630,638,644,653,660,670,677,687,697,707,713,719,727,735,741,747,753,761,770,780,787,794,805],{"__ignoreMap":47},[67,608,609,611,613],{"class":69,"line":70},[67,610,74],{"class":73},[67,612,78],{"class":77},[67,614,82],{"class":81},[67,616,617,619,621],{"class":69,"line":85},[67,618,88],{"class":73},[67,620,78],{"class":77},[67,622,623],{"class":81},"Deployment\n",[67,625,626,628],{"class":69,"line":96},[67,627,99],{"class":73},[67,629,102],{"class":77},[67,631,632,634,636],{"class":69,"line":105},[67,633,108],{"class":73},[67,635,78],{"class":77},[67,637,113],{"class":81},[67,639,640,642],{"class":69,"line":116},[67,641,119],{"class":73},[67,643,102],{"class":77},[67,645,646,648,650],{"class":69,"line":124},[67,647,127],{"class":73},[67,649,78],{"class":77},[67,651,652],{"class":132},"3\n",[67,654,655,658],{"class":69,"line":140},[67,656,657],{"class":73},"  strategy",[67,659,102],{"class":77},[67,661,662,665,667],{"class":69,"line":152},[67,663,664],{"class":73},"    type",[67,666,78],{"class":77},[67,668,669],{"class":81},"RollingUpdate\n",[67,671,672,675],{"class":69,"line":160},[67,673,674],{"class":73},"    rollingUpdate",[67,676,102],{"class":77},[67,678,679,682,684],{"class":69,"line":170},[67,680,681],{"class":73},"      maxSurge",[67,683,78],{"class":77},[67,685,686],{"class":132},"1\n",[67,688,689,692,694],{"class":69,"line":181},[67,690,691],{"class":73},"      maxUnavailable",[67,693,78],{"class":77},[67,695,696],{"class":132},"0\n",[67,698,699,702,704],{"class":69,"line":192},[67,700,701],{"class":73},"  minReadySeconds",[67,703,78],{"class":77},[67,705,706],{"class":132},"10\n",[67,708,709,711],{"class":69,"line":200},[67,710,143],{"class":73},[67,712,102],{"class":77},[67,714,715,717],{"class":69,"line":208},[67,716,155],{"class":73},[67,718,102],{"class":77},[67,720,721,723,725],{"class":69,"line":222},[67,722,163],{"class":73},[67,724,78],{"class":77},[67,726,113],{"class":81},[67,728,729,731,733],{"class":69,"line":232},[67,730,173],{"class":73},[67,732,78],{"class":77},[67,734,178],{"class":81},[67,736,737,739],{"class":69,"line":246},[67,738,184],{"class":73},[67,740,102],{"class":77},[67,742,743,745],{"class":69,"line":254},[67,744,195],{"class":73},[67,746,102],{"class":77},[67,748,749,751],{"class":69,"line":262},[67,750,203],{"class":73},[67,752,102],{"class":77},[67,754,755,757,759],{"class":69,"line":275},[67,756,211],{"class":73},[67,758,78],{"class":77},[67,760,113],{"class":81},[67,762,764,766,768],{"class":69,"line":763},21,[67,765,225],{"class":73},[67,767,78],{"class":77},[67,769,178],{"class":81},[67,771,773,775,777],{"class":69,"line":772},22,[67,774,235],{"class":73},[67,776,78],{"class":77},[67,778,779],{"class":81},"\"0.5\"\n",[67,781,783,785],{"class":69,"line":782},23,[67,784,249],{"class":73},[67,786,102],{"class":77},[67,788,790,792],{"class":69,"line":789},24,[67,791,257],{"class":73},[67,793,102],{"class":77},[67,795,797,799,801,803],{"class":69,"line":796},25,[67,798,265],{"class":77},[67,800,268],{"class":73},[67,802,78],{"class":77},[67,804,113],{"class":81},[67,806,808,810,812],{"class":69,"line":807},26,[67,809,278],{"class":73},[67,811,78],{"class":77},[67,813,283],{"class":81},[19,815],{},[22,817,819],{"id":818},"стратегии-обновления","Стратегии обновления",[16,821,822],{},"Kubernetes предоставляет две встроенные стратегии обновления: Recreate и RollingUpdate.",[30,824,826],{"id":825},"recreate-замена-с-простоем","Recreate --- замена с простоем",[38,828,830],{"className":61,"code":829,"language":63,"meta":47,"style":47},"spec:\n  strategy:\n    type: Recreate\n",[45,831,832,838,844],{"__ignoreMap":47},[67,833,834,836],{"class":69,"line":70},[67,835,119],{"class":73},[67,837,102],{"class":77},[67,839,840,842],{"class":69,"line":85},[67,841,657],{"class":73},[67,843,102],{"class":77},[67,845,846,848,850],{"class":69,"line":96},[67,847,664],{"class":73},[67,849,78],{"class":77},[67,851,852],{"class":81},"Recreate\n",[16,854,855],{},"Порядок действий:",[857,858,859,863,866,869],"ol",{},[860,861,862],"li",{},"Deployment controller масштабирует старый ReplicaSet до 0",[860,864,865],{},"Все старые поды удаляются одновременно",[860,867,868],{},"Создаётся новый ReplicaSet с desired replicas",[860,870,871],{},"Новые поды стартуют одновременно",[38,873,876],{"className":874,"code":875,"language":43},[41],"Timeline:\n  v1 v1 v1 ──── все удалены ──── v2 v2 v2\n                 ↑ DOWNTIME ↑\n",[45,877,875],{"__ignoreMap":47},[16,879,880],{},"Используйте Recreate, когда приложение не может работать в двух версиях одновременно (например, несовместимые миграции БД). Главный минус --- downtime.",[30,882,884],{"id":883},"rollingupdate-постепенная-замена-default","RollingUpdate --- постепенная замена (default)",[38,886,888],{"className":61,"code":887,"language":63,"meta":47,"style":47},"spec:\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxSurge: 0\n      maxUnavailable: 1\n",[45,889,890,896,902,910,916,924],{"__ignoreMap":47},[67,891,892,894],{"class":69,"line":70},[67,893,119],{"class":73},[67,895,102],{"class":77},[67,897,898,900],{"class":69,"line":85},[67,899,657],{"class":73},[67,901,102],{"class":77},[67,903,904,906,908],{"class":69,"line":96},[67,905,664],{"class":73},[67,907,78],{"class":77},[67,909,669],{"class":81},[67,911,912,914],{"class":69,"line":105},[67,913,674],{"class":73},[67,915,102],{"class":77},[67,917,918,920,922],{"class":69,"line":116},[67,919,681],{"class":73},[67,921,78],{"class":77},[67,923,696],{"class":132},[67,925,926,928,930],{"class":69,"line":124},[67,927,691],{"class":73},[67,929,78],{"class":77},[67,931,686],{"class":132},[16,933,934],{},"Поды заменяются постепенно. На каждом шаге:",[857,936,937,940,943,946],{},[860,938,939],{},"Scale down старого RS на 1",[860,941,942],{},"Scale up нового RS на 1",[860,944,945],{},"Ожидание, пока новый под станет ready + minReadySeconds",[860,947,948],{},"Повтор",[38,950,953],{"className":951,"code":952,"language":43},[41],"Timeline (maxSurge=0, maxUnavailable=1):\n  v1 v1 v1  → v1 v1 __  → v1 v1 v2  → v1 __ v2\n            → v1 v2 v2  → __ v2 v2  → v2 v2 v2\n",[45,954,952],{"__ignoreMap":47},[30,956,958],{"id":957},"maxsurge-и-maxunavailable","maxSurge и maxUnavailable",[16,960,961,962,373],{},"Эти два параметра определяют скорость и безопасность обновления. При ",[45,963,964],{},"desired = 3",[407,966,967,986],{},[410,968,969],{},[413,970,971,974,977,980,983],{},[416,972,973],{},"maxSurge",[416,975,976],{},"maxUnavailable",[416,978,979],{},"total",[416,981,982],{},"available",[416,984,985],{},"Поведение",[423,987,988,1004,1019,1032],{},[413,989,990,993,995,998,1001],{},[428,991,992],{},"0",[428,994,430],{},[428,996,997],{},"\u003C= 3",[428,999,1000],{},">= 2",[428,1002,1003],{},"медленно, по одному поду за раз",[413,1005,1006,1008,1010,1013,1016],{},[428,1007,430],{},[428,1009,992],{},[428,1011,1012],{},"\u003C= 4",[428,1014,1015],{},">= 3",[428,1017,1018],{},"сначала создать новый, потом удалить старый --- zero downtime",[413,1020,1021,1023,1025,1027,1029],{},[428,1022,430],{},[428,1024,430],{},[428,1026,1012],{},[428,1028,1000],{},[428,1030,1031],{},"быстрее (2 пода за раз)",[413,1033,1034,1036,1038,1041,1043],{},[428,1035,992],{},[428,1037,992],{},[428,1039,1040],{},"---",[428,1042,1040],{},[428,1044,1045],{},"невалидно (невозможно обновить)",[16,1047,1048],{},"Значения по умолчанию: maxSurge=25%, maxUnavailable=25%. При 10 репликах это значит maxSurge=3 (ceil), maxUnavailable=2 (floor), то есть total \u003C= 13, available >= 8.",[30,1050,1052],{"id":1051},"pod-template-hash-версионирование-replicaset","pod-template-hash --- версионирование ReplicaSet",[16,1054,1055,1056,1059],{},"Deployment controller автоматически добавляет label ",[45,1057,1058],{},"pod-template-hash"," к каждому ReplicaSet и его подам. Значение --- hash от pod template. Каждый новый template порождает новый ReplicaSet с уникальным hash:",[38,1061,1064],{"className":1062,"code":1063,"language":43},[41],"Deployment: kiada\n  │\n  ├── RS: kiada-7bffb9bf96 (hash от template v0.5)\n  │     replicas: 0 (старый, после update)\n  │     pod-template-hash: 7bffb9bf96\n  │\n  └── RS: kiada-58df67c6f6 (hash от template v0.6)\n        replicas: 3 (текущий, active)\n        pod-template-hash: 58df67c6f6\n\nPod names: kiada-58df67c6f6-4knb6\n           ─────┬───────── ──┬──\n           RS name           random suffix\n",[45,1065,1063],{"__ignoreMap":47},[16,1067,1068],{},"Этот механизм предотвращает \"захват\" подов старым ReplicaSet и обеспечивает корректное версионирование.",[30,1070,1072],{"id":1071},"minreadyseconds-защита-от-плохих-версий","minReadySeconds --- защита от плохих версий",[16,1074,1075,1078],{},[45,1076,1077],{},"minReadySeconds"," определяет, сколько секунд под должен быть ready, прежде чем считаться available. Пока под не станет available, rollout не продолжится.",[16,1080,1081,1082,1084,1085,1088],{},"Пример: приложение стартует, проходит readiness probe, но падает через 30 секунд. Без ",[45,1083,1077],{}," все поды обновятся и все упадут --- полный outage. С ",[45,1086,1087],{},"minReadySeconds: 60"," rollout застрянет на первом поде, старые поды продолжат обслуживать трафик, и вы сможете откатиться.",[19,1090],{},[22,1092,582],{"id":1093},"rollback",[16,1095,1096],{},"Одно из ключевых преимуществ Deployment --- возможность быстро откатиться к предыдущей версии.",[38,1098,1100],{"className":376,"code":1099,"language":378,"meta":47,"style":47},"# Откатить к предыдущей ревизии\nkubectl rollout undo deployment kiada\n\n# Откатить к конкретной ревизии\nkubectl rollout undo deployment kiada --to-revision=2\n",[45,1101,1102,1107,1123,1129,1134],{"__ignoreMap":47},[67,1103,1104],{"class":69,"line":70},[67,1105,1106],{"class":136},"# Откатить к предыдущей ревизии\n",[67,1108,1109,1111,1114,1117,1120],{"class":69,"line":85},[67,1110,386],{"class":385},[67,1112,1113],{"class":81}," rollout",[67,1115,1116],{"class":81}," undo",[67,1118,1119],{"class":81}," deployment",[67,1121,1122],{"class":81}," kiada\n",[67,1124,1125],{"class":69,"line":96},[67,1126,1128],{"emptyLinePlaceholder":1127},true,"\n",[67,1130,1131],{"class":69,"line":105},[67,1132,1133],{"class":136},"# Откатить к конкретной ревизии\n",[67,1135,1136,1138,1140,1142,1144,1146],{"class":69,"line":116},[67,1137,386],{"class":385},[67,1139,1113],{"class":81},[67,1141,1116],{"class":81},[67,1143,1119],{"class":81},[67,1145,395],{"class":81},[67,1147,1148],{"class":132}," --to-revision=2\n",[16,1150,1151],{},"Rollback --- это обратный update, который соблюдает текущую strategy. При RollingUpdate --- постепенный откат, при Recreate --- все поды заменяются сразу.",[16,1153,1154,1155,1158,1159,1161,1162,1161,1165,1167,1168,1171],{},"Важно: ",[45,1156,1157],{},"undo"," откатывает только pod template. Поля ",[45,1160,53],{},", ",[45,1163,1164],{},"strategy",[45,1166,1077],{}," не откатываются. Для полного отката используйте ",[45,1169,1170],{},"kubectl apply"," с нужной версией манифеста.",[30,1173,1175],{"id":1174},"revision-history","Revision history",[16,1177,1178,1179,1182],{},"История ревизий хранится в старых ReplicaSet (с replicas: 0). Параметр ",[45,1180,1181],{},"revisionHistoryLimit"," (по умолчанию 10) определяет, сколько RS хранить.",[38,1184,1186],{"className":376,"code":1185,"language":378,"meta":47,"style":47},"# Посмотреть историю ревизий\nkubectl rollout history deploy kiada\n\n# Посмотреть конкретную ревизию\nkubectl rollout history deploy kiada --revision 2\n",[45,1187,1188,1193,1207,1211,1216],{"__ignoreMap":47},[67,1189,1190],{"class":69,"line":70},[67,1191,1192],{"class":136},"# Посмотреть историю ревизий\n",[67,1194,1195,1197,1199,1202,1205],{"class":69,"line":85},[67,1196,386],{"class":385},[67,1198,1113],{"class":81},[67,1200,1201],{"class":81}," history",[67,1203,1204],{"class":81}," deploy",[67,1206,1122],{"class":81},[67,1208,1209],{"class":69,"line":96},[67,1210,1128],{"emptyLinePlaceholder":1127},[67,1212,1213],{"class":69,"line":105},[67,1214,1215],{"class":136},"# Посмотреть конкретную ревизию\n",[67,1217,1218,1220,1222,1224,1226,1228,1231],{"class":69,"line":116},[67,1219,386],{"class":385},[67,1221,1113],{"class":81},[67,1223,1201],{"class":81},[67,1225,1204],{"class":81},[67,1227,395],{"class":81},[67,1229,1230],{"class":132}," --revision",[67,1232,1233],{"class":132}," 2\n",[38,1235,1238],{"className":1236,"code":1237,"language":43},[41],"Deployment\n  │\n  ├── RS rev.1 (replicas: 0, image: v0.5)  ← история\n  ├── RS rev.2 (replicas: 0, image: v0.6)  ← история\n  └── RS rev.3 (replicas: 3, image: v0.7)  ← текущий (active)\n\nRollback to rev.1:\n  RS rev.1 → scale up to 3\n  RS rev.3 → scale down to 0\n  rev.1 получает новый номер (rev.4)\n",[45,1239,1237],{"__ignoreMap":47},[16,1241,1242,1243,54],{},"Номер ревизии хранится в annotation ",[45,1244,1245],{},"deployment.kubernetes.io\u002Frevision",[19,1247],{},[22,1249,1251],{"id":1250},"pause-resume-поэтапные-обновления","Pause \u002F Resume --- поэтапные обновления",[38,1253,1255],{"className":376,"code":1254,"language":378,"meta":47,"style":47},"# Приостановить rollout\nkubectl rollout pause deployment kiada\n\n# Возобновить\nkubectl rollout resume deployment kiada\n",[45,1256,1257,1262,1275,1279,1284],{"__ignoreMap":47},[67,1258,1259],{"class":69,"line":70},[67,1260,1261],{"class":136},"# Приостановить rollout\n",[67,1263,1264,1266,1268,1271,1273],{"class":69,"line":85},[67,1265,386],{"class":385},[67,1267,1113],{"class":81},[67,1269,1270],{"class":81}," pause",[67,1272,1119],{"class":81},[67,1274,1122],{"class":81},[67,1276,1277],{"class":69,"line":96},[67,1278,1128],{"emptyLinePlaceholder":1127},[67,1280,1281],{"class":69,"line":105},[67,1282,1283],{"class":136},"# Возобновить\n",[67,1285,1286,1288,1290,1293,1295],{"class":69,"line":116},[67,1287,386],{"class":385},[67,1289,1113],{"class":81},[67,1291,1292],{"class":81}," resume",[67,1294,1119],{"class":81},[67,1296,1122],{"class":81},[16,1298,1299],{},"Два сценария использования:",[857,1301,1302,1308],{},[860,1303,1304,1307],{},[504,1305,1306],{},"Pause в процессе обновления"," --- rollout останавливается, часть подов на старой версии, часть на новой. Можно проверить новые поды перед продолжением.",[860,1309,1310,1313],{},[504,1311,1312],{},"Pause перед обновлением"," --- вносим несколько изменений (image, env, labels), затем resume --- все изменения применяются в одном rollout.",[30,1315,1317],{"id":1316},"отслеживание-rollout","Отслеживание rollout",[38,1319,1321],{"className":376,"code":1320,"language":378,"meta":47,"style":47},"# Статус rollout\nkubectl rollout status deploy kiada\n\n# Детали и conditions\nkubectl describe deploy kiada\n\n# Restart подов (без изменения template)\nkubectl rollout restart deployment kiada\n",[45,1322,1323,1328,1341,1345,1350,1361,1365,1370],{"__ignoreMap":47},[67,1324,1325],{"class":69,"line":70},[67,1326,1327],{"class":136},"# Статус rollout\n",[67,1329,1330,1332,1334,1337,1339],{"class":69,"line":85},[67,1331,386],{"class":385},[67,1333,1113],{"class":81},[67,1335,1336],{"class":81}," status",[67,1338,1204],{"class":81},[67,1340,1122],{"class":81},[67,1342,1343],{"class":69,"line":96},[67,1344,1128],{"emptyLinePlaceholder":1127},[67,1346,1347],{"class":69,"line":105},[67,1348,1349],{"class":136},"# Детали и conditions\n",[67,1351,1352,1354,1357,1359],{"class":69,"line":116},[67,1353,386],{"class":385},[67,1355,1356],{"class":81}," describe",[67,1358,1204],{"class":81},[67,1360,1122],{"class":81},[67,1362,1363],{"class":69,"line":124},[67,1364,1128],{"emptyLinePlaceholder":1127},[67,1366,1367],{"class":69,"line":140},[67,1368,1369],{"class":136},"# Restart подов (без изменения template)\n",[67,1371,1372,1374,1376,1379,1381],{"class":69,"line":152},[67,1373,386],{"class":385},[67,1375,1113],{"class":81},[67,1377,1378],{"class":81}," restart",[67,1380,1119],{"class":81},[67,1382,1122],{"class":81},[16,1384,1385],{},"Полезные conditions в describe:",[407,1387,1388,1404],{},[410,1389,1390],{},[413,1391,1392,1395,1398,1401],{},[416,1393,1394],{},"Condition",[416,1396,1397],{},"Status",[416,1399,1400],{},"Reason",[416,1402,1403],{},"Значение",[423,1405,1406,1420,1433],{},[413,1407,1408,1411,1414,1417],{},[428,1409,1410],{},"Progressing",[428,1412,1413],{},"True",[428,1415,1416],{},"NewReplicaSetAvailable",[428,1418,1419],{},"Rollout завершён",[413,1421,1422,1425,1427,1430],{},[428,1423,1424],{},"Available",[428,1426,1413],{},[428,1428,1429],{},"MinimumReplicasAvailable",[428,1431,1432],{},"Достаточно ready подов",[413,1434,1435,1437,1440,1443],{},[428,1436,1410],{},[428,1438,1439],{},"False",[428,1441,1442],{},"ProgressDeadlineExceeded",[428,1444,1445],{},"Rollout застрял (> progressDeadlineSeconds, default 600s)",[19,1447],{},[22,1449,1451],{"id":1450},"продвинутые-стратегии-деплоя","Продвинутые стратегии деплоя",[16,1453,1454],{},"Kubernetes из коробки предоставляет Recreate и RollingUpdate. Более сложные стратегии реализуются через комбинацию примитивов Kubernetes или с помощью внешних инструментов.",[30,1456,1458],{"id":1457},"canary-deployment","Canary Deployment",[16,1460,1461],{},"Идея --- направить небольшую часть трафика на новую версию, проверить метрики, и только после этого обновить всё.",[16,1463,1464],{},"Реализация через два Deployment и один Service:",[38,1466,1469],{"className":1467,"code":1468,"language":43},[41],"kiada-stable Deployment (replicas: 9)     kiada-canary Deployment (replicas: 1)\n  selector: app=kiada, rel=stable           selector: app=kiada, rel=canary\n\n        kiada Service\n        selector: app=kiada       ← матчит оба Deployment\n        → 90% трафика → stable поды (9 штук)\n        → 10% трафика → canary под (1 штука)\n\nПроверили canary → обновляем stable Deployment\n→ удаляем canary Deployment\n",[45,1470,1468],{"__ignoreMap":47},[16,1472,1473],{},"Пропорция трафика определяется соотношением replicas. Для точного контроля (например, 1% на canary) нужны инструменты вроде Istio, Flagger или Argo Rollouts.",[30,1475,1477],{"id":1476},"bluegreen-deployment","Blue\u002FGreen Deployment",[16,1479,1480],{},"Два полноценных окружения, мгновенное переключение трафика:",[38,1482,1485],{"className":1483,"code":1484,"language":43},[41],"Step 1: Blue active\n  kiada-blue Deployment:   app=kiada, col=blue   (v1)\n  kiada-green Deployment:  app=kiada, col=green  (v2, готовится)\n  Service selector:        app=kiada, col=blue    ← трафик на blue\n\nStep 2: Switch traffic\n  kubectl set selector svc kiada app=kiada,col=green\n  → мгновенный switch ВСЕГО трафика на green (v2)\n\nStep 3: Cleanup\n  → если всё ок → удалить blue Deployment\n  → если проблемы → switch обратно на blue\n",[45,1486,1484],{"__ignoreMap":47},[16,1488,1489],{},"Плюсы: мгновенное переключение, мгновенный откат. Минусы: нужно 2x ресурсов во время переключения.",[30,1491,1493],{"id":1492},"ab-testing","A\u002FB Testing",[16,1495,1496],{},"Два Deployment, два Service, маршрутизация через Ingress:",[38,1498,1501],{"className":1499,"code":1500,"language":43},[41],"Ingress:\n  if Cookie: beta=true → kiada-B service → Deployment B (v2)\n  else                 → kiada-A service → Deployment A (v1)\n",[45,1502,1500],{"__ignoreMap":47},[16,1504,1505],{},"Конкретные пользователи видят версию B, собираем метрики, сравниваем. Зависит от возможностей Ingress controller.",[30,1507,1509],{"id":1508},"сводная-таблица-стратегий","Сводная таблица стратегий",[407,1511,1512,1525],{},[410,1513,1514],{},[413,1515,1516,1519,1522],{},[416,1517,1518],{},"Стратегия",[416,1520,1521],{},"Тип",[416,1523,1524],{},"Описание",[423,1526,1527,1538,1548,1559,1569,1578,1589],{},[413,1528,1529,1532,1535],{},[428,1530,1531],{},"Recreate",[428,1533,1534],{},"встроенная",[428,1536,1537],{},"все поды удаляются, потом создаются (downtime)",[413,1539,1540,1543,1545],{},[428,1541,1542],{},"RollingUpdate",[428,1544,1534],{},[428,1546,1547],{},"постепенная замена (default, zero downtime)",[413,1549,1550,1553,1556],{},[428,1551,1552],{},"Canary",[428,1554,1555],{},"ручная",[428,1557,1558],{},"два Deployment + один Service",[413,1560,1561,1564,1566],{},[428,1562,1563],{},"Blue\u002FGreen",[428,1565,1555],{},[428,1567,1568],{},"два Deployment + Service selector switch",[413,1570,1571,1573,1575],{},[428,1572,1493],{},[428,1574,1555],{},[428,1576,1577],{},"два Deployment + Ingress routing",[413,1579,1580,1583,1586],{},[428,1581,1582],{},"Traffic Shadowing",[428,1584,1585],{},"внешний инструмент",[428,1587,1588],{},"Ingress \u002F Service Mesh mirroring",[413,1590,1591,1594,1596],{},[428,1592,1593],{},"Advanced Canary",[428,1595,1585],{},[428,1597,1598],{},"Flagger, Argo Rollouts",[19,1600],{},[22,1602,1604],{"id":1603},"statefulset-для-stateful-приложений","StatefulSet --- для stateful-приложений",[16,1606,1607],{},"Deployment подходит для stateless-сервисов, где все поды взаимозаменяемы (cattle). Но для баз данных (MongoDB, PostgreSQL), очередей (Kafka, RabbitMQ), кэшей (Redis Cluster) нужна стабильная идентичность каждого экземпляра. StatefulSet --- контроллер для таких случаев (pets).",[30,1609,1611],{"id":1610},"statefulset-vs-deployment","StatefulSet vs Deployment",[407,1613,1614,1626],{},[410,1615,1616],{},[413,1617,1618,1621,1623],{},[416,1619,1620],{},"Параметр",[416,1622,553],{},[416,1624,1625],{},"StatefulSet",[423,1627,1628,1639,1650,1661,1672,1683,1693,1704,1715],{},[413,1629,1630,1633,1636],{},[428,1631,1632],{},"Имена подов",[428,1634,1635],{},"random (hash+suffix)",[428,1637,1638],{},"ordinal (quiz-0, quiz-1...)",[413,1640,1641,1644,1647],{},[428,1642,1643],{},"Идентичность подов",[428,1645,1646],{},"fungible (cattle)",[428,1648,1649],{},"stable (pets)",[413,1651,1652,1655,1658],{},[428,1653,1654],{},"PVC на каждый под",[428,1656,1657],{},"нет (общий PVC)",[428,1659,1660],{},"да (volumeClaimTemplates)",[413,1662,1663,1666,1669],{},[428,1664,1665],{},"Порядок создания",[428,1667,1668],{},"все сразу",[428,1670,1671],{},"по одному (OrderedReady) или все сразу",[413,1673,1674,1677,1680],{},[428,1675,1676],{},"Порядок удаления",[428,1678,1679],{},"по правилам приоритета",[428,1681,1682],{},"highest ordinal first",[413,1684,1685,1688,1690],{},[428,1686,1687],{},"DNS для каждого пода",[428,1689,585],{},[428,1691,1692],{},"да (через headless Service)",[413,1694,1695,1698,1701],{},[428,1696,1697],{},"При отказе ноды",[428,1699,1700],{},"auto replacement",[428,1702,1703],{},"нет auto replacement (at-most-one)",[413,1705,1706,1709,1712],{},[428,1707,1708],{},"Update strategy",[428,1710,1711],{},"Recreate \u002F RollingUpdate",[428,1713,1714],{},"RollingUpdate \u002F OnDelete",[413,1716,1717,1720,1723],{},[428,1718,1719],{},"Владение подами",[428,1721,1722],{},"через ReplicaSet",[428,1724,1725],{},"напрямую (без RS)",[30,1727,58],{"id":1728},"манифест-2",[38,1730,1732],{"className":61,"code":1731,"language":63,"meta":47,"style":47},"apiVersion: apps\u002Fv1\nkind: StatefulSet\nmetadata:\n  name: quiz\nspec:\n  serviceName: quiz-pods             # headless Service (обязательно)\n  podManagementPolicy: Parallel      # или OrderedReady (default)\n  replicas: 3\n  selector:\n    matchLabels:\n      app: quiz\n  template:\n    metadata:\n      labels:\n        app: quiz\n    spec:\n      containers:\n      - name: mongo\n        image: mongo:5\n        volumeMounts:\n        - name: db-data\n          mountPath: \u002Fdata\u002Fdb\n      volumes:\n      - name: db-data\n        persistentVolumeClaim:\n          claimName: db-data\n  volumeClaimTemplates:              # PVC template --- уникально для StatefulSet\n  - metadata:\n      name: db-data                  # → PVC: db-data-quiz-0, db-data-quiz-1...\n    spec:\n      accessModes: [ReadWriteOnce]\n      resources:\n        requests:\n          storage: 1Gi\n",[45,1733,1734,1742,1751,1757,1766,1772,1785,1798,1806,1812,1818,1826,1832,1838,1844,1852,1858,1864,1875,1884,1891,1903,1913,1920,1930,1937,1946,1958,1968,1982,1989,2004,2012,2020],{"__ignoreMap":47},[67,1735,1736,1738,1740],{"class":69,"line":70},[67,1737,74],{"class":73},[67,1739,78],{"class":77},[67,1741,82],{"class":81},[67,1743,1744,1746,1748],{"class":69,"line":85},[67,1745,88],{"class":73},[67,1747,78],{"class":77},[67,1749,1750],{"class":81},"StatefulSet\n",[67,1752,1753,1755],{"class":69,"line":96},[67,1754,99],{"class":73},[67,1756,102],{"class":77},[67,1758,1759,1761,1763],{"class":69,"line":105},[67,1760,108],{"class":73},[67,1762,78],{"class":77},[67,1764,1765],{"class":81},"quiz\n",[67,1767,1768,1770],{"class":69,"line":116},[67,1769,119],{"class":73},[67,1771,102],{"class":77},[67,1773,1774,1777,1779,1782],{"class":69,"line":124},[67,1775,1776],{"class":73},"  serviceName",[67,1778,78],{"class":77},[67,1780,1781],{"class":81},"quiz-pods",[67,1783,1784],{"class":136},"             # headless Service (обязательно)\n",[67,1786,1787,1790,1792,1795],{"class":69,"line":140},[67,1788,1789],{"class":73},"  podManagementPolicy",[67,1791,78],{"class":77},[67,1793,1794],{"class":81},"Parallel",[67,1796,1797],{"class":136},"      # или OrderedReady (default)\n",[67,1799,1800,1802,1804],{"class":69,"line":152},[67,1801,127],{"class":73},[67,1803,78],{"class":77},[67,1805,652],{"class":132},[67,1807,1808,1810],{"class":69,"line":160},[67,1809,143],{"class":73},[67,1811,102],{"class":77},[67,1813,1814,1816],{"class":69,"line":170},[67,1815,155],{"class":73},[67,1817,102],{"class":77},[67,1819,1820,1822,1824],{"class":69,"line":181},[67,1821,163],{"class":73},[67,1823,78],{"class":77},[67,1825,1765],{"class":81},[67,1827,1828,1830],{"class":69,"line":192},[67,1829,184],{"class":73},[67,1831,102],{"class":77},[67,1833,1834,1836],{"class":69,"line":200},[67,1835,195],{"class":73},[67,1837,102],{"class":77},[67,1839,1840,1842],{"class":69,"line":208},[67,1841,203],{"class":73},[67,1843,102],{"class":77},[67,1845,1846,1848,1850],{"class":69,"line":222},[67,1847,211],{"class":73},[67,1849,78],{"class":77},[67,1851,1765],{"class":81},[67,1853,1854,1856],{"class":69,"line":232},[67,1855,249],{"class":73},[67,1857,102],{"class":77},[67,1859,1860,1862],{"class":69,"line":246},[67,1861,257],{"class":73},[67,1863,102],{"class":77},[67,1865,1866,1868,1870,1872],{"class":69,"line":254},[67,1867,265],{"class":77},[67,1869,268],{"class":73},[67,1871,78],{"class":77},[67,1873,1874],{"class":81},"mongo\n",[67,1876,1877,1879,1881],{"class":69,"line":262},[67,1878,278],{"class":73},[67,1880,78],{"class":77},[67,1882,1883],{"class":81},"mongo:5\n",[67,1885,1886,1889],{"class":69,"line":275},[67,1887,1888],{"class":73},"        volumeMounts",[67,1890,102],{"class":77},[67,1892,1893,1896,1898,1900],{"class":69,"line":763},[67,1894,1895],{"class":77},"        - ",[67,1897,268],{"class":73},[67,1899,78],{"class":77},[67,1901,1902],{"class":81},"db-data\n",[67,1904,1905,1908,1910],{"class":69,"line":772},[67,1906,1907],{"class":73},"          mountPath",[67,1909,78],{"class":77},[67,1911,1912],{"class":81},"\u002Fdata\u002Fdb\n",[67,1914,1915,1918],{"class":69,"line":782},[67,1916,1917],{"class":73},"      volumes",[67,1919,102],{"class":77},[67,1921,1922,1924,1926,1928],{"class":69,"line":789},[67,1923,265],{"class":77},[67,1925,268],{"class":73},[67,1927,78],{"class":77},[67,1929,1902],{"class":81},[67,1931,1932,1935],{"class":69,"line":796},[67,1933,1934],{"class":73},"        persistentVolumeClaim",[67,1936,102],{"class":77},[67,1938,1939,1942,1944],{"class":69,"line":807},[67,1940,1941],{"class":73},"          claimName",[67,1943,78],{"class":77},[67,1945,1902],{"class":81},[67,1947,1949,1952,1955],{"class":69,"line":1948},27,[67,1950,1951],{"class":73},"  volumeClaimTemplates",[67,1953,1954],{"class":77},":              ",[67,1956,1957],{"class":136},"# PVC template --- уникально для StatefulSet\n",[67,1959,1961,1964,1966],{"class":69,"line":1960},28,[67,1962,1963],{"class":77},"  - ",[67,1965,99],{"class":73},[67,1967,102],{"class":77},[67,1969,1971,1974,1976,1979],{"class":69,"line":1970},29,[67,1972,1973],{"class":73},"      name",[67,1975,78],{"class":77},[67,1977,1978],{"class":81},"db-data",[67,1980,1981],{"class":136},"                  # → PVC: db-data-quiz-0, db-data-quiz-1...\n",[67,1983,1985,1987],{"class":69,"line":1984},30,[67,1986,249],{"class":73},[67,1988,102],{"class":77},[67,1990,1992,1995,1998,2001],{"class":69,"line":1991},31,[67,1993,1994],{"class":73},"      accessModes",[67,1996,1997],{"class":77},": [",[67,1999,2000],{"class":81},"ReadWriteOnce",[67,2002,2003],{"class":77},"]\n",[67,2005,2007,2010],{"class":69,"line":2006},32,[67,2008,2009],{"class":73},"      resources",[67,2011,102],{"class":77},[67,2013,2015,2018],{"class":69,"line":2014},33,[67,2016,2017],{"class":73},"        requests",[67,2019,102],{"class":77},[67,2021,2023,2026,2028],{"class":69,"line":2022},34,[67,2024,2025],{"class":73},"          storage",[67,2027,78],{"class":77},[67,2029,2030],{"class":81},"1Gi\n",[30,2032,2034],{"id":2033},"ordinal-имена-и-pvc","Ordinal имена и PVC",[16,2036,2037],{},"Поды StatefulSet получают предсказуемые имена с порядковым номером:",[38,2039,2042],{"className":2040,"code":2041,"language":43},[41],"Pods:   quiz-0    quiz-1    quiz-2\nPVCs:   db-data-quiz-0  db-data-quiz-1  db-data-quiz-2\n",[45,2043,2041],{"__ignoreMap":47},[16,2045,2046],{},"При удалении пода новый под получает то же имя и переподключается к тому же PVC --- состояние сохраняется.",[30,2048,2050],{"id":2049},"headless-service-для-statefulset","Headless Service для StatefulSet",[16,2052,2053],{},"StatefulSet требует headless Service (clusterIP: None) для обеспечения стабильных DNS-записей:",[38,2055,2057],{"className":61,"code":2056,"language":63,"meta":47,"style":47},"apiVersion: v1\nkind: Service\nmetadata:\n  name: quiz-pods\nspec:\n  clusterIP: None\n  publishNotReadyAddresses: true\n  selector:\n    app: quiz\n  ports:\n  - name: mongodb\n    port: 27017\n",[45,2058,2059,2068,2077,2083,2092,2098,2108,2117,2123,2132,2139,2150],{"__ignoreMap":47},[67,2060,2061,2063,2065],{"class":69,"line":70},[67,2062,74],{"class":73},[67,2064,78],{"class":77},[67,2066,2067],{"class":81},"v1\n",[67,2069,2070,2072,2074],{"class":69,"line":85},[67,2071,88],{"class":73},[67,2073,78],{"class":77},[67,2075,2076],{"class":81},"Service\n",[67,2078,2079,2081],{"class":69,"line":96},[67,2080,99],{"class":73},[67,2082,102],{"class":77},[67,2084,2085,2087,2089],{"class":69,"line":105},[67,2086,108],{"class":73},[67,2088,78],{"class":77},[67,2090,2091],{"class":81},"quiz-pods\n",[67,2093,2094,2096],{"class":69,"line":116},[67,2095,119],{"class":73},[67,2097,102],{"class":77},[67,2099,2100,2103,2105],{"class":69,"line":124},[67,2101,2102],{"class":73},"  clusterIP",[67,2104,78],{"class":77},[67,2106,2107],{"class":81},"None\n",[67,2109,2110,2113,2115],{"class":69,"line":140},[67,2111,2112],{"class":73},"  publishNotReadyAddresses",[67,2114,78],{"class":77},[67,2116,357],{"class":132},[67,2118,2119,2121],{"class":69,"line":152},[67,2120,143],{"class":73},[67,2122,102],{"class":77},[67,2124,2125,2128,2130],{"class":69,"line":160},[67,2126,2127],{"class":73},"    app",[67,2129,78],{"class":77},[67,2131,1765],{"class":81},[67,2133,2134,2137],{"class":69,"line":170},[67,2135,2136],{"class":73},"  ports",[67,2138,102],{"class":77},[67,2140,2141,2143,2145,2147],{"class":69,"line":181},[67,2142,1963],{"class":77},[67,2144,268],{"class":73},[67,2146,78],{"class":77},[67,2148,2149],{"class":81},"mongodb\n",[67,2151,2152,2155,2157],{"class":69,"line":192},[67,2153,2154],{"class":73},"    port",[67,2156,78],{"class":77},[67,2158,2159],{"class":132},"27017\n",[16,2161,2162],{},"DNS-записи для каждого пода:",[2164,2165,2166,2172,2178],"ul",{},[860,2167,2168,2171],{},[45,2169,2170],{},"quiz-0.quiz-pods.ns.svc.cluster.local"," --- IP quiz-0",[860,2173,2174,2177],{},[45,2175,2176],{},"quiz-1.quiz-pods.ns.svc.cluster.local"," --- IP quiz-1",[860,2179,2180,2183],{},[45,2181,2182],{},"quiz-2.quiz-pods.ns.svc.cluster.local"," --- IP quiz-2",[16,2185,2186,2187,54],{},"SRV-записи используются для peer discovery, например: ",[45,2188,2189],{},"mongodb+srv:\u002F\u002Fquiz-pods.ns.svc.cluster.local",[16,2191,2192],{},"Типичный паттерн --- два Service:",[407,2194,2195,2205],{},[410,2196,2197],{},[413,2198,2199,2202],{},[416,2200,2201],{},"Service",[416,2203,2204],{},"Назначение",[423,2206,2207,2215],{},[413,2208,2209,2212],{},[428,2210,2211],{},"quiz-pods (headless)",[428,2213,2214],{},"peer discovery, publishNotReadyAddresses: true",[413,2216,2217,2220],{},[428,2218,2219],{},"quiz (regular)",[428,2221,2222],{},"клиентский трафик, только ready поды",[30,2224,2226],{"id":2225},"at-most-one-semantics","At-most-one semantics",[16,2228,2229],{},"При отказе ноды StatefulSet не создаёт replacement-под автоматически, в отличие от ReplicaSet. Причина --- гарантия at-most-one: два пода с одним identity и доступом к одним данным могут привести к split-brain и повреждению данных.",[16,2231,2232],{},"Ручное вмешательство:",[38,2234,2236],{"className":376,"code":2235,"language":378,"meta":47,"style":47},"# 1. Убедиться, что нода действительно отказала\n# 2. Принудительно удалить под\nkubectl delete pod quiz-1 --force --grace-period 0\n# 3. Контроллер создаст новый под\n",[45,2237,2238,2243,2248,2269],{"__ignoreMap":47},[67,2239,2240],{"class":69,"line":70},[67,2241,2242],{"class":136},"# 1. Убедиться, что нода действительно отказала\n",[67,2244,2245],{"class":69,"line":85},[67,2246,2247],{"class":136},"# 2. Принудительно удалить под\n",[67,2249,2250,2252,2254,2257,2260,2263,2266],{"class":69,"line":96},[67,2251,386],{"class":385},[67,2253,389],{"class":81},[67,2255,2256],{"class":81}," pod",[67,2258,2259],{"class":81}," quiz-1",[67,2261,2262],{"class":132}," --force",[67,2264,2265],{"class":132}," --grace-period",[67,2267,2268],{"class":132}," 0\n",[67,2270,2271],{"class":69,"line":105},[67,2272,2273],{"class":136},"# 3. Контроллер создаст новый под\n",[30,2275,2277],{"id":2276},"podmanagementpolicy","podManagementPolicy",[2164,2279,2280,2286],{},[860,2281,2282,2285],{},[504,2283,2284],{},"OrderedReady"," (default) --- поды создаются по одному, каждый должен стать ready перед созданием следующего. При scale down --- обратный порядок. Может привести к deadlock, если pod-0 не может стать ready без pod-1.",[860,2287,2288,2290],{},[504,2289,1794],{}," --- все поды создаются и удаляются одновременно. Быстрее, но не все приложения поддерживают.",[16,2292,2293,2294,2296],{},"Это поле immutable --- чтобы изменить, нужно удалить StatefulSet с ",[45,2295,372],{}," и пересоздать.",[30,2298,2300],{"id":2299},"pvc-retention-policy","PVC Retention Policy",[38,2302,2304],{"className":61,"code":2303,"language":63,"meta":47,"style":47},"spec:\n  persistentVolumeClaimRetentionPolicy:\n    whenScaled: Retain       # при scale down (default: Retain)\n    whenDeleted: Retain      # при удалении StatefulSet (default: Retain)\n",[45,2305,2306,2312,2319,2332],{"__ignoreMap":47},[67,2307,2308,2310],{"class":69,"line":70},[67,2309,119],{"class":73},[67,2311,102],{"class":77},[67,2313,2314,2317],{"class":69,"line":85},[67,2315,2316],{"class":73},"  persistentVolumeClaimRetentionPolicy",[67,2318,102],{"class":77},[67,2320,2321,2324,2326,2329],{"class":69,"line":96},[67,2322,2323],{"class":73},"    whenScaled",[67,2325,78],{"class":77},[67,2327,2328],{"class":81},"Retain",[67,2330,2331],{"class":136},"       # при scale down (default: Retain)\n",[67,2333,2334,2337,2339,2341],{"class":69,"line":105},[67,2335,2336],{"class":73},"    whenDeleted",[67,2338,78],{"class":77},[67,2340,2328],{"class":81},[67,2342,2343],{"class":136},"      # при удалении StatefulSet (default: Retain)\n",[16,2345,2346,2347,2350],{},"По умолчанию PVC сохраняются и при scale down, и при удалении StatefulSet. Политика ",[45,2348,2349],{},"Delete"," удаляет PVC вместе с данными --- используйте осторожно.",[19,2352],{},[22,2354,2356],{"id":2355},"statefulset-стратегии-обновления","StatefulSet: стратегии обновления",[30,2358,2360],{"id":2359},"rollingupdate-default","RollingUpdate (default)",[16,2362,2363],{},"Обновление идёт в обратном порядке ordinal: quiz-2, потом quiz-1, потом quiz-0. Всегда по одному поду за раз (нет maxSurge \u002F maxUnavailable).",[38,2365,2368],{"className":2366,"code":2367,"language":43},[41],"Timeline:\n  quiz-0 (v1)  quiz-1 (v1)  quiz-2 (v1)\n  quiz-0 (v1)  quiz-1 (v1)  quiz-2 (v2) ← highest first\n  quiz-0 (v1)  quiz-1 (v2)  quiz-2 (v2)\n  quiz-0 (v2)  quiz-1 (v2)  quiz-2 (v2) ← complete\n",[45,2369,2367],{"__ignoreMap":47},[30,2371,2373],{"id":2372},"ondelete-полный-контроль","OnDelete --- полный контроль",[38,2375,2377],{"className":61,"code":2376,"language":63,"meta":47,"style":47},"spec:\n  updateStrategy:\n    type: OnDelete\n",[45,2378,2379,2385,2392],{"__ignoreMap":47},[67,2380,2381,2383],{"class":69,"line":70},[67,2382,119],{"class":73},[67,2384,102],{"class":77},[67,2386,2387,2390],{"class":69,"line":85},[67,2388,2389],{"class":73},"  updateStrategy",[67,2391,102],{"class":77},[67,2393,2394,2396,2398],{"class":69,"line":96},[67,2395,664],{"class":73},[67,2397,78],{"class":77},[67,2399,2400],{"class":81},"OnDelete\n",[16,2402,2403],{},"При изменении template ничего не происходит. Вы вручную удаляете поды в нужном порядке --- контроллер пересоздаёт каждый удалённый под с новым template.",[407,2405,2406,2417],{},[410,2407,2408],{},[413,2409,2410,2412,2414],{},[416,2411,1620],{},[416,2413,2360],{},[416,2415,2416],{},"OnDelete",[423,2418,2419,2430,2441,2452,2462],{},[413,2420,2421,2424,2427],{},[428,2422,2423],{},"Автоматизация",[428,2425,2426],{},"автоматически",[428,2428,2429],{},"semi-auto (вы удаляете поды)",[413,2431,2432,2435,2438],{},[428,2433,2434],{},"Порядок",[428,2436,2437],{},"highest ordinal -> lowest",[428,2439,2440],{},"любой (вы выбираете)",[413,2442,2443,2446,2449],{},[428,2444,2445],{},"maxSurge\u002FmaxUnavailable",[428,2447,2448],{},"нет (всегда 1 за раз)",[428,2450,2451],{},"N\u002FA",[413,2453,2454,2456,2459],{},[428,2455,1077],{},[428,2457,2458],{},"да",[428,2460,2461],{},"не применяется",[413,2463,2464,2467,2469],{},[428,2465,2466],{},"Partition",[428,2468,2458],{},[428,2470,2451],{},[30,2472,2474],{"id":2473},"partition-аналог-pause-для-statefulset","partition --- аналог pause для StatefulSet",[38,2476,2478],{"className":61,"code":2477,"language":63,"meta":47,"style":47},"spec:\n  updateStrategy:\n    type: RollingUpdate\n    rollingUpdate:\n      partition: 2   # поды с ordinal \u003C 2 НЕ обновляются\n",[45,2479,2480,2486,2492,2500,2506],{"__ignoreMap":47},[67,2481,2482,2484],{"class":69,"line":70},[67,2483,119],{"class":73},[67,2485,102],{"class":77},[67,2487,2488,2490],{"class":69,"line":85},[67,2489,2389],{"class":73},[67,2491,102],{"class":77},[67,2493,2494,2496,2498],{"class":69,"line":96},[67,2495,664],{"class":73},[67,2497,78],{"class":77},[67,2499,669],{"class":81},[67,2501,2502,2504],{"class":69,"line":105},[67,2503,674],{"class":73},[67,2505,102],{"class":77},[67,2507,2508,2511,2513,2515],{"class":69,"line":116},[67,2509,2510],{"class":73},"      partition",[67,2512,78],{"class":77},[67,2514,438],{"class":132},[67,2516,2517],{"class":136},"   # поды с ordinal \u003C 2 НЕ обновляются\n",[16,2519,2520,2523],{},[45,2521,2522],{},"partition = N"," означает: обновляются только поды с ordinal >= N.",[16,2525,2526],{},"Сценарии использования:",[2164,2528,2529,2535,2541],{},[860,2530,2531,2534],{},[504,2532,2533],{},"Stage update:"," partition = replicas (никто не обновляется). Вносим изменения в template, затем снижаем partition.",[860,2536,2537,2540],{},[504,2538,2539],{},"Canary:"," partition = replicas - 1 (обновится только последний под). Проверяем, затем partition = 0.",[860,2542,2543,2546],{},[504,2544,2545],{},"Phased rollout:"," постепенно уменьшаем partition: 2 -> 1 -> 0.",[16,2548,2549],{},"Важно: если удалить под с ordinal \u003C partition, он пересоздастся со старым template. Если >= partition --- с новым.",[30,2551,1175],{"id":2552},"revision-history-1",[16,2554,2555],{},"В отличие от Deployment, где история хранится в ReplicaSet, StatefulSet использует объекты ControllerRevision:",[38,2557,2559],{"className":376,"code":2558,"language":378,"meta":47,"style":47},"kubectl rollout history sts quiz\nkubectl rollout undo sts quiz --to-revision=1\nkubectl get controllerrevisions\n",[45,2560,2561,2575,2591],{"__ignoreMap":47},[67,2562,2563,2565,2567,2569,2572],{"class":69,"line":70},[67,2564,386],{"class":385},[67,2566,1113],{"class":81},[67,2568,1201],{"class":81},[67,2570,2571],{"class":81}," sts",[67,2573,2574],{"class":81}," quiz\n",[67,2576,2577,2579,2581,2583,2585,2588],{"class":69,"line":85},[67,2578,386],{"class":385},[67,2580,1113],{"class":81},[67,2582,1116],{"class":81},[67,2584,2571],{"class":81},[67,2586,2587],{"class":81}," quiz",[67,2589,2590],{"class":132}," --to-revision=1\n",[67,2592,2593,2595,2598],{"class":69,"line":96},[67,2594,386],{"class":385},[67,2596,2597],{"class":81}," get",[67,2599,2600],{"class":81}," controllerrevisions\n",[16,2602,2603],{},"Rollback соблюдает текущую update strategy: при RollingUpdate --- постепенный, при OnDelete --- semi-auto.",[19,2605],{},[22,2607,2609],{"id":2608},"kubernetes-operators","Kubernetes Operators",[30,2611,2613],{"id":2612},"проблема","Проблема",[16,2615,2616],{},"StatefulSet обеспечивает стабильную идентичность, но не покрывает всех потребностей stateful-приложений:",[2164,2618,2619,2626,2629,2632,2635],{},[860,2620,2621,2622,2625],{},"Инициализация replica set (например, ",[45,2623,2624],{},"rs.initiate"," в MongoDB)",[860,2627,2628],{},"Реконфигурация при scaling (добавить\u002Fудалить members)",[860,2630,2631],{},"Автоматический failover при отказе ноды",[860,2633,2634],{},"Backup и restore",[860,2636,2637],{},"Обновление версий с миграцией данных",[30,2639,2641],{"id":2640},"operator-application-specific-controller","Operator --- application-specific controller",[16,2643,2644],{},"Operator расширяет Kubernetes API через CustomResourceDefinition (CRD) и предоставляет автоматизацию жизненного цикла конкретного приложения:",[857,2646,2647,2653,2656],{},[860,2648,2649,2650,460],{},"Определяет новый тип ресурса через CRD (например, ",[45,2651,2652],{},"MongoDBCommunity",[860,2654,2655],{},"Контроллер (operator) следит за custom-объектами",[860,2657,2658],{},"Автоматически создаёт и управляет StatefulSet, Services, Secrets",[38,2660,2663],{"className":2661,"code":2662,"language":43},[41],"User ──▶ MongoDBCommunity object ──▶ Operator ──▶ StatefulSet + Services\n         (declarative spec)                       (managed automatically)\n",[45,2664,2662],{"__ignoreMap":47},[30,2666,2668],{"id":2667},"пример-mongodb-community-operator","Пример: MongoDB Community Operator",[38,2670,2672],{"className":61,"code":2671,"language":63,"meta":47,"style":47},"apiVersion: mongodbcommunity.mongodb.com\u002Fv1\nkind: MongoDBCommunity\nmetadata:\n  name: my-mongodb\nspec:\n  members: 3\n  type: ReplicaSet\n  version: \"5.0\"\n",[45,2673,2674,2683,2692,2698,2707,2713,2722,2731],{"__ignoreMap":47},[67,2675,2676,2678,2680],{"class":69,"line":70},[67,2677,74],{"class":73},[67,2679,78],{"class":77},[67,2681,2682],{"class":81},"mongodbcommunity.mongodb.com\u002Fv1\n",[67,2684,2685,2687,2689],{"class":69,"line":85},[67,2686,88],{"class":73},[67,2688,78],{"class":77},[67,2690,2691],{"class":81},"MongoDBCommunity\n",[67,2693,2694,2696],{"class":69,"line":96},[67,2695,99],{"class":73},[67,2697,102],{"class":77},[67,2699,2700,2702,2704],{"class":69,"line":105},[67,2701,108],{"class":73},[67,2703,78],{"class":77},[67,2705,2706],{"class":81},"my-mongodb\n",[67,2708,2709,2711],{"class":69,"line":116},[67,2710,119],{"class":73},[67,2712,102],{"class":77},[67,2714,2715,2718,2720],{"class":69,"line":124},[67,2716,2717],{"class":73},"  members",[67,2719,78],{"class":77},[67,2721,652],{"class":132},[67,2723,2724,2727,2729],{"class":69,"line":140},[67,2725,2726],{"class":73},"  type",[67,2728,78],{"class":77},[67,2730,93],{"class":81},[67,2732,2733,2736,2738],{"class":69,"line":152},[67,2734,2735],{"class":73},"  version",[67,2737,78],{"class":77},[67,2739,2740],{"class":81},"\"5.0\"\n",[16,2742,2743,2744,2747],{},"Operator автоматически: создаёт StatefulSet с 3 подами, headless Service, инициирует MongoDB replica set, при изменении ",[45,2745,2746],{},"members"," реконфигурирует кластер.",[30,2749,2751],{"id":2750},"популярные-operators","Популярные Operators",[407,2753,2754,2764],{},[410,2755,2756],{},[413,2757,2758,2761],{},[416,2759,2760],{},"База данных",[416,2762,2763],{},"Operator",[423,2765,2766,2774,2782,2790,2798],{},[413,2767,2768,2771],{},[428,2769,2770],{},"PostgreSQL",[428,2772,2773],{},"Zalando Postgres Operator, CrunchyData PGO",[413,2775,2776,2779],{},[428,2777,2778],{},"Redis",[428,2780,2781],{},"Redis Operator",[413,2783,2784,2787],{},[428,2785,2786],{},"Elasticsearch",[428,2788,2789],{},"ECK (Elastic Cloud on Kubernetes)",[413,2791,2792,2795],{},[428,2793,2794],{},"Kafka",[428,2796,2797],{},"Strimzi",[413,2799,2800,2803],{},[428,2801,2802],{},"MySQL",[428,2804,2805],{},"Oracle MySQL Operator",[19,2807],{},[22,2809,2811],{"id":2810},"полезные-команды","Полезные команды",[38,2813,2815],{"className":376,"code":2814,"language":378,"meta":47,"style":47},"# ReplicaSet\nkubectl get rs                              # список ReplicaSet\nkubectl get rs -o wide                      # + containers, images, selector\nkubectl describe rs kiada                   # детали + events\n\n# Deployment\nkubectl get deploy                          # список\nkubectl get deploy -o wide                  # + containers, images, selector\nkubectl describe deploy kiada               # детали + events + conditions\nkubectl rollout status deploy kiada         # отслеживание rollout\nkubectl rollout history deploy kiada        # история ревизий\nkubectl rollout undo deploy kiada           # rollback\nkubectl rollout pause deploy kiada          # приостановить\nkubectl rollout resume deploy kiada         # возобновить\nkubectl rollout restart deploy kiada        # restart подов\nkubectl set image deploy kiada kiada=luksa\u002Fkiada:0.6  # обновить image\nkubectl scale deploy kiada --replicas 5     # масштабирование\n\n# StatefulSet\nkubectl get sts                             # список StatefulSet\nkubectl describe sts quiz                   # детали\nkubectl rollout status sts quiz             # статус rollout\nkubectl scale sts quiz --replicas 5         # масштабирование\nkubectl get pvc -l app=quiz                 # PVC StatefulSet\nkubectl delete pod quiz-1 --force --grace-period 0  # при node failure\n",[45,2816,2817,2822,2833,2850,2863,2867,2872,2883,2898,2911,2926,2941,2956,2971,2986,3001,3021,3041,3045,3050,3061,3074,3089,3106,3124],{"__ignoreMap":47},[67,2818,2819],{"class":69,"line":70},[67,2820,2821],{"class":136},"# ReplicaSet\n",[67,2823,2824,2826,2828,2830],{"class":69,"line":85},[67,2825,386],{"class":385},[67,2827,2597],{"class":81},[67,2829,392],{"class":81},[67,2831,2832],{"class":136},"                              # список ReplicaSet\n",[67,2834,2835,2837,2839,2841,2844,2847],{"class":69,"line":96},[67,2836,386],{"class":385},[67,2838,2597],{"class":81},[67,2840,392],{"class":81},[67,2842,2843],{"class":132}," -o",[67,2845,2846],{"class":81}," wide",[67,2848,2849],{"class":136},"                      # + containers, images, selector\n",[67,2851,2852,2854,2856,2858,2860],{"class":69,"line":105},[67,2853,386],{"class":385},[67,2855,1356],{"class":81},[67,2857,392],{"class":81},[67,2859,395],{"class":81},[67,2861,2862],{"class":136},"                   # детали + events\n",[67,2864,2865],{"class":69,"line":116},[67,2866,1128],{"emptyLinePlaceholder":1127},[67,2868,2869],{"class":69,"line":124},[67,2870,2871],{"class":136},"# Deployment\n",[67,2873,2874,2876,2878,2880],{"class":69,"line":140},[67,2875,386],{"class":385},[67,2877,2597],{"class":81},[67,2879,1204],{"class":81},[67,2881,2882],{"class":136},"                          # список\n",[67,2884,2885,2887,2889,2891,2893,2895],{"class":69,"line":152},[67,2886,386],{"class":385},[67,2888,2597],{"class":81},[67,2890,1204],{"class":81},[67,2892,2843],{"class":132},[67,2894,2846],{"class":81},[67,2896,2897],{"class":136},"                  # + containers, images, selector\n",[67,2899,2900,2902,2904,2906,2908],{"class":69,"line":160},[67,2901,386],{"class":385},[67,2903,1356],{"class":81},[67,2905,1204],{"class":81},[67,2907,395],{"class":81},[67,2909,2910],{"class":136},"               # детали + events + conditions\n",[67,2912,2913,2915,2917,2919,2921,2923],{"class":69,"line":170},[67,2914,386],{"class":385},[67,2916,1113],{"class":81},[67,2918,1336],{"class":81},[67,2920,1204],{"class":81},[67,2922,395],{"class":81},[67,2924,2925],{"class":136},"         # отслеживание rollout\n",[67,2927,2928,2930,2932,2934,2936,2938],{"class":69,"line":181},[67,2929,386],{"class":385},[67,2931,1113],{"class":81},[67,2933,1201],{"class":81},[67,2935,1204],{"class":81},[67,2937,395],{"class":81},[67,2939,2940],{"class":136},"        # история ревизий\n",[67,2942,2943,2945,2947,2949,2951,2953],{"class":69,"line":192},[67,2944,386],{"class":385},[67,2946,1113],{"class":81},[67,2948,1116],{"class":81},[67,2950,1204],{"class":81},[67,2952,395],{"class":81},[67,2954,2955],{"class":136},"           # rollback\n",[67,2957,2958,2960,2962,2964,2966,2968],{"class":69,"line":200},[67,2959,386],{"class":385},[67,2961,1113],{"class":81},[67,2963,1270],{"class":81},[67,2965,1204],{"class":81},[67,2967,395],{"class":81},[67,2969,2970],{"class":136},"          # приостановить\n",[67,2972,2973,2975,2977,2979,2981,2983],{"class":69,"line":208},[67,2974,386],{"class":385},[67,2976,1113],{"class":81},[67,2978,1292],{"class":81},[67,2980,1204],{"class":81},[67,2982,395],{"class":81},[67,2984,2985],{"class":136},"         # возобновить\n",[67,2987,2988,2990,2992,2994,2996,2998],{"class":69,"line":222},[67,2989,386],{"class":385},[67,2991,1113],{"class":81},[67,2993,1378],{"class":81},[67,2995,1204],{"class":81},[67,2997,395],{"class":81},[67,2999,3000],{"class":136},"        # restart подов\n",[67,3002,3003,3005,3008,3011,3013,3015,3018],{"class":69,"line":232},[67,3004,386],{"class":385},[67,3006,3007],{"class":81}," set",[67,3009,3010],{"class":81}," image",[67,3012,1204],{"class":81},[67,3014,395],{"class":81},[67,3016,3017],{"class":81}," kiada=luksa\u002Fkiada:0.6",[67,3019,3020],{"class":136},"  # обновить image\n",[67,3022,3023,3025,3028,3030,3032,3035,3038],{"class":69,"line":246},[67,3024,386],{"class":385},[67,3026,3027],{"class":81}," scale",[67,3029,1204],{"class":81},[67,3031,395],{"class":81},[67,3033,3034],{"class":132}," --replicas",[67,3036,3037],{"class":132}," 5",[67,3039,3040],{"class":136},"     # масштабирование\n",[67,3042,3043],{"class":69,"line":254},[67,3044,1128],{"emptyLinePlaceholder":1127},[67,3046,3047],{"class":69,"line":262},[67,3048,3049],{"class":136},"# StatefulSet\n",[67,3051,3052,3054,3056,3058],{"class":69,"line":275},[67,3053,386],{"class":385},[67,3055,2597],{"class":81},[67,3057,2571],{"class":81},[67,3059,3060],{"class":136},"                             # список StatefulSet\n",[67,3062,3063,3065,3067,3069,3071],{"class":69,"line":763},[67,3064,386],{"class":385},[67,3066,1356],{"class":81},[67,3068,2571],{"class":81},[67,3070,2587],{"class":81},[67,3072,3073],{"class":136},"                   # детали\n",[67,3075,3076,3078,3080,3082,3084,3086],{"class":69,"line":772},[67,3077,386],{"class":385},[67,3079,1113],{"class":81},[67,3081,1336],{"class":81},[67,3083,2571],{"class":81},[67,3085,2587],{"class":81},[67,3087,3088],{"class":136},"             # статус rollout\n",[67,3090,3091,3093,3095,3097,3099,3101,3103],{"class":69,"line":782},[67,3092,386],{"class":385},[67,3094,3027],{"class":81},[67,3096,2571],{"class":81},[67,3098,2587],{"class":81},[67,3100,3034],{"class":132},[67,3102,3037],{"class":132},[67,3104,3105],{"class":136},"         # масштабирование\n",[67,3107,3108,3110,3112,3115,3118,3121],{"class":69,"line":789},[67,3109,386],{"class":385},[67,3111,2597],{"class":81},[67,3113,3114],{"class":81}," pvc",[67,3116,3117],{"class":132}," -l",[67,3119,3120],{"class":81}," app=quiz",[67,3122,3123],{"class":136},"                 # PVC StatefulSet\n",[67,3125,3126,3128,3130,3132,3134,3136,3138,3141],{"class":69,"line":796},[67,3127,386],{"class":385},[67,3129,389],{"class":81},[67,3131,2256],{"class":81},[67,3133,2259],{"class":81},[67,3135,2262],{"class":132},[67,3137,2265],{"class":132},[67,3139,3140],{"class":132}," 0",[67,3142,3143],{"class":136},"  # при node failure\n",[19,3145],{},[22,3147,3149],{"id":3148},"вопросы-на-собеседовании","Вопросы на собеседовании",[16,3151,3152],{},[504,3153,3154],{},"1. Чем Deployment отличается от ReplicaSet? Зачем нужна обёртка?",[16,3156,3157],{},"ReplicaSet гарантирует количество подов, но при изменении pod template существующие поды не обновляются --- только новые. Deployment управляет ReplicaSet и автоматически выполняет rolling update при изменении template: создаёт новый RS, постепенно переводит поды, сохраняет историю ревизий для rollback. На практике ReplicaSet никогда не используют напрямую.",[16,3159,3160],{},[504,3161,3162],{},"2. Объясните разницу между maxSurge и maxUnavailable. Какие значения обеспечивают zero downtime?",[16,3164,3165,3167,3168,3170,3171,3174],{},[45,3166,973],{}," --- сколько подов сверх desired можно создать одновременно. ",[45,3169,976],{}," --- сколько подов ниже desired допустимо. Для zero downtime нужно ",[45,3172,3173],{},"maxUnavailable: 0"," --- тогда сначала создаётся новый под, и только после того как он станет ready, удаляется старый. Значения по умолчанию (25%\u002F25%) допускают кратковременную недоступность части реплик.",[16,3176,3177],{},[504,3178,3179],{},"3. Когда нужен StatefulSet вместо Deployment?",[16,3181,3182],{},"Когда каждый экземпляр приложения уникален и хранит собственное состояние: базы данных, распределённые кэши, очереди сообщений. StatefulSet даёт стабильные ordinal имена (quiz-0, quiz-1), персональные PVC для каждого пода, стабильные DNS-записи и гарантию at-most-one (не будет двух подов с одним identity одновременно).",[16,3184,3185],{},[504,3186,3187],{},"4. Что такое at-most-one semantics в StatefulSet и почему он не создаёт replacement при отказе ноды?",[16,3189,3190,3191,54],{},"At-most-one гарантирует, что никогда не будет двух подов с одинаковым ordinal и доступом к одним данным. При отказе ноды Kubernetes не может быть уверен, что под действительно остановлен (возможно, нода временно недоступна, но поды на ней работают). Создание replacement пода привело бы к двум экземплярам с доступом к одному PV --- split-brain и повреждение данных. Требуется ручное удаление пода с ",[45,3192,3193],{},"--force --grace-period 0",[16,3195,3196],{},[504,3197,3198],{},"5. Как реализовать canary deployment в Kubernetes без дополнительных инструментов?",[16,3200,3201,3202,3205],{},"Два Deployment с разным количеством replicas (например, stable: 9, canary: 1) и один Service с общим selector (например, ",[45,3203,3204],{},"app=kiada","). Service будет распределять трафик пропорционально количеству подов --- 90% на stable, 10% на canary. После проверки canary обновляем stable Deployment и удаляем canary. Для более точного контроля пропорций нужны Istio, Flagger или Argo Rollouts.",[16,3207,3208],{},[504,3209,3210],{},"6. Чем отличается rollback в Deployment от rollback в StatefulSet?",[16,3212,3213],{},"В Deployment история хранится в старых ReplicaSet (с replicas: 0), rollback масштабирует нужный RS обратно. В StatefulSet история хранится в объектах ControllerRevision. В обоих случаях rollback соблюдает текущую update strategy. При OnDelete в StatefulSet rollback semi-auto --- template откатывается, но поды нужно удалять вручную.",[16,3215,3216],{},[504,3217,3218],{},"7. Что такое Kubernetes Operator и какую проблему он решает?",[16,3220,3221,3222,3224],{},"Operator --- это application-specific контроллер, расширяющий Kubernetes API через CRD. StatefulSet обеспечивает стабильную идентичность подов, но не умеет инициализировать кластер БД, реконфигурировать при scaling, делать автоматический failover или backup. Operator закрывает этот gap: пользователь описывает желаемое состояние через custom resource (например, ",[45,3223,2652],{},"), а operator автоматически управляет StatefulSet, Services и всей специфичной для приложения логикой.",[19,3226],{},[22,3228,3230],{"id":3229},"задачи","Задачи",[30,3232,3234],{"id":3233},"задача-1-rolling-update-с-защитой-от-плохих-версий","Задача 1: Rolling Update с защитой от плохих версий",[16,3236,3237],{},"Создайте Deployment для nginx с 5 репликами и настройте стратегию обновления так, чтобы:",[2164,3239,3240,3243,3246],{},[860,3241,3242],{},"Обновление шло по одному поду за раз",[860,3244,3245],{},"Не допускалось ни одного недоступного пода",[860,3247,3248],{},"Под должен быть ready минимум 30 секунд перед тем как считаться available",[16,3250,3251,3252,3255],{},"Обновите image на несуществующую версию (",[45,3253,3254],{},"nginx:99.99",") и убедитесь, что rollout застрянет. Выполните rollback. Проверьте историю ревизий.",[30,3257,3259],{"id":3258},"задача-2-statefulset-с-headless-service","Задача 2: StatefulSet с headless Service",[16,3261,3262],{},"Создайте StatefulSet для Redis с 3 репликами. Настройте headless Service и volumeClaimTemplates с 500Mi storage. Убедитесь, что:",[2164,3264,3265,3268,3271],{},[860,3266,3267],{},"Поды получают предсказуемые имена (redis-0, redis-1, redis-2)",[860,3269,3270],{},"Каждый под имеет собственный PVC",[860,3272,3273],{},"DNS-записи резолвятся для каждого пода",[16,3275,3276],{},"Удалите под redis-1 и проверьте, что новый под получит то же имя и PVC.",[30,3278,3280],{"id":3279},"задача-3-canary-deployment","Задача 3: Canary Deployment",[16,3282,3283],{},"Реализуйте canary deployment для приложения:",[2164,3285,3286,3296,3304,3309,3312],{},[860,3287,3288,3289,3292,3293],{},"Создайте Deployment ",[45,3290,3291],{},"app-stable"," (image: nginx:1.24) с 9 репликами и label ",[45,3294,3295],{},"app=myapp",[860,3297,3288,3298,3301,3302],{},[45,3299,3300],{},"app-canary"," (image: nginx:1.25) с 1 репликой и label ",[45,3303,3295],{},[860,3305,3306,3307],{},"Создайте Service с selector ",[45,3308,3295],{},[860,3310,3311],{},"Отправьте 20 запросов к Service и подсчитайте, сколько обработал каждый Deployment",[860,3313,3314],{},"Убедитесь, что пропорция примерно 90\u002F10",[19,3316],{},[22,3318,3320],{"id":3319},"интерактивная-практика","Интерактивная практика",[3322,3323,3326,3329,3345],"quiz",{"answer":438,"id":3324,"xp":3325},"infra-workloads-q1","10",[16,3327,3328],{},"За что отвечает Deployment в Kubernetes?",[296,3330,3331],{"v-slot:options":47},[2164,3332,3333,3336,3339,3342],{},[860,3334,3335],{},"За хранение данных pod-а между перезапусками",[860,3337,3338],{},"За управление ReplicaSet, rolling update и rollback stateless-приложения",[860,3340,3341],{},"За выдачу стабильного DNS имени каждому pod-у",[860,3343,3344],{},"За запуск ровно одного pod-а на каждой ноде",[296,3346,3347],{"v-slot:explanation":47},[16,3348,3349],{},"Deployment управляет ReplicaSet-ами и декларативными rollout\u002Frollback для stateless workloads. Стабильные identity даёт StatefulSet, а pod на каждой ноде — DaemonSet.",[3351,3352,3356,3359,3523],"predict",{"answer":3353,"id":3354,"xp":3355},"stable\\ncanary","infra-workloads-p1","15",[16,3357,3358],{},"Что выведет программа?",[296,3360,3361],{"v-slot:code":47},[38,3362,3366],{"className":3363,"code":3364,"language":3365,"meta":47,"style":47},"language-go shiki shiki-themes github-dark","package main\n\nimport \"fmt\"\n\nfunc trafficTarget(requestNo int) string {\n    if requestNo >= 9 {\n        return \"canary\"\n    }\n    return \"stable\"\n}\n\nfunc main() {\n    fmt.Println(trafficTarget(3))\n    fmt.Println(trafficTarget(9))\n}\n","go",[45,3367,3368,3377,3381,3395,3399,3426,3442,3450,3455,3463,3468,3472,3482,3502,3519],{"__ignoreMap":47},[67,3369,3370,3374],{"class":69,"line":70},[67,3371,3373],{"class":3372},"snl16","package",[67,3375,3376],{"class":385}," main\n",[67,3378,3379],{"class":69,"line":85},[67,3380,1128],{"emptyLinePlaceholder":1127},[67,3382,3383,3386,3389,3392],{"class":69,"line":96},[67,3384,3385],{"class":3372},"import",[67,3387,3388],{"class":81}," \"",[67,3390,3391],{"class":385},"fmt",[67,3393,3394],{"class":81},"\"\n",[67,3396,3397],{"class":69,"line":105},[67,3398,1128],{"emptyLinePlaceholder":1127},[67,3400,3401,3404,3407,3410,3414,3417,3420,3423],{"class":69,"line":116},[67,3402,3403],{"class":3372},"func",[67,3405,3406],{"class":385}," trafficTarget",[67,3408,3409],{"class":77},"(",[67,3411,3413],{"class":3412},"s9osk","requestNo",[67,3415,3416],{"class":3372}," int",[67,3418,3419],{"class":77},") ",[67,3421,3422],{"class":3372},"string",[67,3424,3425],{"class":77}," {\n",[67,3427,3428,3431,3434,3437,3440],{"class":69,"line":124},[67,3429,3430],{"class":3372},"    if",[67,3432,3433],{"class":77}," requestNo ",[67,3435,3436],{"class":3372},">=",[67,3438,3439],{"class":132}," 9",[67,3441,3425],{"class":77},[67,3443,3444,3447],{"class":69,"line":140},[67,3445,3446],{"class":3372},"        return",[67,3448,3449],{"class":81}," \"canary\"\n",[67,3451,3452],{"class":69,"line":152},[67,3453,3454],{"class":77},"    }\n",[67,3456,3457,3460],{"class":69,"line":160},[67,3458,3459],{"class":3372},"    return",[67,3461,3462],{"class":81}," \"stable\"\n",[67,3464,3465],{"class":69,"line":170},[67,3466,3467],{"class":77},"}\n",[67,3469,3470],{"class":69,"line":181},[67,3471,1128],{"emptyLinePlaceholder":1127},[67,3473,3474,3476,3479],{"class":69,"line":192},[67,3475,3403],{"class":3372},[67,3477,3478],{"class":385}," main",[67,3480,3481],{"class":77},"() {\n",[67,3483,3484,3487,3490,3492,3495,3497,3499],{"class":69,"line":200},[67,3485,3486],{"class":77},"    fmt.",[67,3488,3489],{"class":385},"Println",[67,3491,3409],{"class":77},[67,3493,3494],{"class":385},"trafficTarget",[67,3496,3409],{"class":77},[67,3498,133],{"class":132},[67,3500,3501],{"class":77},"))\n",[67,3503,3504,3506,3508,3510,3512,3514,3517],{"class":69,"line":208},[67,3505,3486],{"class":77},[67,3507,3489],{"class":385},[67,3509,3409],{"class":77},[67,3511,3494],{"class":385},[67,3513,3409],{"class":77},[67,3515,3516],{"class":132},"9",[67,3518,3501],{"class":77},[67,3520,3521],{"class":69,"line":222},[67,3522,3467],{"class":77},[296,3524,3525],{"v-slot:hint":47},[16,3526,3527],{},"В простом 90\u002F10 canary большая часть запросов остаётся на stable, малая часть уходит на новую версию.",[3529,3530,3534,3541,3671],"code-task",{"expected":3531,"id":3532,"xp":3533},"Deployment\\nStatefulSet\\nDaemonSet","infra-workloads-ct1","20",[16,3535,3536,3537,3540],{},"Реализуй ",[45,3538,3539],{},"WorkloadFor",": выбери тип workload по требованию.",[296,3542,3543],{"v-slot:template":47},[38,3544,3546],{"className":3363,"code":3545,"language":3365,"meta":47,"style":47},"package main\n\nimport \"fmt\"\n\nfunc WorkloadFor(requirement string) string {\n    return \"todo\"\n}\n\nfunc main() {\n    fmt.Println(WorkloadFor(\"stateless\"))\n    fmt.Println(WorkloadFor(\"stable-id\"))\n    fmt.Println(WorkloadFor(\"every-node\"))\n}\n",[45,3547,3548,3554,3558,3568,3572,3593,3600,3604,3608,3616,3633,3650,3667],{"__ignoreMap":47},[67,3549,3550,3552],{"class":69,"line":70},[67,3551,3373],{"class":3372},[67,3553,3376],{"class":385},[67,3555,3556],{"class":69,"line":85},[67,3557,1128],{"emptyLinePlaceholder":1127},[67,3559,3560,3562,3564,3566],{"class":69,"line":96},[67,3561,3385],{"class":3372},[67,3563,3388],{"class":81},[67,3565,3391],{"class":385},[67,3567,3394],{"class":81},[67,3569,3570],{"class":69,"line":105},[67,3571,1128],{"emptyLinePlaceholder":1127},[67,3573,3574,3576,3579,3581,3584,3587,3589,3591],{"class":69,"line":116},[67,3575,3403],{"class":3372},[67,3577,3578],{"class":385}," WorkloadFor",[67,3580,3409],{"class":77},[67,3582,3583],{"class":3412},"requirement",[67,3585,3586],{"class":3372}," string",[67,3588,3419],{"class":77},[67,3590,3422],{"class":3372},[67,3592,3425],{"class":77},[67,3594,3595,3597],{"class":69,"line":124},[67,3596,3459],{"class":3372},[67,3598,3599],{"class":81}," \"todo\"\n",[67,3601,3602],{"class":69,"line":140},[67,3603,3467],{"class":77},[67,3605,3606],{"class":69,"line":152},[67,3607,1128],{"emptyLinePlaceholder":1127},[67,3609,3610,3612,3614],{"class":69,"line":160},[67,3611,3403],{"class":3372},[67,3613,3478],{"class":385},[67,3615,3481],{"class":77},[67,3617,3618,3620,3622,3624,3626,3628,3631],{"class":69,"line":170},[67,3619,3486],{"class":77},[67,3621,3489],{"class":385},[67,3623,3409],{"class":77},[67,3625,3539],{"class":385},[67,3627,3409],{"class":77},[67,3629,3630],{"class":81},"\"stateless\"",[67,3632,3501],{"class":77},[67,3634,3635,3637,3639,3641,3643,3645,3648],{"class":69,"line":181},[67,3636,3486],{"class":77},[67,3638,3489],{"class":385},[67,3640,3409],{"class":77},[67,3642,3539],{"class":385},[67,3644,3409],{"class":77},[67,3646,3647],{"class":81},"\"stable-id\"",[67,3649,3501],{"class":77},[67,3651,3652,3654,3656,3658,3660,3662,3665],{"class":69,"line":192},[67,3653,3486],{"class":77},[67,3655,3489],{"class":385},[67,3657,3409],{"class":77},[67,3659,3539],{"class":385},[67,3661,3409],{"class":77},[67,3663,3664],{"class":81},"\"every-node\"",[67,3666,3501],{"class":77},[67,3668,3669],{"class":69,"line":200},[67,3670,3467],{"class":77},[296,3672,3673],{"v-slot:hints":47},[2164,3674,3675,3680,3685],{},[860,3676,3677,3678,54],{},"Stateless API обычно запускают через ",[45,3679,553],{},[860,3681,3682,3683,54],{},"Стабильные имена и PVC на реплику — это ",[45,3684,1625],{},[860,3686,3687,3688,54],{},"Агент на каждой ноде — это ",[45,3689,3690],{},"DaemonSet",[3692,3693,3694],"style",{},"html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":47,"searchDepth":85,"depth":85,"links":3696},[3697,3705,3709,3716,3719,3722,3728,3737,3743,3749,3750,3751,3756],{"id":24,"depth":85,"text":25,"children":3698},[3699,3700,3701,3702,3703,3704],{"id":32,"depth":96,"text":33},{"id":57,"depth":96,"text":58},{"id":300,"depth":96,"text":301},{"id":401,"depth":96,"text":402},{"id":495,"depth":96,"text":496},{"id":513,"depth":96,"text":514},{"id":522,"depth":85,"text":523,"children":3706},[3707,3708],{"id":538,"depth":96,"text":539},{"id":601,"depth":96,"text":58},{"id":818,"depth":85,"text":819,"children":3710},[3711,3712,3713,3714,3715],{"id":825,"depth":96,"text":826},{"id":883,"depth":96,"text":884},{"id":957,"depth":96,"text":958},{"id":1051,"depth":96,"text":1052},{"id":1071,"depth":96,"text":1072},{"id":1093,"depth":85,"text":582,"children":3717},[3718],{"id":1174,"depth":96,"text":1175},{"id":1250,"depth":85,"text":1251,"children":3720},[3721],{"id":1316,"depth":96,"text":1317},{"id":1450,"depth":85,"text":1451,"children":3723},[3724,3725,3726,3727],{"id":1457,"depth":96,"text":1458},{"id":1476,"depth":96,"text":1477},{"id":1492,"depth":96,"text":1493},{"id":1508,"depth":96,"text":1509},{"id":1603,"depth":85,"text":1604,"children":3729},[3730,3731,3732,3733,3734,3735,3736],{"id":1610,"depth":96,"text":1611},{"id":1728,"depth":96,"text":58},{"id":2033,"depth":96,"text":2034},{"id":2049,"depth":96,"text":2050},{"id":2225,"depth":96,"text":2226},{"id":2276,"depth":96,"text":2277},{"id":2299,"depth":96,"text":2300},{"id":2355,"depth":85,"text":2356,"children":3738},[3739,3740,3741,3742],{"id":2359,"depth":96,"text":2360},{"id":2372,"depth":96,"text":2373},{"id":2473,"depth":96,"text":2474},{"id":2552,"depth":96,"text":1175},{"id":2608,"depth":85,"text":2609,"children":3744},[3745,3746,3747,3748],{"id":2612,"depth":96,"text":2613},{"id":2640,"depth":96,"text":2641},{"id":2667,"depth":96,"text":2668},{"id":2750,"depth":96,"text":2751},{"id":2810,"depth":85,"text":2811},{"id":3148,"depth":85,"text":3149},{"id":3229,"depth":85,"text":3230,"children":3752},[3753,3754,3755],{"id":3233,"depth":96,"text":3234},{"id":3258,"depth":96,"text":3259},{"id":3279,"depth":96,"text":3280},{"id":3319,"depth":85,"text":3320},1781022067046]