우분투(ubuntu) 22.04 -> 26.04 업그레이드 실패, 재설치 없이 복구하기
한 단계 건너뛴 LTS 업그레이드가 configure 단계에서 끊기면 sudo와 네트워크가 함께 사라집니다. dpkg 상태를 세어보면 재설치 없이 복구할 수 있는지 판단이 섭니다.

22.04에서 26.04로 한 번에 올렸습니다. 정상 경로는 22 → 24 → 26인데 중간 단계를 건너뛴 것입니다. 재부팅하고 나니 SSH가 붙지 않았습니다.
처음에는 SSH만 Connection refused였고 웹 서비스 포트는 살아 있었습니다. 잠시 뒤에는 그것마저 끊겼습니다. ping도 안 되고 ARP 조회조차 INCOMPLETE로 나왔습니다. 네트워크 스택 자체가 올라오지 않은 상태였습니다.
세 가지가 동시에 무너져 있었습니다
콘솔로 들어가 확인하니 문제가 하나가 아니었습니다.
첫째, netplan 설정이 파싱되지 않았습니다. 설정 파일에 gateway4: 키가 남아 있었는데 26.04에서 제거된 키입니다. 파싱이 실패하니 인터페이스가 state DOWN인 채로 IP가 붙지 않았습니다.
둘째, 패키지 750개가 install ok unpacked 상태였습니다. 압축은 풀렸는데 configure가 끝나지 않은 상태입니다. 그 750개 안에 sudo, openssh-server, netplan.io가 들어 있었습니다. SSH가 안 되는 것도, 네트워크가 안 되는 것도 여기서 설명됩니다.
셋째, root 계정이 잠겨 있었습니다. /etc/shadow의 root 해시가 *였습니다. 평소에는 문제가 안 됩니다. sudo를 쓰면 되니까요. 그런데 그 sudo가 configure되지 않은 750개 중 하나였습니다.
정리하면 콘솔 앞에 앉아 있어도 권한을 얻을 방법이 없는 상태였습니다. su는 root가 잠겨서 안 되고, sudo는 바이너리가 없어서 안 됩니다.
재설치를 피할 수 있는지부터 셌습니다
이 상황에서 대부분 재설치를 택합니다. 이 서버는 스냅샷도 백업도 없었으니 더 그랬습니다.
그전에 dpkg 상태를 상태별로 세어봤습니다.
dpkg-query -W -f='${db:Status-Abbrev}\n' | sort | uniq -c | sort -rn
결과에서 중요한 것은 두 숫자였습니다.
| dpkg 상태 | 개수 | 의미 |
|---|---|---|
install ok unpacked |
750 | 파일은 다 풀렸고 설정 단계만 남음 |
half-installed |
0 | 파일 손상 없음 |

half-installed는 압축을 푸는 도중에 끊겨 파일이 깨진 상태입니다. 이게 많으면 복구가 어렵습니다. 0이라면 파일은 온전하고 dpkg --configure만 마치면 됩니다.
재설치하지 않기로 한 근거가 이 숫자였습니다.
한 가지 주의할 점이 있습니다. dpkg-query 결과를 grep으로 대충 세면 triggers-pending 같은 상태가 섞여 들어가 과대집계됩니다. 판단의 근거로 쓸 숫자라면 위처럼 상태별로 uniq -c 해서 정확히 세는 편이 좋습니다.
GRUB이 아니라 SeaBIOS였습니다
복구 방향은 정해졌는데 들어갈 방법이 없었습니다. 부팅 시 Esc를 눌러 GRUB에서 init=/bin/bash로 들어가려 했는데, 뜬 것은 GRUB이 아니라 SeaBIOS 부팅 메뉴와 iPXE였습니다. 가상화 환경에서는 Esc가 하이퍼바이저의 펌웨어 메뉴에 먼저 걸립니다.
여기서 GRUB 진입 방법을 계속 찾는 대신 방향을 바꿨습니다. 게스트를 끄고 호스트에서 디스크를 직접 마운트하는 쪽이 확실합니다.
qm stop <VMID>
losetup -fP --show /dev/pve/vm-<VMID>-disk-0
losetup -P는 파티션 테이블을 읽어 /dev/loopNp1, p2 형태로 잡아줍니다. kpartx나 partprobe가 설치돼 있지 않아도 됩니다.
이 디스크는 파티션이 이렇게 나뉘어 있었습니다.
p1 1M
p2 2G xfs /boot
p3 8G swap
p4 90G xfs /
중첩 LVM이 없어서 그대로 마운트할 수 있었습니다. LVM이 끼어 있다면 vgscan 후 vgchange -ay가 한 단계 더 필요합니다.
마운트한 뒤 두 가지를 고쳤습니다.
netplan을 26.04 문법으로 다시 썼습니다.
# 이전 (26.04에서 제거된 키)
gateway4: 192.168.0.1
# 이후
routes:
- to: default
via: 192.168.0.1
netplan 설정 파일은 권한이 느슨하면 경고를 냅니다. chmod 600까지 맞춰줬습니다.
root 잠금을 풀었습니다. /etc/shadow에서 root의 *를, 아는 비밀번호를 가진 일반 계정의 해시로 복사했습니다. 편집 전 shadow.bak으로 백업해 두었습니다. sudo가 없는 상태에서 콘솔 복구 수단을 확보하는 목적입니다.
부팅하니 configure가 알아서 끝났습니다
언마운트하고 losetup -d로 정리한 뒤 게스트를 켰습니다.
부팅 과정에서 미완료 configure가 자동으로 진행됐습니다. unpacked 750개가 0이 되고 sudo가 돌아왔습니다. 별도로 dpkg --configure -a를 칠 필요도 없었습니다.
네트워크가 살아난 것은 netplan을 고쳤기 때문입니다. 이 두 가지가 서로 물려 있었습니다. 네트워크가 없으면 패키지를 못 받고, 패키지가 configure되지 않으면 netplan 도구 자체가 동작하지 않습니다.
DNS는 따로 살려야 했습니다
여기서 한 번 더 막혔습니다. IP는 붙었고 통신도 되는데 이름 해석이 전혀 안 됐습니다. google.com조차 풀리지 않았습니다.
원인은 중간에 급한 대로 ip 명령으로 IP만 수동으로 올려둔 것이었습니다.
resolvectl status
# Current Scopes: none <- DNS 서버가 등록되지 않음
/etc/resolv.conf는 systemd-resolved의 stub(127.0.0.53)을 가리킵니다. 그런데 ip addr add로 주소만 붙이면 DNS 서버 정보가 systemd-resolved에 전달되지 않습니다. 주소는 있고 이름 해석만 없는 상태가 됩니다.
netplan apply
resolvectl status
# Current Scopes: DNS <- 정상
netplan apply로 정식 적용하고 나서야 해석이 됐습니다. 애플리케이션 하나가 계속 기동에 실패하고 있었는데, 로그를 보니 원인이 DB 호스트명에 대한 UnknownHostException이었습니다. DNS를 살리고 재시작하니 4.6초 만에 떴습니다.
수동으로 IP를 올려 급한 불을 껐다면, 그 상태는 임시입니다. 이름 해석이 빠져 있고 재부팅하면 또 사라집니다.
다음 두 대는 사고 없이 올렸습니다
같은 작업을 다른 서버 두 대에도 해야 했습니다. 이번에는 세 가지를 다르게 했습니다.
한 단계씩 올렸습니다. 22.04 → 24.04 → 26.04. 그리고 단계마다 재부팅했습니다. 실제로 한 대는 재부팅 없이 다음 단계를 시도하니 You have not rebooted after updating a package which requires a reboot로 거부당했습니다. 재부팅 후 재시도하니 정상 진행됐습니다.
gateway4를 26.04로 가기 전에 미리 제거했습니다. 24.04 단계에서 routes: 문법으로 바꿔뒀습니다. 이 서버들의 설정에도 gateway4가 남아 있었으니, 그대로 갔으면 같은 사고가 났을 것입니다.
각 단계 뒤에 dpkg --configure -a와 apt -f install을 넣었습니다. configure 중단이 첫 사고의 본질이었기 때문입니다.
결과는 두 대 모두 unpacked 0, half-installed 0, 실패 유닛 0이었습니다.
작업 전에는 스냅샷을 찍었고, root 계정이 활성 상태인지도 확인했습니다. sudo가 날아가도 콘솔로 들어갈 수 있어야 하기 때문입니다. 첫 사고에서 가장 아팠던 것이 그것이었습니다.
한 가지 더 있습니다. 시점에 따라 24.04에서 26.04로 가는 표준 경로가 아직 열려 있지 않을 수 있습니다. do-release-upgrade가 There is no development version of an LTS available를 반환한다면 그 경우입니다. /etc/update-manager/release-upgrades의 Prompt=lts를 normal로 바꿔야 진행되는데, 이건 아직 정식 경로가 아니라는 뜻이기도 하니 판단해서 쓰시는 게 좋습니다.
정리
같은 상황을 만났을 때 순서는 이렇습니다.
dpkg상태를 상태별로 셉니다.half-installed가 0이고unpacked만 많다면 재설치하지 않아도 됩니다- 가상화 환경이면 GRUB과 씨름하지 말고 디스크를 호스트에서 마운트합니다.
losetup -fP한 줄이면 됩니다 - 네트워크 설정 문법 변경을 먼저 확인합니다.
gateway4처럼 릴리스에서 제거된 키가 남아 있으면 부팅 후 손쓸 방법이 사라집니다 - 수동
ip설정은 임시입니다.netplan apply까지 해야 DNS가 살아납니다
그리고 애초에 이 상황을 만들지 않는 조건이 두 가지 있습니다. LTS는 한 단계씩 올리고, 올리기 전에 스냅샷을 찍는 것입니다. 둘 다 하지 않아서 복구에 훨씬 긴 시간이 들었습니다.
이번 건에서 실서비스 중단이 없었던 것은 같은 역할을 하는 서버가 한 대 더 있었기 때문입니다. 덕분에 강제 정지와 재부팅을 여유 있게 할 수 있었습니다. 이중화는 장애를 막아주기도 하지만, 복구하는 사람이 서두르지 않게 해준다는 점에서도 값을 합니다.