воскресенье, 7 мая 2023 г.

Памятка по Docker и Kuber

Docker

Docker - команды

Установка
sudo apt install docker.io
sudo chmod 666 /var/run/docker.sock

список образов (классов):
docker images

список активных контейнеров (объектов)
docker ps -a
-a - историй запусков

запуск образа:
docker run py_app:v0.1
Ключи
-d (--detach) - запускает в фоновом режиме контейнер и выводит в консоль ID-контейнера
-p (--publish) - открывает порт(-ы) для докер-контейнера на хосте
-p 8080:8080 - диапазон портов для которых открывают работать приложение на хосте по тегу, что создавали ранее
-rm - выполнить образ и удалить (удобно для скачиваемых)
-it - интерактивный запуск (перенаправление stdin/out из контейнера в родительскую ОС)
docker run --rm --name stepik-task -it ubuntu:14.04

подключение командной строкой к уже работающему приложению
docker exec -it container-name bash

удаление:
rmi - удалить образ (класс)
rm - удалить контейнер (объект)

Задание тэга для образа:
docker tag go:v0.2 pihel/lesson1:v0.2
* загрузка в докехаб:
docker push pihel/lesson1:v0.2
* загрузка в yandex registry
yc container registry create --name pihel-registry #create and gets ID 123
yc container registry configure-docker
docker tag fastapi cr.yandex/123/fastapi:v0.0.1
docker push cr.yandex/123/fastapi:v0.0.1

Создание образа (класса) из контейнера (объекта)
docker commit название_старое новое
(сохранятся команды изменения, но не точка входа)

слои образа (изменения которые вносились)
docker history
идеалогия copy on write - изменения создают новый слой, оригинальные данные остаются в предыдущем слое (страдает производительность при чтении)

примонтировать папку из родительского хоста:
--mount type=bind,source=/tmp,target=/usr
-v /tmp:/tmp

хранение данных докера в файле на хосте (место определяем докер)
docker volume create my-vol
монтирование -v tratata:/tratata

проброс портов:

-p <port_on_host_machine>:<port_inside_container>
docker run -rm -p 80:80 parseq/stepik-ports-docker

взаимодействие контейнеров через сеть bridge
все контейнеры попадают в 1 сеть bridge и могут взаимодействовать через ип адреса в этой сети

Создание Dockerfile

собираем образ автоматически:
docker build . -t py_app:v0.1
в образ попадают все файлы рядом с Dockerfile

Делаем dockerfile вручную:
vim Dockerfile

FROM python:3.6-alpine3.8                     # Скачиваем легковесный образ python-alpine
COPY 1.py /app/                               # Копируем 1.py с компьютера в дирректорию /app образа (делается 1 раз при сборка)
WORKDIR /app                                  # Делаем /app рабочей дирректорией для след. команды
ENTRYPOINT ["python3", "/app/1.py"]           # Говорим "Питон3, запусти приложение /app/1.py"

операции RUN, COPY, ADD - создает новый слой
по этому несколько однотипных комманд объединяем через &&
изменение слоя вначале приводит к пересборке всех нижних, по этому самое редко используемое должно быть вначале

Копирование образа builder в собираемый:
COPY --from=builder ./app .              # Копируем собранное приложение из образа biulder

Kuber

Установка minikube

minikube - для тестовых локальных запусков
minikube start --vm-driver=none
sudo apt-get install -y conntrack
sudo apt-get install -y go
Дополнительная инструкция по установке minukube

Создание pod из файла

pod - минимальная абстракция для управления кубером
Чаще всего в одном Поде находится один Контейнер
Контейнеры в одном Поде могут обмениваться файлами через общий том (volume).
Контейнеры в одном Поде только на одной Ноде

минимальное описание пода:
#pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  namespace: lesson14
  labels:
    app: goweb #помечает под для перенаправления трафика на него (у нескольких подов может быть 1 label)
spec:
  containers:
    - name: web #имя контейнера в поде
      image: pihel/lesson1:v0.2 #образ для контейнера
      ports:
        - name: web
          containerPort: 8080
          protocol: TCP

Команды kubectl

- Запуск minikube:
minikube start --vm-driver=docker
minikube status
- установка утилиты kubectl и привязка ее к аккаунту яндекса
yc managed-kubernetes cluster get-credentials --id *** --external
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
- установка yaml:
kubectl apply -f pod.yaml
- список куберов
kubectl config get-contexts  
- текущий кубер
kubectl config current-context
- Namespace - логическое разделение ресурсов (аналог - папка) - создание namespace
#ns.yaml
apiVersion: v1
kind: Namespace
metadata:
    name: examples-dev
- список namespaces
kubectl get ns
- список подов в NS
kubectl -n NS get pods 
- проброс порта на лольную машину для портов <1000 нужно sudo -E
sudo -E kubectl port-forward -n fastapi fastapi 80:80
- получить информацию на какой ноде находится под:
kubectl get pod -n lesson14 -o wide
- подробное описание пода
kubectl describe pod -n lesson14
- логи пода:
kubectl logs -f -n kube-system etcd-minikube
- заход в командную строку пода:
kubectl exec -it -n fastapi fastapi -- /bin/bash
- информация о загрузке подов:
kubectl top pods -n fastapi
#NAME      CPU(cores)   MEMORY(bytes)   
#fastapi   2m           54Mi 
- удалить под
kubectl delete -n fastapi pod fastapi --now

Стратегии автоматизации работы

- Deployment - поддержание подов в нужном состоянии
- 3 реплики приложения:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goapp-deployment
  namespace: web-app-stage
  labels:
    app: goapp
spec:
  replicas: 3 #описание реплик
  selector:
    matchLabels:
      app: goapp
  template: #описание подов
    metadata:
      labels:
        app: goapp
    spec:
      containers:
      - name: web
        image: ksxack/lesson1:v0.2 ## Используйте свой Образ
        ports:
        - containerPort: 8080
- плавное обновление
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1  # Не более одного недоступного Пода во время обновления
      maxSurge: 1        # Одновременно можно создавать один новый Под
- DaemonSet - установка пода на отдельную ноду
- CronJob - запуск пода по расписанию
- StatefulSet - гарантия постоянного сетевого адреса, хранилища и порядка деплоя (для баз / файловых хранилищ и т.д.)
- Job - одноразовая задача: создаст и убъет за собой поду
apiVersion: batch/v1
kind: Job
metadata:
    name: pi-job
spec:
    template:
    spec:
        containers:
        - name: pi
        image: python:3.9
        command: ["python", "-c", "from math import pi; print(f'{pi:.20f}')"]
        restartPolicy: Never
    backoffLimit: 4
- список деплойментов
kubectl -n fastapi get deployment
- описание
kubectl -n fastapi describe deployment goapp-deployment
- изменение числа реплик
kubectl -n fastapi scale deployment goapp-deployment --replicas 5

Статичные адреса

- проброс портов между подами:
apiVersion: v1
kind: Service
metadata:
  namespace: fastapi
  name: fastapi-svc
spec:
  selector:
    app: fastapi
  ports:
    - protocol: TCP
      port: 80 ## порт, который слушает Сервис
      targetPort: 80 ## порт Пода, на который Сервис направляет запросы
с помощью service у облачных провайдеров можно сделать статичный ип (отдельный на каждое приложение) и через него организовать балансер

- ingress - маршрутизатор внешних запросов на сервисы Kuber.
В ЯО нужно предварительно поставить Ingress Nginx через Marketplace
Будет один общий ip/dns для всех сервисов.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: fastapi
spec:
  ingressClassName: nginx
  rules:
    - host: fastapi.example
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: fastapi-svc
                port:
                  number: 80
в ЯО неявно создаст load balancer - просмотр параметров
kubectl -n fastapi get ingress
# NAME              CLASS   HOSTS             ADDRESS        PORTS   AGE
# example-ingress   nginx   fastapi.example   51.250.43.78   80      8m2s

Скалирование от нагрузки

горизонтальное скалирование от загрузки цпу и ограничение ресурсов:
requests - минимально доступное число ресурсов
limits - максимум ресурсов
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goapp-deployment
  namespace: demo-ingress
  labels:
    app: goapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: goapp
  template:
    metadata:
      labels:
        app: goapp
    spec:
      containers:
      - name: web
        image: ksxack/lesson1:v0.2
        ports:
        - containerPort: 8080
        resources:
          requests: #минимально доступное число ресурсов
            memory: "100Mi"
            cpu: "200m"
          limits: #максимум
            memory: "150Mi"
            cpu: "300m"
классы подов в зависимости от ограничеий:
- Guaranteed - requests = limits
- Burstable - requests < limits - можно расширять
- Best Effort - requests / limits не указаны, будет работать, если есть ресурсы (низкий приоритет)

Пробы

- livenessProbe - Health чеки, что приложение живо
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: my-pod
  name: my-pod-http
spec:
  containers:
  - name: containername
    image: k8s.gcr.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 3 #секунд между стартом и 1 пробой
      periodSeconds: 2 #периодичность проверки
failureThreshold: количество повторных Проверок перед рестартом. По умолчанию 3
timeoutSeconds: Количество секунд ожидания ответа на Проверке. По умолчанию 1 секунда.
successThreshold: Минимальное количество последовательных проверок, чтобы проба считалась успешной после неудачной. По умолчанию 1.

- проверка загруженности пода, если проверка не проходит, то запрос отправится на другую поду
readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5

- проба, что приложение стартовало, если initialDelaySeconds недостаточно
startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

Передача переменных в pod

- конфигурации
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: demo-cm
data:
  LOG_LEVEL: "debug"
  DATABASE_URL: "postgresql://user:password@db:5432/mydatabase"

- создание переменных окружения из конфиг маппы
spec:
    containers:
    - name: app
    image: busybox
    command: ["/bin/sh", "-c", "echo \"Log Level: $LOG_LEVEL, Database URL: $DATABASE_URL\" && sleep 3600"]
    envFrom:
    - configMapRef:
        name: env-config

Helm

- шаблонизация kuber манифестов
env:
  - name: {{ .Values.secret.name }}
    valueFrom:
      secretKeyRef:
        name: {{ .Values.secret.name }}
        key: password
Задание значени в values.yaml
secret:
  name: load-secret
  password: loadqwerty
Применение значений
helm install my-helm-release  test-chart -n tst-namespace -f test-chart/values.yaml

Rest API

Раздел будет дополняться.

Практический пример

Сухая теория плохо запоминается, по этому попробуем создать питон приложение и развернуть его в kuber

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

Простейшее FastApi приложение
#main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}


@app.get('/calculate')
async def calculate(num1: int, num2: int):
    return {f'sum of numbers {num1} and {num2} is ': f'{num1+num2}'}

Docker образ программы

Dockerfile с питоном и нашим приложением. Точка входа - запуск приложения.
FROM python:3.12-slim

RUN pip install fastapi[all]

WORKDIR /app

COPY ./main.py /app/

EXPOSE 8000

CMD ["fastapi", "run", "/app/main.py", "--port", "80"]
Собираем Image
docker build -t fastapi .
Можем сразу его запустить, пробросить порты и проверить, что при обращении к localhost возвращается {"message": "Hello World"}
docker run -d --name fastapi -p 80:80 fastapi

Загрузка Docker образа в Яндекс

Создаем Docker registry, загружаем туда образ и проверяем работу
#создаем registry
yc container registry create --name pihel-registry
yc container registry configure-docker
#задаем тег
docker tag fastapi cr.yandex/***/fastapi:v0.0.1
#загружаем в registry
docker push cr.yandex/***/fastapi:v0.0.1
#проверяем, что образ запускается из удаленного registry
docker run --rm -p 80:80 cr.yandex/***/fastapi:v0.0.1

Установка Kuber

- Ставим managed kuber в ЯО, все параметры можно оставить поумолчанию.
- Kuber создастся без узлов, по этому заходим в "Управление узлами" и создаем группу из 3 минимальных узлов.
- Ставим и привязываем kubectl
yc managed-kubernetes cluster get-credentials --id *** --external
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

Создание pod

- Сначала создадим namespace
#ns.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: fastapi

kubectl apply -f ns.yaml
- Эмулируем некоторые настроечные параметры, которые будут использоваться в env
#conf.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config-test
  namespace: fastapi
data:
  TEST_PARAM: "test_value"

kubectl apply -f conf.yaml
- Pod с нашим приложением в 2 экземплярах
#depl.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-deployment
  namespace: fastapi
  labels:
    app: fastapi
spec:
  replicas: 2 #2 реплики для отказоустойсивости
  selector:
    matchLabels:
      app: fastapi
  template:
    metadata:
      labels:
        app: fastapi
    spec:
      containers:
      - name: fastapi
        image: cr.yandex/***/fastapi:v0.0.1 #наш образ с приложением 
        ports:
        - containerPort: 80 #открыт 80 порт
        resources: #ограничения на ресурсы
          requests:
            memory: "100Mi"
            cpu: "200m"
          limits:
            memory: "150Mi"
            cpu: "300m"
        envFrom: #переменные окружения из конфига
        - configMapRef:
            name: env-config-test
- проверяем что все запустилось
#деплоим
kubectl apply -f depl.yaml

#работающие Поды
kubectl -n fastapi get pods 
#NAME                                 READY   STATUS    RESTARTS   AGE
#fastapi-deployment-***-2p7s2   1/1     Running   0          11m
#fastapi-deployment-***-ppfgw   1/1     Running   0          11m
- пробросим порты и проверим работу в браузере:
#для портов <1000 нужно sudo -E
sudo -E kubectl port-forward  -n fastapi fastapi-deployment-*** 80:80

curl 127.0.0.1
#{"message":"Hello World"}
- проверим, что конфигурационные параметры видны в ENV
kubectl exec -it -n fastapi fastapi-deployment-*** -- sh -c 'echo $TEST_PARAM'
# test_value

Настройка Ingress

Настроим Ingress, чтобы иметь едную точку входа для всех приложений и чтобы была балансировка нагрзуки между 2 подами запущенных приложений.
В ЯО нужно предварительно поставить Ingress Nginx через Marketplace
- Ставим service для проброса портов
apiVersion: v1
kind: Service
metadata:
  namespace: fastapi
  name: fastapi-svc
spec:
  selector:
    app: fastapi
  ports:
    - protocol: TCP
      port: 80 ## порт, который слушает Сервис
      targetPort: 80 ## порт Пода, на который Сервис направляет запросы
- деплоим настройки ingress, чтобы он смотрел на наш сервис по префиксу /
#ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: fastapi
spec:
  ingressClassName: nginx
  rules:
    - host: fastapi.example
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: fastapi-svc
                port:
                  number: 80
с деплоем неявно создастся балансировщик на 2 наши поды.
- информация о ingress endpoint:
kubectl -n fastapi get ingress
#NAME              CLASS   HOSTS             ADDRESS        PORTS   AGE
#example-ingress   nginx   fastapi.example   51.250.43.78   80      8m2s
- Добавляем ip эндпойнта в hosts и проверяем, что обращение к хосту по днс работает
cat /etc/hosts | grep fastapi
# 51.250.43.78 fastapi.example

curl fastapi.example
# {"message":"Hello World"}

Комментариев нет:

Отправить комментарий