Kubernetes для начинающих, быстрый старт с нуля

Эта статья предназначена для быстрого старта в Kubernetes. Цель статьи – максимально просто рассказать о среде Kubernetes так, чтобы вы сразу могли начать работать с ней. Я специально опущу такие вещи, как: пространство имен, задачи, расписания, версионность, репликация, авто-масштабирование, авто определение сервисов, супервайзинг и прочие страшные слова. Оставим их, как бонус, на потом. Мы так же не будем углубляться в архитектуру k8s, это тема для отдельной статьи.

Также мы писали про Обзор k9s — продвинутого терминального интерфейса для Kubernetes.

Что такое Kubernetes?

Для простоты будем представлять его, как менеджер контейнеров.

Контейнер – это изолированная среда для работы какого-то приложения. Чаще всего на данный момент таким контейнером служит Docker. Наверняка вам приходилось запускать приложения в Docker-е. А может и несколько приложений, работающий совместно, используя docker-compose.

Pod

В k8s один экземпляр приложения называется pod (от англ. – стручок). Контейнеры в нем – горошины. Дело в том, что kubernetes позволяет запускать несколько контейнеров в одном pod. Все контейнеры внутри pod, как правило, объединены одной логикой. К примеру pod может называться web, а в нем 2 контейнера: php и db.

Запущенный Docker контейнер – это горошина в стручке

Чаще всего внутри pod вы найдете всего один контейнер – само приложение, и это нормально. Вторыми, третьими контейнерами там как правило бывают служебные (типа сбора метрик, адаптера и пр.).

Внутри pod все контейнеры имеют один TCP/IP адрес и могут обращаться друг с другом через localhost !

Deployment и StatefulSet

У приложения (pod), помимо контейнеров существуют: описание, методанные, имя, метки, количество запущенных копий (replicas). Плюс к pod-у мы можем подключать внешние тома (volumes) для хранения данных, init-контейнеры (которые будут настраивать наше приложение до его запуска), и много чего еще.

Такое описание pod-а, с его дополнительными ресурсами называется Deployment. Описание в k8s происходит на языке разметки yaml.

Вот пример описания Deployment-а:

---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: web-site
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: php
          image: php-fpm:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: data
              mountPath: /var/www
              subPath: data
        - name: db
          image: mysql:latest
          volumeMounts:
            - name: db
              mountPath: /var/lib/mysql
              subPath: db

В данном примере image – имя образа Docker, доступного из репозитария (docker hub или личного). Пример: image: osixia/openldap:1.2.5 или image: localhost:5000/openldap:latest. Еще вы можете увидеть описание экспортируемых TCP портов ports и внешних томов volumeMounts.

ЗАМЕТКА: Другой вариант Deployment называется StatefulSet – это тот же Deployment, только со статичными именами хостов и особым способом разделения ресурсов: каждый экземпляр реплики получает доступ только ко своей директории. Тогда как в Deployment все реплики имеют доступ к общим директориям. StatefulSet используется реже, как правило для балансировки нагрузки и репликации внутри приложения. К примеру его можно встретить в СУБД PostgreSQL с master-slave репликацией, где у каждой реплики – свой набор данных.

Service

Итак, у нас уже есть приложение, описанное со всеми ресурсами в Deployment. Нам надо к нему обратиться из-вне, к примеру открыть страницу нашего web. Для этого служит новый тип ресурсов: Service. Он позволяет описать что и на каком порту предоставлять в использование:

apiVersion: v1
kind: Service
metadata: 
  name: web-page
spec:
  selector:   
    app: web
  type: NodePort
  ports: 
  - name: http
    port: 80
    targetPort: 80
    nodePort: 9000
    protocol: TCP

В данном примере мы описали новый сервис (Service) с именем web-page. Работать он будет с помощью селектора (selector), выбирая все, что попадает под его правила. В данном случаи: app: web. То есть наш pod, описанный в Deployment как раз имеет такую метку (label) (см. выше).

В k8s выборки делаются через метки (label).

Выше указан тип сервиса: type: NodePort. Существует несколько способов открыть доступ до приложения:

  • NodePort – этот тип вывешивает TCP/IP порт прямо на ноде, на которой запущен kubernetes. То есть вы можете обратиться по внешнему IP адресу сервера k8s и этот порт (у нас nodePort: 9000) будет перенаправлен в соответствующий pod.
  • ClusterIP – вывешивает сервис на выделенный IP адрес внутри кластера. Как правило он серый. Это самый используемый тип сервиса, так как позволяет экспортировать порты внутри кластера для общения приложений между собой.
  • LoadBalancerIP – выделенный внешний (белый) IP адрес для сервиса. Располагается на балансировщике нагрузок, расположенным перед нодами k8s кластера.

К примеру реверсивный proxy-сервер в вашем кластере может иметь тип LoadBalancerIP и висеть на выделенном IP адресе, обращаясь к другим сервисам, которые имеют тип ClusterIP. Такой proxy-сервер в k8s называется Ingress, о нем будет ниже.

Все! Deployment + Service – нам этого достаточно, чтобы запустить наше приложение в kubernetes!

Принцип работы Kubernetes

Вот упрощенно как будет выглядеть работа приложения в k8s:

Kubernetes для начинающих, быстрый старт с нуля
Kubernetes для начинающих, быстрый старт с нуля

Тут app: web – наше приложение, запущенное в единственном экземпляре. И какое-то другое (app: other) – масштабируемое. У них разные сервисы и разные метки. По этому прокси сервер при обращении к определенному порту (или имени, в случаи если мы используем Ingress) – будет переправлять нас на соответствующее приложение внутри k8s.

Ingress

Ingress – это http прокси сервер k8s, позволяющий обращаться к ресурсам по именам. Вот пример описания ingress-сервиса:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: web-ingress
spec:
  rules:
    - host: www.burlutsky.su
      http:
        paths:
          - path: /
            backend:
              serviceName: web-page
              servicePort: http

По указанным правилам (имя хоста и путь) – прокси сервер выберет соответствующий сервис: serviceName: web-page, который был описан нами ранее.

ЗАМЕТКА: Тип нашему сервису (Service) web-page, в данном случаи, лучше поставить ClusterIP, убрав директиву nodePort, чтобы не вывешивать его прямо на ноде.

Helm

Все это конечно хорошо, но когда же мы приступим к практике? С чего начать? Я предлагаю сразу в бой! Но для этого нам понадобится шлем (Helm).

Helm
Helm

Чтобы не составлять все эти yaml – файлы с описанием вручную, не искать их по интернету – мы будем использовать готовые шаблоны (templates).

Дело в том, что работать напрямую с yaml-файлами kubernetes не очень удобно. Во-первых их надо много (чтобы описать все возможные ресурсы). Во-вторых, чтобы что-то изменить – надо искать и править эти файлы. Такой подход не приемлем для множественных установок одного и того же приложения, но с разными параметрами (именами, настройками, логинами, паролями и пр.).

Одним из самых популярных инструментов шаблонизации yaml-файлов для k8s на сегодня является Helm.

Вышеприведенный yaml-файл на нем выглядел бы как-то так:


{{- $fullName := include "fullname" . -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: {{ $fullName }}-ingress
spec:
  rules:
    - host: {{ .Values.hostname }}
      http:
        paths:
          - path: /
            backend:
              serviceName: {{ $fullName }}
              servicePort: http

Тут вы видите, что изменяемые данные заменены на переменные:

  • .Values.hostname – переменная из специального values.yaml – файла. Записывается в файле так:
hostname: www.burlutsky.su

После чего становится доступна, как .Values.hostname. То есть мы вынесли уже статичные данные из yaml-файла и сделали его шаблоном, пригодным для применения с другими данными. Ничего не надо править – только values.yaml .

  • $fullName – какая-то дополнительная переменная. В данном случаи обозначает имя нашего приложения. Для таких вспомогательных переменных служит специальный файл: _helpers.tpl.

Все файлы шаблонов находятся в директории templates. Посмотрите любой удобный для вас пример от сюда: https://github.com/helm/charts/tree/master/stable

Найдите в выбранном вами примере шаблоны описания Deployment (или StatefulSet), Service, Ingress. Как правило они располагаются в соответствующих файлах директории templates: deployment.yamlservices.yaml и т.п.

Установка приложения с Helm сводится к правке values.yaml-файла, или созданию нового my-values.yaml – файла, с нужными вам значениями переменных, а затем использованию клиента helm для установки данного набора шаблонов в k8s:

helm install -f my-values.yaml stable/postgresql

Данная команда возьмет переменные из вашего my-values.yaml – файла, совместит их с другими переменными values.yaml – файла, идущего в поставке. После чего на их основе сгенерирует конечные yaml-файлы и применит их в k8s.

Полезные команды

  • kubectl get pods – получить все запущенные pod
  • kubectl describe pod <name> – получить детальные описания pod
  • kubectl get services – получить все сервисы
  • kubectl get deployments – получить все Deployment
  • helm list – получить все установленные пакеты приложений (релизы)

Minikube

Для старта работы с k8s вы можете использовать minikube, который позволяет получить окружение kubernetes у вас на компьютере или в виртуальной машине: https://github.com/kubernetes/minikube

источник