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
, будет выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
apiVersion: v1 kind: Service metadata: name: ads-redis-test namespace: default spec: selector: app: ads-redis-test ports: - name: redis port: 6379 clusterIP: None --- apiVersion: v1 kind: ConfigMap metadata: name: ads-redis-test namespace: default data: flow-rules-key: | [{ "resource": "loopme.grpc.ssp.v0.AdsTxtRecordService/GetAdsTxtRelationships", "count": 100.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "loopme.grpc.ssp.v0.PublisherAccountService/GetPublisherById", "count": 5.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "loopme.grpc.ssp.v1.PublisherAccountService/GetPublisherById", "count": 5.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "loopme.grpc.ssp.v0.BundleLegacyService/GetBundleByKey", "count": 20.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "loopme.lsm.ssp.v0.BundleService/GetBundleById", "count": 20.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "loopme.lsm.ssp.v0.BundleService/QueryBundle", "count": 20.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "loopme.grpc.ssp.v0.AppLegacyService/GetAppById", "count": 10.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "loopme.grpc.ssp.v0.AppLegacyService/GetAppIdByKey", "count": 10.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "loopme.grpc.ssp.v0.AppLegacyService/GetAppIdByContainerKey", "count": 16.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "loopme.grpc.ssp.v0.AppLegacyService/GetAppByContainerKey", "count": 10.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "ExchangeThrottleRateService/GetThrottleRatesByKeys", "count": 20.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "dsp-fetcher", "count": 25.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "exchange-fetcher", "count": 300.0, "grade": "THREAD", "limit-app": "default" },{ "resource": "kafka_dmp_ads_requests_info", "count": 500.0, "grade": "QPS", "limit-app": "default" }] degrade-rules-key: | [{ "resource": "analytics.AnalyticsApiService/AnalyzeCall", "count": 10.0, "grade": "EXCEPTION_COUNT", "time-window": 10, "min-request-amount": 100, "stat-interval-ms": 20000, "slow-ratio-threshold": 0.6 },{ "resource": "analytics.AnalyticsApiService/AnalyzeCall", "count": 10.0, "grade": "EXCEPTION_RATIO", "time-window": 10, "min-request-amount": 100, "stat-interval-ms": 20000, "slow-ratio-threshold": 0.6 }] --- apiVersion: apps/v1 kind: Deployment metadata: name: ads-redis-test namespace: default spec: replicas: 1 selector: matchLabels: app: ads-redis-test template: metadata: labels: app: ads-redis-test spec: containers: - name: redis image: redis:6.0.9 ports: - name: redis containerPort: 6379 resources: limits: cpu: "0.5" memory: 1Gi requests: cpu: "0.5" memory: 1Gi lifecycle: postStart: exec: command: ["/bin/bash", "-c", "cd /script/ && for FILE in *key; do cat ${FILE} | redis-cli -n 2 -x set ${FILE}; done"] livenessProbe: exec: command: - sh - -c - redis-cli -h $(hostname) ping initialDelaySeconds: 5 periodSeconds: 3 readinessProbe: exec: command: - sh - -c - redis-cli -h $(hostname) ping initialDelaySeconds: 5 timeoutSeconds: 3 volumeMounts: - mountPath: /script name: script volumes: - name: script configMap: name: ads-redis-test |
Вся “магия” заключается в команде, которая определена в postStart
хуке:
1 2 |
command: ["/bin/bash", "-c", "cd /script/ && for FILE in *key; do cat ${FILE} | redis-cli -n 2 -x set ${FILE}; done"] |
Здесь для каждого файла в каталоге /script
, который заканчивается на key
выполняется следующее:
- с помощью команды
cat
выводится содержимое файла в STDOUT; - через конвейер
|
передаются следующей команде - консольной утилитеredis-cli
(здесь в STDIN попадает содержимое STDOUT из предыдущего шага); redis-cli
выполняет вставку данных во вторую БД (ключ-n 2
) с помощью команды SET.
Примечание Именем ключа будет значение переменной ${FILE}
(имя файла), а значением - данные из STDIN (об этом заботится ключ -x
).