CentOS 7 → AlmaLinux 10 — Actualización in situ
Este es un flujo consolidado a partir de una migración real de producción (servidor bastión de Huawei Cloud, CentOS 7.7 → AlmaLinux 10.2, manteniendo nginx/Docker/xxl-job/MySQL/NetBird/kubectl). Su objetivo es que cada máquina pueda actualizarse de forma segura, reversible y verificable, y que se pueda realizar en lote.
Idea central: Evaluar primero → instantánea de respaldo → pasos pequeños (preverificar→actualizar→reiniciar→comparar línea base→finalizar) → modernizar al final. La mayoría de los problemas surgen de elementos "no oficiales" (programas compilados manualmente, paquetes pip, repositorios de terceros, OpenSSH compilado manualmente, políticas de reinicio de contenedores, rutas de acceso VPN), por lo que la fase de diagnóstico es la más valiosa.
Cuándo NO actualizar in situ
Primero revise los BLOQUEOS de survey.sh. En los siguientes casos se recomienda crear una nueva máquina con AlmaLinux 10 y migrar los servicios, en lugar de actualizar in situ:
- Es un nodo de Kubernetes (kubelet en ejecución) — debe tratarse como un cambio a nivel de clúster, siguiendo
references/kubernetes.md(ya probado: kubeadm 1.21 + dockershim + flannel/ipvs funciona en EL10, clúster 3m3w ~6 horas); también se puede drenar y reemplazar el nodo. - La base de datos instalada desde rpm debe saltar múltiples versiones principales (MariaDB 5.5→10.11, PostgreSQL 9.2→16) y tiene grandes volúmenes de datos.
- No es x86_64, usa btrfs, o el disco del sistema es extremadamente pequeño y no se puede ampliar.
- La CPU no es compatible con x86-64-v3 (la edición estándar de AlmaLinux 10 no funcionará; solo se puede actualizar a 9, o usar la variante x86_64_v2 de AlmaLinux 10).
- Los servicios ya están contenerizados y la configuración está en compose/k8s — crear una nueva máquina y redesplegar suele ser más rápido que actualizar in situ.
Si no está seguro, proporcione al usuario los criterios de evaluación y deje que él decida.
Kit de herramientas
El directorio scripts/ del skill (todo se sube a la máquina de destino mediante SSH a /root/os-upgrade-kit/ para su ejecución):
| Script | Función |
|---|---|
remote.py | Se ejecuta localmente: toma credenciales de una tabla de activos (xlsx/csv), ejecuta comandos SSH, sube/descarga archivos, espera reinicios; mantiene keepalive; no muestra contraseñas |
survey.sh | Diagnóstico de solo lectura, genera BANDERAS DE RIESGO (BLOQUEADOR/ALTO/MEDIO/INFO) al final |
backup.sh | Respaldar configuración + generar línea base de servicios baseline.tsv (servicios, puertos, contenedores, códigos HTTP de cada dominio, kubectl) |
state.sh / verify.sh | Generar estado actual / comparar con línea base, reportar FALLO/ADVERTENCIA |
prep-el7.sh | Preparación para EL7: repos (ajustar repositorios), update (a 7.9), pipfix, inhibitors |
convert-el8.sh | Cuando el sistema origen es CentOS 8: prep → run (almalinux-deploy oficial) → post, convertir a AlmaLinux 8 |
swap-openssh.sh | Reemplazar OpenSSH compilado manualmente por el paquete oficial (con respaldo automático) |
hop.sh | Cada salto: prep→pre→report/answer→up→reboot→post |
nginx-static-build.sh | Recompilación estática de nginx compilado manualmente con la misma versión (para mantenerlo funcional durante la transición) |
fleet_summary.py | Se ejecuta localmente: resume los resultados de survey de múltiples máquinas → clasificación A/B/C/D, marcas VPN/EL8, sugerencias de canario y lotes (--groups para que servidores principal y de respaldo del mismo servicio no estén en el mismo lote) |
finalize.sh | Finalización de EL10: empaquetado de compose, alineación de kubectl con el clúster, helm, postfix lmdb, fuente nginx.org, compatibilidad cgroup v1, empaquetado SysV, módulos iptables legacy, redis→valkey, convertir procesos iniciados manualmente a servicios systemd |
k8s.sh | Para nodos K8s: backup (snapshot de etcd/configuración/binarios/lista de pods, pin automático de versiones), stop, nfs-off/nfs-on, nm, fix, check, pods (comparar con antes de la actualización), jvm-maxram (OOM en cgroup v2 con JDK antiguo) |
Al invocarlo desde Windows Git Bash, remote.py ya maneja automáticamente la reescritura de MSYS que convierte /root/... en C:/Program Files/Git/root/....
Uso común:
export FLEET_INVENTORY="/path/to/servers.xlsx" # Columnas: name, host, port, username, password (incluye contraseña, ¡no subir al repositorio!)
S=<directorio del skill>/scripts
python $S/remote.py HOST put $S /root/os-upgrade-kit
python $S/remote.py HOST run "bash /root/os-upgrade-kit/survey.sh > /root/os-upgrade-kit/survey.txt; sed -n '/RISK FLAGS/,\$p' /root/os-upgrade-kit/survey.txt"
python $S/remote.py HOST wait-ssh --timeout 3600
Flujo de trabajo
Fase 0: Confirmaciones obligatorias con el usuario antes de comenzar (no negociables)
Estas son "decisiones del usuario", no asuma por él:
- Instantánea de nube/respaldo completo de la máquina ya realizado (disco del sistema + discos de datos). Es el único medio confiable de reversión; Claude no puede hacerlo desde dentro de la máquina.
- Ventana de inactividad: cada salto requiere un reinicio, todo el proceso dura aproximadamente 2~4 horas (la mayor parte del tiempo se descargan paquetes; depende de la velocidad del espejo de repositorio); el servicio se interrumpirá varias veces.
- Consola fuera de banda disponible: VNC de Alibaba Cloud / VNC de Huawei Cloud / AWS EC2 Serial Console (requisitos previos en
references/cloud-providers.md). Si la actualización se detiene en initramfs, solo se puede rescatar desde aquí. - Ruta de acceso: ¿Cómo te conectas a esta máquina desde tu máquina local? Si es a través de VPN/túnel, ejecuta los comandos en esa máquina (survey reportará BLOQUEADOR). Ten especial cuidado con reinicios y operaciones de contenedores — en nuestro incidente real,
docker stopde todos los contenedores provocó que el contenedor de ruta NetBird no se iniciara al arrancar, perdiendo conectividad en toda la VPC. - Qué elementos ya no se usan: Después del diagnóstico, pasa la lista al usuario y pídele que confirme qué componentes/sitios se pueden dar de baja (por ejemplo, PHP, sitios antiguos). Antes de eliminar, explica claramente el impacto (p. ej., "eliminar PHP provocará que el sitio xxx devuelva 404") y que el usuario decida.
Fase 1: Diagnóstico + Respaldo + Línea base
- Sube el kit de herramientas, ejecuta
survey.sh, revisa cada BANDERA DE RIESGO, y determina un plan de tratamiento para cada una consultandoreferences/app-compat.md. - Ejecuta
backup.sh(por defecto en un disco que no sea del sistema, como /data) y anota elBACKUP_DIR.baseline.tsvserá la base para comparar en cada salto — los elementos que ya estaban anómalos antes de la actualización (p. ej., 502) deben indicarse en el informe como "ya anómalo antes de la actualización" para evitar confusiones posteriores. - Comunica al usuario de una sola vez las conclusiones del diagnóstico, los planes de tratamiento y los elementos que requieren su decisión (ver Fase 0).
Fase 2: Preparación de EL7
bash prep-el7.sh repos <MIRROR> # Recomendado en China: https://mirrors.aliyun.com; red interna de Alibaba Cloud: http://mirrors.cloud.aliyuncs.com; en el extranjero: upstream
bash prep-el7.sh update # Actualizar a 7.9; los paquetes que fallen suelen ser conflictos con pip
bash prep-el7.sh pipfix
bash prep-el7.sh inhibitors
bash swap-openssh.sh # Solo si survey reporta OpenSSH no oficial; después de hacerlo, verificar con una [nueva conexión]
Programas que dependen de bibliotecas de EL7 (como nginx compilado manualmente): recompílelos estáticamente en EL7 usando nginx-static-build.sh (o acepte desactivarlos durante la transición e instale el paquete oficial al final).
Bases de datos en el servidor: realice primero un respaldo lógico.
Cuando el sistema de origen es CentOS Linux 8 / CentOS Stream 8 (survey reportará BLOQUEADOR): ELevate no lo admite como origen, así que omita la preparación de EL7 anterior y primero realice la conversión a la misma versión, luego comience desde el salto 8→9 en la Fase 3:
bash convert-el8.sh prep <MIRROR> # Ajustar repos (vault, EPEL8), conflictos con pip, kernel-devel sobrante
bash convert-el8.sh update # Actualizar a 8.5 (almalinux-deploy requiere ≥8.4)
bash convert-el8.sh run <MIRROR> <almalinux-deploy.sh> # almalinux-deploy oficial: -d permite degradar, -l usa espejo
bash hop.sh reboot && python remote.py HOST wait-ssh # Reiniciar para entrar al kernel de AlmaLinux
bash convert-el8.sh post # Eliminar kernels que no sean de AlmaLinux, dnf upgrade
Los servidores en China generalmente no pueden acceder a GitHub raw, por lo que primero debe descargar almalinux-deploy.sh en su máquina local y luego subirlo.
Fase 3: Cada salto (7→8, 8→9, 9→10 — repetir cada uno)
bash hop.sh prep <MIRROR> # Instalar ELevate para este salto, usar espejos para el repositorio de destino y repos de proveedores (docker-ce/epel), deshabilitar secciones test/nightly, limpiar pip, verificar conectividad
bash hop.sh pre # Ejecutar preupgrade en segundo plano y esperar → reporte automático
bash hop.sh answer # Responder automáticamente a problemas de seguridad conocidos; para el resto de elementos bloqueantes, usar los comandos de reparación del reporte y volver a hacer pre hasta que no haya bloqueos
bash hop.sh report # Revisar especialmente "paquetes que serán eliminados" y "destino de paquetes críticos" — si un servicio depende de ellos, hay que buscar solución primero
bash hop.sh up # Descargar en segundo plano + generar entorno de actualización
bash hop.sh reboot [nombres_de_contenedores_adicionales_a_detener] # Detener solo contenedores con estado restart=always; no tocar contenedores VPN ni los con unless-stopped; si el negocio dice "no toques", usa --no-stop
python remote.py HOST wait-ssh --timeout 3600 # Esperar desde la máquina local; si hay timeout → consola, ver references/recovery.md
bash hop.sh post <MIRROR> # Verificar marcas de fallo, limpiar leapp/kernels antiguos/repos residuales, dnf upgrade
bash verify.sh <BACKUP_DIR> # FALLO debe corregirse; ADVERTENCIA evaluar si es esperado
Después de cada salto, corrija los problemas de servicio antes de pasar al siguiente; no los acumule para el final. Los bloqueos específicos de cada salto y su manejo están en references/pitfalls.md bajo "clasificación por salto".
Las tareas largas deben ejecutarse siempre en segundo plano: los scripts ya usan systemd-run para ejecutar leapp/dnf, por lo que si se corta la conexión SSH no se ven afectados; solo debes consultar periódicamente. Cuando escribas tus propios comandos, haz lo mismo; no dejes una sesión SSH colgada durante decenas de minutos.
Fase 4: Finalización y modernización en EL10
Según las necesidades del usuario, usa los subcomandos de finalize.sh y consulta references/app-compat.md:
- nginx compilado manualmente → paquete oficial estable de nginx.org (
ssl on;cambiar alisten 443 ssl;, TLS 1.2/1.3, certificados en /etc/nginx/ssl, systemd autoarranque) docker-composev1 → empaquetar como compose v2; aplicaciones antiguas con cgroup v1 añadir shim- kubectl → fuente pkgs.k8s.io, misma minor que el clúster; helm → última v3
- postfix hash → lmdb; elementos de inicio en rc.local convertirlos a unidades systemd
dnf -y upgradepara actualizar todo, confirmar quednf check-updateesté vacío- Finalmente, reiniciar una vez más para probar el arranque automático, ejecutar
verify.sh, todos los servicios deben iniciarse solos para considerar la tarea completa
Fase 5: Informe de entrega
Usa la estructura de assets/report-template.md para escribir al usuario: comparación de versiones, tabla de comparación de servicios (antes/después de la actualización), cambios de compatibilidad realizados, elementos pendientes que requieren acción del usuario (certificados expirados, plugins de proveedores de nube no reparados, etc.), ubicación del respaldo. En escenarios por lotes, actualiza también la tabla de seguimiento de activos (assets/fleet-tracker.csv).
Fase 6: Consolidar la experiencia (retroalimentar al repositorio del skill)
Este skill evoluciona con ejecuciones reales. Dirección del proyecto: https://github.com/seaworld008/centos7-lighthouse . Después de cada ejecución real (ya sea exitosa, fallida o con reversión):
- Sigue la
field-reports/TEMPLATE.mddel repositorio para escribir un informe de campo desinfectado (sin IP reales, dominios, nombres de host ni credenciales). - Los nuevos problemas escríbelos en
references/pitfalls.md; los que puedan detectarse automáticamente, agrégalos asurvey.sh; los que puedan manejarse automáticamente, mejora los scripts. - Si el directorio de trabajo actual es ese repositorio, sigue su
AGENTS.md; de lo contrario, entrega el informe al usuario y sugiérele que cree un issue o PR.
Principios clave (cada uno corresponde a un incidente real)
- Nunca ejecutes
docker stop $(docker ps -q)antes de reiniciar: los contenedores conunless-stoppedque se detengan manualmente no se reiniciarán solos; si se detiene un contenedor VPN, perderás la conexión. Usahop.sh reboot. - Los elementos instalados por pip en site-packages del sistema deben eliminarse antes de cada salto: si entran en conflicto con archivos rpm, la actualización fallará a medio camino en initramfs, dejando el sistema en modo de emergencia y en estado "semi-actualizado".
- Todo lo que no sea oficial y no se reconozca debe tratarse primero: OpenSSH compilado manualmente, nginx compilado manualmente, repositorios de terceros,
mysql-community-libsreemplazando amariadb-libs... leapp no los maneja y se romperán tras la actualización. - En China, usa espejos locales y desactiva las secciones test/nightly de los repositorios de proveedores: mirrorlist/metalink son a menudo lentos o dan timeout; la fuente nightly de docker puede hacer que leapp falle por timeout. Los espejos de Huawei Cloud pueden limitar la tasa (429); Alibaba Cloud es el más estable.
- Los patrones de
pkill -f/pgrep -fno deben aparecer en tu propia línea de comandos; de lo contrario, podrías matar tu propia sesión SSH (nos pasó dos veces). Usa PID, archivos de script o la notación[l]eapp. - Compara la línea base en cada salto: detecta y corrige problemas de servicio en el mismo salto, como la rotura de Yearning con cgroup v2 o la falla de docker-compose v1.
- Las decisiones que cambien el comportamiento del negocio deben dejarlas al usuario: eliminar componentes, dar de baja sitios, deshabilitar TLS 1.0/1.1, cambiar a una versión principal de base de datos.
Documentos de referencia (leer según necesidad)
references/pitfalls.md— Todos los problemas: fenómeno → causa → solución, clasificados por salto + generales. Si encuentras un error, consulta esto primero.references/app-compat.md— Manejo de compatibilidad de diversas aplicaciones al migrar de EL7 a EL10: nginx, Docker/compose/cgroup v2, Java, Python, PHP, bases de datos, postfix, red, SSH, kubectl/helm, SysV, políticas de cifrado TLS.references/cloud-providers.md— Diferencias entre Alibaba Cloud/Huawei Cloud/AWS: instantáneas, requisitos previos de consola fuera de banda, espejos de repositorio, agentes de proveedores de nube, cloud-init.references/recovery.md— Cómo rescatar si el sistema se queda en modo de emergencia de initramfs, no se conecta al arrancar, o está en estado semi-actualizado (incluye trucos de operación en VNC).references/fleet.md— Gestión de lotes de decenas o cientos de máquinas: tabla de activos, diagnóstico paralelo, clasificación, canario, lotes, registro.references/kubernetes.md— Actualización in situ de nodos K8s: cosas que deben aclararse antes de empezar (ruta de acceso/punto de entrada del plano de control/etcd/almacenamiento), conclusiones de compatibilidad, orden recomendado, problemas encontrados, lista de verificación de recuperación.