본문으로 건너뛰기 개발자가 알아야 할 우분투 필수 명령어 및 활용법 | apt·systemd·ufw 실전 가이드

개발자가 알아야 할 우분투 필수 명령어 및 활용법 | apt·systemd·ufw 실전 가이드

개발자가 알아야 할 우분투 필수 명령어 및 활용법 | apt·systemd·ufw 실전 가이드

이 글의 핵심

우분투(Ubuntu) 배포판에 특화된 apt 패키지 관리, systemd/systemctl 서비스 운영, ufw 방화벽 설정, journalctl 로그 분석, 사용자 관리, 서버 초기 세팅까지 실전 예제와 함께 정리한 가이드입니다.

들어가며

클라우드 VM을 새로 발급받으면 대부분 우분투(Ubuntu) 이미지를 선택하게 됩니다. AWS EC2, GCP Compute Engine, DigitalOcean, Vultr 등 거의 모든 클라우드 제공업체가 Ubuntu LTS를 기본 옵션으로 제공하기 때문에, 백엔드 개발자든 DevOps 엔지니어든 우분투 서버를 다뤄야 하는 순간이 반드시 옵니다.

문제는 범용 리눅스 명령어(ls, cd, grep 등)만 알아서는 우분투 서버를 제대로 운영할 수 없다는 점입니다. 패키지를 설치하려면 apt를, 서비스를 관리하려면 systemctl을, 방화벽을 설정하려면 ufw를 알아야 합니다. 이들은 Debian 계열 배포판(우분투 포함)에 특화된 도구이며, CentOS/RHEL 계열의 yum/dnf, firewalld와는 완전히 다른 문법을 씁니다.

이 글은 우분투 배포판 고유의 관리 도구에 초점을 맞춥니다. 파일 조작이나 텍스트 처리 같은 범용 명령어는 리눅스·맥 명령어 실전 가이드에서 이미 다뤘으므로, 이 글에서는 짧게만 언급하고 넘어갑니다.

대상 독자

  • 클라우드에 처음 우분투 서버를 배포하는 백엔드 개발자
  • Docker/Kubernetes 노드로 우분투를 운영하는 DevOps 엔지니어
  • apt, systemd, ufw의 차이가 헷갈리는 리눅스 입문자

이 글을 읽으면

  • apt 패키지 관리와 저장소 구조를 이해하고 안전하게 시스템을 업데이트할 수 있습니다.
  • systemd로 서비스를 등록·관리하고 부팅 시 자동 실행되도록 설정할 수 있습니다.
  • ufw로 방화벽 규칙을 구성하고, journalctl로 서비스 로그를 분석할 수 있습니다.
  • 우분투 서버를 처음 발급받았을 때 해야 할 초기 세팅 체크리스트를 갖게 됩니다.

실전 경험에서 배운 교훈

처음 우분투 서버를 운영할 때 가장 흔히 저지르는 실수는 apt upgradeapt dist-upgrade(또는 apt full-upgrade)를 구분하지 않고 습관적으로 실행하는 것입니다. 커널 패키지나 의존성 구조가 바뀌는 업데이트가 섞여 있을 때 무심코 full-upgrade를 돌리면, 운영 중인 서비스가 재부팅 이후 예상치 못하게 동작을 멈추는 경우가 있습니다. 반대로 보안 패치가 필요한데도 apt upgrade만 반복하면 커널 취약점이 방치되기도 합니다.

또 하나 자주 겪는 문제는 ufw를 활성화하다가 SSH 접속이 통째로 막히는 사고입니다. 원격 서버에서 ufw enable을 실행하기 전에 SSH 포트를 허용하는 규칙을 먼저 추가하지 않으면, 그 순간 콘솔 접근 수단이 없는 이상 서버를 다시 살릴 방법이 없습니다. 이 글의 방화벽 섹션에서는 이런 사고를 피하는 순서를 명확히 짚습니다.

systemctl로 서비스를 직접 만들어 배포할 때도 함정이 있습니다. .service 유닛 파일을 수정한 뒤 systemctl daemon-reload를 빼먹으면 변경 사항이 반영되지 않은 채로 재시작만 반복하게 됩니다. 이런 사소하지만 반복되는 실수들을 이 글 곳곳의 트러블슈팅 노트에 정리했습니다.

1. apt 패키지 관리

apt와 apt-get, dpkg의 관계

우분투의 패키지 관리는 세 계층으로 이루어져 있습니다. 가장 아래는 dpkg로, .deb 패키지 파일을 실제로 설치·제거하는 저수준 도구입니다. 그 위에 apt-get/apt-cache가 있어 저장소(repository)에서 패키지와 의존성을 자동으로 내려받아 dpkg에 넘겨줍니다. 가장 위에 있는 apt는 Ubuntu 16.04(Xenial)부터 도입된 명령어로, apt-getapt-cache의 자주 쓰는 기능을 하나로 합치고 진행률 표시, 컬러 출력 같은 사용성 개선을 더한 것입니다.

# 패키지 목록(저장소 인덱스) 갱신 — 실제 설치는 하지 않음
sudo apt update

# 설치된 패키지를 최신 버전으로 업그레이드 (의존성 추가/제거 없이)
sudo apt upgrade

# 의존성 구조 변경이 필요한 업그레이드까지 포함 (커널 등)
sudo apt full-upgrade

# 패키지 설치
sudo apt install nginx

# 특정 버전 지정 설치
sudo apt install nginx=1.18.0-0ubuntu1

# 패키지 제거 (설정 파일은 남김)
sudo apt remove nginx

# 패키지 + 설정 파일까지 완전 제거
sudo apt purge nginx

# 더 이상 필요 없는 의존 패키지 정리
sudo apt autoremove

# 패키지 검색
apt search "web server"

# 설치 전 패키지 정보 확인
apt show nginx

# 다운로드된 .deb 캐시 정리 (디스크 확보)
sudo apt clean

apt update는 새 패키지를 설치하는 명령이 아니라 저장소 메타데이터를 갱신하는 명령이라는 점을 기억해야 합니다. apt install을 실행하기 전에 apt update를 먼저 하지 않으면, 오래된 인덱스를 기준으로 패키지를 찾다가 “찾을 수 없음” 오류를 만나거나 이미 삭제된 버전을 받으려다 실패하는 경우가 있습니다. 반대로 apt upgrade는 이미 설치된 패키지를 최신화할 뿐 새 패키지를 설치하지 않으므로, 이 세 명령은 항상 update → upgrade 순서로 세트처럼 실행하는 습관을 들이는 것이 좋습니다.

upgradefull-upgrade(옛 이름 dist-upgrade)의 차이는 실무에서 특히 중요합니다. upgrade는 패키지를 제거해야 하는 상황이 생기면 해당 업그레이드를 보류합니다. 반면 full-upgrade는 필요하다면 기존 패키지를 제거하면서까지 업그레이드를 강행합니다. 커널 업데이트나 메이저 라이브러리 교체가 걸린 경우 full-upgrade가 필요하지만, 운영 중인 서버라면 사전에 apt list --upgradable로 무엇이 바뀌는지 확인한 뒤 점검 시간에 실행하는 것이 안전합니다.

/etc/apt/sources.list와 저장소 구조

우분투는 패키지를 어디서 받아올지를 /etc/apt/sources.list/etc/apt/sources.list.d/ 아래의 개별 파일들로 관리합니다.

# 기본 저장소 설정 파일 확인
cat /etc/apt/sources.list

# PPA(Personal Package Archive) 등 추가 저장소는 여기 개별 파일로 저장됨
ls /etc/apt/sources.list.d/

# 서드파티 PPA 추가 (예: 최신 버전의 특정 도구)
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update

# 더 이상 필요 없는 PPA 제거
sudo add-apt-repository --remove ppa:deadsnakes/ppa

sources.list 안의 각 줄은 deb <URL> <배포판 코드명> <구성요소> 형식을 따릅니다. 예를 들어 deb http://archive.ubuntu.com/ubuntu jammy main restricted에서 jammy는 22.04 LTS의 코드명이고, main/restricted/universe/multiverse는 라이선스와 지원 범위에 따라 나뉜 패키지 구성요소입니다. main은 캐노니컬이 직접 지원하는 오픈소스, universe는 커뮤니티가 유지보수하는 오픈소스, restricted는 독점 드라이버, multiverse는 라이선스 제약이 있는 패키지를 담습니다. 서버에 필요한 소프트웨어가 apt search로 나오지 않는다면, 이 구성요소 중 하나가 비활성화되어 있거나 애초에 저장소에 없는 패키지일 가능성을 먼저 의심해야 합니다.

Ubuntu 22.04부터는 .deb822 형식(/etc/apt/sources.list.d/ubuntu.sources)이 점차 기본으로 자리잡고 있어, 최신 버전에서는 sources.list가 비어 있고 대신 이 파일이 저장소 정보를 담고 있는 경우도 있습니다. 버전에 따라 파일 위치가 달라질 수 있으므로 apt-cache policy 명령으로 실제 적용 중인 저장소를 확인하는 습관이 유용합니다.

2. systemd와 systemctl로 서비스 관리하기

우분투는 15.04부터 기존의 init.d/upstart를 대체해 systemd를 기본 초기화 시스템으로 사용합니다. systemd는 서비스(데몬)의 시작·중지·재시작뿐 아니라 부팅 순서 관리, 소켓 활성화, 리소스 제한(cgroups)까지 담당하는 핵심 구성요소입니다.

# 서비스 상태 확인
systemctl status nginx

# 서비스 시작 / 중지 / 재시작
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx

# 설정만 다시 읽고 무중단으로 반영 (nginx, 일부 서비스 지원)
sudo systemctl reload nginx

# 부팅 시 자동 시작 여부 설정
sudo systemctl enable nginx
sudo systemctl disable nginx

# 현재 활성화된 모든 서비스 목록
systemctl list-units --type=service --state=running

# 부팅 시 자동 시작되도록 등록된 서비스만 확인
systemctl list-unit-files --type=service --state=enabled

enablestart는 서로 다른 축의 작업이라는 점이 초보자가 가장 헷갈리는 부분입니다. start지금 당장 서비스를 실행하고, enable다음 부팅부터 자동으로 실행되도록 심볼릭 링크를 걸어두는 것입니다. 즉 enable만 해두고 start를 하지 않으면 서버를 재부팅하기 전까지는 서비스가 돌지 않습니다. 반대로 start만 하고 enable을 하지 않으면 서버가 재부팅될 때 서비스가 살아나지 않습니다. 운영 서버에서는 대부분 sudo systemctl enable --now nginx처럼 두 동작을 한 번에 실행하는 것이 편리합니다.

직접 서비스 유닛 파일 작성하기

Node.js나 Go로 만든 자체 애플리케이션을 systemd 서비스로 등록하면, 서버가 재부팅되거나 프로세스가 죽었을 때 자동으로 재시작되도록 만들 수 있습니다.

# /etc/systemd/system/myapp.service
[Unit]
Description=My Backend API Server
After=network.target

[Service]
Type=simple
User=deploy
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node /opt/myapp/server.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
# 유닛 파일을 새로 만들거나 수정한 뒤에는 반드시 실행
sudo systemctl daemon-reload

# 등록 및 즉시 시작
sudo systemctl enable --now myapp

[Unit]After=network.target은 네트워크가 준비된 뒤 서비스를 시작하라는 순서 지정이며, [Service]Restart=on-failure는 프로세스가 비정상 종료됐을 때만 자동 재시작하도록 합니다(정상 종료 시에는 재시작하지 않음). 이 설정을 빼먹으면 메모리 부족이나 예외로 프로세스가 죽었을 때 서비스가 다운된 채로 방치될 수 있습니다.

가장 흔히 마주치는 함정은 .service 파일을 수정한 뒤 daemon-reload를 잊는 것입니다. systemd는 유닛 파일을 부팅 시점이나 daemon-reload 호출 시점에 캐시하기 때문에, 파일만 고치고 systemctl restart를 실행하면 예전 설정 그대로 재시작될 뿐 변경 사항이 반영되지 않습니다. systemctl status에 “Unit file changed on disk”라는 경고가 뜨면 이 신호입니다.

3. journalctl로 로그 분석하기

systemd는 각 서비스의 로그를 syslog가 아니라 자체 바이너리 로그 저장소인 journald에 기록합니다. 이 로그를 조회하는 도구가 journalctl입니다.

# 전체 로그를 최신순으로 보기 (pager 사용)
journalctl

# 특정 서비스 로그만 보기
journalctl -u nginx

# 실시간 추적 (tail -f 와 유사)
journalctl -u nginx -f

# 최근 부팅 이후 로그만
journalctl -b

# 최근 100줄만
journalctl -u nginx -n 100

# 특정 시간 범위 지정
journalctl -u nginx --since "2026-09-08 09:00:00" --until "2026-09-08 12:00:00"

# 최근 1시간 이내 로그
journalctl -u nginx --since "1 hour ago"

# 에러 수준(priority) 이상만 필터링
journalctl -u nginx -p err

# 디스크 사용량 확인 및 오래된 로그 정리 (예: 1주일 초과분 삭제)
journalctl --disk-usage
sudo journalctl --vacuum-time=1w

journalctlgrep으로 로그 파일을 뒤지는 것보다 유용한 이유는 구조화된 필드 검색이 가능하다는 점입니다. -u 옵션으로 서비스 단위 필터링이 되고, --since/--until로 특정 장애 시각의 로그만 정확히 뽑아낼 수 있으며, -p err처럼 로그 심각도로도 걸러낼 수 있습니다. 장애 대응 중에는 “몇 시부터 몇 시까지 nginx가 무슨 에러를 냈는가”를 빠르게 좁혀야 하는데, 텍스트 파일 기반 로그보다 이 작업이 훨씬 빠릅니다.

한 가지 주의할 점은 journald가 기본적으로 디스크 용량이나 보관 기간에 제한을 두지 않으면 로그가 무한정 쌓여 디스크를 가득 채울 수 있다는 것입니다. /etc/systemd/journald.conf에서 SystemMaxUse=500M 같은 값을 설정하거나, 정기적으로 journalctl --vacuum-size=500M(또는 --vacuum-time)을 크론으로 돌려 오래된 로그를 정리하는 운영 정책이 필요합니다.

4. ufw 방화벽 설정

ufw(Uncomplicated Firewall)는 리눅스 커널의 iptables/nftables를 사람이 다루기 쉬운 명령어로 감싼 우분투의 기본 방화벽 프론트엔드입니다.

# 현재 상태 확인 (비활성화 상태에서 시작하는 것이 기본)
sudo ufw status verbose

# SSH 허용 (반드시 enable 전에 먼저 실행!)
sudo ufw allow OpenSSH
# 또는 포트 번호로 직접 지정
sudo ufw allow 22/tcp

# HTTP, HTTPS 허용
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# 특정 IP에서만 특정 포트 허용 (예: 사내망에서만 DB 접근)
sudo ufw allow from 203.0.113.10 to any port 5432

# 특정 포트 범위 허용
sudo ufw allow 3000:3010/tcp

# 규칙 삭제
sudo ufw delete allow 80/tcp

# 방화벽 활성화 (이 시점 이후 허용되지 않은 포트는 모두 차단됨)
sudo ufw enable

# 방화벽 비활성화
sudo ufw disable

# 번호가 매겨진 규칙 목록 (삭제 시 번호로 지정 가능)
sudo ufw status numbered

ufw를 다룰 때 가장 치명적인 사고는 원격 SSH 세션에서 allow OpenSSH 규칙 없이 ufw enable을 실행하는 것입니다. 활성화 순간 방화벽은 명시적으로 허용되지 않은 모든 인바운드 트래픽을 차단하는데, 여기에 SSH(22번 포트)가 포함되면 그 즉시 서버에 재접속할 방법이 사라집니다. 클라우드 제공업체의 시리얼 콘솔이나 VNC 콘솔로 복구해야 하는 번거로움을 피하려면, 반드시 enable보다 먼저 SSH 허용 규칙을 추가하는 순서를 지켜야 합니다. 이 글 서두 FAQ에서도 이 부분을 다시 한 번 강조했습니다.

또한 클라우드 환경에서는 ufw(OS 레벨 방화벽)와 AWS 보안 그룹, GCP 방화벽 규칙 같은 클라우드 레벨 방화벽이 별도로 존재한다는 점을 기억해야 합니다. 둘 중 하나만 열려 있어도 트래픽이 막힐 수 있으므로, 접속 문제가 생기면 두 레이어를 모두 점검해야 합니다.

5. 사용자 및 권한 관리

# 새 사용자 추가 (홈 디렉토리, 셸 등 대화형 설정)
sudo adduser deploy

# 저수준 명령어 (옵션을 직접 지정, 스크립트에 적합)
sudo useradd -m -s /bin/bash deploy

# 사용자를 sudo 그룹에 추가 (관리자 권한 부여)
sudo usermod -aG sudo deploy

# 사용자를 docker 그룹에 추가 (sudo 없이 docker 명령 실행)
sudo usermod -aG docker deploy

# 사용자가 속한 그룹 확인
groups deploy

# 사용자 삭제 (홈 디렉토리까지 함께 삭제)
sudo deluser --remove-home deploy

# 특정 사용자로 전환
sudo -u deploy -i

# 비밀번호 없이 특정 명령만 sudo 허용 (배포 스크립트용)
# visudo로 안전하게 편집
sudo visudo -f /etc/sudoers.d/deploy
# 파일 내용 예: deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp

adduseruseradd는 둘 다 사용자를 생성하지만 성격이 다릅니다. useradd는 커널/glibc 수준의 저수준 명령어로 기본값이 최소화되어 있어 홈 디렉토리조차 자동으로 만들지 않습니다(-m 옵션 필요). 반면 adduser는 Perl로 작성된 대화형 스크립트로, 홈 디렉토리 생성, 기본 셸 설정, 비밀번호 입력까지 안내해줍니다. 사람이 직접 계정을 만들 때는 adduser가 실수를 줄여주고, 자동화 스크립트(예: Ansible, 서버 프로비저닝 스크립트)에서는 동작이 예측 가능한 useradd를 쓰는 것이 일반적입니다.

usermod -aG 명령에서 -a(append) 옵션을 빠뜨리는 실수도 잦습니다. -a 없이 -G만 쓰면 기존에 속해 있던 다른 그룹이 모두 사라지고 지정한 그룹으로 덮어써집니다. 예를 들어 이미 docker 그룹에 속한 사용자에게 usermod -G sudo deploy를 실수로 실행하면 docker 그룹 멤버십이 조용히 사라져, 이후 docker 명령이 권한 오류로 실패하는 원인을 한참 찾아 헤매게 됩니다. 그룹을 추가할 때는 항상 -aG를 세트로 기억해야 합니다.

새로 그룹에 추가된 사용자는 로그아웃 후 재접속해야 그룹 변경이 적용됩니다. usermod로 그룹을 추가했는데도 docker ps가 여전히 권한 오류를 내는 경우, 대부분 원인은 이 세션 갱신을 하지 않은 것입니다.

6. snap 패키지 관리

# 설치된 snap 목록
snap list

# snap 패키지 설치
sudo snap install code --classic

# 패키지 정보 확인
snap info code

# 업데이트 (보통 자동으로 실행되지만 수동으로도 가능)
sudo snap refresh

# 특정 패키지만 업데이트
sudo snap refresh code

# 제거
sudo snap remove code

# 자동 업데이트 일정 확인
snap refresh --time

snap은 캐노니컬이 만든 컨테이너화 패키지 포맷으로, 의존 라이브러리를 패키지 안에 함께 묶어 배포판 버전에 관계없이 동일하게 동작하는 것이 장점입니다. --classic 옵션은 snap의 샌드박스 격리를 해제하고 시스템 전체 파일에 접근할 수 있게 하는 옵션으로, VS Code처럼 파일시스템 전반에 접근해야 하는 도구에 필요합니다.

서버 환경에서는 snap보다 apt를 우선하는 것이 일반적인 관례입니다. snap 패키지는 각각 별도의 런타임 마운트(squashfs)를 사용하기 때문에 시작 속도가 apt 패키지보다 느리고, 자동 업데이트가 예상치 못한 타이밍에 서비스를 재시작시킬 수 있습니다. 다만 apt 저장소에 없는 최신 버전의 CLI 도구(예: 특정 버전의 certbot)가 필요할 때는 snap이 유용한 대안이 됩니다.

7. 우분투 버전과 LTS

# 현재 우분투 버전 확인
lsb_release -a

# 또는
cat /etc/os-release

# 커널 버전 확인
uname -r

# 배포 가능한 업그레이드 확인
do-release-upgrade -c

우분투는 매년 4월과 10월에 새 버전을 출시하며, 짝수 연도 4월 버전만 LTS(Long Term Support)로 지정됩니다. 예를 들어 20.04, 22.04, 24.04가 LTS이고 각각 코드명 Focal Fossa, Jammy Jellyfish, Noble Numbat입니다. LTS 버전은 출시 후 5년간 보안 패치를 받으며, Ubuntu Pro(구 ESM)를 통해 최대 10년까지 연장할 수 있습니다. 반면 짝수가 아닌 해나 10월에 나오는 일반 버전(예: 23.10)은 9개월만 지원되므로 프로덕션 서버에는 적합하지 않습니다.

버전 선택 시 실무적인 기준은 다음과 같습니다. 새 프로젝트를 시작한다면 가장 최근 LTS를 쓰는 것이 안전하지만, 출시 직후(예: 24.04가 나온 지 한 달 이내)에는 서드파티 패키지 저장소나 Docker 이미지가 아직 해당 버전을 완전히 지원하지 않는 경우가 있으므로, 프로덕션에는 한두 분기 정도 지난 뒤 도입하는 것을 권장합니다. 기존 서버가 지원 종료(EOL)에 가까워지면 do-release-upgrade로 다음 LTS로 업그레이드할 수 있지만, 반드시 스냅샷이나 백업을 먼저 만들고 스테이징 환경에서 먼저 검증한 뒤 진행해야 합니다.

8. 범용 명령어 간단 정리

파일 탐색, 텍스트 처리 같은 배포판에 상관없이 동일한 명령어는 리눅스·맥 명령어 실전 가이드에서 자세히 다루므로, 여기서는 우분투 서버 운영에서 특히 자주 함께 쓰이는 것만 짧게 짚습니다.

# 디스크 사용량 확인
df -h

# 디렉토리별 용량 확인
du -sh /var/log/*

# 메모리/CPU 사용량 실시간 확인
htop   # 없으면: sudo apt install htop

# 프로세스에서 포트 사용 확인
sudo ss -tulpn | grep :80

# 네트워크 인터페이스 확인
ip addr show

우분투 서버에서는 netstat 대신 ss가 기본으로 권장됩니다. net-tools 패키지(netstat, ifconfig 포함)는 최신 우분투에서 기본 설치되지 않는 경우가 많고, iproute2 패키지의 ss/ip가 더 빠르고 최신 커널 기능을 지원하기 때문입니다. netstat -tulpn 대신 ss -tulpn, ifconfig 대신 ip addr을 쓰는 습관을 들이는 것이 최신 우분투 환경에 더 잘 맞습니다.

9. 우분투 서버 초기 세팅 체크리스트

클라우드에서 새 우분투 서버를 발급받았을 때 순서대로 실행하면 좋은 절차를 정리합니다.

# 1. 패키지 인덱스 갱신 및 보안 업데이트 적용
sudo apt update && sudo apt upgrade -y

# 2. 배포자용 non-root 사용자 생성 및 sudo 권한 부여
sudo adduser deploy
sudo usermod -aG sudo deploy

# 3. SSH 키 기반 인증 설정 (비밀번호 로그인 비활성화 전 필수)
sudo mkdir -p /home/deploy/.ssh
sudo cp ~/.ssh/authorized_keys /home/deploy/.ssh/
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys

# 4. SSH 비밀번호 로그인 비활성화 (키 등록 확인 후에만!)
sudo nano /etc/ssh/sshd_config
# PasswordAuthentication no 로 변경
sudo systemctl restart sshd

# 5. 방화벽 설정 (SSH 허용을 반드시 먼저!)
sudo ufw allow OpenSSH
sudo ufw enable

# 6. 타임존 설정
sudo timedatectl set-timezone Asia/Seoul

# 7. fail2ban으로 무차별 대입 공격 방어
sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban

# 8. 자동 보안 업데이트 활성화
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

이 순서에서 중요한 것은 SSH 키 인증을 먼저 검증한 뒤에 비밀번호 로그인을 끄는 것입니다. 새 터미널 창을 하나 더 열어 키 기반 로그인이 실제로 되는지 확인하기 전에 PasswordAuthentication no를 적용하면, 키 설정에 오류가 있었을 경우 서버에 접근할 방법이 완전히 사라집니다. 방화벽 설정과 마찬가지로, 접근 수단을 차단하는 작업은 항상 대체 접근 경로를 먼저 확인한 뒤에 진행해야 합니다.

fail2ban은 SSH 로그인 실패가 짧은 시간 내 반복되는 IP를 자동으로 차단해주는 도구로, 인터넷에 노출된 서버라면 사실상 필수입니다. unattended-upgrades는 보안 패치만 선별적으로 자동 적용해주므로, 사람이 매번 접속해 apt upgrade를 챙기지 않아도 최소한의 보안 수준을 유지할 수 있습니다.

트러블슈팅

E: Could not get lock /var/lib/dpkg/lock-frontend 다른 apt/dpkg 프로세스가 이미 실행 중이거나 비정상 종료로 잠금 파일이 남아있는 경우입니다. ps aux | grep -i apt로 실제 실행 중인 프로세스가 있는지 먼저 확인하고, 없다면 sudo rm /var/lib/dpkg/lock-frontendsudo dpkg --configure -a로 정리합니다. 실행 중인 프로세스가 있다면 강제로 지우지 말고 해당 프로세스가 끝날 때까지 기다려야 패키지 데이터베이스 손상을 피할 수 있습니다.

systemctl start 이후 서비스가 계속 죽어 있음 systemctl status myapp로 마지막 종료 코드를 확인하고, journalctl -u myapp -n 50 --no-pager로 최근 로그를 봅니다. 대부분 애플리케이션 자체 오류(포트 충돌, 환경변수 누락)이지만, 유닛 파일의 ExecStart 경로 오타나 User= 지정 사용자의 파일 접근 권한 문제도 흔한 원인입니다.

ufw를 켰는데도 외부에서 계속 접속이 안 됨 sudo ufw status로 규칙이 실제로 적용됐는지 확인합니다. 규칙은 맞는데도 안 된다면 클라우드 제공업체의 보안 그룹/방화벽 규칙이 별도로 막고 있는지 확인해야 합니다. OS 레벨 방화벽과 클라우드 레벨 방화벽은 독립적으로 작동하므로 둘 다 열려 있어야 트래픽이 통과합니다.

apt 저장소에서 패키지를 찾을 수 없음 apt update를 먼저 실행했는지, /etc/apt/sources.list에 필요한 구성요소(universe, multiverse 등)가 활성화되어 있는지 확인합니다. sudo add-apt-repository universe로 universe 저장소를 추가한 뒤 다시 apt update를 실행하면 해결되는 경우가 많습니다.

마치며

우분투 서버 운영의 핵심은 결국 apt(무엇을 설치할지), systemd(무엇을 실행 상태로 유지할지), ufw(무엇을 외부에 노출할지), journalctl(무슨 일이 있었는지)이라는 네 가지 축으로 요약됩니다. 이 네 가지 도구의 기본기를 익혀두면 CentOS/RHEL 계열이 아닌 이상 대부분의 클라우드 우분투 서버를 어려움 없이 다룰 수 있습니다.

특히 방화벽 활성화와 SSH 비밀번호 인증 비활성화처럼 접근 수단을 스스로 차단할 수 있는 작업은 항상 대체 접근 경로(콘솔 접속, 키 기반 인증 검증)를 먼저 확보한 뒤 진행하는 습관을 들이는 것이 중요합니다. 이 글의 초기 세팅 체크리스트를 새 서버를 발급받을 때마다 순서대로 따라 하면, 최소한의 보안 기준을 갖춘 우분투 서버를 빠르게 준비할 수 있습니다.

범용 파일/프로세스 명령어가 더 필요하다면 리눅스·맥 명령어 실전 가이드를, 리눅스 시스템 전반의 개념을 더 깊게 다루고 싶다면 리눅스 완전 가이드를 함께 참고하세요.

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.