Как развернуть сервис в Kubernetes: гайд для начинающих
Привет, Хабр! Сегодня мы попробуем развернуть простой сервис в Kubernetes на примере KaaS в облачной платформе Рег.ру. В качестве самого сервиса будем использовать imgproxy — минималистичный сервис подготовки изображений для web с предельно простым API.
Этот гайд будет полезен новичкам, которые только начинают работу с Kubernetes. Рассмотрим, как настраивать среду и управлять ей, и освоим принципы работы с контейнерами. Кроме того, развертывание imgproxy в качестве примера поможет научиться обрабатывать изображения с помощью Kubernetes простым и удобным способом.
Навигация по тексту:
→ Создание кластера Kubernetes
→ Получение kubeconfig
→ Проверка kubeconfig
→ Установка ingress-nginx
→ Установка cert-manager
→ Разворачиваем imgproxy
→ Проверка работоспособности
Создание кластера Kubernetes
Для начала нам нужно создать кластер Kubernetes в личном кабинете облака cloud.reg.ru, например, с тремя воркер-нодами:
Получение kubeconfig
После создания кластера, чтобы к нему подключиться через kubectl, нужно получить для него файл kubeconfig со страницы деталей:
Подсказка: если это ваш единственный кластер Kubernetes, вы можете переместить файл kubeconfig в место по умолчанию: ~/.kube/config и не использовать далее параметр --kubeconfig. Для этого достаточно выполнить в терминале:
→ mv -v <путь к скачанному файлу> ~/.kube/config
В случае, если кластер не единственный и/или управление контекстами в kubectl кажется неудобным, можно использовать разные файлы kubeconfig для разных окружений. Такой способ может быть безопаснее в некоторых случаях: например, если некоторые файлы kubeconfig находятся на внешних зашифрованных носителях.
Проверка kubeconfig
После получения файла kubeconfig нужно проверить его работоспособность. Запросим список нод, неймспейсов, подов:
→ kubectl --kubeconfig ~/Downloads/kubeconfig_4137209 get nodes
NAME STATUS ROLES AGE VERSION
k8s4137209-az1-md1-gb86x-6fbhz Ready 24m v1.30.2
k8s4137209-az1-md1-gb86x-cxxwq Ready 24m v1.30.2
k8s4137209-az1-md1-gb86x-pkvvk Ready 24m v1.30.2
→ kubectl --kubeconfig ~/Downloads/kubeconfig_4137209 get namespaces
NAME STATUS AGE
default Active 26m
kube-node-lease Active 26m
kube-public Active 26m
kube-system Active 26m
→ kubectl --kubeconfig ~/Downloads/kubeconfig_4137209 get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system cilium-99rxc 1/1 Running 0 24m
kube-system cilium-envoy-72sxw 1/1 Running 0 25m
kube-system cilium-envoy-88bcm 1/1 Running 0 24m
kube-system cilium-envoy-xk7t7 1/1 Running 0 24m
kube-system cilium-gfb8h 1/1 Running 0 25m
kube-system cilium-l4q2c 1/1 Running 0 24m
kube-system cilium-operator-5cb45868f8-n55fq 1/1 Running 0 25m
kube-system coredns-68d4f9776f-l2sbw 1/1 Running 0 25m
kube-system coredns-68d4f9776f-xjwm7 1/1 Running 0 25m
kube-system konnectivity-agent-4924v 1/1 Running 0 24m
kube-system konnectivity-agent-gmgvb 1/1 Running 0 24m
kube-system konnectivity-agent-v4f2l 1/1 Running 0 24m
Похоже, что все компоненты на месте, ноды есть. kube-apiserver, отдающий нам эту информацию, тоже работает.
Установка ingress-nginx
Начнем с установки ingress controller«а. Он нужен нам для того, чтобы получить трафик внутрь кластера Kubernetes. Использовать ingress«ы — не единственный способ этого достичь, но зачастую самый простой и удобный. В качестве ingress контроллера мы будем использовать ingress-nginx и дополним его cert-manager«ом для обеспечения возможности выписывания TLS-сертификатов от Let«s Encrypt.
Для установки ingress-nginx проще всего использовать helm:
→ helm \
--kubeconfig ~/Downloads/kubeconfig_4137209 \
upgrade \
--install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.kind=DaemonSet
Затем смотрим список подов и сервисов в namespace«е ingress-nginx и убеждаемся, что под с ingress controller«ом запущен, сервис создан и ему выделен внешний IP-адрес:
→ kubectl --kubeconfig ~/Downloads/kubeconfig_4137209 get pods,svc -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-controller-55dd9c5f4-bv2mc 1/1 Running 0 92s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.105.18.179 89.108.100.213 80:31362/TCP,443:32472/TCP 92s
service/ingress-nginx-controller-admission ClusterIP 10.102.26.77 443/TCP 92s
Установка cert-manager
cert-manager устанавливается по аналогии:
→ helm \
--kubeconfig ~/Downloads/kubeconfig_4137209 \
upgrade \
--install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true
Если по какой-то причине эта операция завершится ошибкой, в этом нет ничего страшного. Нужно просто повторить команду.
Единственное отличие: нам нужно будет создать ClusterIssuer для использования Let«s Encrypt, который мы возьмем прямо из документации cert-manager:
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: user@example.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginx
Сохраним в файл и выполним kubectl apply на него:
→ cat < clusterissuer.yaml
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: user@example.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginx
EOF
→ kubectl --kubeconfig ~/Downloads/kubeconfig_4137209 apply -f clusterissuer.yaml
Теперь нам нужно определиться с доменным именем, на котором будет работать наше тестовое приложение imgproxy. Я выберу поддомен imgproxy к одному из своих доменов и добавлю для него A-запись в DNS со значением внешнего IP сервиса — 89.108.100.213 в примере выше (у вас будет свой IP-адрес, ну и поддомен вы также можете выбрать свой).
Разворачиваем imgproxy
На этом инфраструктурная часть фактически закончена, и мы можем развернуть imgproxy. Он уже собран и поставляется в виде образа контейнера, для него даже есть готовый Helm Chart. Сам imgproxy в своей конфигурации очень прост, и в данном случае хочется показать «подкапотное пространство», потому егомы развернем вручную, т.е. с использованием описаний самого Kubernetes. Для этого нам потребуется создать три ресурса: Deployment — с самим с приложением, Service — для организации балансировки между несколькими инстансами imgproxy и Ingress — для обеспечения доступа из сети Интернет. Бонусом, т.к. мы настроили cert-manager, мы получим TLS для нашего imgproxy.
Создаем deployment.yaml:
→ cat < deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: imgproxy
labels:
app.kubernetes.io/name: imgproxy
app.kubernetes.io/app: imgproxy
spec:
replicas: 3
strategy:
type: RollingUpdate
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/name: imgproxy
app.kubernetes.io/app: imgproxy
template:
metadata:
labels:
app.kubernetes.io/name: imgproxy
app.kubernetes.io/app: imgproxy
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/app
operator: In
values:
- imgproxy
topologyKey: "kubernetes.io/hostname"
terminationGracePeriodSeconds: 30
automountServiceAccountToken: false
containers:
- name: imgproxy
image: darthsim/imgproxy:latest
imagePullPolicy: IfNotPresent
env:
# the maximum duration (in seconds) for processing the response.
# Default: 10
- name: IMGPROXY_WRITE_TIMEOUT
value: '30'
# the maximum duration (in seconds) to wait for the next request
# before closing the connection. When set to 0, keep-alive is
# disabled. Default: 10
- name: IMGPROXY_KEEP_ALIVE_TIMEOUT
value: '30'
# the maximum duration (in seconds) to wait for the next request
# before closing the HTTP client connection. The HTTP client is
# used to download source images. When set to 0, keep-alive is
# disabled. Default: 90
- name: IMGPROXY_CLIENT_KEEP_ALIVE_TIMEOUT
value: '40'
# the maximum duration (in seconds) for downloading the source
# image. Default: 5
- name: IMGPROXY_DOWNLOAD_TIMEOUT
value: '30'
# when set to true, enables using the ETag HTTP header for HTTP
# cache control. Default: false
- name: IMGPROXY_USE_ETAG
value: 'true'
# when set to true, enables using the Last-Modified HTTP header
# for HTTP cache control. Default: false
- name: IMGPROXY_USE_LAST_MODIFIED
value: 'true'
# when set to true, imgproxy will add debug headers to the response.
# Default: false. The following headers will be added:
#
# X-Origin-Content-Length: the size of the source image
# X-Origin-Width: the width of the source image
# X-Origin-Height: the height of the source image
# X-Result-Width: the width of the resultant image
# X-Result-Height: the height of the resultant image
- name: IMGPROXY_ENABLE_DEBUG_HEADERS
value: 'true'
# the maximum resolution of the source image, in megapixels.
# Images with larger actual size will be rejected. Default: 50
- name: IMGPROXY_MAX_SRC_RESOLUTION
value: '70'
# the maximum size of the source image, in bytes. Images with
# larger file size will be rejected. When set to 0, file size
# check is disabled. Default: 0
- name: IMGPROXY_MAX_SRC_FILE_SIZE
value: '104857600'
# the default quality of the resultant image, percentage.
# Default: 80
- name: IMGPROXY_QUALITY
value: '90'
# default quality of the resulting image per format, separated
# by commas. Example: jpeg=70,avif=40,webp=60. When a value for
# the resulting format is not set, the IMGPROXY_QUALITY value is
# used. Default: avif=65
- name: IMGPROXY_FORMAT_QUALITY
value: 'jpeg=90'
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
EOF
service.yaml:
→ cat < service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: imgproxy
labels:
app.kubernetes.io/name: imgproxy
app.kubernetes.io/app: imgproxy
spec:
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app.kubernetes.io/name: imgproxy
app.kubernetes.io/app: imgproxy
type: ClusterIP
EOF
В ingress.yaml нужно поменять название домена на свой собственный, для которого была добавлена A-запись:
→ cat < ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/client-body-buffer-size: 256m
nginx.ingress.kubernetes.io/proxy-body-size: 256m
labels:
app.kubernetes.io/name: imgproxy
app.kubernetes.io/app: imgproxy
name: imgproxy
spec:
ingressClassName: nginx
rules:
- host: imgproxy.my-domain.ru
http:
paths:
- backend:
service:
name: imgproxy
port:
number: 8080
path: /
pathType: Prefix
tls:
- hosts:
- imgproxy.my-domain.ru
secretName: imgproxy.my-domain.ru
EOF
Примечание: в примере выше используется доменное имя imgproxy.my-domain.ru, его нужно заменить на свое собственное.
После создания файлов применяем изменения:
kubectl apply -f deployment.yaml -f service.yaml -f ingress.yaml
Проверка работоспособности
Открываем браузер и идем по адресу https://<наш домен>/unsafe/resize: fit:300:300/plain/https:%2F%2Fmars.nasa.gov%2Fsystem%2Fdownloadable_items%2F40368_PIA22228.jpg
В результате мы видим изображение с размером, вписанным в квадрат 300×300 пикселей, т.е. все работает.
Мы пошагово разобрали, как развернуть сервис imgproxy в Kubernetes с помощью KaaS в облаке Рег.ру. Теперь, когда сервис работает, можно смело настраивать его, масштабировать и оптимизировать для своих задач. Если вы хотите углубиться в тему Kubernetes, прочитайте наши предыдущие статьи. Ранее мы писали про архитектуру KaaS и рассказывали о том, как мы его запускали в облаке Рег.ру.