infra
Platform

모듈 맵

[Linux] BIOS/UEFI, GRUB 로더부터 systemd 시작까지의 부팅 프로세스 완전 분석

0 / 37 완료

펼치기
0 / 37 완료0%

Linux · 24 / 37

[Linux] BIOS/UEFI, GRUB 로더부터 systemd 시작까지의 부팅 프로세스 완전 분석

BIOS/UEFI부터 GRUB, systemd까지 — 서버가 켜지는 과정을 완전히 이해합니다

🚨INCIDENT ALERT
HIGH

새 EC2 인스턴스에 접속했는데 'Permission denied (publickey)'가 납니다. 키 파일은 분명히 지정했는데 왜 안 되는지, 혹은 서버가 갑자기 emergency mode로 빠져 GRUB에서 복구해야 하는 상황입니다. 부팅 프로세스 전 단계(BIOS/UEFI → GRUB → systemd)와 cloud-init 프로비저닝을 이해하면 이런 장애가 터졌을 때 어느 단계에서 무엇을 봐야 하는지 바로 짚을 수 있습니다.

Linux 서버 프로비저닝 & 부팅 프로세스

이번 챕터에서 배울 것
  • 1BIOS/UEFI → GRUB → 커널 → systemd 부팅 전 과정을 단계별로 설명할 수 있다
  • 2AWS EC2 인스턴스를 프로비저닝하고 cloud-init으로 첫 부팅 자동화를 구성할 수 있다
  • 3SSH 키페어를 생성하고 공개키 인증으로 안전하게 접속할 수 있다
  • 4sshd_config로 PasswordAuthentication·PermitRootLogin 등 SSH 보안을 강화할 수 있다
  • 5GRUB Rescue Mode로 진입하고 Emergency Mode 부팅 장애를 복구할 수 있다
실습 환경 준비
현재 시스템 부팅 로그 확인
journalctl -b --no-pager | head -50
SSH 키페어 생성 (ED25519 권장)
ssh-keygen -t ed25519 -C 'your@email.com'
cloud-init 로그 위치

실행 결과와 오류는 /var/log/cloud-init.log에서 확인합니다

실습 환경으로 AWS EC2 또는 로컬 VM(VirtualBox/QEMU) 사용 권장

GRUB 실습은 스냅샷을 먼저 생성한 뒤 진행하세요

💡개념

커널과 쉘의 역할 분리

Linux 부팅 프로세스 — BIOS/UEFI → GRUB → 커널 → systemd 전 단계 흐름

실수로 /usr/local/bin/ 아래 바이너리를 모두 지웠는데, 서버는 살아있고 SSH도 연결됩니다. 그런데 ls, cp, mv 같은 기본 명령어가 전부 "command not found"입니다. 이 상황에서 커널과 쉘이 분리되어 있다는 사실이 복구의 실마리가 됩니다. 커널은 멀쩡히 돌아가고 있으므로, 다른 서버에서 바이너리를 scp로 가져오거나 busybox를 직접 올려서 명령어를 복원할 수 있습니다. "왜 어떤 작업은 sudo가 필요한가", "왜 쉘이 죽어도 서버는 살아있는가"도 같은 구조에서 답이 나옵니다.

Linux 시스템은 크게 **커널 영역(Kernel Space)**과 **사용자 영역(User Space)**으로 나뉩니다. 이 구분을 이해하면 "왜 어떤 작업은 sudo가 필요한가", "왜 쉘이 죽어도 커널은 살아있는가"를 직관적으로 파악할 수 있습니다.

커널(Kernel)의 역할

커널은 하드웨어와 소프트웨어 사이의 중재자입니다. 사용자 프로그램이 직접 하드웨어에 접근하지 못하도록 막고, 시스템 콜(system call) 인터페이스를 통해 제어된 접근을 제공합니다.

커널 기능설명
프로세스 관리CPU 스케줄링, 프로세스 생성/종료 (fork, exec)
메모리 관리가상 메모리, 페이지 할당, OOM Killer
파일시스템ext4, xfs, btrfs 등 VFS 레이어를 통한 추상화
네트워크 스택TCP/IP, 소켓, 방화벽 (netfilter/iptables)
디바이스 드라이버하드웨어 장치와의 통신
보안권한 모델, SELinux/AppArmor, namespace, cgroup

쉘(Shell)의 역할

쉘은 사용자가 입력한 명령을 해석하고 커널에 전달하는 **명령 해석기(command interpreter)**입니다. 쉘 자체는 사용자 영역에서 실행되는 하나의 프로세스입니다.

로컬 터미널
# 현재 사용 중인 쉘 확인
echo $SHELL
# 출력: /bin/bash

# 사용 가능한 쉘 목록
cat /etc/shells
# 출력:
# /bin/sh
# /bin/bash
# /usr/bin/bash
# /bin/zsh
# /usr/bin/zsh
# /usr/bin/fish

주요 쉘 비교:

특징주 사용처
bashBourne Again Shell, 가장 보편적서버 스크립팅 표준
shPOSIX 호환, 경량시스템 초기화 스크립트
zsh자동완성, 플러그인 생태계 풍부개발자 로컬 환경
fish문법 하이라이팅, 사용자 친화적초보자 학습 환경
dash경량, POSIX 호환Ubuntu /bin/sh의 실체

커널-쉘 상호작용 예시:

로컬 터미널
# 사용자가 'ls /var/log'를 입력하면:
# 1. bash(쉘)가 명령을 파싱
# 2. bash가 fork() 시스템콜로 자식 프로세스 생성
# 3. 자식 프로세스가 execve('/bin/ls') 시스템콜 호출
# 4. 커널이 /bin/ls 바이너리를 메모리에 로드
# 5. ls가 openat(), getdents64() 시스템콜로 디렉터리 읽기
# 6. 결과를 write() 시스템콜로 터미널에 출력

# 시스템콜 추적 (실제로 커널과 어떻게 대화하는지 확인)
strace -e trace=openat,read,write ls /var/log 2>&1 | head -20

2. 부팅 프로세스 전체 흐름

💡개념

BIOS/UEFI → GRUB → 커널 → systemd 완전 해부

BIOS/UEFI → GRUB → 커널 → systemd 4단계 부팅 해부와 단계별 장애 진단

서버가 부팅이 안 될 때 "GRUB rescue>" 프롬프트만 뜨고 아무것도 안 됩니다. 어디서부터 손을 대야 할지 모르면 그냥 OS 재설치로 처리하게 됩니다. 부팅 프로세스를 단계별로 이해하면 어느 단계에서 멈췄는지 보이고, 재설치 없이 복구할 수 있습니다. BIOS/UEFI가 하드웨어를 초기화하고 부트 장치를 찾는 단계, GRUB이 커널 이미지를 메모리에 로드하는 단계, 커널이 초기 RAM 디스크(initramfs)를 풀어서 루트 파일시스템을 마운트하는 단계, systemd가 나머지 서비스를 순서대로 올리는 단계 — 각 단계의 역할을 알면 장애 위치를 짚어낼 수 있습니다.

서버의 전원 버튼을 누른 순간부터 로그인 프롬프트가 나타나기까지, 총 4단계의 부팅 과정이 진행됩니다.

전원 ON
  │
  ▼
[1단계] BIOS / UEFI
  │  - 하드웨어 자가진단(POST)
  │  - 부팅 장치 탐색
  │  - 부트로더 로드
  │
  ▼
[2단계] GRUB (부트로더)
  │  - 커널 이미지(vmlinuz) 로드
  │  - initrd/initramfs 로드
  │  - 커널에 파라미터 전달
  │
  ▼
[3단계] 리눅스 커널
  │  - 하드웨어 초기화
  │  - 루트 파일시스템 마운트
  │  - PID 1 프로세스 실행
  │
  ▼
[4단계] systemd (PID 1)
  │  - 타겟(target) 순서대로 서비스 병렬 시작
  │  - 네트워크, 로그인 서비스 활성화
  │
  ▼
로그인 프롬프트 / SSH 접속 준비 완료

1단계: BIOS vs UEFI

항목BIOSUEFI
출시 시기1975년~2005년~
파티션 테이블MBR (최대 2TB, 파티션 4개)GPT (최대 9.4ZB, 128개 파티션)
부팅 속도느림빠름 (병렬 초기화)
보안 부팅없음Secure Boot 지원
설정 UI텍스트 기반그래픽 GUI 가능
AWS EC2레거시 타입Nitro 기반 최신 인스턴스

2단계: GRUB (Grand Unified Bootloader)

GRUB는 커널을 메모리에 로드하고 실행시키는 부트로더입니다.

로컬 터미널
# GRUB 설정 파일 위치
cat /etc/default/grub

# 출력 예시:
# GRUB_TIMEOUT=5
# GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
# GRUB_DEFAULT=saved
# GRUB_DISABLE_SUBMENU=true
# GRUB_TERMINAL_OUTPUT="console"
# GRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"
# GRUB_DISABLE_RECOVERY="true"

GRUB 설정 변경 후 반드시 반영:

로컬 터미널
# RHEL/CentOS/Amazon Linux (BIOS)
grub2-mkconfig -o /boot/grub2/grub.cfg

# RHEL/CentOS (UEFI)
grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg

# Ubuntu (BIOS/UEFI 공통)
update-grub

3단계: initramfs — 임시 루트 파일시스템

커널은 실제 루트 파일시스템을 마운트하기 전에 **initramfs(initial RAM filesystem)**를 메모리에 올려 필요한 드라이버와 도구를 로드합니다.

로컬 터미널
# initramfs 파일 확인
ls -lh /boot/initramfs-$(uname -r).img
# 출력: -rw-------. 1 root root 34M Mar 26 09:00 /boot/initramfs-5.14.0-570.el9.x86_64.img

# initramfs 내용 확인
lsinitrd /boot/initramfs-$(uname -r).img | head -30

4단계: systemd — 현대적 Init 시스템

커널이 루트 파일시스템을 마운트하고 나면 /sbin/init를 실행합니다. 현대 배포판에서 이것은 systemd의 심볼릭 링크입니다.

로컬 터미널
ls -la /sbin/init
# 출력: lrwxrwxrwx. 1 root root 22 Jan 15 12:00 /sbin/init -> ../lib/systemd/systemd

# 부팅 시간 분석
systemd-analyze
# 출력:
# Startup finished in 1.823s (kernel) + 2.918s (initrd) + 8.431s (userspace) = 13.173s
# graphical.target reached after 8.385s in userspace

# 어떤 서비스가 부팅을 느리게 하는지 확인
systemd-analyze blame | head -15
# 출력:
# 3.201s NetworkManager-wait-online.service
# 1.456s dnf-makecache.service
# 0.892s dracut-initqueue.service
# 0.654s systemd-udev-settle.service
# 0.543s lvm2-monitor.service

3. 런레벨 vs systemd Target

전통적인 SysVinit 시스템은 **런레벨(Runlevel)**을 사용했습니다. systemd는 이를 Target으로 대체했으며, 하위 호환성을 위해 런레벨 번호도 여전히 인식합니다.

런레벨과 systemd Target 대응표:

런레벨systemd Target설명
0poweroff.target시스템 종료
1rescue.target단일 사용자 모드 (복구)
2multi-user.target네트워크 없는 다중 사용자
3multi-user.target네트워크 있는 다중 사용자 (서버 표준)
4multi-user.target사용자 정의 (거의 미사용)
5graphical.targetGUI 포함 다중 사용자
6reboot.target재부팅
서버 터미널
# 현재 기본 target 확인
systemctl get-default
# 출력: multi-user.target

# 기본 target 변경 (영구 적용)
systemctl set-default graphical.target

# 즉시 target 전환 (재부팅 없이)
systemctl isolate rescue.target

# 현재 활성화된 target 목록
systemctl list-units --type=target --state=active
# 출력:
# UNIT                   LOAD   ACTIVE SUB    DESCRIPTION
# basic.target           loaded active active Basic System
# cloud-config.target    loaded active active Cloud-config availability
# multi-user.target      loaded active active Multi-User System
# network-online.target  loaded active active Network is Online
# network.target         loaded active active Network
# paths.target           loaded active active Path Units
# remote-fs.target       loaded active active Remote File Systems
# slices.target          loaded active active Slice Units
# sockets.target         loaded active active Socket Units
# sshd-keygen.target     loaded active active OpenSSH Server Key Generation
# sysinit.target         loaded active active System Initialization
# timers.target          loaded active active Timer Units

Target 의존성 확인:

서버 터미널
# multi-user.target이 필요로 하는 서비스 확인
systemctl list-dependencies multi-user.target
# 출력:
# multi-user.target
# ● ├─auditd.service
# ● ├─crond.service
# ● ├─dbus.service
# ● ├─firewalld.service
# ● ├─NetworkManager.service
# ● ├─rsyslog.service
# ● ├─sshd.service
# ● ├─tuned.service
# ● └─basic.target
#   ├─ ...

4. VM/EC2 인스턴스 생성 기초

실습 전 디렉토리와 예제 파일을 먼저 준비합니다.

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/linux/part5/exam_21 && cd /tmp/linux/part5/exam_21

이제 실습을 진행합니다.

AWS EC2 인스턴스 프로비저닝 실습

클라우드에서 서버를 처음 만드는 과정입니다. 여기서는 AWS CLI를 사용한 방법을 다루며, 콘솔 UI와 1:1로 대응됩니다.

사전 준비: AWS CLI 설치 및 설정

로컬 터미널
# AWS CLI 설치 확인
aws --version
# 출력: aws-cli/2.15.0 Python/3.11.6 Linux/5.14.0 exe/x86_64.amzn.2023

# 자격증명 설정
aws configure
# AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region name [None]: ap-northeast-2
# Default output format [None]: json

Key Pair 생성 (EC2 접속용)

로컬 터미널
# Key Pair 생성 (프라이빗 키를 로컬에 저장)
aws ec2 create-key-pair \
  --key-name my-server-key \
  --query 'KeyMaterial' \
  --output text > ~/.ssh/my-server-key.pem

# 권한 설정 (소유자만 읽기 가능 — 없으면 SSH가 거부함)
chmod 400 ~/.ssh/my-server-key.pem

ls -la ~/.ssh/my-server-key.pem
# 출력: -r--------. 1 ec2-user ec2-user 1675 Mar 26 09:00 /home/ec2-user/.ssh/my-server-key.pem

보안 그룹 생성

로컬 터미널
# SSH 접속을 허용하는 보안 그룹 생성
aws ec2 create-security-group \
  --group-name web-server-sg \
  --description "Web server security group" \
  --vpc-id vpc-0123456789abcdef0

# 출력:
# {
#     "GroupId": "sg-0123456789abcdef0"
# }

# SSH 포트(22) 오픈
aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp \
  --port 22 \
  --cidr 0.0.0.0/0

# HTTP 포트(80) 오픈
aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp \
  --port 80 \
  --cidr 0.0.0.0/0

EC2 인스턴스 시작

로컬 터미널
# Amazon Linux 2023 AMI로 t3.micro 인스턴스 시작
aws ec2 run-instances \
  --image-id ami-0c2d3e23e757b5d84 \
  --instance-type t3.micro \
  --key-name my-server-key \
  --security-group-ids sg-0123456789abcdef0 \
  --subnet-id subnet-0123456789abcdef0 \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=my-web-server}]' \
  --count 1

# 인스턴스 상태 확인 (running이 될 때까지 대기)
aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=my-web-server" \
  --query 'Reservations[0].Instances[0].{ID:InstanceId,State:State.Name,IP:PublicIpAddress}'

# 출력:
# {
#     "ID": "i-0123456789abcdef0",
#     "State": "running",
#     "IP": "54.180.123.45"
# }
🔍실행 후 확인할 것
  • aws ec2 describe-instances 결과에서 State.Name이 'running'으로 표시된다
  • PublicIpAddress 필드에 실제 공인 IP가 할당된 것을 확인한다
  • ssh -i my-key.pem ec2-user@<IP> 로 비밀번호 없이 접속이 성공한다
  • aws ec2 describe-security-groups 로 22, 80 포트가 인바운드 허용 목록에 있는 것을 확인한다

5. SSH Key 생성과 sshd_config 설정

SSH Key Pair 생성 — ssh-keygen 완전 활용

SSH 키 기반 인증은 비밀번호 인증보다 훨씬 안전합니다. 서버 접속의 표준 방식입니다.

RSA vs ED25519 키 타입 비교:

항목RSA-4096ED25519
알고리즘RSAEdDSA (타원곡선)
키 길이4096비트256비트 (동등 보안성)
성능느림빠름
호환성거의 모든 시스템OpenSSH 6.5+ (2014년~)
권장레거시 시스템현대 서버
로컬 터미널
# ED25519 키 생성 (현대 표준, 권장)
ssh-keygen -t ed25519 -C "admin@company.com" -f ~/.ssh/id_ed25519

# 출력:
# Generating public/private ed25519 key pair.
# Enter passphrase (empty for no passphrase): [패스프레이즈 입력]
# Enter same passphrase again: [재입력]
# Your identification has been saved in /home/user/.ssh/id_ed25519
# Your public key has been saved in /home/user/.ssh/id_ed25519.pub
# The key fingerprint is:
# SHA256:abc123def456ghi789jkl012mno345pqr678stu admin@company.com
# The key's randomart image is:
# +--[ED25519 256]--+
# |      .o+.       |
# |     . o=+       |
# |    o . =+o      |
# +----[SHA256]-----+

# RSA 4096 키 생성 (레거시 호환 필요 시)
ssh-keygen -t rsa -b 4096 -C "admin@company.com" -f ~/.ssh/id_rsa

# 생성된 키 파일 확인
ls -la ~/.ssh/
# 출력:
# total 20
# drwx------. 2 user user  80 Mar 26 09:00 .
# drwx------. 8 user user 215 Mar 26 09:00 ..
# -rw-------. 1 user user 419 Mar 26 09:01 id_ed25519      ← 프라이빗 키 (절대 공유 금지)
# -rw-r--r--. 1 user user 105 Mar 26 09:01 id_ed25519.pub  ← 퍼블릭 키 (서버에 등록)

# 퍼블릭 키 내용 확인
cat ~/.ssh/id_ed25519.pub
# 출력:
# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBl0mzGpFqKvGHFj8YKo5VzNm9Bg5f2LqXyZ3wPkQrst admin@company.com

서버에 퍼블릭 키 등록:

로컬 터미널
# 방법 1: ssh-copy-id 사용 (권장)
ssh-copy-id -i ~/.ssh/id_ed25519.pub ec2-user@54.180.123.45

# 방법 2: 수동 등록
ssh ec2-user@54.180.123.45 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" < ~/.ssh/id_ed25519.pub

# 방법 3: EC2 user-data로 초기화 시 자동 등록
# (AWS Console의 'Advanced Details > User data'에 입력)
sshd_config — SSH 서버 보안 강화 설정

SSH 데몬의 설정 파일 /etc/ssh/sshd_config를 튜닝하면 보안을 크게 향상시킬 수 있습니다.

로컬 터미널
# 현재 sshd_config 백업
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

# 설정 파일 편집
vi /etc/ssh/sshd_config

보안 강화를 위한 핵심 설정:

로컬 터미널
# /etc/ssh/sshd_config 주요 설정 항목

# 1. SSH 포트 변경 (기본 22 → 다른 포트로, 스캔 공격 감소)
Port 2222

# 2. 루트 직접 로그인 금지
PermitRootLogin no

# 3. 비밀번호 인증 비활성화 (키 인증만 허용)
PasswordAuthentication no
ChallengeResponseAuthentication no

# 4. 빈 비밀번호 허용 금지
PermitEmptyPasswords no

# 5. 퍼블릭 키 인증 활성화
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

# 6. 최대 인증 시도 횟수 제한
MaxAuthTries 3

# 7. 유휴 세션 타임아웃 (300초 = 5분)
ClientAliveInterval 300
ClientAliveCountMax 2

# 8. X11 포워딩 비활성화 (필요 없는 경우)
X11Forwarding no

# 9. 특정 사용자/그룹만 SSH 허용
AllowUsers ec2-user deploy-user
# AllowGroups sysadmin developers

# 10. 배너 설정 (로그인 전 경고 메시지)
Banner /etc/ssh/banner.txt
로컬 터미널
# 설정 문법 검사 (재시작 전 반드시 확인)
sshd -t
# 오류가 없으면 아무 출력도 없음

# 상세 문법 검사
sshd -T | grep -E "^(port|permitrootlogin|passwordauthentication|pubkeyauthentication)"
# 출력:
# port 2222
# permitrootlogin no
# passwordauthentication no
# pubkeyauthentication yes

# SSH 서비스 재시작 (기존 세션은 유지됨)
systemctl reload sshd
# 또는 완전 재시작
systemctl restart sshd

# 서비스 상태 확인
systemctl status sshd
# 출력:
# ● sshd.service - OpenSSH server daemon
#      Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; preset: enabled)
#      Active: active (running) since Thu 2026-03-26 09:05:00 KST; 5s ago
#    Main PID: 1234 (sshd)
첫 SSH 접속 및 접속 설정 파일 관리

매번 긴 명령어를 입력하지 않도록 ~/.ssh/config 파일로 접속 정보를 관리합니다.

로컬 터미널
# ~/.ssh/config 파일 생성
cat > ~/.ssh/config << 'EOF'
# 개발 서버
Host dev-server
    HostName 54.180.123.45
    User ec2-user
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 60

# 운영 서버 (포트 변경)
Host prod-server
    HostName 52.79.200.100
    User deploy-user
    Port 2222
    IdentityFile ~/.ssh/id_rsa
    ServerAliveInterval 60

# 점프 호스트를 통한 내부망 접속
Host internal-db
    HostName 10.0.1.50
    User ubuntu
    ProxyJump dev-server
    IdentityFile ~/.ssh/id_ed25519
EOF

# 파일 권한 설정
chmod 600 ~/.ssh/config

# 이제 간단하게 접속 가능
ssh dev-server
# 위 명령은 아래와 동일:
# ssh -i ~/.ssh/id_ed25519 -p 22 ec2-user@54.180.123.45

# 첫 접속 시 호스트 지문 확인 메시지
# The authenticity of host '54.180.123.45 (54.180.123.45)' can't be established.
# ED25519 key fingerprint is SHA256:abc123def456...
# Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
# Warning: Permanently added '54.180.123.45' (ED25519) to the list of known hosts.

# 접속 후 시스템 정보 확인
uname -a
# 출력: Linux ip-10-0-1-100 5.10.209-198.812.amzn2023.x86_64 #1 SMP Mon Mar 4 20:28:28 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

cat /etc/os-release | head -5
# 출력:
# NAME="Amazon Linux"
# VERSION="2023"
# ID="amzn"
# ID_LIKE="fedora"
# VERSION_ID="2023"

6. 부팅 실패 시 Rescue Mode 진입

GRUB에서 Rescue Mode 진입하기

시스템이 정상 부팅되지 않을 때, GRUB 메뉴에서 Rescue Mode(단일 사용자 모드)로 진입하여 복구 작업을 수행할 수 있습니다.

방법 1: GRUB 메뉴에서 직접 진입

TEXT
# 서버 재부팅 후 GRUB 메뉴가 나타날 때 'e' 키를 눌러 편집 모드 진입

                GNU GRUB  version 2.06

 +------------------------------------------------------------------+
 | Amazon Linux 2023 (5.14.0-570.52.1.el9_6.x86_64)                |
 | Amazon Linux 2023 (5.14.0-284.11.1.el9_3.x86_64)                |
 |                                                                   |
 +------------------------------------------------------------------+

      Use the ^ and v keys to select which entry is highlighted.
      Press enter to boot the selected OS, 'e' to edit the commands...
TEXT
# 편집 모드에서 'linux' 줄 끝을 찾아 아래 파라미터 추가

linux   ($root)/vmlinuz-5.14.0-570.el9.x86_64 root=/dev/mapper/rhel-root \
        ro crashkernel=auto resume=/dev/mapper/rhel-swap \
        rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet \
        systemd.unit=rescue.target    ← 이 줄 추가
        # 또는 단일 사용자 모드:
        # single
        # 또는 init 교체:
        # init=/bin/bash

# Ctrl+X 또는 F10으로 부팅

방법 2: systemctl 명령으로 즉시 전환 (운영 중인 시스템에서)

서버 터미널
# Rescue mode로 전환 (root 패스워드 필요)
systemctl rescue

# Emergency mode로 전환 (최소한의 환경, 네트워크 없음)
systemctl emergency

Rescue Mode에서 일반적인 복구 작업:

로컬 터미널
# 루트 파일시스템 마운트 확인
mount | grep " / "
# 출력: /dev/mapper/rhel-root on / type xfs (ro,relatime,attr2,inode64,logbufs=8,...)

# 읽기-쓰기로 재마운트 (파일 수정을 위해)
mount -o remount,rw /

# 잊어버린 root 패스워드 변경
passwd root
# New password: [새 패스워드 입력]
# Retype new password: [재입력]
# passwd: all authentication tokens updated successfully.

# 손상된 파일시스템 검사 및 복구
fsck -y /dev/sda1

# /etc/fstab 오류로 부팅 실패한 경우 — 잘못된 항목 주석 처리
vi /etc/fstab
# 문제 있는 줄 앞에 # 추가하여 주석 처리

# SELinux 레이블 재설정 (SELinux 문제로 부팅 실패 시)
touch /.autorelabel

# 복구 완료 후 정상 재부팅
systemctl reboot

7. 장애 진단: SSH 접속 거부 (Permission denied) 원인 파악

상황

새 EC2 인스턴스에 키 파일을 지정해서 SSH 접속을 시도했는데 공개키 인증이 거부되어 로그인이 안 됩니다.

원인

클라이언트의 프라이빗 키 권한이 644(600이어야 함), 서버의 authorized_keys 권한·내용 오류, EC2 AMI별 기본 사용자명 불일치(Amazon Linux → ec2-user, Ubuntu → ubuntu), sshd_config에서 PubkeyAuthentication no 설정 중 하나입니다.

진단

SSH 접속 후
# 클라이언트 측 — 상세 디버그로 어느 단계에서 실패하는지 확인
ssh -vvv ec2-user@54.180.123.45 2>&1 | grep -E "Offering|accepts|refused|denied"
# 출력 예:
# debug1: Offering public key: /home/user/.ssh/id_ed25519 ED25519 SHA256:abc123...
# debug1: send_pubkey_test: no mutual signature algorithm   ← 알고리즘 불일치

# 프라이빗 키 권한 확인
ls -la ~/.ssh/
# -rw-r--r--. 1 user user 419 id_ed25519  ← 644는 오류 (600이어야 함)

# 서버 SSH 로그 확인 (콘솔 접근 가능한 경우)
journalctl -u sshd -n 30 --no-pager
# Authentication refused: bad ownership or modes for directory /home/ec2-user/.ssh

해결

로컬 터미널
# 클라이언트 키 권한 수정
chmod 600 ~/.ssh/id_ed25519
chmod 700 ~/.ssh/

# 서버 authorized_keys 권한 수정 (콘솔 접근 후)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

# EC2 AMI별 올바른 사용자명으로 재시도
ssh -i ~/.ssh/id_ed25519 ec2-user@54.180.123.45     # Amazon Linux
ssh -i ~/.ssh/id_ed25519 ubuntu@54.180.123.45       # Ubuntu
ssh -i ~/.ssh/id_ed25519 admin@54.180.123.45        # Debian

상황

서버에 SSH 접속을 시도했는데 ping은 되지만 포트 22에서 Connection refused가 납니다.

원인

sshd 프로세스가 죽었거나, OS 방화벽(firewalld/iptables)이 22번 포트를 차단하고 있거나, AWS 보안그룹에 22번 인바운드 규칙이 누락됐거나, sshd_config 오타로 sshd 시작이 실패한 경우입니다.

진단

로컬 터미널
# 포트 연결 여부 먼저 확인
nc -zv 54.180.123.45 22
# Connection refused → sshd 미구동 또는 방화벽 차단

# 서버 콘솔에서 sshd 상태 확인
systemctl status sshd
# ● sshd.service - OpenSSH server daemon
#    Active: inactive (dead)   ← 죽어있음

# sshd_config 문법 오류 확인
sshd -t
# /etc/ssh/sshd_config: line 42: Bad configuration option: PermitRootLogi  ← 오타

# OS 방화벽 확인
firewall-cmd --list-all | grep -E "services|ports"

해결

서버 터미널
# sshd 재시작 및 부팅 자동 시작 설정
systemctl start sshd
systemctl enable sshd

# firewalld에 SSH 포트 허용
firewall-cmd --permanent --add-service=ssh
firewall-cmd --reload

# AWS 보안그룹에 22번 인바운드 추가
aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp --port 22 --cidr 0.0.0.0/0

# sshd_config 오타 수정 후 재시작
vi /etc/ssh/sshd_config
sshd -t && systemctl restart sshd

상황

서버를 재부팅했는데 콘솔에 emergency mode 메시지가 뜨고 일반 부팅이 안 됩니다. SSH 접속 자체가 불가능합니다.

원인

/etc/fstab에 존재하지 않는 UUID나 마운트 포인트를 지정했거나(볼륨 분리 후 fstab 미수정), 파일시스템이 손상되어 fsck가 필요하거나, 추가 EBS 볼륨을 unmount 없이 제거한 경우입니다.

진단

로컬 터미널
# 증상: 서버 콘솔에 아래 메시지 출력 후 정지
# [FAILED] Failed to mount /data.
# See 'journalctl -xb' for details.
# [DEPEND] Dependency failed for Local File Systems.
#
# Welcome to emergency mode! After logging in, type "journalctl -xb" to view
# system logs, "systemctl reboot" to reboot, "systemctl default" or ^D to
# try again to boot into default mode.
# Give root password for maintenance
# (or press Control-D to continue):

해결

서버 터미널
# root 패스워드 입력 후 emergency shell 진입

# 에러 로그 확인
journalctl -xb | grep -i "failed\|error" | head -20
# Mar 26 09:00:05 server systemd[1]: Failed to mount /data.

# 루트를 읽기-쓰기로 재마운트 후 fstab 확인
mount -o remount,rw /
cat /etc/fstab
# UUID=yyy  /data   ext4    defaults  0 0  ← 없는 장치 UUID

# 현재 연결된 블록 장치 UUID 확인 후 fstab 수정
blkid
vi /etc/fstab
# 문제 줄 주석 처리: #UUID=yyy  /data   ext4    defaults  0 0

# 파일시스템 손상 시 fsck로 복구
fsck -y /dev/xvdb1

# 복구 후 정상 재부팅
systemctl reboot

cloud-init 사용자 스크립트 실행 오류 디버깅

AWS EC2 등 클라우드 인스턴스 기동 시 주입한 user-data 스크립트가 실행되지 않거나 조용히 실패하는 경우가 허다합니다. 이때 SRE는 반드시 아래의 부팅 프로비저닝 물리 로그 경로를 추적해야 합니다.

  • /var/log/cloud-init-output.log: 사용자 데이터 스크립트의 실행 과정에서 발생하는 모든 표준 출력(stdout)과 표준 에러(stderr)가 날것으로 기록되는 핵심 파일입니다. 스크립트 문법 오류나 설치 에러는 모두 이 파일 안에 범죄 증거로 수록되어 있습니다.

8. 실무 관점: 프로비저닝 자동화와 보안

💼
실무 맥락EC2 첫 배포 자동화 — User Data 스크립트와 Terraform IaC로 반복 가능한 서버 프로비저닝 구성
현업 패턴

실제 운영 환경에서는 서버를 수동으로 한 대씩 설정하지 않습니다. Infrastructure as Code(IaC) 도구를 사용하여 프로비저닝을 코드로 정의하고 자동화합니다.

User Data를 통한 EC2 초기화 자동화:

로컬 터미널
#!/bin/bash
# EC2 User Data 스크립트 — 인스턴스 최초 부팅 시 한 번 실행

# 시스템 업데이트
yum update -y

# 필수 패키지 설치
yum install -y nginx git htop

# nginx 설정
cat > /etc/nginx/conf.d/app.conf << 'NGINX'
server {
    listen 80;
    server_name _;
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
NGINX

# nginx 활성화 및 시작
systemctl enable nginx
systemctl start nginx

# SSH 보안 강화
sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd

# 완료 로그
echo "$(date): Provisioning complete" >> /var/log/user-data.log

Terraform을 이용한 선언적 프로비저닝:

로컬 터미널
# main.tf (Terraform 설정 예시)
# resource "aws_instance" "web_server" {
#   ami           = "ami-0c2d3e23e757b5d84"
#   instance_type = "t3.micro"
#   key_name      = aws_key_pair.deployer.key_name
#
#   vpc_security_group_ids = [aws_security_group.web.id]
#
#   user_data = file("setup.sh")
#
#   tags = {
#     Name        = "web-server"
#     Environment = "production"
#   }
# }

# 인프라 배포
terraform init
terraform plan    # 변경 사항 미리 확인
terraform apply   # 실제 적용

현업 프로비저닝 도구 비교:

도구특징주 사용처
Terraform선언적 IaC, 멀티 클라우드인프라 생성/관리
Ansible에이전트리스, YAML 플레이북서버 구성 관리
AWS CDK프로그래밍 언어로 IaC 작성AWS 전용
Packer이미지 빌드 자동화AMI/VM 이미지 생성
cloud-init클라우드 초기화 표준부팅 시 초기 설정
💼
실무 맥락SSH 보안 강화 체크리스트 — fail2ban 브루트포스 차단, 감사 로그 설정, 비밀번호 인증 비활성화
현업 패턴

서버 보안에서 SSH는 가장 많이 공격받는 진입점입니다. 현업에서는 다음 사항들을 필수로 체크합니다.

SSH 접속 로그 모니터링:

로컬 터미널
# 최근 성공한 SSH 로그인 확인
last | grep ssh | head -20
# 출력:
# ec2-user pts/0  203.0.113.10  Thu Mar 26 09:00   still logged in
# ec2-user pts/1  198.51.100.5  Thu Mar 26 08:30 - 08:45  (00:15)

# 실패한 로그인 시도 확인 (무차별 대입 공격 탐지)
journalctl -u sshd | grep "Failed password\|Invalid user\|Connection closed" | tail -20
# 출력:
# Mar 26 02:13:05 server sshd[5678]: Invalid user admin from 185.220.101.45
# Mar 26 02:13:06 server sshd[5679]: Invalid user root from 185.220.101.45
# Mar 26 02:13:07 server sshd[5680]: Invalid user postgres from 185.220.101.45

# 공격 IP 확인 및 차단
# fail2ban 설치 (자동 IP 차단)
yum install -y fail2ban
systemctl enable --now fail2ban

# fail2ban 상태 확인
fail2ban-client status sshd
# 출력:
# Status for the jail: sshd
# |- Filter
# |  |- Currently failed: 3
# |  |- Total failed:     127
# |  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
# `- Actions
#    |- Currently banned: 5
#    |- Total banned:     23
#    `- Banned IP list:   185.220.101.45 185.220.101.46 ...

# SSH 감사 로그 설정 (누가 어떤 명령을 실행했는지 기록)
# /etc/profile.d/audit.sh
# export HISTTIMEFORMAT="%F %T "
# export PROMPT_COMMAND='history -a >(tee -a /var/log/user-commands.log | logger -t "user_cmd")'

현업 SSH 보안 체크리스트:

로컬 터미널
# 1. 비밀번호 인증 비활성화 확인
sshd -T | grep passwordauthentication
# 출력: passwordauthentication no  ← 반드시 no여야 함

# 2. Root 직접 접속 금지 확인
sshd -T | grep permitrootlogin
# 출력: permitrootlogin no  ← 반드시 no여야 함

# 3. 오래된 SSH 키 확인 및 교체 (90일 이상 된 키)
find /home -name authorized_keys -exec ls -la {} \;

# 4. 사용하지 않는 계정 비활성화
usermod -L old-employee-user    # 계정 잠금
passwd -l old-employee-user     # 비밀번호 잠금

# 5. SSH 접속 허용 IP 범위 제한 (보안 그룹 또는 TCP Wrappers)
cat /etc/hosts.allow
# sshd: 10.0.0.0/8, 203.0.113.0/24

cat /etc/hosts.deny
# sshd: ALL

# 6. 정기적인 보안 업데이트 적용
yum update --security -y
# 또는
apt-get update && apt-get upgrade -y

다음 모듈에서는 Cron & 작업 스케줄링 — crontab -e로 주기적 명령 실행을 자동화하고 systemd timer로 더 강력한 작업 스케줄링을 구성하는 방법을 다룹니다.

지식 확인

퀴즈 — 5문제

Q1

cloud-init에서 서버 최초 부팅 시 패키지를 설치하고 명령을 실행하려면 user-data에 어떤 헤더로 시작해야 합니까?

Q2

cloud-config에서 첫 부팅 시 임의 명령을 실행하려면 어떤 디렉티브를 사용합니까?

Q3

새 EC2 인스턴스에 user-data로 배포한 스크립트가 실행됐는지, 왜 실패했는지 확인하려 합니다. 가장 적절한 확인 방법은?

Q4

cloud-config의 write_files 디렉티브로 서버에 파일을 생성할 때 필수로 지정해야 하는 필드는 무엇입니까?

Q5

cloud-init을 사용해 패키지를 설치할 때 올바른 cloud-config 작성 방법은 무엇입니까?

0 / 5 답변

🧪 실습으로 확인하기

새 서버 인수인계 — 처음 30분

초급

낯선 Linux 서버를 인수받았을 때 OS, 서비스, 로그를 빠르게 파악하는 루틴을 직접 수행한다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

linux중급 · 40
[Linux] chrony & NTP 서버 동기화로 트랜잭션 시간 오차 방지
Linux 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점