Kubernetes. Deployment. Развертывание и связывание приложений NGINX+PHP в отдельных POD
Описание
- Развернем 2 приложения в среде Kubernetes через Deployment: PHP и NGINX
- Приложения должны видеть друг друга внутри частной сети Kubernetes по меткам app=php,tier=backend и app=nginx,tier=backend
- Доступ для пользователей по порту 80/HTTP к приложению NGINX организуем через "внешний" маршрутизируемый адрес 192.168.199.184 (локальный адрес хоста Kubernetes-Master)
Ссылки
Эта статья - близкий пересказ статьи по ссылке выше с необходимыми добавлениями для адаптации к нашей задаче. В статье расписано всё настолько подробно и понятно что у меня рука не поднялась менять изложение мыслей её уважаемого автора. Однако я решил сократить некоторые моменты, которые, как мне кажется, будут лишними для нашей задачи.
Настройка
Создание служб 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