Merge k8s в main: добавлена поддержка Kubernetes Kind кластеров

This commit is contained in:
Сергей Антропов
2025-10-27 11:21:45 +03:00
35 changed files with 4457 additions and 127 deletions

244
scripts/create_k8s_cluster.py Executable file
View File

@@ -0,0 +1,244 @@
#!/usr/bin/env python3
"""
Скрипт для создания Kind кластеров
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import sys
import yaml
import subprocess
import os
def run_cmd(cmd):
"""Выполнить команду"""
print(f"[run] {cmd}")
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"[error] {result.stderr}")
sys.exit(1)
print(result.stdout)
return result.stdout
def main():
if len(sys.argv) < 3:
print("Usage: create_k8s_cluster.py <preset_file> <container_name>")
sys.exit(1)
preset_file = sys.argv[1]
container_name = sys.argv[2]
print(f"📋 Читаю пресет: {preset_file}")
with open(preset_file, 'r') as f:
preset = yaml.safe_load(f)
# Создаем Docker сеть если её нет
docker_network = preset.get('docker_network', 'labnet')
print(f"\n🌐 Проверка Docker сети: {docker_network}")
result = subprocess.run(f"docker network ls --format '{{{{.Name}}}}' | grep -x {docker_network}",
shell=True, capture_output=True, text=True)
if not result.stdout.strip():
print(f"📡 Создание Docker сети: {docker_network}")
run_cmd(f"docker network create {docker_network}")
else:
print(f"✅ Сеть {docker_network} уже существует")
# Получаем конфигурацию для hosts
hosts = preset.get('hosts', [])
images = preset.get('images', {})
systemd_defaults = preset.get('systemd_defaults', {})
# Создаем контейнеры если определены hosts
if hosts:
print(f"\n🐳 Создание контейнеров (всего: {len(hosts)})")
for host in hosts:
host_name = host['name']
family = host['family']
# Проверяем существование контейнера
result = subprocess.run(f"docker ps -a --format '{{{{.Names}}}}' | grep -x {host_name}",
shell=True, capture_output=True, text=True)
if result.stdout.strip():
print(f"⚠️ Контейнер '{host_name}' уже существует, удаляем старый")
run_cmd(f"docker rm -f {host_name}")
# Получаем образ
image = images.get(family, f"inecs/ansible-lab:{family}-latest")
# Формируем команду docker run
cmd_parts = [
"docker run -d",
f"--name {host_name}",
f"--network {docker_network}",
"--restart=unless-stopped"
]
# Добавляем systemd настройки
if systemd_defaults.get('privileged'):
cmd_parts.append("--privileged")
for vol in systemd_defaults.get('volumes', []):
cmd_parts.append(f"-v {vol}")
for tmpfs in systemd_defaults.get('tmpfs', []):
cmd_parts.append(f"--tmpfs {tmpfs}")
if systemd_defaults.get('capabilities'):
for cap in systemd_defaults['capabilities']:
cmd_parts.append(f"--cap-add {cap}")
cmd_parts.append(image)
# Добавляем command в конец если задан
if systemd_defaults.get('command'):
cmd_parts.append(systemd_defaults['command'])
cmd = " ".join(cmd_parts)
print(f"🚀 Создание контейнера: {host_name}")
run_cmd(cmd)
print(f"✅ Контейнер '{host_name}' создан")
kind_clusters = preset.get('kind_clusters', [])
if not kind_clusters:
print("\n⚠️ В пресете не определены kind кластеры")
print("✅ Создание контейнеров завершено")
sys.exit(0)
os.makedirs("/ansible/.kind", exist_ok=True)
for cluster in kind_clusters:
name = cluster['name']
config_file = f"/ansible/.kind/{name}.yaml"
print(f"\n☸️ Создание конфигурации для кластера: {name}")
# Создаем конфигурацию Kind
config = {
'kind': 'Cluster',
'apiVersion': 'kind.x-k8s.io/v1alpha4',
'nodes': [
{'role': 'control-plane'}
],
'networking': {
'apiServerAddress': '0.0.0.0',
'apiServerPort': cluster.get('api_port', 0)
}
}
# Добавляем extraPortMappings для всех портов из addon_ports
addon_ports = cluster.get('addon_ports', {})
# Ingress порты для проброса на host
if addon_ports.get('ingress_http') or addon_ports.get('ingress_https'):
# Добавляем extraPortMappings к control-plane узлу
if 'extraPortMappings' not in config['nodes'][0]:
config['nodes'][0]['extraPortMappings'] = []
if addon_ports.get('ingress_http'):
config['nodes'][0]['extraPortMappings'].append({
'containerPort': 80,
'hostPort': addon_ports['ingress_http'],
'protocol': 'TCP'
})
if addon_ports.get('ingress_https'):
config['nodes'][0]['extraPortMappings'].append({
'containerPort': 443,
'hostPort': addon_ports['ingress_https'],
'protocol': 'TCP'
})
# Не добавляем extraPortMappings для портов аддонов - используем port-forward
# Добавляем worker nodes
workers = cluster.get('workers', 0)
for i in range(workers):
config['nodes'].append({'role': 'worker'})
# Записываем конфигурацию
with open(config_file, 'w') as f:
yaml.dump(config, f)
print(f"✅ Конфигурация сохранена: {config_file}")
# Проверяем существование кластера
result = subprocess.run(f"kind get clusters", shell=True, capture_output=True, text=True)
existing = result.stdout.strip().split('\n') if result.returncode == 0 else []
if name in existing:
print(f"⚠️ Кластер '{name}' уже существует, пропускаю")
else:
print(f"🚀 Создание кластера: {name}")
run_cmd(f"kind create cluster --name {name} --config {config_file}")
# Подключаем контейнер k8s-controller к сети kind
print(f"🔗 Подключение контейнера к сети kind...")
result = subprocess.run(f"docker network inspect kind", shell=True, capture_output=True, text=True)
if result.returncode == 0:
# Получаем имя контейнера из аргументов (второй аргумент)
controller_name = sys.argv[2] if len(sys.argv) > 2 else "k8s-controller"
result = subprocess.run(f"docker network connect kind {controller_name}", shell=True, capture_output=True, text=True)
if result.returncode == 0:
print(f"✅ Контейнер {controller_name} подключен к сети kind")
else:
print(f"⚠️ Не удалось подключить контейнер к сети kind: {result.stderr}")
else:
print(f"⚠️ Сеть kind не найдена")
# Устанавливаем аддоны
addons = cluster.get('addons', {})
if not addons:
continue
print(f"\n📦 Установка аддонов для кластера: {name}")
if addons.get('ingress_nginx'):
print(" - Installing ingress-nginx")
run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify apply --validate=false -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml")
run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s")
if addons.get('metrics_server'):
print(" - Installing metrics-server")
run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify apply --validate=false -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml")
patch_json = '{"spec":{"template":{"spec":{"containers":[{"name":"metrics-server","args":["--kubelet-insecure-tls","--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname"]}]}}}}'
run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n kube-system patch deploy metrics-server -p '{patch_json}'")
if addons.get('istio'):
print(" - Installing Istio")
# Генерируем kubeconfig и заменяем 0.0.0.0 на IP control-plane узла
run_cmd(f"kind get kubeconfig --name {name} > /tmp/istio-kubeconfig-{name}.yaml")
# Получаем IP control-plane узла и заменяем в kubeconfig
result = subprocess.run(f"docker inspect {name}-control-plane --format='{{{{.NetworkSettings.Networks.kind.IPAddress}}}}'",
shell=True, capture_output=True, text=True)
if result.returncode == 0:
control_plane_ip = result.stdout.strip()
# Заменяем 0.0.0.0 на IP control-plane
subprocess.run(f"sed -i 's/0\\.0\\.0\\.0:6443/{control_plane_ip}:6443/g' /tmp/istio-kubeconfig-{name}.yaml", shell=True)
# Устанавливаем Istio используя kubeconfig
run_cmd(f"KUBECONFIG=/tmp/istio-kubeconfig-{name}.yaml istioctl install -y --set profile=demo")
run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n istio-system rollout status deploy/istiod --timeout=180s")
run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s")
if addons.get('kiali'):
print(" - Installing Kiali")
subprocess.run(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns istio-system", shell=True, capture_output=True)
# Добавляем Helm репозиторий Kiali
run_cmd(f"helm repo add kiali https://kiali.org/helm-charts")
run_cmd(f"helm repo update")
# Используем исправленный kubeconfig
run_cmd(f"KUBECONFIG=/tmp/istio-kubeconfig-{name}.yaml helm upgrade --install kiali-server kiali/kiali-server --namespace istio-system --set auth.strategy=anonymous --wait --timeout 180s")
if addons.get('prometheus_stack'):
print(" - Installing Prometheus Stack")
# Добавляем Helm репозиторий Prometheus
subprocess.run(f"helm repo add prometheus-community https://prometheus-community.github.io/helm-charts", shell=True, capture_output=True)
subprocess.run(f"helm repo update", shell=True, capture_output=True)
subprocess.run(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns monitoring", shell=True, capture_output=True)
# Используем исправленный kubeconfig
run_cmd(f"KUBECONFIG=/tmp/istio-kubeconfig-{name}.yaml helm upgrade --install monitoring prometheus-community/kube-prometheus-stack --namespace monitoring --set grafana.adminPassword=admin --set grafana.defaultDashboardsTimezone=browser --wait --timeout 600s")
run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n monitoring rollout status deploy/monitoring-grafana --timeout=300s")
print(f"✅ Кластер '{name}' готов!")
print("\n🎉 Все кластеры созданы!")
if __name__ == '__main__':
main()

44
scripts/delete_hosts.py Normal file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""
Скрипт для удаления контейнеров из секции hosts пресета
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import sys
import yaml
import subprocess
def main():
if len(sys.argv) < 2:
print("Usage: delete_hosts.py <preset_file>")
sys.exit(1)
preset_file = sys.argv[1]
print(f"📋 Читаю пресет: {preset_file}")
with open(preset_file, 'r') as f:
preset = yaml.safe_load(f)
hosts = preset.get('hosts', [])
if not hosts:
print("⚠️ В пресете нет контейнеров для удаления")
sys.exit(0)
print(f"🗑️ Удаление контейнеров (всего: {len(hosts)})")
for host in hosts:
host_name = host['name']
# Проверяем существование контейнера
result = subprocess.run(f"docker ps -a --format '{{{{.Names}}}}' | grep -x {host_name}",
shell=True, capture_output=True, text=True)
if result.stdout.strip():
print(f"🗑️ Удаление контейнера: {host_name}")
subprocess.run(f"docker rm -f {host_name}", shell=True, capture_output=True, text=True)
print(f"✅ Контейнер '{host_name}' удален")
else:
print(f"⚠️ Контейнер '{host_name}' не найден")
print("✅ Удаление завершено")
if __name__ == "__main__":
main()

300
scripts/k8s_status.py Executable file
View File

@@ -0,0 +1,300 @@
#!/usr/bin/env python3
"""
Скрипт для детального отчета о состоянии Kubernetes кластера
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import sys
import subprocess
import json
def get_cluster_name():
"""Получает имя кластера"""
result = subprocess.run("docker exec k8s-controller kind get clusters | head -1", shell=True, capture_output=True, text=True)
if result.returncode == 0:
return result.stdout.strip()
return None
def run_kubectl_cmd(cmd):
"""Выполняет команду kubectl внутри контейнера k8s-controller"""
cluster_name = get_cluster_name()
if cluster_name:
# Используем прямой адрес control-plane
server = f"https://{cluster_name}-control-plane:6443"
cmd_with_server = f"--server={server} --insecure-skip-tls-verify {cmd}"
else:
cmd_with_server = cmd
full_cmd = f"docker exec k8s-controller kubectl {cmd_with_server}"
result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True)
return result.stdout
def print_section(title):
"""Выводит заголовок секции"""
print(f"\n{'='*70}")
print(f" {title}")
print(f"{'='*70}\n")
def print_subsection(title):
"""Выводит подзаголовок"""
print(f"\n{''*70}")
print(f" {title}")
print(f"{''*70}\n")
def get_cluster_info():
"""Получает информацию о кластере"""
print_section("📊 ОБЩАЯ ИНФОРМАЦИЯ О КЛАСТЕРЕ")
# Версия Kubernetes
version = run_kubectl_cmd("version --short")
print(version)
def get_nodes_status():
"""Получает статус узлов"""
print_section("🖥️ УЗЛЫ КЛАСТЕРА")
nodes = run_kubectl_cmd("get nodes -o wide")
print(nodes)
# Детальная информация о каждом узле
node_names = run_kubectl_cmd("get nodes -o jsonpath='{.items[*].metadata.name}'")
if node_names.strip():
for node in node_names.strip().split():
print_subsection(f"Узел: {node}")
node_info = run_kubectl_cmd(f"describe node {node}")
print(node_info)
def get_namespaces():
"""Получает список namespace"""
print_section("📁 NAMESPACES")
namespaces = run_kubectl_cmd("get namespaces")
print(namespaces)
def get_pods_by_namespace():
"""Получает поды по namespace"""
print_section("🪟 PODS")
# Получаем список namespace
namespaces_output = run_kubectl_cmd("get namespaces -o json")
try:
namespaces_data = json.loads(namespaces_output)
namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
except:
namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
for ns in namespaces:
print_subsection(f"Namespace: {ns}")
pods = run_kubectl_cmd(f"get pods -n {ns} -o wide")
if pods.strip():
print(pods)
else:
print(" (пусто)")
def get_services():
"""Получает сервисы"""
print_section("🔗 SERVICES")
namespaces_output = run_kubectl_cmd("get namespaces -o json")
try:
namespaces_data = json.loads(namespaces_output)
namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
except:
namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
for ns in namespaces:
print_subsection(f"Namespace: {ns}")
services = run_kubectl_cmd(f"get services -n {ns}")
if services.strip():
print(services)
else:
print(" (пусто)")
def get_ingress():
"""Получает Ingress ресурсы"""
print_section("🌐 INGRESS")
namespaces_output = run_kubectl_cmd("get namespaces -o json")
try:
namespaces_data = json.loads(namespaces_output)
namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
except:
namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
ingress_found = False
for ns in namespaces:
ingress = run_kubectl_cmd(f"get ingress -n {ns} 2>/dev/null")
if ingress.strip() and "No resources found" not in ingress:
ingress_found = True
print_subsection(f"Namespace: {ns}")
print(ingress)
if not ingress_found:
print(" Ingress ресурсы не найдены")
def get_pvcs():
"""Получает PersistentVolumeClaims"""
print_section("💾 VOLUMES (PVC)")
namespaces_output = run_kubectl_cmd("get namespaces -o json")
try:
namespaces_data = json.loads(namespaces_output)
namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
except:
namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
pvc_found = False
for ns in namespaces:
pvcs = run_kubectl_cmd(f"get pvc -n {ns} 2>/dev/null")
if pvcs.strip() and "No resources found" not in pvcs:
pvc_found = True
print_subsection(f"Namespace: {ns}")
print(pvcs)
if not pvc_found:
print(" PVC не найдены")
def get_deployments():
"""Получает Deployments"""
print_section("🚀 DEPLOYMENTS")
namespaces_output = run_kubectl_cmd("get namespaces -o json")
try:
namespaces_data = json.loads(namespaces_output)
namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
except:
namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
for ns in namespaces:
print_subsection(f"Namespace: {ns}")
deployments = run_kubectl_cmd(f"get deployments -n {ns}")
if deployments.strip():
print(deployments)
else:
print(" (пусто)")
def get_daemonsets():
"""Получает DaemonSets"""
print_section("🔧 DAEMONSETS")
namespaces_output = run_kubectl_cmd("get namespaces -o json")
try:
namespaces_data = json.loads(namespaces_output)
namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
except:
namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
for ns in namespaces:
print_subsection(f"Namespace: {ns}")
daemonsets = run_kubectl_cmd(f"get daemonsets -n {ns}")
if daemonsets.strip():
print(daemonsets)
else:
print(" (пусто)")
def get_statefulsets():
"""Получает StatefulSets"""
print_section("🗄️ STATEFULSETS")
namespaces_output = run_kubectl_cmd("get namespaces -o json")
try:
namespaces_data = json.loads(namespaces_output)
namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
except:
namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
statefulsets_found = False
for ns in namespaces:
statefulsets = run_kubectl_cmd(f"get statefulsets -n {ns} 2>/dev/null")
if statefulsets.strip() and "No resources found" not in statefulsets:
statefulsets_found = True
print_subsection(f"Namespace: {ns}")
print(statefulsets)
if not statefulsets_found:
print(" StatefulSets не найдены")
def get_events():
"""Получает события"""
print_section("📅 СОБЫТИЯ (EVENTS)")
namespaces_output = run_kubectl_cmd("get namespaces -o json")
try:
namespaces_data = json.loads(namespaces_output)
namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
except:
namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
for ns in namespaces:
print_subsection(f"Namespace: {ns}")
events = run_kubectl_cmd(f"get events -n {ns} --sort-by='.lastTimestamp' | tail -20")
if events.strip():
print(events)
else:
print(" (пусто)")
def get_helm_releases():
"""Получает Helm релизы"""
print_section("📦 HELM RELEASES")
helm_output = run_kubectl_cmd("helm list --all-namespaces 2>/dev/null")
if helm_output.strip():
print(helm_output)
else:
print(" Helm релизы не найдены")
def get_resource_usage():
"""Получает использование ресурсов"""
print_section("📈 ИСПОЛЬЗОВАНИЕ РЕСУРСОВ")
try:
# Проверяем наличие metrics-server
top_nodes = run_kubectl_cmd("top nodes 2>/dev/null")
if top_nodes.strip():
print_subsection("Узлы:")
print(top_nodes)
except:
print(" metrics-server не установлен или недоступен")
try:
top_pods = run_kubectl_cmd("top pods --all-namespaces 2>/dev/null")
if top_pods.strip():
print_subsection("Pods (топ-20):")
# Берем только первые 20 строк
lines = top_pods.strip().split('\n')
print('\n'.join(lines[:21])) # + заголовок
except:
pass
def main():
"""Главная функция"""
# Проверяем доступность контейнера
result = subprocess.run("docker ps | grep k8s-controller", shell=True, capture_output=True, text=True)
if result.returncode != 0:
print("❌ Контейнер k8s-controller не запущен")
sys.exit(1)
print("="*70)
print(" ДЕТАЛЬНЫЙ ОТЧЕТ О СОСТОЯНИИ KUBERNETES КЛАСТЕРА")
print("="*70)
get_cluster_info()
get_nodes_status()
get_namespaces()
get_resource_usage()
get_pods_by_namespace()
get_deployments()
get_daemonsets()
get_statefulsets()
get_services()
get_ingress()
get_pvcs()
get_events()
get_helm_releases()
print("\n" + "="*70)
print(" ОТЧЕТ ЗАВЕРШЕН")
print("="*70 + "\n")
if __name__ == '__main__':
main()

253
scripts/portforward.py Executable file
View File

@@ -0,0 +1,253 @@
#!/usr/bin/env python3
"""
Скрипт для управления port-forward для Kubernetes сервисов
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import sys
import yaml
import subprocess
import os
import signal
import time
def get_cluster_name():
"""Получаем имя кластера из preset файла"""
preset_file = "molecule/presets/k8s/kubernetes.yml"
with open(preset_file, 'r') as f:
preset = yaml.safe_load(f)
return preset['kind_clusters'][0]['name']
def run_cmd(cmd):
"""Выполняет команду и возвращает результат"""
print(f"[run] {cmd}")
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"[error] {result.stderr}")
else:
print(result.stdout)
return result
def get_portforward_pids():
"""Получает PID процессов port-forward"""
result = subprocess.run("ps aux | grep 'kubectl.*port-forward' | grep -v grep", shell=True, capture_output=True, text=True)
pids = []
for line in result.stdout.split('\n'):
if line.strip():
pids.append(int(line.split()[1]))
return pids
def list_portforwards():
"""Показывает список всех активных port-forward"""
pids = get_portforward_pids()
if not pids:
print("❌ Нет активных port-forward")
return
print("📋 Активные port-forward:")
result = subprocess.run("ps aux | grep 'kubectl.*port-forward' | grep -v grep", shell=True, capture_output=True, text=True)
for line in result.stdout.split('\n'):
if line.strip():
print(f" {line}")
def clear_portforwards():
"""Завершает все процессы port-forward"""
pids = get_portforward_pids()
if not pids:
print("❌ Нет активных port-forward")
return
print(f"🗑️ Завершение {len(pids)} процессов port-forward...")
for pid in pids:
try:
os.kill(pid, signal.SIGTERM)
print(f"✅ Процесс {pid} завершен")
except ProcessLookupError:
print(f"⚠️ Процесс {pid} уже не существует")
# Ждем завершения процессов
time.sleep(2)
# Принудительно убиваем оставшиеся
remaining_pids = get_portforward_pids()
if remaining_pids:
print("⚠️ Принудительное завершение оставшихся процессов...")
for pid in remaining_pids:
try:
os.kill(pid, signal.SIGKILL)
print(f"✅ Процесс {pid} принудительно завершен")
except ProcessLookupError:
pass
def create_portforwards():
"""Создает port-forward для всех сервисов из preset на локальном компьютере"""
# Загружаем preset
preset_file = "molecule/presets/k8s/kubernetes.yml"
with open(preset_file, 'r') as f:
preset = yaml.safe_load(f)
cluster_name = preset['kind_clusters'][0]['name']
addon_ports = preset['kind_clusters'][0].get('addon_ports', {})
# Получаем kubeconfig из контейнера k8s-controller
print(f"🔌 Создание port-forward для кластера: {cluster_name}")
print("📋 Получение kubeconfig из контейнера k8s-controller...")
# Копируем kubeconfig из контейнера
result = subprocess.run(
f"docker exec k8s-controller kind get kubeconfig --name {cluster_name}",
shell=True, capture_output=True, text=True
)
if result.returncode != 0:
print(f"❌ Ошибка получения kubeconfig: {result.stderr}")
return
# Сохраняем kubeconfig во временный файл
kubeconfig_file = "/tmp/kubeconfig-lab.yaml"
with open(kubeconfig_file, 'w') as f:
f.write(result.stdout)
# Меняем server с 0.0.0.0 на localhost для локального доступа
subprocess.run(f"sed -i.bak 's|server: https://0.0.0.0:6443|server: https://localhost:6443|g' {kubeconfig_file}", shell=True)
print("✅ Kubeconfig подготовлен")
# Ingress HTTP (80)
if addon_ports.get('ingress_http'):
port = addon_ports['ingress_http']
print(f" - Ingress HTTP: localhost:{port} -> ingress-nginx-controller:80")
subprocess.Popen([
"kubectl",
f"--kubeconfig={kubeconfig_file}",
"port-forward",
"-n", "ingress-nginx",
"svc/ingress-nginx-controller",
f"{port}:80"
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# Ingress HTTPS (443)
if addon_ports.get('ingress_https'):
port = addon_ports['ingress_https']
print(f" - Ingress HTTPS: localhost:{port} -> ingress-nginx-controller:443")
subprocess.Popen([
"kubectl",
f"--kubeconfig={kubeconfig_file}",
"port-forward",
"-n", "ingress-nginx",
"svc/ingress-nginx-controller",
f"{port}:443"
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# Prometheus
if addon_ports.get('prometheus'):
port = addon_ports['prometheus']
print(f" - Prometheus: localhost:{port} -> monitoring/monitoring-kube-prometheus-prometheus:9090")
subprocess.Popen([
"kubectl",
f"--kubeconfig={kubeconfig_file}",
"port-forward",
"-n", "monitoring",
"svc/monitoring-kube-prometheus-prometheus",
f"{port}:9090"
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# Grafana
if addon_ports.get('grafana'):
port = addon_ports['grafana']
print(f" - Grafana: localhost:{port} -> monitoring/monitoring-grafana:80")
subprocess.Popen([
"kubectl",
f"--kubeconfig={kubeconfig_file}",
"port-forward",
"-n", "monitoring",
"svc/monitoring-grafana",
f"{port}:80"
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# Kiali
if addon_ports.get('kiali'):
port = addon_ports['kiali']
print(f" - Kiali: localhost:{port} -> istio-system/kiali:20001")
subprocess.Popen([
"kubectl",
f"--kubeconfig={kubeconfig_file}",
"port-forward",
"-n", "istio-system",
"svc/kiali",
f"{port}:20001"
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# Metrics Server
if addon_ports.get('metrics_server'):
port = addon_ports['metrics_server']
print(f" - Metrics Server: localhost:{port} -> kube-system/metrics-server:4443")
subprocess.Popen([
"kubectl",
f"--kubeconfig={kubeconfig_file}",
"port-forward",
"-n", "kube-system",
"svc/metrics-server",
f"{port}:4443"
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
time.sleep(2)
print("✅ Port-forward создан")
list_portforwards()
def delete_portforward(port):
"""Удаляет port-forward для конкретного порта"""
pids = get_portforward_pids()
if not pids:
print(f"❌ Нет активных port-forward на порту {port}")
return
# Находим процесс с нужным портом
result = subprocess.run(f"ps aux | grep 'kubectl.*port-forward.*:{port}' | grep -v grep", shell=True, capture_output=True, text=True)
if not result.stdout.strip():
print(f"Не найден port-forward на порту {port}")
return
# Извлекаем PID
pid = int(result.stdout.split()[1])
print(f"🗑️ Завершение port-forward на порту {port} (PID: {pid})...")
try:
os.kill(pid, signal.SIGTERM)
print(f"✅ Port-forward на порту {port} завершен")
except ProcessLookupError:
print(f"⚠️ Процесс {pid} уже не существует")
def recreate_portforwards():
"""Пересоздает port-forward: удаляет существующие и создает заново"""
print("🔄 Пересоздание port-forward...")
clear_portforwards()
time.sleep(1)
create_portforwards()
def main():
if len(sys.argv) < 2:
print("Usage: portforward.py <create|list|delete|clear|recreate> [port]")
sys.exit(1)
command = sys.argv[1]
if command == "create":
create_portforwards()
elif command == "list":
list_portforwards()
elif command == "clear":
clear_portforwards()
elif command == "recreate":
recreate_portforwards()
elif command == "delete":
if len(sys.argv) < 3:
print("Usage: portforward.py delete <port>")
sys.exit(1)
port = sys.argv[2]
delete_portforward(port)
else:
print(f"❌ Неизвестная команда: {command}")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -1,11 +1,11 @@
#!/bin/bash
# Автоматическая настройка CI/CD для AnsibleLab
# Автоматическая настройка CI/CD для DevOpsLab
# Автор: Сергей Антропов
# Сайт: https://devops.org.ru
set -euo pipefail
echo "🔧 Настройка CI/CD для AnsibleLab..."
echo "🔧 Настройка CI/CD для DevOpsLab..."
# Создание директории .github/workflows
mkdir -p .github/workflows

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# Скрипт для тестирования собственных образов AnsibleLab
# Скрипт для тестирования собственных образов DevOpsLab
# Автор: Сергей Антропов
# Сайт: https://devops.org.ru
@@ -190,7 +190,7 @@ cleanup() {
# Основная функция
main() {
log "🚀 Тестирование собственных образов AnsibleLab"
log "🚀 Тестирование собственных образов DevOpsLab"
echo "=========================================="
# Проверки