우분투 커널 업데이트 후 겪은 트러블슈팅

오늘 우분투 시스템의 커널을 업데이트했다. 평소처럼 별 생각 없이 진행한 업데이트였는데, 갑자기 중간에 문제가 발생했다. igc(Intel Ethernet Controller) 드라이버가 작동을 안 하는 거다. 당황해서 ifconfig를 확인해보니 외부 네트워크 인터페이스가 아예 사라져있었다.

문제를 해결하기 위해 바로 modprobe로 드라이버를 수동으로 로드하려 했다:

sudo modprobe igc
module igc not found in directory /lib/modules/5.15.0-126-generic

나중에 알고 보니 이런 드라이버 문제가 발생했을 때는 먼저 dmesg 로그를 확인해서 정확한 오류 원인을 파악하는 것이 더 올바른 접근 방법이었다. 하지만 그땐 몰라서 바로 modprobe를 시도했다.

네트워크가 안 되니까 당황스러웠지만, 차분히 원인을 생각해봤다. 보통 이런 문제가 생기는 건 몇 가지 경우가 있더라:

  1. 커널 업데이트하면서 드라이버 모듈이 제대로 안 깔렸거나 파일이 없어진 경우
  2. linux-modules-extra 패키지가 업데이트 과정에서 문제가 생긴 경우
  3. 네트워크 드라이버 패키지들이 의존성 문제로 제대로 설치가 안 된 경우

다행히 이전에 비슷한 경험이 있어서, 먼저 이전 커널이 남아있는지 확인해봤다:

ls /boot/vmlinuz*

이전 버전이 있길래, GRUB 설정을 수정하기로 했다. 이때도 실수를 했는데, GRUB 설정을 수정하기 전에 백업을 먼저 만들어두는 게 안전한 방법이었다는 걸 나중에 알았다. 아무튼 설정을 수정했다:

sudo cp /etc/default/grub /etc/default/grub.backup  # 이렇게 백업을 먼저 했어야 했다
sudo nano /etc/default/grub

GRUB 설정에서 이것들을 바꿨다:

GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=10

그리고 바로 적용:

sudo update-grub

시스템을 재부팅하고 GRUB 메뉴에서 "Advanced options for Ubuntu"를 선택한 다음, 이전 버전인 5.15.0-125-generic 커널을 선택했다.

다행히 이전 커널로 들어오니까 네트워크가 정상적으로 돌아왔다! 여기서 다시 업데이트를 시도해보니 이번엔 문제없이 잘 됐다. 아마도 이전 업데이트 과정에서 뭔가 충돌이 있었던 것 같다.

나중에 찾아보니 이런 상황에서 해결할 수 있는 여러 방법들이 있었다. sudo apt-get -f install로 의존성 문제를 해결할 수 있고, 더 복잡한 의존성 문제는 sudo apt-get dist-upgrade로 해결할 수 있다고 한다. 문제가 있는 커널은 sudo apt-get purge linux-image-<version>으로 완전히 제거할 수도 있었다.

또 Ubuntu에서는 Canonical의 Livepatch 서비스를 사용하면 재부팅 없이도 중요 보안 패치를 적용할 수 있다는 것도 알게 됐다. 특히 커널 업데이트는 공식 Ubuntu 리포지토리를 통해서만 하는 게 가장 안전하다고 한다. 다음번엔 이런 방법들도 참고해야겠다.

이번 문제를 해결하면서 리눅스 시스템의 부팅 과정을 더 깊이 이해하게 됐다. 전체 부팅 과정이 얼마나 체계적인지 새삼 실감했다.

시스템이 시작될 때는 먼저 CPU가 리셋벡터를 통해 BIOS/UEFI 펌웨어에 접근해서 BIOS 코드를 실행한다. 이때 POST(Power On Self Test) 과정으로 CPU, 메모리, 주변장치 등의 하드웨어를 검사하고 초기화한다.

그 다음엔 GRUB이라는 부트로더가 동작하는데, 이게 /boot/grub 파일시스템에서 커널(vmlinuz)을 찾아서 압축을 풀어 메모리에 올린다. 내가 겪은 문제도 이 단계에서 필요한 네트워크 드라이버와 모듈이 제대로 로드되지 않아서 생긴 거였다.

커널이 초기화되면 하드웨어를 점검하고 그 결과를 /var/log/dmesg에 기록한다. 여기서 루트 파일시스템을 읽기 전용으로 마운트하는데, 이 과정이 실패하면 "커널 패닉"이라는 심각한 오류가 발생한다. 그 다음 swapper 프로세스(PID 0)가 장치 드라이버를 초기화하고 init 프로세스(PID 1)를 실행한다.

다행히도 Ubuntu나 RHEL/CentOS 같은 리눅스 시스템들은 새 커널이 설치되어도 이전 커널을 자동으로 보관한다. 이런 멀티 커널 지원 덕분에 새 커널에서 문제가 생겨도 이전 커널로 안전하게 돌아갈 수 있다. 오늘 내 경우처럼 말이다.

문제를 해결하고 난 후에 커널 업데이트 전에 해야 할 것들도 찾아봤다. 중요 파일들은 미리 백업해두고, 패키지 목록도 최신화(sudo apt update)한 다음에 시스템 전체 업그레이드(sudo apt upgrade)를 진행하는 게 좋다고 한다. 그리고 의존성 문제가 있다면 sudo apt-get dist-upgrade를 사용하는 것도 좋은 방법이라고 한다.

이번 경험을 통해 시스템 문제 해결의 기본이 로그 분석이라는 걸 깨달았다. dmesg, journalctl, /var/log/ 아래의 각종 로그 파일들이 문제 해결의 실마리를 제공한다. 특히 하드웨어 드라이버 문제는 dmesg가, 시스템 서비스 문제는 journalctl이 유용하다는 걸 배웠다.

또한 리눅스의 계층적 구조가 얼마나 유용한지도 알게 됐다. 커널, 드라이버, 시스템 서비스 등 각 계층이 독립적으로 작동하면서도 서로 연결되어 있어서, 문제가 발생했을 때 영향받는 계층을 격리하고 진단할 수 있다. 이런 구조 덕분에 오늘처럼 커널 문제가 생겼을 때도 다른 커널로 부팅해서 시스템을 복구할 수 있었다.

앞으로는 시스템 변경 전에 현재 상태를 꼼꼼히 기록하고, 각종 로그를 주기적으로 모니터링하면서 시스템을 관리해야겠다. 리눅스 시스템은 문제가 생겼을 때 원인을 찾을 수 있는 도구들을 이미 잘 갖추고 있으니, 이것들을 잘 활용하는 게 핵심일 것 같다.

0개의 댓글