Приложение
Приложения
Приложения
Приложение А. Пример NetworkPolicy для namespace lab-*
Ниже приведён пример манифеста Kubernetes NetworkPolicy, реализующего политику default-deny для учебного namespace с единственным разрешённым входящим соединением от Lab Controller (namespace ksailab).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: lab-001
spec:
podSelector: {} # применяется ко всем Pod
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ksailab
podSelector:
matchLabels:
app: lab-session-controller
ports:
- protocol: TCP
port: 8080
egress: [] # исходящий трафик запрещён
Приложение Б. Пример SecurityContext и ResourceQuota для лабораторного Pod
Ниже приведён пример блока securityContext, применяемого к лабораторным Pod-ам платформы, и ресурсной квоты namespace.
# SecurityContext
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
seccompProfile:
type: RuntimeDefault
capabilities:
drop:
- ALL
# ResourceQuota для учебного namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: lab-namespace-quota
namespace: lab-001
spec:
hard:
pods: "3"
requests.cpu: "500m"
requests.memory: "512Mi"
limits.cpu: "1000m"
limits.memory: "1Gi"
services: "1"
persistentvolumeclaims: "0"
Приложение В. Структура Helm-чарта платформы
Ниже представлена структура основного Helm-чарта ksailab и фрагмент values.yaml.
ksailab/
├── Chart.yaml # метаданные: name, version, appVersion, dependencies
├── values.yaml # параметры по умолчанию (без секретов)
├── values-prod.yaml # production-переопределения
├── templates/
│ ├── _helpers.tpl # вспомогательные шаблоны (labels, annotations)
│ ├── deployment-api.yaml # Deployment API-сервиса
│ ├── deployment-controller.yaml # Deployment Lab Session Controller
│ ├── service-api.yaml # Service для API
│ ├── ingress.yaml # Ingress с TLS
│ ├── configmap.yaml # параметры конфигурации
│ ├── serviceaccount.yaml # ServiceAccount и RBAC
│ ├── networkpolicy.yaml # NetworkPolicy для ksailab
│ ├── hpa.yaml # HorizontalPodAutoscaler
│ └── NOTES.txt # инструкция после установки
└── charts/
├── postgresql/ # зависимость: Bitnami PostgreSQL
├── redis/ # зависимость: Bitnami Redis
└── keycloak/ # зависимость: Bitnami Keycloak
# Фрагмент values.yaml
api:
replicaCount: 2
image:
repository: registry.example.com/ksailab/api
tag: "" # переопределяется при деплое из CI/CD
resources:
limits:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 8
targetCPUUtilizationPercentage: 70
labController:
sessionTTLDefault: 3600 # TTL сессии по умолчанию, секунды
maxConcurrentSessions: 50
postgresql:
auth:
existingSecret: ksailab-db-secret # Secret создаётся вне чарта
keycloak:
auth:
existingSecret: ksailab-keycloak-secret
Приложение Г. Структура GitLab CI/CD конвейера
Ниже приведён сокращённый фрагмент .gitlab-ci.yml, реализующего конвейер сборки, сканирования, подписи и доставки образа.
stages:
- lint
- test
- build
- scan
- sign
- deploy
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
lint:
stage: lint
image: node:20-alpine
script:
- npm ci && npm run lint && npm run typecheck
test:
stage: test
image: node:20-alpine
script:
- npm ci && npm run test:unit && npm run test:integration
build:
stage: build
script:
- docker build -t $IMAGE_TAG . && docker push $IMAGE_TAG
scan:
stage: scan
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE_TAG
allow_failure: false # HIGH/CRITICAL CVE блокируют конвейер
sign:
stage: sign
image: gcr.io/projectsigstore/cosign:latest
only:
- main
script:
- echo "$COSIGN_KEY" | cosign sign --key /dev/stdin $IMAGE_TAG
deploy-prod:
stage: deploy
image: alpine/helm:3.14
only:
- main
when: manual # ручное подтверждение перед деплоем в production
script:
- helm upgrade --install ksailab ./charts/ksailab
--namespace ksailab
--values values-prod.yaml
--set api.image.tag=$CI_COMMIT_SHORT_SHA
--wait --timeout 5m
Таблица Г.1 - Условия блокировки конвейера
| Условие | Стадия | Действие |
|---|---|---|
| Ошибки линтера / typecheck | lint | Прерывание конвейера |
| Провал юнит- или интеграционных тестов | test | Прерывание конвейера |
| CVE HIGH/CRITICAL в образе | scan | Прерывание конвейера |
| Нет подписи Cosign | sign | Образ не публикуется |
| Нет ручного подтверждения | deploy-prod | Деплой не выполняется |
Приложение Д. Карта маршрутов и ролевые сценарии клиентской части
Д.1 Карта маршрутов приложения
| Маршрут | Доступные роли | Описание |
|---|---|---|
/ | Все | Редирект на главную страницу по роли |
/login | Гость | Keycloak OIDC-редирект |
/student/my-labs | Student | Список назначенных лабораторных работ |
/student/session/:id | Student | Активная лабораторная сессия |
/teacher/courses | Teacher | Список курсов преподавателя |
/teacher/courses/:id | Teacher | Управление курсом и назначениями |
/teacher/monitoring | Teacher | Мониторинг сессий студентов |
/admin/dashboard | Admin | Сводная панель администратора |
/admin/users | Admin | Управление пользователями и ролями |
/admin/infrastructure | Admin | Состояние кластера и ресурсов |
/admin/audit-log | Admin | Журнал аудита событий |
/403 | Все | Страница отказа в доступе |
Маршруты защищены компонентом <ProtectedRoute>, проверяющим наличие токена и соответствие роли. При несоответствии выполняется редирект на /403.
Д.2 Сценарий запуска лабораторной среды студентом
Страница /student/my-labs
└─▶ [Запустить] → POST /api/sessions { lab_assignment_id }
└─▶ Статус: Requested → спиннер «Среда создаётся...»
└─▶ Polling GET /api/sessions/{id} каждые 5 с
└─▶ Статус: Provisioning → «Настройка окружения...»
└─▶ Статус: Running
└─▶ Отображается точка подключения
Таймер TTL (обратный отсчёт)
Кнопка [Завершить сессию]
└─▶ DELETE /api/sessions/{id}
└─▶ Статус: Destroyed
Д.3 Сценарий назначения ЛР преподавателем
/teacher/courses/:id
└─▶ [Создать назначение]
└─▶ Форма: выбор ЛР, группа, дедлайн, TTL
└─▶ POST /api/assignments
└─▶ Уведомление «Назначение создано»
└─▶ /teacher/monitoring
└─▶ Таблица активных сессий
(обновление каждые 10 с)