Использование PostStart хука при запуске пода в Kubernetes-кластере

Thank you for reading this post, don't forget to subscribe!

Хуки дают воз­мож­ность полу­чать инфор­ма­цию о жиз­нен­ном цик­ле управ­ле­ния кон­тей­не­ра­ми и выпол­нять код, реа­ли­зо­ван­ный в обра­бот­чи­ке (handler), при сра­ба­ты­ва­нии опре­де­лен­но­го хука.

Для каж­до­го кон­тей­не­ра в поде хуки опре­де­ля­ют­ся отдель­но. Суще­ству­ют два типа хуков - PostStart и PreStop. Пер­вый явля­ет­ся асин­хрон­ным и выпол­ня­ет­ся сра­зу же при созда­нии кон­тей­не­ра, одна­ко нет ника­кой гаран­тии, что дан­ный хук будет выпол­нен до запус­ка инструк­ции ENTRYPOINT кон­тей­не­ра. Сто­ит отме­тить, что если выпол­не­ние PostStart хука зани­ма­ет очень мно­го вре­ме­ни (или зави­са­ет), то кон­тей­нер не может перей­ти в состо­я­ние Running.

Хук PreStop, как вид­но из его назва­ния, выпол­ня­ет­ся перед тем как кон­тей­нер будет оста­нов­лен (terminated) - будь то API-запрос или дру­гое собы­тие (напри­мер, неудач­ная liveness probe, “выдав­ли­ва­ние” пода с узла кла­сте­ра, пере­бор исполь­зу­е­мых ресур­сов). Этот вызов син­хрон­ный, а это зна­чит, что он обя­за­тель­но дол­жен быть завер­шен до того, как будет отправ­лен сиг­нал оста­нов­ки контейнера.

Для хуков в жиз­нен­ном цик­ле кон­тей­не­ров преду­смот­ре­но два вари­ан­та обра­бот­чи­ков (handlers):

  • Exec - выпол­ня­ет опре­де­лен­ную коман­ду (скрипт) в про­стран­стве имен кон­тей­не­ра. Ресур­сы, кото­рые исполь­зу­ют­ся дан­ной коман­дой так­же учи­ты­ва­ют­ся в исполь­зу­е­мых ресур­сах кон­тей­не­ра (важ­но при опре­де­ле­нии памя­ти и CPU);
  • HTTP - выпол­ня­ет HTTP-запрос на опре­де­лен­ный энд­по­инт контейнера.

Если какой-то из хуков PostStart или PreStop завер­ша­ет­ся с ошиб­кой, то кон­тей­нер так­же будет оста­нов­лен. Логи хуков недо­ступ­ны при выпол­не­нии коман­ды kubectl logs <pod_name>, но если по какой-то при­чине они выпол­ни­лись неудач­но, то про­ис­хо­дит собы­тие FailedPostStartHook или FailedPreStopHook соот­вет­ствен­но. Эти собы­тия мож­но уви­деть выпол­нив коман­ду kubectl describe pod <pod_name>.

Итак, мы вполне можем исполь­зо­вать PostStart хук для встав­ки дан­ных в Redis при стар­те контейнера.

Идея состо­ит в сле­ду­ю­щем: с помо­щью ConfigMap мы доба­вим файл(ы) внутрь кон­тей­не­ра, при­чем назва­ни­ем клю­ча в реди­се будет имя, а зна­че­ни­ем - содер­жи­мое это­го фай­ла. Далее, исполь­зуя PostStart хук, мы “обра­бо­та­ем” каж­дый из фай­лов и вста­вим соот­вет­ству­ю­щие дан­ные в БД Redis.

Мани­фест, содер­жа­щий в себе все необ­хо­ди­мые объ­ек­ты Kubernetes, будет выгля­деть так:

Вся “магия” заклю­ча­ет­ся в коман­де, кото­рая опре­де­ле­на в postStart хуке:

Здесь для каж­до­го фай­ла в ката­ло­ге /script, кото­рый закан­чи­ва­ет­ся на key выпол­ня­ет­ся следующее:

  • с помо­щью коман­ды cat выво­дит­ся содер­жи­мое фай­ла в STDOUT;
  • через кон­вей­ер | пере­да­ют­ся сле­ду­ю­щей коман­де - кон­соль­ной ути­ли­те redis-cli (здесь в STDIN попа­да­ет содер­жи­мое STDOUT из преды­ду­ще­го шага);
  • redis-cli выпол­ня­ет встав­ку дан­ных во вто­рую БД (ключ -n 2) с помо­щью коман­ды SET.

При­ме­ча­ние Име­нем клю­ча будет зна­че­ние пере­мен­ной ${FILE} (имя фай­ла), а зна­че­ни­ем - дан­ные из STDIN (об этом забо­тит­ся ключ -x).