Kubernetes. Deployment. Развертывание и связывание приложений NGINX+PHP в отдельных POD

Материал из Wiki
Перейти к навигации Перейти к поиску

Описание

  • Развернем 2 приложения в среде Kubernetes через Deployment: PHP и NGINX
  • Приложения должны видеть друг друга внутри частной сети Kubernetes по меткам app=php,tier=backend и app=nginx,tier=backend
  • Доступ для пользователей по порту 80/HTTP к приложению NGINX организуем через "внешний" маршрутизируемый адрес 192.168.199.184 (локальный адрес хоста Kubernetes-Master)

Ссылки

https://www.digitalocean.com/community/tutorials/how-to-deploy-a-php-application-with-kubernetes-on-ubuntu-16-04-ru

Эта статья - близкий пересказ статьи по ссылке выше с необходимыми добавлениями для адаптации к нашей задаче. В статье расписано всё настолько подробно и понятно что у меня рука не поднялась менять изложение мыслей её уважаемого автора. Однако я решил сократить некоторые моменты, которые, как мне кажется, будут лишними для нашей задачи.

Настройка

Создание служб PHP-FPM и Nginx

PHP-FPM

Служба позволяет получать доступ к набору подов внутри кластера. Службы внутри кластера могут напрямую взаимодействовать, используя только имена без необходимости в IP-адресах. Служба PHP-FPM позволяет получать доступ к подам PHP-FPM, а служба Nginx - доступ к подам Nginx.

Поскольку поды Nginx будут проксировать поды PHP-FPM, вам нужно показать службе, как их найти. Вместо IP-адресов вы сможете использовать преимущества автоматической службы обнаружения Kubernetes для использования человекочитаемых имен для перенаправления запросов на соответствующую службу.

Чтобы создать службу, вы создадите файл определения объекта. Каждое определение объекта Kubernetes — это файл YAML, содержащий по крайней мере следующие элементы:

  •    - apiVersion: версия API Kubernetes, к которой принадлежит определение.
  •    - kind: объект Kubernetes, который представляет этот файл. Например, pod или service.
  •    - metadata: здесь содержится name объекта наряду с labels, который вы можете захотеть применить к объекту.
  •    - spec: этот элемент содержит конкретную конфигурацию в зависимости от вида создаваемого объекта, например, образ контейнера или порты, через которые контейнер будет доступен.

Сначала мы создадим директорию для хранения определений объекта Kubernetes

mkdir definitions

cd definitions

Создайте службу PHP-FPM с помощью создания файла php_service.yaml:

  • Используйте метку tier: backend для привязки пода к уровню бекэнда. Затем вы должны добавить метку app: php, чтобы указать, что этот под запускает PHP
  • Служба определяет, какие поды следует подключить, с помощью меток selector. Под, соответствующий этим меткам, будет обслуживаться независимо от того, был ли под создан до или после службы
  • Укажите порт, используемый для доступа к службе PHP-FPM: 9000/TCP

nano php_service.yaml

apiVersion: v1
kind: Service
metadata:
  name: php
  labels:
    tier: backend
spec:
  selector:
    app: php
    tier: backend
  ports:
  - protocol: TCP
    port: 9000

Применить

kubectl apply -f php_service.yaml

Проверка

kubectl get svc

    ...
    NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    10m
    php          ClusterIP   10.100.59.238   <none>        9000/TCP   5m
    ...

Существуют различные типы служб, поддерживаемые Kubernetes. Служба php использует тип службы по умолчанию, ClusterIP. Данный тип службы назначает внутренний IP-адрес и делает службу доступной только внутри кластера.

NGINX

Данная служба будет затрагивать поды Nginx, поэтому вы должны будете назвать ее nginx. Затем вы должны будете добавить метку tier: backend, потому что она принадлежит к уровню бекэнда.

Как и в случае со службой php, настройте работу с подами с помощью меток selector — app: nginx и tier: backend. Cделайте эту службу доступной для порта 80, порта HTTP по умолчанию.

ClusterIP Service

Служба Nginx будет доступна из Интернета (конечно, нет, но в нашей задаче подойдет и локальный адрес машины kub-master-1) при использовании вашего открытого IP-адреса: 192.168.199.184 - статический локальный IP машины kub-master-1, для внешнего доступа к приложению

nano nginx_service.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    tier: backend
spec:
  selector:
    app: nginx
    tier: backend
  ports:
  - protocol: TCP
    port: 80
  externalIPs:
  - 192.168.199.184

Применить

kubectl apply -f nginx_service.yaml

Проверка

kubectl get svc

...
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP        PORT(S)    AGE
nginx        ClusterIP   10.102.160.47   192.168.199.184    80/TCP     50s
...

NodePort Service

Служба Nginx будет доступна из Интернета при использовании вашего открытого IP-адреса любого Worker, на котором запущен Pod на порте 30100 (далее можно настроить балансировщик, который будет смотреть на IP всех Worker'ов (на порт 30100), на которых запущен POD NGINX)

!!! Важное замечание

Вы не сможете увидеть listen port 30100 на воркерах используя netstat -luptn

- When a NodePort service is created in a cluster of v1. 21.7 or later, the port on the node is not displayed using netstat by default. If the cluster forwarding mode is iptables, run the iptables -t nat -L command to view the port.

Тем не менее можно увидеть что порт открыт если запустить команду на произвольном хосте, с которого имеется сетевой доступ до воркера (НЕ на хосте-воркере, на котором открыт порт):

nmap <IP_воркера_на_котором_открыт_порт_30100> -p30100

nano nginx_service.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    tier: backend
spec:
  selector:
    app: nginx
    tier: backend
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30100
  type: NodePort

kubectl get services

...
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx        NodePort    10.101.45.122   <none>        80:30100/TCP   57m
...

Создание постоянного тома (PV - Persistent Volume)

Описание

Постоянный том (Persistent Volume, PV) — это блочное хранилище заданного размера, которое существует независимо от жизненного цикла пода. Использование постоянного тома позволит вам управлять или обновлять поды, не беспокоясь о потере кода приложения. Доступ к постоянному тому можно получить с помощью PersistentVolumeClaim, или PVC, который монтирует постоянный том по требуемому пути.

Nginx и PHP-FPM требуется доступ к файлу с одним путем, поэтому вы должны смонтировать том code в /code на обоих POD - Nginx и PHP-FPM

В данной статье мы смонтируем PV, который создадим на сетевом ресурсе SMB

Установка CSI-драйвера для Samba

Для установки драйвера нужен пакетный менеджер helm.

Скачайте пакетный менеджер helm chart и CSI-драйвер с GitHub Kubernetes CSI.

Установите последнюю версию драйвера

helm repo add csi-driver-smb https://raw.githubusercontent.com/kubernetes-csi/csi-driver-smb/master/charts

helm install csi-driver-smb csi-driver-smb/csi-driver-smb --namespace kube-system --version v1.4.0

Проверьте, что поды установлены и запущены:

kubectl --namespace=kube-system get pods --selector="app=csi-smb-controller"

Создание секрета

Создайте секрет для хранения логина и пароля для ресурса SMB (по умолчанию guest/guest)

kubectl create secret generic smbcreds --from-literal username=user1 --from-literal password=PassWord

Создать манифест для StorageClass

nano ./sfs_storage_class.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: smb
provisioner: smb.csi.k8s.io
parameters:
  source: "//192.168.1.1/share/code"
  csi.storage.k8s.io/provisioner-secret-name: "smbcreds"
  csi.storage.k8s.io/provisioner-secret-namespace: "default"
  csi.storage.k8s.io/node-stage-secret-name: "smbcreds"
  csi.storage.k8s.io/node-stage-secret-namespace: "default"
reclaimPolicy: Delete
volumeBindingMode: Immediate
mountOptions:
  - dir_mode=0777
  - file_mode=0777

Примените манифест

kubectl apply -f ./sfs_storage_class.yaml

Проверьте, что StorageClass создался

kubectl get storageclass

Создать манифест для PersistentVolumeClaim

storage: 10Gi — размер файлового хранилища

nano ./sfs_persistent_volume_claim.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: code
  annotations:
    volume.beta.kubernetes.io/storage-class: smb
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 10Gi

Примените манифест в кластер

kubectl apply -f ./sfs_persistent_volume_claim.yaml

Проверьте, что PersistentVolumeClaim создался

kubectl get pvc

    ...
    NAME   STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    code   Bound    pvc-bf00cd44-2c4e-4be0-a588-277c9ff62307   1Gi        RWX            smb            66m
    ...

kubectl get pv

    ...
    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE
    pvc-bf00cd44-2c4e-4be0-a588-277c9ff62307   1Gi        RWX            Delete           Bound    default/code   smb                     67m
    ...

Смотрим что на шаре создалась директория

\\192.168.199.174\share\code\pvc-480016f0-1351-42c9-ba4e-146397c7cd1d\

Далее мы подключим этот PVC "code", на котором будет храниться код приложения NGINX, к поду

Создание Deployment (Развертывания) PHP-FPM

На этом шаге мы используем Deployment (Развертывание) для создания пода PHP-FPM. Развертывания предоставляют единообразный способ создания, обновления подов и управления ими с помощью ReplicaSets. Если обновление не работает ожидаемым образом, Deployment будет автоматически возвращать свои поды к предыдущему образу.

Ключ для развертывания spec.selector будет содержать список меток подов, которыми будет управлять. Также мы будем использовать ключ template для создания требуемых подов.

Кроме того, на этом шаге мы познакомимся с использованием Init-контейнеров. Init-контейнеры запускают одну или несколько команд, прежде чем будут запущены обычные контейнеры, указанные в ключе template пода. В этом обучающем руководстве ваш Init-контейнер будет получать пример файла index.php из GitHub Gist с помощью wget. Вот как выглядит содержимое примера файла:

index.php

<?php
echo phpinfo();

Это развертывание будет управлять вашими подами PHP-FPM, поэтому вы должны использовать для объекта Deployment имя php. Поды принадлежат к уровню бекэнда, поэтому вы должны объединить развертывание в эту группу с помощью метки tier: backend

Для элемента spec развертывания вы должны будете указать, сколько экземпляров этого пода нужно создать, используя параметр replicas. Значение параметра replicas будет варьироваться в зависимости от ваших потребностей и доступных ресурсов. В этом обучающем руководстве мы создадим одну реплику

Это развертывание будет управлять подами с метками app: php и tier: backend. Опции - рядом с ключом selector

Далее для элемента spec необходимо задать значение template для определения объекта вашего пода. Этот шаблон определяет спецификации, на основании которых создается под. Во-первых, вам нужно добавить метки, которые были указаны для selectors службы php и matchLabels развертывания. Добавьте app: php и tier: backend в template.metadata.labels:

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

Во-первых, укажите тома, к которым будут подключены ваши контейнеры. Вы создали PVC с именем code для хранения кода приложения, поэтому вы должны присвоить этому тому аналогичное имя code. В разделе spec.template.spec.volumes

Затем укажите контейнер, который вы хотите запустить в этом поде. Вы можете найти различные образы в магазине Docker, но в этом руководстве мы будем использовать образ registry.domain123.ru:5000/php54-fpm:latest из приватного Docker Registry используя для авторизации imagePullSecrets.name: registry.domain123.ru.5000

Теперь нужно смонтировать тома, к которым контейнеру требуется доступ. Этот контейнер будет запускать ваш код PHP, поэтому ему потребуется доступ к тому code. Также вы будете использовать mountPath, чтобы задать /code в качестве точки для монтирования.

Теперь, когда вы смонтировали ваш том, вам нужно поместить код приложения в том. Возможно, вы ранее использовали FTP/SFTP или клонировали код через соединение SSH, чтобы добиться этого, но на данном этапе мы покажем, как скопировать код с помощью Init-контейнера.

В зависимости от сложности процесса настройки вы можете использовать либо один initContainer для запуска скрипта, который собирает ваше приложение, либо один initContainer для каждой команды. Убедитесь, что тома монтируются в initContainer.

В этом руководстве вы будете использовать один Init-контейнер с busybox для загрузки кода. busybox — это небольшой образ, содержащий утилиту wget, которую вы будете использовать для этой цели.

В spec.template.spec добавьте ваш initContainer и укажите образ busybox

Вашему Init-контейнеру потребуется доступ к тому code, чтобы выполнить загрузку кода в это место. В spec.template.spec.initContainers смонтируйте том code на путь /code

Для каждого Init-контейнера необходимо запустить command. Ваш Init-контейнер будет использовать wget для загрузки кода с Github в рабочую директорию /code. Параметр -O предоставляет загруженному файлу имя, и вы должны будете назвать этот файл index.php

nano php_deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php
  labels:
    tier: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: php
      tier: backend
  template:
    metadata:
      labels:
        app: php
        tier: backend
    spec:
      volumes:
      - name: code
        persistentVolumeClaim:
          claimName: code
      containers:
      - name: php
        image: registry.domain123.ru:5000/php54-fpm:latest
        volumeMounts:
        - name: code
          mountPath: /code
      imagePullSecrets:
        - name: registry.domain123.ru.5000
      initContainers:
      - name: install
        image: busybox
        volumeMounts:
        - name: code
          mountPath: /code
        command:
        - wget
        - "-O"
        - "/code/index.php"
        - https://raw.githubusercontent.com/do-community/php-kubernetes/master/index.php

Применяем

kubectl apply -f php_deployment.yaml

Проверка

kubectl get pods

    ...
    php-848d5d956-cpxrx                            1/1     Running   0          14h   172.16.213.88     kub-node-2   <none>           <none>
    php-848d5d956-shfll                            1/1     Running   0          14h   172.16.213.180    kub-node-1   <none>           <none>
    php-848d5d956-zbwgk                            1/1     Running   0          14h   172.16.213.212    kub-node-3   <none>           <none>
    ...

Создание абстракции configMap для подключения конфига к NGINX

ConfigMap хранит вашу конфигурацию в формате ключ-значение, и вы можете ссылаться на нее в других определениях объектов Kubernetes. Такой подход будет предоставлять возможность повторного использования образа или переключения образа на другую версию Nginx в случае необходимости. Обновление ConfigMap автоматически будет воспроизводить изменения для любого пода, который его монтирует.

  • - Поскольку Kubernetes может перенаправлять запросы на соответствующий хост для службы, вы можете ввести имя службы PHP-FPM для параметра fastcgi_pass вместо его IP-адреса (fastcgi_pass php:9000;)
  • - root /code; - путь до кода внутри контейнера

nano nginx_configMap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  labels:
    tier: backend
data:
  config : |
    server {
      index index.php index.html;
      error_log  /var/log/nginx/error.log;
      access_log /var/log/nginx/access.log;
      root /code;

      location / {
          try_files $uri $uri/ /index.php?$query_string;
      }

      location ~ \.php$ {
          try_files $uri =404;
          fastcgi_split_path_info ^(.+\.php)(/.+)$;
          fastcgi_pass php:9000;
          fastcgi_index index.php;
          include fastcgi_params;
          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
          fastcgi_param PATH_INFO $fastcgi_path_info;
        }
    }

Применяем

kubectl apply -f nginx_configMap.yaml

Проверка

kubectl get configmaps

    ...
    NAME               DATA   AGE
    nginx-config       1      49m
    ...

Создание Deployment (Развертывания) NGINX

Это развертывание будет управлять подами с метками app:nginx и tier:backend

Затем добавьте шаблон пода. Вам нужно использовать те же метки, которые вы добавили для развертывания selector.matchLabels

Предоставьте Nginx доступ к code PVC, который вы создали ранее. В spec.template.spec.volumes

Поды могут монтировать ConfigMap в качестве тома. При указании имени файла и ключа будет создан файл с содержимым в виде его значения. Для использования ConfigMap настройте в качестве path имя файла, где будет храниться содержимое key. Вам нужно создать файл site.conf из ключа config. В разделе spec.template.spec.volumes

!!! Предупреждение: если файл не указан, содержимое key будет заменять mountPath тома. Это означает, что если путь не был явно указан, вы потеряете все содержимое в папке назначения.

Образ nginx будем выкачивать из приватного Docker Registry registry.domain123.ru:5000/nginx179:latest используя secret imagePullSecrets.name: registry.domain123.ru.5000

Образ nginx179 будет автоматически загружать любые файлы конфигурации в директорию /etc/nginx/conf.d. При монтировании тома config в этой директории будет создан файл /etc/nginx/conf.d/site.conf. В volumeMounts

Nginx и PHP-FPM требуется доступ к файлу с одним путем, поэтому вы должны смонтировать том code в /code

nano nginx_deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    tier: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
      tier: backend
  template:
    metadata:
      labels:
        app: nginx
        tier: backend
    spec:
      volumes:
      - name: code
        persistentVolumeClaim:
          claimName: code
      - name: config
        configMap:
          name: nginx-config
          items:
          - key: config
            path: site.conf
      containers:
      - name: nginx
        image: registry.domain123.ru:5000/nginx179:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: code
          mountPath: /code
        - name: config
          mountPath: /etc/nginx/conf.d
      imagePullSecrets:
        - name: registry.domain123.ru.5000

Применяем

kubectl apply -f nginx_deployment.yaml

Проверка

kubectl get pods -o wide

    ...
    NAME                                           READY   STATUS    RESTARTS   AGE    IP                NODE         NOMINATED NODE   READINESS GATES
    nginx-649fc98965-khr98                         1/1     Running   0          2m4s   172.16.213.220    kub-node-3   <none>           <none>
    nginx-649fc98965-r5dd4                         1/1     Running   0          2m4s   172.16.213.92     kub-node-2   <none>           <none>
    nginx-649fc98965-rgqrv                         1/1     Running   0          2m4s   172.16.213.188    kub-node-1   <none>           <none>
    ...

Идем браузером по внешнему адресу сервиса NGINX (мы указывали его на этапе создания SERVICE NGINX, это локальный адрес нашего kub-master-1)

http://192.168.199.184/

Получим информацию о PHP, то есть интерпретацию phpinfo() из index.php

Таким образом, мы подключились к "внешнему" адресу 192.168.199.184, указанному в SERVICE NGINX, сервис по меткам app=php,tier=backend нашел интерпретатор PHP - это deployment PHP, интерпретировал PHP язык и вывел на страницу информацию о PHP в deployment PHP

Готово.

У нас развернуто 2 приложения deployment - PHP и NGINX, которые видят друг друга внутри частной сети Kubernetes по меткам app=php,tier=backend, app=nginx,tier=backend, а доступ для пользователей организован через "внешний" адрес 192.168.199.184