이전 글에서 만든 `docker-compose.yml`을 AWS 클라우드 환경에서 배포를 진행해보려 한다.
1. EC2 인스턴스 생성
EC2를 검색해서 인스턴스 시작 버튼을 눌러보자.

이름은 원하는 걸 적고, OS 이미지는 우분투를 선택했다.

💡우분투를 선택한 이유
우분투는 리눅스 커널(엔진) + APT(앱스토어)가 추가된 OS이다. 서버 개발자들이 가장 많이 사용하기 때문에 구글링 했을 때 자료가 압도적으로 많고, 초심자가 문제 해결을 하기 쉬워서 선택했다.

키 페어도 생성하자.
💡키 페어란?
서버의 보안을 위한 '디지털 자물쇠와 열쇠'이다.
공개 키(자물쇠): AWS 서버에 저장된다.
개인 키(열쇠): 내 컴퓨터에 .pem 파일로 저장된다.
비밀번호 입력 방식보다 훨씬 안전하며, 이 `.pem`파일을 잃어버리면 서버에 접속할 수 없어 잘 보관해야 한다.
네트워크 설정 옆에 [편집]을 누르고 왼쪽 하단에 [보안 그룹 규칙 추가]를 누른다.

열고 싶은 포트를 설정하고, CIDR 블록도 설정하자.

💡CIDR 블록이란?
'누가 접속할 수 있는가?'를 결정하는 IP 주소의 범위다.
`0.0.0.0/0`: 모든 IPv4 주소
`0.0.0.0/8`: 앞의 한 숫자만 고정하고 나머지 3자리는 아무거나
`0.0.0.0/16`: 앞의 두 숫자를 고정하고 나머지 2자리는 아무거나
`0.0.0.0/24`: 앞의 세 숫자를 고정하고 나머지 1자리는 아무거나
`0.0.0.0/32`: 1개의 IP 주소
`::/0`: 모든 IPv6 주소
다 만들었으면 [인스턴스 시작]을 누른다.
2. SSH 서버 접속하기
EC2 목록에서 방금 만든 인스턴스를 클릭하고 왼쪽 상단에 있는 [연결] 버튼을 클릭한다. 그 다음 [EC2 인스턴스 연결]에서 [연결] 버튼을 클릭하면

해당 인스턴스의 터미널을 확인할 수 있다.
3. 도커 설치하기
이 인스턴스엔 아직 아무것도 설치 되어있지 않아서, APT를 업데이트하고 docker engine과 docker compose를 설치해야 한다.
sudo apt update // 설치 가능한 프로그램 목록을 최신화
sudo apt install docker.io -y // 도커 엔진 설치
sudo apt install docker-compose -y // 도커 컴포즈 설치
sudo usermod -aG docker $USER // 현재 사용자에게 도커 사용 권한 부여 (sudo를 매번 안 치기 위해)

그리고 나서 바로 `docker ps`를 하면 나오지 않는다. 리눅스에선 그룹 설정을 바꿔도, 지금 켜져 있는 세션에 바로 반영이 되지 않아서 그렇다. 터미널을 껐다가 다시 SSH 연결을 하면 해결된다. 그렇지만 그건 귀찮으니까
newgrp docker
위 명령어를 통해 변경한 도커 그룹 설정을 바로 적용할 수 있다.

`docker ps`로 다음과 같이 나오면 docker 설정이 끝난다.
4. 배포하기
이제 저번에 만들었던 `docker-compose.yml` 내용을 이 서버로 옮겨서 실행해보자.
mkdir my-server
cd my-server
nano docker-compose.yml

폴더를 만들어서 해당 파일 안에 `docker-compose.yml`을 생성한다.

`Ctrl + X` -> `Y` -> `Enter`로 저장하자.
docker compose up -d

근데 `docker ps`엔 nginx가 잘 뜨는데

nginx에 접속해보니 접속되지가 않았다.
보안 그룹 체크하기
위에서 보안 그룹을 체크할 때, 포트를 `8080`으로 열었는데 `docker-compose.yml`에는 `8081`로 포트를 열었다. 이럴 땐, 보안 그룹의 포트를 변경하거나 `docker-compose.yml`의 값을 변경하고 `docker-compose down`후 다시 `up -d`를 하면 된다. 난 보안 그룹의 포트를 변경하기로 했다. (Mysql 포트도 설정하지 않아서 추가로 설정해주었다.)

볼륨 설정하기

이젠 403에러가 발생한다. 보안 그룹까지는 통과했는데 nginx에서 요청을 거부한 것이다.

어젠 volume을 설정했는데, 오늘 EC2 인스턴스엔 볼륨을 설정한 파일이 없기 때문이다.
nano index.html
<h1>Hello! This is AWS Server</h1>


볼륨에 해당하는 파일을 만들면, 다음과 같이 잘 접속 가능한 것을 확인할 수 있다.

Mysql도 잘 접속된 것을 확인할 수 있다.
배운 점
1. 네트워크 계층의 이해 (Security Group vs Docker Port) Nginx 컨테이너가 정상 실행 중(Up)임에도 접속이 되지 않았던 원인은 포트 불일치였다. AWS의 보안 그룹(Security Group)은 8080 포트만 허용했고, Docker Compose는 8081 포트를 리스닝하고 있었다. 단순히 서버를 띄우는 것보다, 외부 요청이 방화벽을 통과해 컨테이너까지 도달하는 네트워크 경로를 일치시키는 것이 중요함을 배웠다.
2. 도커 볼륨(Volume)의 동작 원리 (403 Forbidden) 보안 그룹 해결 후 발생한 403 에러는 도커 볼륨의 특성을 간과해서 발생했다. 로컬 환경과 달리, 초기화된 EC2 인스턴스에는 마운트할 소스 파일(index.html)이 존재하지 않았다. `-v` 옵션은 파일의 '복사'가 아닌 호스트 경로의 '마운트'임을 명확히 이해했고, 호스트에 해당 파일을 생성하여 해결했다.
3. 리눅스 권한과 세션 관리 docker 명령어를 `sudo` 없이 사용하기 위해 그룹 권한을 부여했으나 즉시 적용되지 않았다. 리눅스는 로그인 시점에 권한을 로드하기 때문이다. 재접속 대신 `newgrp` 명령어로 현재 세션의 그룹 정보를 갱신하여 해결할 수 있었다.
이번 배포 과정을 통해 OS(Ubuntu) -> Network(Aws Security Group) -> Middleware(Docker) -> Application(Nginx/Mysql)로 이어지는 인프라 계층 구조를 설계할 수 있게 되었다.
'Infra' 카테고리의 다른 글
| [Infra] GitHub Actions와 Docker로 CI/CD 파이프라인 구축하기 (1) | 2025.12.24 |
|---|---|
| [Infra] Docker란? (Docker에서 AWS EC2까지-1) (1) | 2025.12.05 |