BlueNyang
Cloud Infrastructure
· IaaS 배포 실전 (1편)

[Cloud Infra] 서버와 SSH

BlueNyangBlueNyang
·
·
약 10분
·
# SSH# Linux# Rocky Linux# IaaS# Deployment# Swap# firewalld# SELinux# sshd
시리즈·IaaS 배포 실전(2개의 글)
  • 1.[Cloud Infra] 서버와 SSH현재
  • 2.[Cloud Infra] 도커의 시작

0. 배포의 시작

개발자로서 아이디어를 기획하고, 아키텍처를 설계하고, 코드를 짜고 테스트를 거쳐 빌드해낸 순간, 성취감을 느끼면서도 결과물 앞에서 문득 이런 막막함이 찾아옵니다.

다 만들었는데... 그래서 이걸 어떻게 배포하지?

로컬 환경에서는 터미널에 명령어 몇 개만으로 localhost 위에서 잘 구동되던 서비스가, 막상 리눅스 기반의 원격 클라우드 서버에 올리려고 하면 방화벽, 포트, 권한 문제 등 자주 막힙니다.

최근에는 AI의 발달 덕분에 배포 과정에서 필요한 설정이나 명령어를 쉽게 받을 수 있게 되었지만, 그럼에도 "내 로컬에서는 잘 되는데 이상하게 서버만 가면 죽어버리는" 의문의 현상은 여전히 발생합니다. 결국 인프라의 동작 원리와 배포 파이프라인의 구조에 대한 이해 없이는 계속 막히기 마련입니다.

그리고 이번 시리즈에서 다뤄볼 기록이 바로 이 "배포"입니다. 직접 AWS, GCP, Vultr, OCI, Netlify, Render를 거쳐 홈랩 서버까지. 많은 클라우드 환경을 구축하고 죽을 쒀가며 얻은 것들을 공유해보려고 합니다.

단순히 코드를 짜는 것을 넘어, 내가 만든 프론트엔드/백엔드 서비스를 클라우드 서버에 안전하게 안착시키고, 리버스 프록시와 SSL을 통해 공인 도메인과 연결하며, 나아가 Git Push나 버튼 한 번으로 배포가 알아서 끝나는 완전한 CI/CD 파이프라인을 내 손으로 직접 구축해 보는 것이 이 시리즈의 목표입니다.

이번 글에서는 본격적인 내용에 앞서, 인프라의 시작점인 클라우드 서버. 그리고 이 서버와 대화하는 창구인 SSH(Secure Shell) 이야기부터 차근차근 풀어보겠습니다.

Note
컴퓨팅 자원을 빌리는 AWS, GCP, Vultr, OCI와 같은 IaaS나 홈랩 서버를 사용하는 것을 기준으로 서술합니다. 각 플랫폼별 서버, 네트워크, 게이트웨이 설정은 다르기 때문에 다른 글에서 다뤄보는 것으로 하고, 인스턴스나 운용 가능한 서버가 있다는 것을 전제로 하겠습니다. 명령어와 설정 예시는 Rocky Linux(RHEL 계열) 기준이지만, 기본적인 흐름은 비슷하니 참고바랍니다.

1. SSH, 서버와의 안전한 통신

리눅스 서버에 접근하여 명령어를 실행하기 위해서는 다양한 방법이 있을 수 있습니다. 물론 홈랩 서버라면 모니터를 연결해 직접 접속할 수 있겠지만, IaaS의 인스턴스에서는 쉽지 않습니다. 물론 AWS나 GCP에서는 웹 콘솔을 제공하나, Vultr와 같이 모든 플랫폼에서 제공하는 것은 아닙니다.

이러한 인스턴스에 접속하는 방법 중 많은 플랫폼이 권장하는 방법은 SSH입니다. SSH는 인터넷과 같이 암호화되지 않은 네트워크 상에서, 컴퓨터 간에 안전하게 통신하기 위해 사용하는 암호화 프로토콜입니다.

SSH 인증은 비밀번호 또는 키를 사용합니다. ID/PW는 고유의 문자열을 사용해 무차별 대입 공격에 취약하기도 하고, 매번 입력을 요구하기 때문에 자동화 및 생산성 측면에서 비교적 불리합니다. 반면, SSH의 비대칭 키 인증 방식은 수학적 알고리즘으로 생성된 공개키개인키 한 쌍을 매칭하여 인증합니다. 또한 암호 입력이 불필요하거나 최초 1회 입력으로 사용할 수 있으며 보안 강도가 매우 높습니다.

1.1. 비대칭 키 인증 메커니즘

  • 개인 키(Private Key/id_ed25519): 클라이언트(로컬 PC)에만 안전하게 보관하는 비밀 키. 절대 유출 금지.
  • 공개 키 (Public Key / id_ed25519.pub): 원격 서버의 특정 경로(~/.ssh/authorized_keys)에 복사하여 등록하는 키.
  • 인증 과정:
인증(handshake) 과정
인증(handshake) 과정

1.2. SSH 키 생성하기

SSH 키 페어는 ssh-keygen 명령어를 사용합니다. Windows/MacOS/Linux 모두 동일한 명령어를 사용합니다. 명령어의 대소문자에 주의하세요.

Note
키를 생성하는 알고리즘은 RSA, Ed25519를 많이 사용합니다. 2015년부터 OpenSSH7.0에서는 Ed25519를 기본 알고리즘으로 채택하고 있습니다. Ed25519의 공개키는 68자, RSA-4096은 724자이며 Ed25519의 서명 검증 속도가 RSA보다 훨씬 빠릅니다. 또한 RSA는 이론적으로 특정 결함 공격에 취약할 수 있으나, Ed25519는 현재까지 그렇지 않습니다. RSA는 레거시 호환성 때문이며, 기본적으로 Ed25519를 권장합니다.
  • SSH 키 페어 생성:
bash
ssh-keygen -t ed25519 -C "your-email@example.com"

명령어를 실행하면 다음과 같이 출력됩니다.

text
Generating public/private ed25519 key pair.
Enter file in which to save the key (~/.ssh/id_ed25519):

만약 key를 다른 위치에 저장하고 싶거나 이름을 다르게 하고 싶다면 경로와 파일명을 적고, 그렇지 않다면 그냥 엔터를 누릅니다. 그러면 다음과 같이 ssh key의 암호를 입력하라는 메시지를 볼 수 있습니다.

text
Enter passphrase for "~/.ssh/id_ed25519" (empty for no passphrase):
Enter same passphrase again:

입력하지 않거나, 기억하기 쉬운 암호를 입력하면 되며, 암호를 입력할 때 화면에 아무런 글자도 써지지 않는 것이 정상입니다. 그러면 다음과 같이 키가 생성됩니다.

text
Your identification has been saved in ~/.ssh/id_ed25519
Your public key has been saved in ~/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:Dt9xInrCREhqQ/48ff9VgOAfEYZVtPyS3BwgNaHWxio kai@bluenyang.kr
The key's randomart image is:
+--[ED25519 256]--+
|  . .     .+*O+  |
| o o .   ..o=+o. |
|  = . .   .oo++. |
| . + o    ..oo *.|
|    + + SEo.o + =|
|     + * +.+   o |
|      + + o   .  |
|       o   . .   |
|            .    |
+----[SHA256]-----+

1.3. SSH 키를 등록하고 접속하기

키 페어를 생성했다면, 공개키를 서버에 전송하여 등록합니다.

bash
# SSH 포트가 22번이 아닌 경우, 포트(-p) 지정 필수
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p [포트번호] [유저명]@[도메인/IP]
Caution
파일명은 id_ed25519가 아니라 id_ed25519.pub 입니다.

이후 원격 서버 접속 방법은 다음과 같습니다.

bash
ssh -p [포트번호] [유저명]@[도메인/IP]

1.4. SSH Config 설정 (~/.ssh/config)

매번 복잡한 옵션을 주는 대신, 로컬 환경에 아래와 같이 구성하여 간편하게 접속할 수 있습니다.

sh
vi ~/.ssh/config
text
Host <별명>
    Hostname <도메인 또는 IP>
    User <유저명>
    Port <포트번호>
    IdentityFile ~/.ssh/id_ed25519

키 생성시 파일 위치를 따로 지정했다면 IdentityFile에 해당 위치를 지정하면 됩니다. 이렇게하면, ssh <별명>으로 명령어를 단축할 수 있습니다.


2. Linux VPS 기초 설정 (Rocky Linux 9 기준)

2.1. Swap Memory

일반적으로 인스턴스에 접속했을 때, 제가 가장 먼저 해두는 설정은 Swap Memory 할당입니다. 홈랩이나 유료 인스턴스와 같이 RAM이 4GB 이상이라면 문제가 없지만, 일반적으로 Free Tier나 저렴한 컴퓨팅 자원에서는 2GB 미만의 RAM을 할당받습니다.

이 작은 용량의 RAM은 Docker를 돌리기 이전에, OS의 패키지를 업데이트하는 작업만으로 OOM(Out of Memory) 에러로 서버가 다운되거나 SSH 연결이 끊어질 수 있습니다. 이를 방지하기 위해서는 디스크의 일부 공간을 메모리처럼 사용하는 Swap 영역을 추가적으로 확보하는 것이 중요합니다.

특히, EL 계열의 dnf update는 순간적으로 3~4GB까지도 소모하기 때문에 유의해야 합니다. 저도 처음 AWS/GCP/OCI에서 VM을 설정할 때, dnf update만 하면 서버가 멈춰버리는 상황을 항상 겪었습니다.

Note
EL 계열인 Rocky Linux는 기본적으로 Swap 메모리를 적절히 할당하도록 구성되어 있습니다. 하지만 IaaS 인스턴스 생성 시 그렇지 않은 경우도 종종 있으므로, free -h를 통해 Swap Memory의 할당 상태를 확인하시기 바랍니다. 이러한 환경에서 Swap Memory는 총합 6~8GB를 할당하는 것을 권장합니다.
  • 설정하기:
bash
# free 명령어 결과 현재 4GB의 Swap이 있다는 전제 하에, 4GB 크기의 빈 파일 확보 (총합 8GB)
# dd(저수준 데이터 복사 도구): if=/dev/zero(0x00 바이트를 반환하는 파일), of(output file), bs(block size), count(block 수)
# 즉, 0x00으로 채워진 1M 사이즈의 블록이 4096개 있는 파일을 생성 = 4GB의 빈 파일
sudo dd if=/dev/zero of=/swapfile bs=1M count=4096

# 소유자 제한 권한 부여 (보안 필수)
# 의미: root(시스템)에만 읽고 쓰는 권한 부여. 다른 사용자는 접근 불가
sudo chmod 600 /swapfile

# 스왑 파일 포맷 및 활성화
sudo mkswap /swapfile
sudo swapon /swapfile

# 재부팅 시 영구 마운트 설정 추가
# (vi/vim이나 nano 등으로 직접 파일을 열어 작성해도 됩니다.)
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab

# 스왑 메모리 할당 여부 확인
free -h
Note
dd 명령어 대신 fallocate -l 4G /swapfile이라는 명령어도 있습니다. 하지만 일부 파일 시스템이나 커널 버전 조합에서 Swap 활성화시 문제가 보고된 적이 있기 때문에 안전하게 dd를 권장하는 경우도 있습니다.

2.2. Swappiness

일반적인 서버에서는 통상적으로 지연 시간 스파이크를 피하고 캐시를 우선하기 위해 낮은 swappiness(10~20)를 유지합니다. 하지만, 2GB 미만의 매우 작은 메모리를 가진 환경에서는 그렇지 않습니다.

우선, 낮은 swappiness는 "RAM이 거의 다 찰 때까지 버티다가 스왑을 사용하겠다"는 의미입니다. 그러나 2GB 미만일 때, RAM이 가득 찬 그 순간 유휴(inactive) 페이지를 Swap으로 미리 빼놓지 못한 상태에서 큰 프로세스가 작성하면 스왑할 시간도 없이 OOM Killer가 발동해버립니다. 즉, dnf update 등을 사용하는 순간 Swap을 사용할 틈도 없이 RAM이 가득 차버리는 상황이 발생합니다.

이 때문에 4GB 이상의 일반적인 서버나 홈랩 서버 환경에서는 10~20의 낮은 swappiness를, 2GB 미만의 환경에서는 60~80의 높은 swappiness를 권장합니다.

bash
# 현재 swappiness 확인
sudo sysctl vm.swappiness
# 출력: vm.swappiness = 60

# 필요 시 조정 (위의 내용을 참고하여 지정)
# 현재 값을 조정
sudo sysctl vm.swappiness=10

# 영구 조정 (vi/vim이나 nano로 직접 파일을 수정해도 됩니다.)
# 현재 값을 변경해 주지는 않기 때문에, 위 명령어와 함께 사용하세요
echo "vm.swappiness=10" | sudo tee -a "/etc/sysctl.conf"

2.3. 패키지 업데이트 및 설치

시스템을 처음 설치하면, 우선 시스템에서 의존하고 있는 패키지와 커널을 업데이트해야 합니다. EL 계열은 dnf, Debian 계열은 apt를 사용합니다.

bash
# Rocky/RHEL
dnf -y update # 또는 dnf -y upgrade

# Debian/Ubuntu 참고
# apt update && apt upgrade

업데이트 이후, 기본적으로 많이 사용하고 필요한 패키지를 설치합니다.

bash
# Rocky/RHEL
dnf install -y vim git lsof net-tools curl wget util-linux-user

# Debian/Ubuntu 참고 (Debian/Ubuntu에서는 해당 기능이 passwd 패키지에 포함됩니다)
# apt install -y vim git lsof net-tools curl wget
Note
이후 시스템을 재부팅(reboot)하는 것을 권장합니다.

2.4. 서버 보안 설정

내부 네트워크를 사용하거나 홈랩 서버가 아니라면, IaaS 인스턴스의 경우 서버의 SSH 포트는 현재 인터넷에 열려 있을 것입니다. SSH가 아무리 안전하다지만, 22번 포트를 Root 로그인 허용 및 ID/PW 허용으로 열어둔다면 큰 보안 위협이 될 수 있습니다.

저 또한 홈랩 서버를 개발팀 협업툴을 self-hosting 하며 침입 시도를 겪어본 경험이 있고, 그 이후로는 ssh 설정부터 fail2ban 도입 등을 통해 보안에 신경쓰고 있습니다. 누군가 노린 것이 아니라, 포트를 스캔해보고 일단 침입을 시도하는 경우도 있는 만큼 보안 설정은 중요합니다. fail2ban은 분량상 다른 글에서 다루고, 이번에는 SSH 설정만 다뤄보겠습니다.

Warning
이후 SSH 설정 > SELinux 정책 허용 > 방화벽 허용 순으로 진행됩니다. 변경 포트를 적용하고 바로 sshd를 재시작한 경우, SELinux로 구동에 실패하거나 firewalld에 막힐 수 있습니다. 현재 세션은 유지되지만, 만약 접속이 끊어지면 다시 접속할 방법이 사라지므로 반드시 방화벽까지 설정한 뒤 재시작 해주어야 합니다.

2.4.1. SSH 설정 변경

우선 SSH 설정입니다. 서버는 sshd라는 데몬을 통해 사용자가 ssh 접속을 할 때, 이 통신을 받아들여주는 프로세스가 있습니다. 기본적으로 22번 포트를 Listening하고 있습니다. 이제, 이 기본 포트 번호부터 로그인을 제한하는 작업을 수행해 보겠습니다.

bash
sudo vim /etc/ssh/sshd_config

이후 다음 내용을 찾아 주석을 해제하고 값을 조정합니다.

bash
# ...
Port <포트번호>
# ...
PermitRootLogin no
StrictModes yes
MaxAuthTries 6 # 원하는 만큼으로 조정
MaxSessions 5 # 원하는 만큼으로 조정
# ...
PasswordAuthentication no # Username/Password로 로그인 제한
# ...
Note

포트 번호를 변경하는 이유: security through obscurity(은닉을 통한 보안)

  • 인터넷을 무차별로 스캔하는 봇으로부터 22번 포트 자동 공격 트래픽을 회피하고, fail2ban 등의 부하를 감소시킬 수 있습니다.
  • 단, 타겟팅된 공격에는 효과가 없습니다.
Tip
포트 번호는 1024~49151 내에서 선택하는 것이 좋습니다. 기본적으로 0~1023 포트는 "Well-known Port"라고 부르는, 잘 알려진 서비스가 사용하는 포트이므로 충돌가능성이 있습니다. 반대로, 49152~65535 포트는 OS가 아웃바운드 연결에서 임시로 사용하는 대역이라 충돌 가능성이 있습니다. 또한, 2222, 22222, 2200 등은 흔히 "SSH 대체포트"라고 부릅니다. 이미 봇들의 스캔 리스트에 포함되어 있으니, 이러한 포트도 피하는 것이 좋습니다.
Caution

  • 포트 번호 변경 시, 1.4에서 설정한 포트번호나 ssh 명령어의 포트 번호도 변경해야 합니다.
  • PermitRootLogin no를 수행할 때는 현재 root가 아닌 계정이 있는지 확인하고 적용하시기 바랍니다.
  • PasswordAuthentication nossh 키로 접속할 수 있는지 확인하고 적용하시기 바랍니다.

2.4.2. 정책 허용(EL 계열)

EL 계열의 리눅스의 경우, ssh 포트를 변경해도 SELinux의 정책으로 인해 해당 포트를 사용할 수 없습니다. 이 때문에, SELinux의 정책에서 SSH가 해당 포트를 사용할 수 있도록 구성해야 합니다.

bash
# 정책 중 ssh가 사용가능하게 허용된 포트 확인
sudo semanage port -l | grep ssh
# 만약 semanage가 없다는 오류가 뜬다면
# sudo dnf -y install policycoreutils-python-utils

# SSH가 특정 TCP 포트를 사용할 수 있도록 허용
# -a: add, -t ssh_port_t: SELinux 타입, -p: 포트
sudo semanage port -a -t ssh_port_t -p tcp <포트번호>

2.4.3. 방화벽 설정

SELinux는 SSH가 해당 포트를 사용할 수 없게 하는 것이고, 방화벽은 외부에서 본 시스템으로 접근할 때 해당 포트를 사용할 수 없도록 제한하는 것이 다른 점입니다. 본 글에서는 firewalld를 사용합니다.

bash
# 우선 firewalld 동작 확인
sudo systemctl status firewalld

# 만약 서비스를 찾을 수 없다면
sudo dnf install -y firewalld # debian은 apt install -y firewalld
# firewalld 활성화 (또는 서비스가 활성화되지 않았다면)
sudo systemctl start firewalld && sudo systemctl enable firewalld

# 현재 정책 확인
sudo firewall-cmd --list-all --zone=public
  • Output(조금 다를 수 있음):
text
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp1s0
  sources:
  services: cockpit dhcpv6-client ssh
  ports:
  protocols:
  forward: yes
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

이제 변경한 SSH 포트를 열어줍니다.

bash
# 정책 추가
sudo firewall-cmd --zone=public --permanent --add-port=<포트번호>/tcp
# firewalld 정책 리로드(필수)
sudo firewall-cmd --reload
# 변경된 정책 확인
sudo firewall-cmd --list-all --zone=public

결과에서 ports에 추가한 포트가 적혀있다면 잘 적용된 것입니다. 각 IaaS SG/네트워크(홈랩이라면 라우터나 공유기)에서도 정책을 추가하여 포트를 허용해준 뒤, sshd를 재시작 해줍니다.

bash
sudo systemctl restart sshd
Note
기존 22번 포트는 네트워크에서 막아주면 되며, firewalld에서 services의 ssh(22번)를 비허용하는 것은 선택입니다.

3. 결론

여기까지 하면, 인스턴스(또는 홈랩 머신)에 키 기반으로 SSH 접속할 수 있고, 저스펙에서 자주 터지던 OOM을 Swap으로 완충한 뒤, 패키지를 갱신하고 SSH 포트·로그인 정책·SELinux·방화벽까지 최소한이 갖춰진 상태가 됩니다. 클라우드 콘솔에서 VM만 켠 "빈 서버"가 아니라, 이후에 서비스를 올려도 접속과 기본 운영이 버틸 수 있는 출발점에 가깝습니다.

서론에서 말한 것처럼, 설정과 명령어는 AI로도 쉽게 얻을 수 있습니다. 다만 Swap이 왜 필요한지, 포트를 바꿨는데 SELinux·firewalld·SG 중 어디서 막히는지처럼 원리를 한 번 짚어 두면, 같은 종류의 장애를 덜 헤매게 됩니다. 저도 Free Tier의 인스턴스를 여러번 만들면서 처음에는 몰랐고, 그 이후로는 다양한 오류나 실수를 많이 했던 경험이 있습니다. 그리고 dnf update로 서버가 멈추던 경험만 열 번을 넘게 겪은 이후에야, 접속 다음 순서로 Swap과 보안 기본값을 고정하게 되었습니다.

이 글이 인프라를 공부하는 개발자들에게 도움이 되었으면 하는 바램을 가지며, 다음 글에서는 이 Rocky 서버 위에 Docker를 설치하고 컨테이너로 서비스를 올리는 쪽으로 이어가겠습니다. 리버스 프록시·SSL·CI/CD는 그 이후 단계에서 차근차근 붙이면 됩니다.

BlueNyang
작성자BlueNyang
라이선스
CC BY NC
BlueNyang

BlueNyang

BlueNyang의 개발 log

카테고리

  • Development
  • Framework
  • Language
  • Dev Tools
  • DevOps & Infra
  • Studies

페이지

© 2026 BlueNyang. All rights reserved.

Made with Nuxt.js and Directus