Пишу Backend на PHP более 5 лет.
В ближайшее время планирую активно погружаться в Golang.
Люблю природу и катать на велике.
А еще это первый доклад на открытую публику =)
Кто переезжает в кубер - отладка проекта
Кто думает переезжать - проверить совместимость
Кого интересует кубер и новые инструменты
Итерационно посмотрим как реализовать джентльменский набор проекта:
Хардкор и много кода на yaml
├── src
│ ├── app
│ ├── bootstrap
│ ├── config
│ ├── database
│ ├── public
│ ├── resources
│ ├── routes
│ ├── storage
│ ├── tests
│ ├── vendor
│ ├── artisan
│ ├── composer.json
│ ├── composer.lock
│ ├── .editorconfig
│ ├── .env
│ ├── .env.example
│ ├── .gitattributes
│ ├── .gitignore
│ ├── package.json
│ ├── phpunit.xml
│ ├── README.md
│ └── vite.config.js
├── .gitignore
└── README.md
kind.yaml
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
name: php-kubernetes-example
nodes:
- role: control-plane
extraMounts:
- hostPath: ./
containerPath: /project
Makefile
up:
kind create cluster --config kind.yaml
down:
kind delete cluster --name php-kubernetes-example
helm create .helm/app
.helm/app
├── charts
├── templates
│ ├── tests
│ │ └── test-connection.yaml
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ └── service.yaml
├── Chart.yaml
└── values.yaml
Оставляем нужное
.helm/app
├── templates
│ ├── deployment.yaml
│ └── _helpers.tpl
├── Chart.yaml
└── values.yaml
.helm/app/Chart.yaml
apiVersion: v2
name: php-kubernetes-example-app
version: 0.1.0
.helm/app/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include ".helm.fullname" . }}
labels:
{{- include ".helm.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
{{- include ".helm.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include ".helm.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: php
image: php-kubernetes-example/php:latest
imagePullPolicy: Never
securityContext:
runAsUser: 1000
runAsGroup: 1000
volumeMounts:
- name: source-code
mountPath: /var/www/html
volumes:
- name: source-code
hostPath:
path: /project/src
.docker/php/Dockerfile
FROM php:8.2-fpm-alpine
COPY --from=composer:2.7 /usr/bin/composer /usr/local/bin/composer
Makefile
up:
kind create cluster --config kind.yaml
down:
kind delete cluster --name php-kubernetes-example
Makefile
up: down
# Сборка контейнера
docker build . \
--file .docker/php/Dockerfile \
--tag php-kubernetes-example/php:latest
# Установка зависимостей
docker run --rm -it \
-v $$PWD/src:/var/www/html \
php-kubernetes-example/php:latest \
composer install
# Создание кластера
kind create cluster --config kind.yaml
# Загрузка контейнера
kind load \
docker-image php-kubernetes-example/php:latest \
--name php-kubernetes-example
# Запуск приложения
helm upgrade --install --wait \
php-kubernetes-example-app .helm/app
down:
kind delete cluster --name php-kubernetes-example
Проверяем работу
$ kubectl get po
NAME READY STATUS RESTARTS AGE
php-kubernetes-example-app-b7b57d858-dpp8n 1/1 Running 0 25m
$ kubectl exec -it php-kubernetes-example-app-b7b57d858-dpp8n -- sh
$ php artisan inspire
“ Well begun is half done. ”
— Aristotle
.helm/app/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
...
spec:
...
spec:
containers:
- name: nginx
image: nginxinc/nginx-unprivileged:1.25-alpine-slim
ports:
- containerPort: 8080
securityContext:
runAsUser: 101
runAsGroup: 101
volumeMounts:
- name: nginx-config-files
subPath: nginx-php.conf
mountPath: /etc/nginx/conf.d/default.conf
...
volumes:
- name: nginx-config-files
configMap:
name: {{ include ".helm.fullname" . }}
items:
- key: nginx-php.conf
path: nginx-php.conf
...
.helm/app/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include ".helm.fullname" . }}
labels:
{{- include ".helm.labels" . | nindent 4 }}
data:
nginx-php.conf: |-
server {
listen 0.0.0.0:8080;
root /var/www/html/public;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
}
}
Проверяем работу
$ kubectl get po
NAME READY STATUS RESTARTS AGE
php-kubernetes-example-app-79677db779-l9mkh 2/2 Running 0 25m
$ kubectl port-forward pods/php-kubernetes-example-app-79677db779-l9mkh 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080
.env
SESSION_DRIVER=file
#SESSION_DRIVER=database
Проверяем работу
.helm/db
.helm/db
├── templates
│ ├── _helpers.tpl
│ ├── service.yaml
│ └── statefulset.yaml
├── Chart.yaml
└── values.yaml
.helm/db/Chart.yaml
apiVersion: v2
name: php-kubernetes-example-db
version: 0.1.0
.helm/db/templates/statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include ".helm.fullname" . }}
labels:
{{- include ".helm.labels" . | nindent 4 }}
spec:
serviceName: {{ include ".helm.fullname" . }}
replicas: 1
selector:
matchLabels:
{{- include ".helm.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include ".helm.selectorLabels" . | nindent 8 }}
spec:
initContainers:
- name: permission
image: busybox
command: [ "sh", "-c", "chown 70:70 /var/lib/postgresql/data" ]
volumeMounts:
- name: pgsql-data
mountPath: /var/lib/postgresql/data
containers:
- name: pgsql
image: postgres:16.2-alpine
ports:
- containerPort: 5432
securityContext:
runAsUser: 70
runAsGroup: 70
env:
- name: POSTGRES_PASSWORD
value: 'password'
volumeMounts:
- name: pgsql-data
mountPath: /var/lib/postgresql/data
volumes:
- name: pgsql-data
hostPath:
path: /project/.docker/pgsql/data
.helm/db/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include ".helm.fullname" . }}
labels:
{{- include ".helm.labels" . | nindent 4 }}
spec:
selector:
{{- include ".helm.selectorLabels" . | nindent 4 }}
ports:
- port: 5432
targetPort: 5432
.helm/app/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
...
spec:
...
spec:
containers:
...
- name: php
...
env:
- name: DB_CONNECTION
value: 'pgsql'
- name: DB_HOST
value: 'php-kubernetes-example-db'
- name: DB_DATABASE
value: 'postgres'
- name: DB_USERNAME
value: 'postgres'
- name: DB_PASSWORD
value: 'password'
.docker/php/Dockerfile
FROM php:8.2-fpm-alpine
COPY --from=composer:2.7 /usr/bin/composer /usr/local/bin/composer
RUN apk --no-cache add libpq-dev \
&& docker-php-ext-install pdo_pgsql
.env
#SESSION_DRIVER=file
SESSION_DRIVER=database
Makefile
up: down
# Сборка контейнера
docker build . \
--file .docker/php/Dockerfile \
--tag php-kubernetes-example/php:latest
# Установка зависимостей
docker run --rm -it \
-v $$PWD/src:/var/www/html \
php-kubernetes-example/php:latest \
composer install
# Создание кластера
kind create cluster --config kind.yaml
# Загрузка контейнера
kind load \
docker-image php-kubernetes-example/php:latest \
--name php-kubernetes-example
# Запуск DB
helm upgrade --install --wait \
php-kubernetes-example-db .helm/db
# Запуск приложения
helm upgrade --install --wait \
php-kubernetes-example-app .helm/app
down:
kind delete cluster --name php-kubernetes-example
Проверяем работу
$ kubectl get po
NAME READY STATUS RESTARTS AGE
php-kubernetes-example-app-7f76bc54cc-sv2tq 2/2 Running 0 13m
php-kubernetes-example-db-0 1/1 Running 0 14m
$ kubectl exec -it php-kubernetes-example-app-7f76bc54cc-sv2tq -c php -- sh
$ php artisan migrate
INFO Preparing database.
Creating migration table ................ 15.23ms DONE
INFO Running migrations.
0001_01_01_000000_create_users_table .... 36.57ms DONE
0001_01_01_000001_create_cache_table .... 21.20ms DONE
0001_01_01_000002_create_jobs_table ..... 36.21ms DONE
.helm/app/templates/migrations.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include ".helm.fullname" . }}-migrations
labels:
{{- include ".helm.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "1"
"helm.sh/hook-delete-policy": before-hook-creation
spec:
backoffLimit: 0
activeDeadlineSeconds: 120
template:
spec:
restartPolicy: Never
containers:
- name: php
image: php-kubernetes-example/php:latest
imagePullPolicy: Never
securityContext:
runAsUser: 1000
runAsGroup: 1000
volumeMounts:
- name: source-code
mountPath: /var/www/html
env:
- name: DB_CONNECTION
value: 'pgsql'
- name: DB_HOST
value: 'php-kubernetes-example-db'
- name: DB_DATABASE
value: 'postgres'
- name: DB_USERNAME
value: 'postgres'
- name: DB_PASSWORD
value: 'password'
command: [ "php", "artisan", "migrate" ]
volumes:
- name: source-code
hostPath:
path: /project/src
Проверяем работу
$ kubectl get po
NAME READY STATUS RESTARTS AGE
php-kubernetes-example-app-6c8b44fc98-6m6np 2/2 Running 0 21s
php-kubernetes-example-app-migrations-clx8d 0/1 Completed 0 25s
php-kubernetes-example-db-0 1/1 Running 0 51s
$ kubectl logs php-kubernetes-example-app-migrations-clx8d
INFO Preparing database.
Creating migration table ................ 21.81ms DONE
INFO Running migrations.
0001_01_01_000000_create_users_table .... 34.75ms DONE
0001_01_01_000001_create_cache_table .... 15.22ms DONE
0001_01_01_000002_create_jobs_table ..... 25.33ms DONE
.helm/app/templates/_php.tpl
{{- define ".helm.php" }}
image: php-kubernetes-example/php:latest
imagePullPolicy: Never
securityContext:
runAsUser: 1000
runAsGroup: 1000
volumeMounts:
- name: source-code
mountPath: /var/www/html
env:
- name: DB_CONNECTION
value: 'pgsql'
- name: DB_HOST
value: 'php-kubernetes-example-db'
- name: DB_DATABASE
value: 'postgres'
- name: DB_USERNAME
value: 'postgres'
- name: DB_PASSWORD
value: 'password'
{{- end }}
{{- define ".helm.php_mounts" }}
- name: source-code
hostPath:
path: /project/src
{{- end }}
.helm/app/templates/cronjobs.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: {{ include ".helm.fullname" . }}-inspire
labels:
{{- include ".helm.labels" . | nindent 4 }}
spec:
schedule: "*/1 * * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: php
{{- include ".helm.php" . | indent 14 }}
command: [ "php", "artisan", "inspire" ]
volumes:
{{- include ".helm.php_mounts" . | indent 12 }}
.helm/app/templates/deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include ".helm.fullname" . }}-queue
labels:
{{- include ".helm.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
{{- include ".helm.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include ".helm.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: php
{{- include ".helm.php" . | indent 10 }}
command: [ "php", "artisan", "queue:work" ]
volumes:
{{- include ".helm.php_mounts" . | indent 8 }}
Проверяем работу
$ kubectl get po
NAME READY STATUS RESTARTS AGE
php-kubernetes-example-app-6c8b44fc98-q8glq 2/2 Running 0 104s
php-kubernetes-example-app-inspire-28585090-4qvsx 0/1 Completed 0 66s
php-kubernetes-example-app-inspire-28585091-6x82d 0/1 Completed 0 6s
php-kubernetes-example-app-queue-84595dc8dd-q24hx 1/1 Running 0 104s
php-kubernetes-example-db-0 1/1 Running 0 2m15s
$ kubectl logs php-kubernetes-example-app-queue-84595dc8dd-q24hx
2024-05-07 18:10:50 App\Jobs\Hello ............ RUNNING
RnD PHP #7 2024-05-07 18:10:50 App\Jobs\Hello .. 4.73ms DONE
2024-05-07 18:10:50 App\Jobs\Hello ............ RUNNING
RnD PHP #7 2024-05-07 18:10:50 App\Jobs\Hello .. 1.91ms DONE
2024-05-07 18:10:50 App\Jobs\Hello ............ RUNNING
RnD PHP #7 2024-05-07 18:10:50 App\Jobs\Hello .. 1.67ms DONE
$ kubectl logs php-kubernetes-example-app-inspire-28585091-6x82d
“ Smile, breathe, and go slowly. ”
— Thich Nhat Hanh
kind.yaml
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
name: php-kubernetes-example
nodes:
- role: control-plane
extraMounts:
- hostPath: ./
containerPath: /project
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
.helm/app/templates/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include ".helm.fullname" . }}
labels:
{{- include ".helm.labels" . | nindent 4 }}
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ include ".helm.fullname" . }}
port:
number: 8080
.helm/app/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include ".helm.fullname" . }}
labels:
{{- include ".helm.labels" . | nindent 4 }}
spec:
selector:
{{- include ".helm.selectorLabels" . | nindent 4 }}
ports:
- port: 8080
targetPort: 8080
.helm/app/templates/deployment.yaml
...
spec:
replicas: {{ .Values.replicaCount }}
...
.helm/app/values.yaml
replicaCount: 2
Makefile
up: down
# Сборка контейнера
docker build . \
--file .docker/php/Dockerfile \
--tag php-kubernetes-example/php:latest
# Установка зависимостей
docker run --rm -it \
-v $$PWD/src:/var/www/html \
php-kubernetes-example/php:latest \
composer install
# Создание кластера
kind create cluster --config kind.yaml
# Загрузка контейнера
kind load \
docker-image php-kubernetes-example/php:latest \
--name php-kubernetes-example
# Добавляем ingress
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
# Запуск DB
helm upgrade --install --wait \
php-kubernetes-example-db .helm/db
# Запуск приложения
helm upgrade --install --wait \
php-kubernetes-example-app .helm/app
down:
kind delete cluster --name php-kubernetes-example
Проверяем работу
$ kubectl get po
NAME READY STATUS RESTARTS AGE
php-kubernetes-example-app-6c8b44fc98-lq2dz 2/2 Running 0 73s
php-kubernetes-example-app-6c8b44fc98-t2x78 2/2 Running 0 73s
php-kubernetes-example-app-inspire-28585136-kc2cb 0/1 Completed 0 46s
php-kubernetes-example-app-queue-84595dc8dd-4wqbt 1/1 Running 0 73s
php-kubernetes-example-db-0 1/1 Running 0 119s
$ curl 127.0.0.1/api/info
{"hostname":"php-kubernetes-example-app-6c8b44fc98-lq2dz"}
$ curl 127.0.0.1/api/info
{"hostname":"php-kubernetes-example-app-6c8b44fc98-lq2dz"}
$ curl 127.0.0.1/api/info
{"hostname":"php-kubernetes-example-app-6c8b44fc98-t2x78"}
├── .helm
│ ├── app
│ │ ├── templates
│ │ │ ├── _helpers.tpl
│ │ │ ├── _php.tpl
│ │ │ ├── configmap.yaml
│ │ │ ├── cronjobs.yaml
│ │ │ ├── deployment.yaml
│ │ │ ├── ingress.yaml
│ │ │ ├── migrations.yaml
│ │ │ └── service.yaml
│ │ ├── Chart.yaml
│ │ └── values.yaml
│ └── db
│ ├── templates
│ │ ├── _helpers.tpl
│ │ ├── service.yaml
│ │ └── statefulset.yaml
│ ├── Chart.yaml
│ └── values.yaml
├── kind.yaml
└── Makefile
kubectl logs
kubectl describe
kubectl events
Сергей Буланов @ave404