- 1.[Cloud Infra] 서버와 SSH현재
- 2.
[Cloud Infra] 도커의 시작
0. 배포의 시작
개발자로서 아이디어를 기획하고, 아키텍처를 설계하고, 코드를 짜고 테스트를 거쳐 빌드해낸 순간, 성취감을 느끼면서도 결과물 앞에서 문득 이런 막막함이 찾아옵니다.
로컬 환경에서는 터미널에 명령어 몇 개만으로 localhost 위에서 잘 구동되던 서비스가, 막상 리눅스 기반의 원격 클라우드 서버에 올리려고 하면 방화벽, 포트, 권한 문제 등 자주 막힙니다.
최근에는 AI의 발달 덕분에 배포 과정에서 필요한 설정이나 명령어를 쉽게 받을 수 있게 되었지만, 그럼에도 "내 로컬에서는 잘 되는데 이상하게 서버만 가면 죽어버리는" 의문의 현상은 여전히 발생합니다. 결국 인프라의 동작 원리와 배포 파이프라인의 구조에 대한 이해 없이는 계속 막히기 마련입니다.
그리고 이번 시리즈에서 다뤄볼 기록이 바로 이 "배포"입니다. 직접 AWS, GCP, Vultr, OCI, Netlify, Render를 거쳐 홈랩 서버까지. 많은 클라우드 환경을 구축하고 죽을 쒀가며 얻은 것들을 공유해보려고 합니다.
단순히 코드를 짜는 것을 넘어, 내가 만든 프론트엔드/백엔드 서비스를 클라우드 서버에 안전하게 안착시키고, 리버스 프록시와 SSL을 통해 공인 도메인과 연결하며, 나아가 Git Push나 버튼 한 번으로 배포가 알아서 끝나는 완전한 CI/CD 파이프라인을 내 손으로 직접 구축해 보는 것이 이 시리즈의 목표입니다.
이번 글에서는 본격적인 내용에 앞서, 인프라의 시작점인 클라우드 서버. 그리고 이 서버와 대화하는 창구인 SSH(Secure Shell) 이야기부터 차근차근 풀어보겠습니다.
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)에 복사하여 등록하는 키. - 인증 과정:
1.2. SSH 키 생성하기
SSH 키 페어는 ssh-keygen 명령어를 사용합니다. Windows/MacOS/Linux 모두 동일한 명령어를 사용합니다. 명령어의 대소문자에 주의하세요.
- SSH 키 페어 생성:
명령어를 실행하면 다음과 같이 출력됩니다.
만약 key를 다른 위치에 저장하고 싶거나 이름을 다르게 하고 싶다면 경로와 파일명을 적고, 그렇지 않다면 그냥 엔터를 누릅니다. 그러면 다음과 같이 ssh key의 암호를 입력하라는 메시지를 볼 수 있습니다.
입력하지 않거나, 기억하기 쉬운 암호를 입력하면 되며, 암호를 입력할 때 화면에 아무런 글자도 써지지 않는 것이 정상입니다. 그러면 다음과 같이 키가 생성됩니다.
1.3. SSH 키를 등록하고 접속하기
키 페어를 생성했다면, 공개키를 서버에 전송하여 등록합니다.
id_ed25519가 아니라 id_ed25519.pub 입니다.이후 원격 서버 접속 방법은 다음과 같습니다.
1.4. SSH Config 설정 (~/.ssh/config)
매번 복잡한 옵션을 주는 대신, 로컬 환경에 아래와 같이 구성하여 간편하게 접속할 수 있습니다.
키 생성시 파일 위치를 따로 지정했다면 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만 하면 서버가 멈춰버리는 상황을 항상 겪었습니다.
free -h를 통해 Swap Memory의 할당 상태를 확인하시기 바랍니다.
이러한 환경에서 Swap Memory는 총합 6~8GB를 할당하는 것을 권장합니다.- 설정하기:
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를 권장합니다.
2.3. 패키지 업데이트 및 설치
시스템을 처음 설치하면, 우선 시스템에서 의존하고 있는 패키지와 커널을 업데이트해야 합니다. EL 계열은 dnf, Debian 계열은 apt를 사용합니다.
업데이트 이후, 기본적으로 많이 사용하고 필요한 패키지를 설치합니다.
reboot)하는 것을 권장합니다.2.4. 서버 보안 설정
내부 네트워크를 사용하거나 홈랩 서버가 아니라면, IaaS 인스턴스의 경우 서버의 SSH 포트는 현재 인터넷에 열려 있을 것입니다. SSH가 아무리 안전하다지만, 22번 포트를 Root 로그인 허용 및 ID/PW 허용으로 열어둔다면 큰 보안 위협이 될 수 있습니다.
저 또한 홈랩 서버를 개발팀 협업툴을 self-hosting 하며 침입 시도를 겪어본 경험이 있고, 그 이후로는 ssh 설정부터 fail2ban 도입 등을 통해 보안에 신경쓰고 있습니다. 누군가 노린 것이 아니라, 포트를 스캔해보고 일단 침입을 시도하는 경우도 있는 만큼 보안 설정은 중요합니다. fail2ban은 분량상 다른 글에서 다루고, 이번에는 SSH 설정만 다뤄보겠습니다.
2.4.1. SSH 설정 변경
우선 SSH 설정입니다. 서버는 sshd라는 데몬을 통해 사용자가 ssh 접속을 할 때, 이 통신을 받아들여주는 프로세스가 있습니다. 기본적으로 22번 포트를 Listening하고 있습니다. 이제, 이 기본 포트 번호부터 로그인을 제한하는 작업을 수행해 보겠습니다.
이후 다음 내용을 찾아 주석을 해제하고 값을 조정합니다.
포트 번호를 변경하는 이유: security through obscurity(은닉을 통한 보안)
- 인터넷을 무차별로 스캔하는 봇으로부터 22번 포트 자동 공격 트래픽을 회피하고, fail2ban 등의 부하를 감소시킬 수 있습니다.
- 단, 타겟팅된 공격에는 효과가 없습니다.
2222, 22222, 2200 등은 흔히 "SSH 대체포트"라고 부릅니다. 이미 봇들의 스캔 리스트에 포함되어 있으니, 이러한 포트도 피하는 것이 좋습니다.- 포트 번호 변경 시,
1.4에서 설정한 포트번호나ssh명령어의 포트 번호도 변경해야 합니다. PermitRootLogin no를 수행할 때는 현재 root가 아닌 계정이 있는지 확인하고 적용하시기 바랍니다.PasswordAuthentication no는 ssh 키로 접속할 수 있는지 확인하고 적용하시기 바랍니다.
2.4.2. 정책 허용(EL 계열)
EL 계열의 리눅스의 경우, ssh 포트를 변경해도 SELinux의 정책으로 인해 해당 포트를 사용할 수 없습니다. 이 때문에, SELinux의 정책에서 SSH가 해당 포트를 사용할 수 있도록 구성해야 합니다.
2.4.3. 방화벽 설정
SELinux는 SSH가 해당 포트를 사용할 수 없게 하는 것이고, 방화벽은 외부에서 본 시스템으로 접근할 때 해당 포트를 사용할 수 없도록 제한하는 것이 다른 점입니다. 본 글에서는 firewalld를 사용합니다.
- Output(조금 다를 수 있음):
이제 변경한 SSH 포트를 열어줍니다.
결과에서 ports에 추가한 포트가 적혀있다면 잘 적용된 것입니다. 각 IaaS SG/네트워크(홈랩이라면 라우터나 공유기)에서도 정책을 추가하여 포트를 허용해준 뒤, sshd를 재시작 해줍니다.
3. 결론
여기까지 하면, 인스턴스(또는 홈랩 머신)에 키 기반으로 SSH 접속할 수 있고, 저스펙에서 자주 터지던 OOM을 Swap으로 완충한 뒤, 패키지를 갱신하고 SSH 포트·로그인 정책·SELinux·방화벽까지 최소한이 갖춰진 상태가 됩니다. 클라우드 콘솔에서 VM만 켠 "빈 서버"가 아니라, 이후에 서비스를 올려도 접속과 기본 운영이 버틸 수 있는 출발점에 가깝습니다.
서론에서 말한 것처럼, 설정과 명령어는 AI로도 쉽게 얻을 수 있습니다. 다만 Swap이 왜 필요한지, 포트를 바꿨는데 SELinux·firewalld·SG 중 어디서 막히는지처럼 원리를 한 번 짚어 두면, 같은 종류의 장애를 덜 헤매게 됩니다. 저도 Free Tier의 인스턴스를 여러번 만들면서 처음에는 몰랐고, 그 이후로는 다양한 오류나 실수를 많이 했던 경험이 있습니다. 그리고 dnf update로 서버가 멈추던 경험만 열 번을 넘게 겪은 이후에야, 접속 다음 순서로 Swap과 보안 기본값을 고정하게 되었습니다.
이 글이 인프라를 공부하는 개발자들에게 도움이 되었으면 하는 바램을 가지며, 다음 글에서는 이 Rocky 서버 위에 Docker를 설치하고 컨테이너로 서비스를 올리는 쪽으로 이어가겠습니다. 리버스 프록시·SSL·CI/CD는 그 이후 단계에서 차근차근 붙이면 됩니다.