Работа с Kubernetes: поднимаем локальный кластер и деплоим в него приложения
- суббота, 16 марта 2024 г. в 00:00:15
Привет! Меня зовут Павел Агалецкий, я ведущий разработчик юнита Platform as a Service в Авито.
Kubernetes — один из самых популярных инструментов для деплоя приложений и сервисов. В Авито мы используем его не только в продакшене, но и в качестве среды для локального запуска сервисов на машинах разработчиков. В этой статье я подробно разбираю, как поднять маленький кластер Kubernetes на своём компьютере с помощью общедоступных инструментов и деплоить простейшие приложения.
Перед началом работы мне нужно поставить на MacBook виртуальную машину, на которой будем запускать кластер. Сделать это можно с помощью специального инструмента — Colima, который устанавливается командой brew install colima
. Это включит Kubernetes внутри запускаемой машины. Команда colima start --kubernetes --network-address
сделает машину доступной и присвоит ей сетевой адрес.
После скачивания образов и всех необходимых компонентов, запустим среду:
colima start \
--kubernetes \
--network-address
Узнаем её статус:
colima status
Узнать статус можно с помощью команды colima status
. У нас он вот такой:
Kubectl — это утилита для работы с Kubernetes, которая взаимодействует с кластером через его API. Для ее установки нужно выполнить команду brew install kubernetes-cli.
С помощью API можно выполнить различные изменения в самом кластере и в запущенных в нём сервисах. Одна из основных команд, которые для этого используются — kubectl get
. Она показывает список ресурсов определенного типа, например name space
. Они нужны, чтобы сгруппировать внутри себя другие типы ресурсов и управлять ими, например задать права доступа к ним со стороны других сущностей в кластере, в том числе аккаунтов пользователей. Список namespace в вашем кластере можно узнать с помощью команды kubectl get namespace
или её короткого варианта kubectl get ns
.
Pod — минимальная единица, которую можно запустить в Kubernetes, состоящая из контейнеров. Покажу, как запустить Pod внутри только что созданного кластера, на примере небольшого приложения на Go, которое состоит из единственного файла main.go
.
Приложение представляет собой простейший веб-сервер, который отвечает на запрос "hello"
сообщением "Hello World!"
.
package main
import (
"fmt"
"log/slog"
"net/http"
)
func main() {
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
slog.Info("Received request to /hello endpoint")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "Hello World!")
})
slog.Info("Starting server on port 8890")
err := http.ListenAndServe(":8890", nil)
if err != nil {
slog.Error("Application finished with an error", "error", err)
}
}
Кроме этого, оно выводит в лог сообщение о своем запуске и о получении запроса. Для проверки запускаем приложение локально командой go run main.go
.
Когда приложение написало о запуске, можно выполнить запрос к его API. Для этого в отдельной консоли введите команду curl localhost:8890/hello
. Приложение ответило на запрос и вывело на экран сообщение:
Теперь запустим приложение в кластере. Для этого понадобится Dockerfile, который описывает, как упаковать приложение в контейнер. Вот содержимое файла:
FROM golang:1.21-alpine
COPY . /app
WORKDIR /app
RUN ls -la . && \
go install . && \
which kubeapp
ENTRYPOINT ["/go/bin/kubeapp"]
Вместе с кластером Kubernetes Colima запустила docker host, который можно использовать для того, чтобы собрать приложение в контейнер. Выполняю docker build командой docker build -t kubeapp
. С помощью -t
обозначаю имя приложения, а точка в конце значит, что я собираю его из текущего контекста. Результат можно посмотреть через команду docker images
— контейнер действительно собрался.
Чтобы запустить приложение, нужно ввести команду:
docker run --rm -p 8890:8890 kubeapp
rm
— флаг, требующий удалить контейнер после завершения.
8890 — порт, на котором будет доступно запущенное приложение.
kubeapp
— image приложения.
Лог сообщает, что приложение запустилось, но я проверю его API еще раз. Можно обратиться к нему так же, как в случае локального запуска: curl localhost:8890/hello.
Такой способ обращения к приложению работает, так как Colima пробрасывает порты контейнеров на нашу локальную машину.
Теперь запускаю приложение непосредственно в Kubernetes — для этого нужен ресурс с типом deployment. Это еще один тип ресурсов, который позволяет запустить сразу несколько подов и контролировать их количество. Также он определяет, что и как будет происходить с подами в случае их остановки или перезапуска.
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubeapp-example
labels:
app: kubeapp
spec:
selector:
matchLabels:
app: kubeapp
replicas: 2
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: kubeapp
spec:
containers:
- name: app
image: kubeapp
imagePullPolicy: Never
ports:
- containerPort: 8890
Чтобы применить deployment в Kubernetes, нужна специальная команда kubectl apply -f deployment.yaml
. -f
здесь указывает на использование определённого файла.
Утилита сообщает, что deployment создан. Попробуем его посмотреть с помощью команды kubectl get + название только что созданного deployment.
В deployment я указал, что хочу запустить две реплики приложения. Проверяю, так ли это, командой kubectl get pods
. Затем я могу посмотреть на контейнеры приложения командой docker ps
.
Я вижу запущенные контейнеры, которые находятся в составе подов.
Главное отличие от того случая, когда приложение запускали просто в контейнере — если удалить один из подов, он будет автоматически пересоздан Kubernetes.
Чтобы проверить это, я удаляю один из подов командой kubectl delete pod + название пода.
Я получил сообщение, что под удален и проверяю, что произошло в кластере.
Вижу, что у меня есть два пода, но один из них заменил только что удалённый.
Теперь у нас есть запущенное приложение, и я хочу получить к нему доступ через IP виртуальной машины. Для этого:
Узнаю его Colima-статус командой colima status.
Вижу IP-адрес.
Выполняю команду curl
.
Приложение не работает, потому что в данном случае поды и их порты по умолчанию не пробрасываются на виртуальную машину.
Чтобы исправить это, я могу указать Kubernetes, что хочу выставить deployment наружу, используя директиву kubectl expose -- type=NodePort deployment
/ его название
. NodePort
означает, что я хочу сделать deployment доступным на одном из портов виртуальной машины.
Утилита создала service — ещё один тип ресурса внутри Kubernetes. Он определяет то, как мы получаем доступ к различным видам других ресурсов. На него можно посмотреть с помощью команды kubectl get service
или её краткого варианта kubectl get svc
.
Затем делаю запрос к машине ещё раз, но в этот раз будем использовать тот порт, который Kubernetes присвоил для сервиса:
Теперь сервис работает доступен по заданному порту. Посмотреть его логин можно с помощью команды kubectl logs
. Она позволяет увидеть логи любого пода внутри кластера Kubernetes. Попробую выполнить команду для какого либо из подов приложения, например первого, командой kubectl logs pod
/ название пода
:
У этой команды есть ещё и режим следования за логами, который позволяет получать их по мере того, как под будет писать из в свой файл stdout
. Чтобы включить режим, добавляем -f
. Теперь команда ждёт, когда под что-то напишет в лог:
Делаем несколько новых запросов и видим, что в лог действительно выводится информация о них:
Число реплик уже запущенного приложения можно уменьшить или увеличить командой kubectl scale. Флаг -- replicas= показывает число копий
После этого я вижу, что запущенных подов стало больше — в команде я указал пять.
Число реплик можно уменьшить с помощью той же команды, например до одной.
Такого набора инструментов достаточно, чтобы начать работать с Kubernetes, а ещё чувствовать себя более уверенно при работе с настоящим продакшн-кластером.
А в одной из следующих статей мы рассмотрим способы отладки приложений, запущенных в Kubernetes. До встречи!