CentOS 7 → AlmaLinux 10 인플레이스 업그레이드
이것은 실제 프로덕션 마이그레이션(Huawei Cloud 점프 서버, CentOS 7.7 → AlmaLinux 10.2, nginx/Docker/xxl-job/MySQL/NetBird/kubectl 모두 유지)을 통해 정리된 프로세스입니다. 목표는 각 머신을 안전하고, 롤백 가능하며, 검증 가능하게 업그레이드하는 동시에 일괄 진행할 수 있도록 하는 것입니다.
핵심 접근 방식: 먼저 파악하기 → 스냅샷으로 안전 장치 마련 → 작은 단계로 한 걸음씩(사전 검사→업그레이드→재부팅→기준선 비교→마무리) → 마지막으로 현대화. 거의 모든 문제는 '비공식적인 것'(셀프 컴파일 프로그램, pip 패키지, 타사 소스, 셀프 패키징 openssh, 컨테이너 재시작 정책, VPN 접근 경로)에서 발생하므로, 조사 단계의 가치가 가장 큽니다.
인플레이스 업그레이드를 하지 말아야 할 경우
먼저 survey.sh의 BLOCKER를 확인하십시오. 다음 상황에서는 인플레이스 업그레이드 대신 새 AlmaLinux 10 머신에 서비스를 마이그레이션하는 것이 좋습니다:
- Kubernetes 노드(kubelet 실행 중)인 경우 — 클러스터 수준 변경으로 취급하여
references/kubernetes.md를 따르거나(실제 테스트 완료: kubeadm 1.21 + dockershim + flannel/ipvs, EL10 에서 작동, 3m3w 클러스터 약 6시간 소요), drain 후 새 노드로 교체 - 호스트 머신에 rpm으로 설치된 데이터베이스를 여러 메이저 버전 건너 업그레이드해야 하는 경우(MariaDB 5.5→10.11, PostgreSQL 9.2→16) 및 데이터 양이 많은 경우
- 비 x86_64, btrfs, 시스템 디스크가 매우 작고 확장 불가능한 경우
- CPU가 x86-64-v3를 지원하지 않는 경우(AlmaLinux 10 표준 에디션 실행 불가; 9까지만 업그레이드하거나 AlmaLinux 10의 x86_64_v2 변형 사용 가능)
- 서비스 자체가 컨테이너화되어 있고 구성이 compose/k8s에 있는 경우 — 새 머신에 재배포하는 것이 인플레이스 업그레이드보다 빠른 경우가 많음
확실하지 않은 경우 판단 근거를 사용자에게 알리고 사용자가 선택하도록 하십시오.
도구 모음
스킬 디렉토리 scripts/ 아래에 있음(모두 SSH를 통해 대상 머신 /root/os-upgrade-kit/에 업로드되어 실행됨):
| 스크립트 | 기능 |
|---|---|
remote.py | 로컬 실행: 자산 테이블(xlsx/csv)에서 자격 증명을 가져와 SSH 명령 실행/업로드/다운로드/재부팅 대기; keepalive 활성화; 비밀번호 출력 안 함 |
survey.sh | 읽기 전용 조사, 끝에 등급별 RISK FLAGS(BLOCKER/HIGH/MEDIUM/INFO) 출력 |
backup.sh | 구성 백업 + 서비스 기준선 baseline.tsv 생성(서비스/포트/컨테이너/각 도메인 HTTP 코드/kubectl) |
state.sh / verify.sh | 현재 상태 생성 / 기준선과 비교, FAIL/WARN 출력 |
prep-el7.sh | EL7 준비: repos (저장소 수정), update (7.9로 업데이트), pipfix, inhibitors |
convert-el8.sh | 소스 시스템이 CentOS 8인 경우: prep → run (공식 almalinux-deploy) → post, AlmaLinux 8로 변환 |
swap-openssh.sh | 셀프 컴파일 openssh를 공식 패키지로 교체(자동 안전 장치 포함) |
hop.sh | 각 점프: prep→pre→report/answer→up→reboot→post |
nginx-static-build.sh | 셀프 컴파일 nginx 동일 버전 정적 재컴파일(과도 기간 동안 계속 사용 가능) |
fleet_summary.py | 로컬 실행: 여러 대의 survey 결과를 종합 → A/B/C/D 분류, VPN/EL8 표시, 카나리 및 배치 제안(--groups로 동일 비즈니스의 주/예비를 다른 배치로 지정) |
finalize.sh | EL10 마무리: compose 패키징, kubectl 클러스터 정렬, helm, postfix lmdb, nginx.org 소스, cgroup v1 호환, SysV 패키징, legacy iptables 모듈, redis→valkey, 수동 시작 프로세스를 systemd 서비스로 전환 |
k8s.sh | K8s 노드: backup(etcd 스냅샷/구성/바이너리/pod 목록, 자동 pin 버전), stop, nfs-off/nfs-on, nm, fix, check, pods(업그레이드 전과 비교), jvm-maxram(cgroup v2 하의 구형 JDK OOM 문제) |
로컬 Windows Git Bash에서 호출 시, remote.py가 MSYS에서 /root/...를 C:/Program Files/Git/root/...로 변경하는 문제를 자동으로 처리합니다.
자주 사용:
export FLEET_INVENTORY="/path/to/servers.xlsx" # 열: name, host, port, username, password(비밀번호 포함, 저장소에 커밋 금지)
S=<스킬 디렉토리>/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
프로세스
단계 0: 시작 전 사용자와 반드시 확인해야 할 사항(하나도 빠짐 없이)
다음은 '사용자의 결정' 사항이므로 사용자를 대신해 가정하지 마십시오:
- 클라우드 스냅샷/전체 머신 백업이 완료되었는지 (시스템 디스크 + 데이터 디스크). 이것이 유일하게 신뢰할 수 있는 롤백 수단입니다. Claude는 머신 내부에서 이를 수행할 수 없습니다.
- 중단 시간: 각 점프마다 재부팅이 필요하며, 전체 과정은 약 2~4시간 소요됩니다(대부분 패키지 다운로드 시간이며 미러 소스 속도에 따라 다름); 서비스는 여러 번 중단됩니다.
- 대역 외 콘솔 사용 가능: Alibaba Cloud VNC / Huawei Cloud VNC / AWS EC2 Serial Console (각각의 전제 조건은
references/cloud-providers.md참조). 업그레이드가 initramfs에서 멈출 경우 여기서만 복구할 수 있습니다. - 접근 경로: 당신(로컬 머신)은 이 머신에 어떻게 연결되어 있습니까? VPN/터널을 통해 접속 중이라면 이 머신에서 작업을 실행해야 합니다(survey가 BLOCKER를 보고함). 재부팅 및 컨테이너 작업 시 각별히 주의하십시오 — 실제 사고는
docker stop으로 모든 컨테이너를 중지했는데 NetBird 라우트 컨테이너가 부팅 시 자동 시작되지 않아 전체 VPC가 연결이 끊긴 경우였습니다. - 더 이상 사용하지 않는 것은 무엇인가: 조사가 끝나면 목록을 사용자에게 제공하여 어떤 구성 요소/사이트를 내릴 수 있는지(예: PHP, 이전 사이트) 사용자가 확인하도록 하십시오. 삭제하기 전에 영향 범위를 명확히 설명하고(예: "PHP를 삭제하면 xxx 사이트가 404가 됩니다"), 사용자가 결정을 내리도록 하십시오.
단계 1: 조사 + 백업 + 기준선
- 도구 모음 업로드,
survey.sh실행, RISK FLAGS를 하나씩 확인하고references/app-compat.md를 참조하여 각 항목에 대한 처리 방안을 결정합니다. backup.sh실행(기본적으로 비시스템 디스크 예: /data에 저장),BACKUP_DIR기록.baseline.tsv는 이후 각 점프 시 비교 기준이 됩니다 — 업그레이드 전에 이미 비정상이었던 항목(예: 502)은 보고서에 "업그레이드 전부터 그러함"이라고 표시하여 사후에 오해가 생기지 않도록 해야 합니다.- 조사 결과, 처리 방안, 사용자가 결정해야 할 사항을 한 번에 사용자에게 알립니다(단계 0 참조).
단계 2: EL7 준비
bash prep-el7.sh repos <MIRROR> # 국내 추천: https://mirrors.aliyun.com; Alibaba Cloud 내부망: http://mirrors.cloud.aliyuncs.com; 해외는 upstream
bash prep-el7.sh update # 7.9로 업데이트; Failed가 뜨는 패키지는 대부분 pip 충돌
bash prep-el7.sh pipfix
bash prep-el7.sh inhibitors
bash swap-openssh.sh # survey가 openssh 비공식 패키지를 보고한 경우에만; 완료 후 【새 연결】로 확인
셀프 컴파일 nginx 등 EL7 라이브러리에 의존하는 프로그램: EL7에서 nginx-static-build.sh로 정적 재컴파일하거나(또는 과도 기간 동안 중단을 수용하고 마지막에 공식 패키지 설치).
호스트 머신 데이터베이스: 먼저 논리적 백업.
소스 시스템이 CentOS Linux 8 / CentOS Stream 8인 경우(survey가 BLOCKER로 보고): ELevate가 해당 소스를 지원하지 않으므로 위의 EL7 준비를 건너뛰고 먼저 동일 버전으로 변환한 후 단계 3의 8→9부터 시작합니다:
bash convert-el8.sh prep <MIRROR> # 저장소 수정(vault, EPEL8), pip 충돌, 불필요한 kernel-devel
bash convert-el8.sh update # 8.5로 업데이트(almalinux-deploy 요구 사항: ≥8.4)
bash convert-el8.sh run <MIRROR> <almalinux-deploy.sh> # 공식 almalinux-deploy: -d는 다운그레이드 허용, -l은 미러 사용
bash hop.sh reboot && python remote.py HOST wait-ssh # 재부팅하여 AlmaLinux 커널로 진입
bash convert-el8.sh post # 비 AlmaLinux 커널 삭제, dnf upgrade
국내 서버는 일반적으로 GitHub raw에 접속할 수 없으므로, 먼저 로컬에서 almalinux-deploy.sh를 다운로드한 후 업로드해야 합니다.
단계 3: 각 점프(7→8, 8→9, 9→10 각각 한 번씩)
bash hop.sh prep <MIRROR> # 이번 점프용 ELevate, 목표 저장소 및 docker-ce/epel 공급업체 소스를 미러로 변경, test/nightly 섹션 비활성화, pip 정리, 연결 가능성 확인
bash hop.sh pre # 백그라운드에서 preupgrade 실행 및 대기 → 자동 report
bash hop.sh answer # 알려진 보안 문제 자동 응답; 나머지 차단 항목은 report의 수정 명령에 따라 처리한 후 pre를 다시 실행하여 차단이 0이 될 때까지 반복
bash hop.sh report # 중요한 것은 "삭제될 패키지"와 "핵심 패키지의 이동" — 업무 의존성이 있는 경우 먼저 해결 방안 마련
bash hop.sh up # 백그라운드 다운로드 + 업그레이드 환경 생성
bash hop.sh reboot [추가로 중지할 컨테이너 이름] # restart=always인 상태 저장 컨테이너만 중지; VPN 컨테이너와 unless-stopped 컨테이너는 건드리지 않음; 업무 측에서 "건드리지 마"라고 하면 --no-stop 사용
python remote.py HOST wait-ssh --timeout 3600 # 로컬에서 대기; 시간 초과 → 콘솔, references/recovery.md 참조
bash hop.sh post <MIRROR> # 실패 플래그 확인, leapp/이전 커널/잔여 저장소 정리, dnf upgrade
bash verify.sh <BACKUP_DIR> # FAIL은 반드시 처리; WARN은 예상된 것인지 판단
각 점프 후 업무 문제를 해결한 후 다음 점프로 진행하고, 마지막에 한꺼번에 해결하려고 하지 마십시오. 각 점프별 특수 차단 항목 및 처리는 references/pitfalls.md의 "점프별 분류"를 참조하십시오.
긴 작업은 반드시 백그라운드에서 실행: 스크립트는 이미 systemd-run을 사용하여 leapp/dnf를 실행하므로 SSH 연결이 끊어져도 영향이 없습니다; 당신은 폴링만 하면 됩니다. 직접 명령을 작성할 때도 이와 같이 하여 하나의 SSH 세션이 수십 분 동안 붙잡혀 있지 않도록 하십시오.
단계 4: EL10 마무리 및 현대화
사용자의 필요에 따라 finalize.sh 하위 명령을 선택하고 references/app-compat.md를 참조하십시오:
- 셀프 컴파일 nginx → nginx.org 공식 stable 패키지(
ssl on;을listen 443 ssl;로 변경, TLS1.2/1.3, 인증서는 /etc/nginx/ssl에 배치, systemd 자동 시작) docker-composev1 → compose v2로 패키징; cgroup v1 이전 애플리케이션에 shim 추가- kubectl → pkgs.k8s.io 저장소, 클러스터와 동일한 minor 버전; helm → 최신 v3
- postfix hash → lmdb; rc.local의 시작 항목을 systemd unit으로 변경
dnf -y upgrade전체 업데이트,dnf check-update가 비어 있는지 확인- 마지막으로 한 번 더 재부팅하여 부팅 시 자동 시작 테스트 실행,
verify.sh실행, 모든 서비스가 자체적으로 시작되어야 완료
단계 5: 결과 보고서 제공
assets/report-template.md 구조를 사용하여 사용자에게 작성: 버전 비교, 업무 비교 테이블(업그레이드 전/후), 수행된 호환성 변경 사항, 남은 사항 및 사용자가 처리해야 할 사항(인증서 만료, 수정되지 않은 클라우드 공급업체 플러그인 등), 백업 위치. 일괄 시나리오에서는 동시에 자산 추적 테이블(assets/fleet-tracker.csv) 업데이트.
단계 6: 경험 축적(스킬 저장소에 피드백)
이 스킬은 실제 실행을 통해 지속적으로 발전합니다. 프로젝트 주소: https://github.com/seaworld008/centos7-lighthouse . 실제 실행(성공, 실패, 롤백 모두 포함)이 있을 때마다:
- 저장소의
field-reports/TEMPLATE.md에 따라 비식별화된 실전 보고서를 작성합니다(실제 IP, 도메인, 호스트 이름, 자격 증명은 작성하지 마십시오). - 새로운 문제는
references/pitfalls.md에 기록합니다; 자동 감지 가능한 것은survey.sh에 추가합니다; 자동 처리 가능한 것은 스크립트를 개선합니다. - 현재 작업 디렉토리가 해당 저장소인 경우
AGENTS.md를 따릅니다; 그렇지 않으면 보고서를 사용자에게 전달하고 issue 또는 PR을 제출할 것을 권장합니다.
핵심 원칙(각각은 실제 부딪힌 문제에 해당함)
- 재부팅 전 절대
docker stop $(docker ps -q)하지 마십시오:unless-stopped로 설정된 컨테이너를 수동으로 중지하면 부팅 시 자동 시작되지 않습니다; VPN 컨테이너가 중지되면 연결이 끊깁니다.hop.sh reboot를 사용하십시오. - pip로 시스템 site-packages에 설치된 것은 각 점프 전에 반드시 옮겨야 합니다: rpm 파일과 충돌할 경우 업그레이드가 initramfs에서 중간에 실패하여 시스템이 응급 모드에서 '반쯤 업그레이드된' 상태로 멈춥니다.
- 알 수 없거나 비공식적인 것은 모두 먼저 처리해야 합니다: 셀프 컴파일 openssh, 셀프 컴파일 nginx, 타사 repo,
mariadb-libs를 대체한mysql-community-libs... leapp은 이를 무시하며, 업그레이드 후에는 손상됩니다. - 국내에서는 국내 미러를 사용하고 공급업체 소스의 test/nightly 섹션을 비활성화합니다: mirrorlist/metalink는 항상 느리거나 타임아웃이 발생합니다. docker nightly 소스 타임아웃은 leapp을 직접 실패시킵니다. Huawei Cloud 미러는 429 제한이 있을 수 있으며, Alibaba Cloud가 가장 안정적입니다.
pkill -f/pgrep -f패턴을 자신의 명령줄에 사용하지 마십시오, 그렇지 않으면 자신의 SSH 세션을 종료하게 됩니다(우리는 두 번 당했습니다). PID, 스크립트 파일 또는[l]eapp과 같은 표기법을 사용하십시오.- 각 점프마다 기준선을 비교합니다: 업무 문제는 해당 점프에서 발견하여 해당 점프에서 수정합니다. 예: cgroup v2로 인해 Yearning이 충돌하거나 docker-compose v1이 작동하지 않음.
- 업무 동작을 변경하는 결정은 사용자에게 맡깁니다: 구성 요소 삭제, 사이트 내리기, TLS1.0/1.1 비활성화, 데이터베이스 메이저 버전 업그레이드.
참조 문서(필요 시 읽기)
references/pitfalls.md— 모든 문제점: 현상 → 원인 → 처리, 점프별 분류 + 일반. 오류 발생 시 먼저 여기를 확인.references/app-compat.md— 다양한 애플리케이션의 EL7→EL10 호환 처리: nginx, Docker/compose/cgroup v2, Java, Python, PHP, 데이터베이스, postfix, 네트워크, SSH, kubectl/helm, SysV, TLS 암호화 정책.references/cloud-providers.md— Alibaba Cloud/Huawei Cloud/AWS 차이점: 스냅샷, 대역 외 콘솔 전제 조건, 미러 소스, 클라우드 공급업체 에이전트, cloud-init.references/recovery.md— initramfs 응급 모드, 부팅 시 연결 끊김, 반쯤 업그레이드된 상태에서 복구하는 방법(VNC 조작 팁 포함).references/fleet.md— 수십 수백 대 일괄 진행: 자산 테이블, 병렬 조사, 분류, 카나리, 배치, 기록.references/kubernetes.md— K8s 노드 인플레이스 업그레이드: 시작 전에 반드시 알아야 할 사항(접근 경로/컨트롤 플레인 진입점/etcd/스토리지), 호환성 결론, 권장 순서, 부딪힌 문제, 복구 검사 체크리스트.