Вставка данных в Redis при запуске контейнера в Kubernetes кластере

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

Доволь­но часто встре­ча­ют­ся вари­ан­ты кон­фи­гу­ри­ро­ва­ния и запус­ка Redis сер­ви­са в кла­сте­ре Kubernetes клас­си­фи­ци­ру­е­мые как stateless (т.е. без сохра­не­ния дан­ных). В дан­ной ста­тье рас­смот­рим вари­ант встав­ки дан­ных в Redis на стар­те пода в кластере

кейс доволь­но спе­ци­фи­че­ский, и если ваши при­ло­же­ния зави­сят от дан­ных в Redis (или вы хоти­те вста­вить пару мил­ли­о­нов клю­чей за 1 секун­ду - об этом будет отдель­ная ста­тья), то луч­ше исполь­зо­вать реше­ния для stateful при­ло­же­ний - опе­ра­то­ры, StatefulSet и т. д.

Итак, в нашем кон­крет­ном слу­чае исполь­зу­ет­ся самый насто­я­щий stateless Redis - деп­лой­мент с одной репли­кой. То есть мы гото­вы к поте­ре дан­ных - это некри­тич­но, но “было бы хоро­шо” иметь в реди­се одну пару ключ/значение сра­зу же после запус­ка контейнера.

Оче­вид­но, что вари­ант с initContainers не под­хо­дит (Redis уже дол­жен быть запу­щен на момент встав­ки дан­ных), а вари­ант с пере­пи­сы­ва­ни­ем Dockerfile и/или entrypoint-скрип­тов, мяг­ко гово­ря, не элегантный.

Самым инте­рес­ным вари­ан­том для дан­но­го кей­са ока­за­лось напи­са­ние соб­ствен­но­го кон­трол­ле­ра, кото­рый, будучи под­клю­чен к кла­сте­ру Kubernetes “слу­ша­ет” инте­ре­су­ю­щие нас собы­тия и выпол­ня­ет преду­смот­рен­ные операции.

Go кли­ент для Kubernetes (client-go) содер­жит пакет, кото­рый поз­во­ля­ет с лег­ко­стью полу­чать собы­тия (events) исполь­зуя Kubernetes APIk8s.io/client-go/tools/cache. Кро­ме того, этот пакет поз­во­ля­ет нам лег­ко добав­лять функ­ции, кото­рые будут вызва­ны при полу­че­нии опре­де­лен­но­го собы­тия и хра­нить объ­ек­ты в памя­ти (в так назы­ва­е­мом хра­ни­ли­ще, Store).

Хотя пакет cache предо­став­ля­ет необ­хо­ди­мые нам инстру­мен­ты, его ини­ци­а­ли­за­ция и исполь­зо­ва­ние могут быть избы­точ­ны­ми и несколь­ко обре­ме­ни­тель­ны­ми, когда речь идет о полу­че­нии про­стых обнов­ле­ний от Kubernetes API. Прак­ти­че­ски все ста­тьи по исполь­зо­ва­нию k8s.io/client-go/tools/cache, кото­рые мне уда­лось най­ти, пред­ла­га­ют исполь­зо­вать этот пакет “как есть”.

Одна­ко суще­ству­ет еще один пакет, кото­рый объ­еди­ня­ет кон­цеп­ции, кото­рые предо­став­лен­ные паке­том cache, в один: k8s.io/client-go/informers. Он постав­ля­ет­ся с про­стой фаб­ри­кой для всех ресур­сов Kubernetes - то, что нам нужно!

Ини­ци­а­ли­за­ция фаб­ри­ки выгля­дит при­мер­но так:

Так мы полу­ча­ем новый экзем­пляр SharedInformerFactory с допол­ни­тель­ны­ми опци­я­ми (нейм­с­пейс default и LabelSelector “app=ads-redis-statistic”). Пер­вым агру­мен­том явля­ет­ся наш кли­ент, кото­рый под­клю­ча­ет­ся и вза­и­мо­дей­ству­ет с Kubernetes API, а вто­рым - пери­о­дич­ность, с кото­рой инфор­мер будет син­хро­ни­зи­ро­вать данные.

Теперь мы можем с лег­ко­стью начать полу­чать необ­хо­ди­мые нам собы­тия о инте­ре­су­ю­щем нас ресур­се - новом поде в кла­сте­ре Kubernetes. Выгля­деть это будет при­мер­но так:

Пер­вая стро­ка ини­ци­а­ли­зи­ру­ет инфор­мер, кото­рый настро­ен на полу­че­ние собы­тий о подах групы Core/v1 API (филь­тра­ция по неймспейсу/селектору у нас ука­за­на еще на эта­пе созда­ния фабрики).

Вто­рая стро­ка добав­ля­ет функ­цию-обра­бот­чик (handler), кото­рая будет вызы­вать­ся каж­дый раз, когда инфор­мет полу­ча­ет от Kubernetes API инте­ре­су­ю­щее нас собы­тие (а имен­но, добав­ле­ние в нейм­с­пей­се default ново­го пода с селек­то­ром app: ads-redis-statistic).

Сама функ­ция onAdd выгля­дит сле­ду­ю­щим образом:

Функ­ция ини­ци­а­ли­зи­ру­ет redis-кли­ент с задан­ны­ми пара­мет­ра­ми, выпол­ня­ет под­клю­че­ние к сер­ви­су, встав­ля­ет дан­ные в БД, для про­вер­ки полу­ча­ет зна­че­ние для ука­зан­но­го клю­ча и отклю­ча­ет­ся от дан­но­го экзем­пля­ра Redis.