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

2026. 6. 14. 20:14·Linux
SMALL

" - "## cloud-init" answer: 1 explanation: "#cloud-config 헤더로 시작하면 cloud-init이 해당 내용을 YAML cloud-config 형식으로 파싱합니다. 이 형식에서 packages, runcmd, write_files 등의 디렉티브를 사용할 수 있습니다."

  • q: "cloud-config에서 첫 부팅 시 임의 명령을 실행하려면 어떤 디렉티브를 사용합니까?" options:
    • "commands:"
    • "scripts:"
    • "runcmd:"
    • "exec:" answer: 2 explanation: "runcmd 디렉티브는 cloud-init이 완료된 후 실행할 쉘 명령 목록을 지정합니다. 각 항목은 문자열이나 리스트로 작성할 수 있으며, 순서대로 한 번 실행됩니다."
  • q: "새 EC2 인스턴스에 user-data로 배포한 스크립트가 실행됐는지, 왜 실패했는지 확인하려 합니다. 가장 적절한 확인 방법은?" options:
    • "cat /etc/cloud/cloud.cfg에서 실행 기록을 확인한다"
    • "cat /var/log/cloud-init-output.log에서 스크립트 출력을 확인한다"
    • "journalctl -u user-data로 systemd 로그를 확인한다"
    • "EC2 콘솔 인스턴스 스크린샷에서 부팅 로그를 확인한다" answer: 1 explanation: "/var/log/cloud-init-output.log에는 user-data 스크립트의 표준 출력과 오류가 그대로 기록됩니다. 스크립트가 실행됐는지, 어느 줄에서 실패했는지 파악할 수 있는 가장 직접적인 로그입니다. cloud-init.log는 cloud-init 자체 동작 로그입니다."
  • q: "cloud-init의 write_files로 서버 부팅 시 /etc/myapp/config.yaml을 생성하려 합니다. 설정이 전혀 반영되지 않는다면, 어떤 필드가 누락됐을 가능성이 높습니까?" options:
    • "name과 data — write_files에 없는 필드"
    • "path(파일 경로)와 content(파일 내용) — 두 필드가 모두 필수"
    • "destination과 text — write_files에 없는 필드"
    • "file과 body — write_files에 없는 필드" answer: 1 explanation: "write_files 디렉티브에서 path(파일 경로)와 content(파일 내용)는 필수 필드입니다. 이 둘 중 하나라도 없으면 파일이 생성되지 않습니다. 선택적으로 permissions(권한), owner(소유자), encoding 등을 지정할 수 있습니다."
  • q: "신규 EC2 인스턴스에 cloud-init으로 nginx와 git을 자동 설치하려 합니다. 다음 cloud-config 중 올바르게 작동하는 것은?" options:
    • "install:\n - nginx\n - git (install 디렉티브는 존재하지 않음)"
    • "apt:\n packages:\n - nginx (Ubuntu 전용, 다른 OS에서 동작 안 함)"
    • "packages:\n - nginx\n - git (배포판 무관하게 자동 감지)"
    • "pkg_install:\n - nginx\n - git (존재하지 않는 디렉티브)" answer: 2 explanation: "packages 디렉티브를 사용하면 cloud-init이 시스템의 패키지 관리자(apt/yum/dnf 등)를 자동으로 감지해 설치합니다. 배포판 독립적으로 동작하는 표준 방법입니다."

— — —

시나리오

새 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)**입니다. 쉘 자체는 사용자 영역에서 실행되는 하나의 프로세스입니다.

bash# 현재 사용 중인 쉘 확인
echo $SHELL
# 출력: /bin/bash

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

주요 쉘 비교:

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

커널-쉘 상호작용 예시:

bash# 사용자가 '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

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

2단계: GRUB (Grand Unified Bootloader)

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

bash# 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 설정 변경 후 반드시 반영:

bash# 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)**를 메모리에 올려 필요한 드라이버와 도구를 로드합니다.

bash# 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의 심볼릭 링크입니다.

bashls -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 설명
0 poweroff.target 시스템 종료
1 rescue.target 단일 사용자 모드 (복구)
2 multi-user.target 네트워크 없는 다중 사용자
3 multi-user.target 네트워크 있는 다중 사용자 (서버 표준)
4 multi-user.target 사용자 정의 (거의 미사용)
5 graphical.target GUI 포함 다중 사용자
6 reboot.target 재부팅
bash# 현재 기본 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 의존성 확인:

bash# 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 인스턴스 생성 기초

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

bash# 실습 디렉토리 준비
mkdir -p /tmp/linux/part5/exam_21 && cd /tmp/linux/part5/exam_21

이제 실습을 진행합니다.

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

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

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

bash# 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 접속용)

bash# 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

보안 그룹 생성

bash# 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 인스턴스 시작

bash# 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"
# }
관찰 포인트

— — —

5. SSH Key 생성과 sshd_config 설정

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

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

RSA vs ED25519 키 타입 비교:

항목 RSA-4096 ED25519
알고리즘 RSA EdDSA (타원곡선)
키 길이 4096비트 256비트 (동등 보안성)
성능 느림 빠름
호환성 거의 모든 시스템 OpenSSH 6.5+ (2014년~)
권장 레거시 시스템 현대 서버
bash# 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

서버에 퍼블릭 키 등록:

bash# 방법 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를 튜닝하면 보안을 크게 향상시킬 수 있습니다.

bash# 현재 sshd_config 백업
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

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

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

bash# /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
bash# 설정 문법 검사 (재시작 전 반드시 확인)
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 파일로 접속 정보를 관리합니다.

bash# ~/.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 명령으로 즉시 전환 (운영 중인 시스템에서)

bash# Rescue mode로 전환 (root 패스워드 필요)
systemctl rescue

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

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

bash# 루트 파일시스템 마운트 확인
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) 원인 파악

트러블슈팅
ssh ec2-user@54.180.123.45 — Permission denied (publickey,gssapi-keyex,gssapi-with-mic)

상황

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

원인

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

진단

bash# 클라이언트 측 — 상세 디버그로 어느 단계에서 실패하는지 확인
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

해결

bash# 클라이언트 키 권한 수정
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: connect to host 54.180.123.45 port 22: Connection refused (sshd 미구동 또는 방화벽/보안그룹 차단)

상황

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

원인

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

진단

bash# 포트 연결 여부 먼저 확인
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"

해결

bash# 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
트러블슈팅
You are in emergency mode! — /etc/fstab 오류 또는 파일시스템 손상으로 서버 부팅 실패

상황

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

원인

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

진단

bash# 증상: 서버 콘솔에 아래 메시지 출력 후 정지
# [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):

해결

bash# 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 초기화 자동화:

bash#!/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을 이용한 선언적 프로비저닝:

bash# 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 접속 로그 모니터링:

bash# 최근 성공한 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 보안 체크리스트:

bash# 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로 더 강력한 작업 스케줄링을 구성하는 방법을 다룹니다.

💡 더 많은 인프라 관련 실무 지식과 실습을 경험해보고 싶으신가요?

리눅스, 도커, 데이터베이스, 네트워킹 등 다양한 인프라 기술을 가상 환경에서 직접 실습하며 학습할 수 있습니다.
기술에 관심이 있고 한 단계 더 성장하고 싶다면, 인프라 학습 플랫폼에서 무료로 학습을 시작해 보세요!
👇 실습 플랫폼으로 이동하려면 아래 사진을 클릭해 주세요! 👇

인프라 학습 플랫폼 바로가기
반응형
LIST

'Linux' 카테고리의 다른 글

[Linux] cron으로 리눅스 주기적 반복 작업(배치) 예약 및 백업 자동화  (0) 2026.06.14
[Linux] firewalld & iptables로 특정 IP/포트 차단 및 허용 규칙 설정  (0) 2026.06.14
[Linux] 서버 장애 초동 대응 — 5분 트리아지  (1) 2026.05.23
[Linux] SSH 고급 & 보안 하드닝  (0) 2026.05.22
[Linux] 포트 & 네트워크 진단  (0) 2026.05.22
'Linux' 카테고리의 다른 글
  • [Linux] cron으로 리눅스 주기적 반복 작업(배치) 예약 및 백업 자동화
  • [Linux] firewalld & iptables로 특정 IP/포트 차단 및 허용 규칙 설정
  • [Linux] 서버 장애 초동 대응 — 5분 트리아지
  • [Linux] SSH 고급 & 보안 하드닝
cumo
cumo
  • cumo
    이것저것
    cumo
    • 분류 전체보기 (157)
      • 이것저것 (1)
      • 보안뉴스 (15)
      • Project (12)
      • wargame (1)
      • Cloud (25)
      • DevOps (21)
      • Linux (46)
      • 네트워크 (23)
      • AWS Developer BootCamp (1)
      • WEB&WAS (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 도구모음 사이트
    • 참고 기술 블로그
  • 공지사항

  • 인기 글

  • 태그

    CIS 벤치마크
    vi
    bash 알림
    서버 헬스체크 스크립트
    슬랙 알림 bash
    서비스 다운 감지
    서버관리
    Linux
    ubuntu
    리눅스
    루트 로그인 비활성화
    데몬
    nano
    텍스트편집기
    vim
    서버편집
    curl 헬스체크
    bash 자동화
    인프라
    자동 재시작 스크립트
  • 최근 댓글

  • 최근 글

  • 반응형
  • hELLO· Designed By정상우.v4.10.3
cumo
[Linux] BIOS/UEFI, GRUB 로더부터 systemd 시작까지의 부팅 프로세스 완전 분석
상단으로

티스토리툴바