Первое знакомство с Kubernetes создаем приложение для развертывания в кластере

kubernetes app

Пишем приложение (Python + Flask)

Поскольку Python – единственный язык программирования, на котором я что-то умею писать, особого выбора у меня нет. Приложения будет запускаться как веб-сервер, слушать указанный порт и при обращении выдавать приветствие “Hello from Python”. Кроме Python потребуется фреймворк Flask.

Код приложения:

app.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Python"
if __name__ == "__main__":
    app.run(host='0.0.0.0')

В первых двух строчках мы подключаем Flask, а далее создаем обработку корневого запроса. Приложение неидеально, но как минимальный вариант для развертывания в кластере k8s оно сгодится. Так как k8s – это среда запуска контейнеров, для переноса приложение необходимо упаковать, например, в Docker.

Упаковываем приложение в контейнер Docker

Чтобы собрать контейнер, нужно поставить Docker на локальную машину (да, да – Капитан очевидность). Инструкция по инсталляции есть на официальном сайте – процесс довольно несложен. Далее собирать образ можно средствами Docker, я использую для этих целей утилиту buildah. Она ни разу меня не подводила, рекомендую.

Для сборки контейнера на хосте должны быть установлены Docker и buildah. Все действия я провожу в CentOS, в других дистрибутивах Linux они могут немного отличаться.

Для начала создаем директорию, из которой будем собирать контейнер. Первый файл в директории назовем, например, app.py (его код приведен выше). Для установки зависимостей нам потребуется файл requirements.txt . Поскольку приложение простенькое, достаточно добавить только модуль Flask:

Flask==1.0.2

Третий файл называется Dockerfile – без расширения. Его содержимое:

Dockerfile

FROM python:3.9-alpine
RUN mkdir /app
WORKDIR /app
ADD . /app/
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "/app/app.py"]

Разберем его построчно:

  • FROM python:3.9-alpine – добавляем в контейнер интерпретатор Python 3.9, сборка alpine.
  • RUN mkdir /app – создаем директорию /app внутри контейнера.
  • WORKDIR /app – позволяет один раз указать конкретный путь (каталог на диске), после чего большинство инструкций (например, RUN или COPY) будут выполняться в контексте этого каталога.
  • ADD . /app/ – очень важная команда, если мы делаем контейнер для k8s. Дело в том что когда отрабатывает запуск контейнера, k8s думает, что нам необходима внешняя директория /app . Ее в кластере нет, и контейнер падает с ошибкой (не найден файл /app/app.py ).
  • RUN pip install -r requirements.txt – запускаем pip и скармливаем ему файл зависимостей. Flask скачивается и устанавливается в контейнер.
  • EXPOSE 5000 – пробрасываем порт 5000 для общения с нашим приложением.
  • CMD [“python”, “/app/app.py”] – запускаем интерпретатор Python и наше приложение app.py .


Готово, директория в итоге должна содержать следующие файлы: app.py, requirements.txt, Dockerfile.

Находясь в директории с файлами, выполняем команду для сборки контейнера на основе Dockerfile с помощью утилиты buildah:

buildah bud -f ./Dockerfile .

Запустится сборка, прогрузятся все зависимости из requirements.txt , и в итоге сгенерируется хеш – строка Storing signatures , которая нам в дальнейшем понадобится (копируем ее в буфер обмена)

Дальше необходимо запушить сборку в локальный репозиторий Docker. Выполняем следующую команду, подставляя в параметры свой хеш:

buildah push dedd9a5526d3c97231e9a0b73ca1e4a91ece0d70a7f7bff254f61f7d28d8e9fb docker-daemon:app:v0
  • :app – это имя нашего контейнера (можно указать свой).
  • :v0 – это tag. Обычно через него выставляется версионность для удобной навигации в репозитории.

Контейнер уже можно увидеть в списке локального репозитория Docker, выполнив следующую команду:

docker image ls



Итак, у нас на локальном хосте есть собранный контейнер который мы можем запустить для проверки работоспособности с помощью curl.

docker run --rm -d -v `pwd`:/app -p 5000:5000 app:v0


Приложение откликается, значит контейнер рабочий. Последний штрих – запушить контейнер из локального репозитория в удаленный, например, в Docker Hub. Это нужно, чтобы мы могли скачать контейнер в кластер k8s и запуститься его уже там.

Чтобы это сделать, потребуется авторизоваться на Docker Hub и создать пустой репозиторий. Детально описывать эту процедуру не буду: процесс похож на создание Git-репозитория.

docker login
<выполните авторизацию>. Делается один раз, пароль и логин запоминаются
docker tag app:v0 <ваш логин docker hub>/<ваш репозиторий>/app:v0
buildah push <image ID> <ваш логин docker hub>/<ваш репозиторий>:app

Теперь можно на любом хосте с Docker запустить контейнер и в качестве расположения указать уже не локальный репозиторий, а Docker Hub.

docker run --rm -d -v `pwd`:/app -p 5000:5000 test/learn_images:app:v0

Цель – развернуть приложение Python из контейнера, но уже в кластере k8s в нескольких репликах.

Для этого необходимо создать два объекта (подробнее мы рассматривали их в предыдущей статье):

  • Deployment – развернет приложение и будет поддерживать необходимое количество реплик.
  • Service – обеспечит сетевое взаимодействие внутри кластера.

Создаем Deployment для приложения в кластере k8s

Заходим по SSH на мастер-узел (ноду), проверяем работоспособность kubectl . Далее необходимо создать deployment.yaml .

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  labels:
    app: app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
      - name: app
        image: <ваш логин docker hub>/<ваш репозиторий>:app
        ports:
        - containerPort: 5000
          protocol: TCP
          name: http
        resources:
          limits:
            cpu: 50m
            memory: 20Mi
          requests:
            cpu: 50m
            memory: 20Mi

Разбираем подробнее:

  • kind: Deployment – определяем, какой у нас будет объект. В данном случае Deployment , но может быть и Service или, скажем, Pod .
  • name: app – имя нашего Deployment (именно так он будет отображаться в кластере k8s).
  • matchLabels: и app: app – метка приложения, ее уникальный идентификатор для дальнейшего мапинга с сервисом и другими объектами k8s.
  • replicas: 2 – сколько экземпляров приложения необходимо создать
  • containers: – этой секции описывается, откуда нам спулить контейнер (ясно дело, с Docker Hub). Порт для сетевого взаимодействия.
  • resources: – интересная секция. Тут описаны минимально необходимые ресурсы для запуска ( limits ). Без этих ресурсов приложение не стартует и планировщик кластера будет искать подходящий узел, удовлетворяющую минимальным ресурсам. Запрашиваемые ( requests ) – это ресурсы, которые резервируются на ноде для вашего приложения. Сверх этих ограничений приложение не получит ресурсов CPU и RAM.

Сохраняем файл deployment.yaml и выполняем команду для развертывания приложения:

kubectl create -f deployment.yaml

Если ошибок в файле не было, успешный исход – активное приложение в 2 репликах. Посмотреть состояние можно следующей командой:

kubectl get pod -o wide

Приложение развернуто в двух репликах – с помощью curl можно обратиться к каждой из них по IP пода, но это плохой вариант (поды рано или поздно переедут на другие узлы и IP поменяются). Давайте напишем еще один объект кластера, который закроет эту проблему – Service .

Создаем Service приложения в кластере k8s

Создаем еще один файл service.yaml .

service.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: app
  name: app
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 5000
  selector:
    app: app
  type: ClusterIP

Его главные отличительные особенности:

  • ports: port – порт, по которому Service будет принимать трафик.
  • targetPort – порт для связи с приложением.
  • selector: – мапим наш Deployment и все реплики по этой метке.
  • type: clusterIP – тип объекта Service . В нашем случае для сетевого взаимодействия внутри кластера k8s.

Запускаем процедуру создания следующей командой:

kubectl create -f service.yaml

Далее смотрим детали Service :

kubectl get svc -o wide

Супер! У нас есть IP, по которому можно обращаться к сервису в кластере с любого пода.

Подведем итог статьи. Мы добились следующих результатов:

  • Написали приложение на Python + Flask.
  • Упаковали приложение в контейнер Docker.
  • Разместили контейнер в репозитории Docker hub.
  • Сделали Deployment и Service в кластере k8s.

Поздравляю, вы развернули первое приложение в кластере k8s! Возможно оно простовато, но в следующих статьях мы будем доводить его до ума.

Друзья помогите этому контенту стать доступнее в социальных сетях.

Не проходи мимо жмакни по кнопке возможно кому то еще он будет полезен!