golang

Работа с Kubernetes: поднимаем локальный кластер и деплоим в него приложения

  • суббота, 16 марта 2024 г. в 00:00:15
https://habr.com/ru/companies/avito/articles/799689/

Привет! Меня зовут Павел Агалецкий, я ведущий разработчик юнита 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

Kubectl — это утилита для работы с Kubernetes, которая взаимодействует с кластером через его API. Для ее установки нужно выполнить команду brew install kubernetes-cli. 

С помощью API можно выполнить различные изменения в самом кластере и в запущенных в нём сервисах. Одна из основных команд, которые для этого используются — kubectl get. Она показывает список ресурсов определенного типа, например name space. Они нужны, чтобы сгруппировать внутри себя другие типы ресурсов и управлять ими, например задать права доступа к ним со стороны других сущностей в кластере, в том числе аккаунтов пользователей. Список namespace в вашем кластере можно узнать с помощью команды kubectl get namespace или её короткого варианта kubectl get ns.

Запускаем Pod

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 + название пода.

Я получил сообщение, что под удален и проверяю, что произошло в кластере.

Вижу, что у меня есть два пода, но один из них заменил только что удалённый. 

Получаем доступ к приложению и создаем service

Теперь у нас есть запущенное приложение, и я хочу получить к нему доступ через IP виртуальной машины. Для этого:

  1. Узнаю его Colima-статус командой colima status.

  2. Вижу IP-адрес.

  1. Выполняю команду 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. До встречи!