CampusMeet

[AWS] EC2 생성 및 실행

dong_seok 2024. 9. 30. 18:17

지금까지 FastAPI로 만든 애플리케이션을 Docker, AWS 를 활용하여 배포해보도록 하겠습니다.

1. AWS EC2 생성
2. Docker Image Build and Push
3. EC2 인스턴스 접속 및 실행

1. AWS EC2 생성

AWS 프리티어를 사용할 예정이기에 "프리 티어 사용 가능" 이라고 적혀있는 범위 내에서 타입들을 선택 해줍니다.

 

 

저는 Ubuntu  OS를 선택해 주었고

 

 

인스턴스 유형은 프리 티어가 지원하는 t2.micro를 선택해주었습니다. 키 페어는 인스턴스에 접근할 수 있는 강력하게 설정된 비밀번호입니다. 기존에 생성해둔 키페어가 있다면 사용하면 되고, 없다면 새로운 키 페어를 만들어줍니다. 저는 RSA 유형에 OpenSSH와 함께 사용하는 .pem 프라이빗 키 파일 형식을 선택해주었습니다. 이름은 자유롭게 만들어주셔도 무방합니다. 단, 생성한 키 페어는 위치를 알 수 있는곳에 잘 저장해두길 권장드립니다. 저는 "~/.ssh" 에 저장 해두었습니다.

 

 

네트워크 설정은 따로 건드리지 않았고 스토리지는 프리티어에서 제공하는 최대 공간인 30GB를 할당하였습니다. 나중에 프리티어가 끝나면 해당 스토리지의 용량에 따라 과금이 달라지기 때문에 주의해서 저장공간을 할당해야 합니다.  이렇게 설정을 마치고 인스턴스 시작을 클릭하면 인스턴스가 생성됩니다.  인스턴스 목록에 들어가면 하단과 같이 "실행 중" 상태의 인스턴스를 볼 수 있습니다.

 

 

2. Docker Image Build and Push

docker 이미지를 빌드하는 명령어는 "docker compose build" 를 사용했습니다. 이미 docker-compose.yml 파일에 내용을 다 입력해두었기 때문에 따로 다른 설정 없이 간단하게 이미지를 빌드 할 수 있었습니다. 여기서 주의해야할점은 image의 이름을 반드시 [Username]/[이미지이름] 으로 해주어야한다는 것 입니다. 이 설정을 해줘야 docker hub에 이미지를 올릴 수 있습니다.

 

 

저는 Docker Desktop을 쓰기 때문에 로그인을 통해 Push to Hub 로 간단하게 허브에 올릴 수 있었습니다.

 

 

제가 생성한 이미지에 "AMD 64"라는 부분이 표시되는 점이 의아할 수 있습니다. 이는 Dockerfile을 수정하면서 겪었던 시행착오에서 비롯된 것입니다. 현재 제 로컬 PC는 맥OS M1을 사용 중이며, M1의 경우 시스템 아키텍처로 "arm64"를 사용합니다.

(확인하려면 터미널에서 uname -m 명령어를 입력하시면 됩니다.)

 

문제가 발생한 이유는 제가 EC2 인스턴스를 생성할 때 arm 아키텍처가 아닌 64비트(x86)를 선택했기 때문입니다. 도커 이미지를 생성할 때, 별도의 아키텍처 설정을 하지 않으면 사용 중인 Mac의 아키텍처인 arm64가 자동으로 반영됩니다. 그 결과, 생성된 이미지의 아키텍처는 arm64이지만 EC2 호스트 시스템은 amd64 아키텍처를 사용하게 되어 불일치로 인해 오류가 발생한 것입니다.

 

이 문제를 해결하기 위해 Dockerfile에서 생성할 이미지의 아키텍처를 "AMD 64"로 명시하였고, 그래서 이미지에 "AMD 64"라고 표시된 것입니다. 이제 인스턴스에 접속해서 서버를 실행해보도록 하겠습니다.

 

3. EC2 인스턴스 접속 및 실행

먼저 인스턴스 연결에 들어가서 SSH 클라이언트를 클릭 후 하단의 명령을 복사해줍니다.

 

그리고 인스턴스를 생성하면서 등록했던 키페어 파일이 위치한 디렉터리로 이동해줍니다. 저의 경우엔 위에서 말씀 드렸듯이 "~/.ssh" 입니다.  그 후 위에서 복사한 명령어를 실행해주면 우리가 생성한 EC2에 접속할 수 있습니다.

 

 

이제 Docker Hub에서 이미지를 가져와서 실행해 보도록 하겠습니다. 인스턴스 내에서 명령어를 실행할때는 앞에 관리자 권한을 위해 앞에 sudo 를 붙혀줄 필요가 있습니다 "sudo docker pull dskang207/campus_meet-main_server:latest" 명령어를 사용해 이미지를 가져와 줍니다. 이제 이미지가 잘 가져와졌는지 확인해 보도록 하겠습니다.

 

 

이미지가 잘 가져와졌으니 이제 컨테이너로 만들고 실행 시켜보도록 하겠습니다. "sudo docker run dskang207/campus_meet-main_server:latest" 명령어를 사용해 주었습니다.

 

 

한눈에 봐도 제대로 실행이 된 모습이 아니라는 생각이 들었습니다. 그래서 우선 컨테이너는 만들어졌는지 확인해보도록 하였습니다.

 

 

컨테이너는 정상적으로 만들어졌습니다. 그렇다면 이미지 자체의 문제보다는 컨테이너를 실행중에 무슨 문제가 생겼을 것 같다는 생각이 들었습니다. 디버깅을 해보니 이미지를 실행하기 위해 필요한 환경 변수 값들이 ".env" 파일을 통해 관리되고 있었는데, 현재 AWS EC2 인스턴스 내에는 파일에 대한 정보가 없기 때문에 발생한 오류일 것 같다는 생각이 들었습니다. 그래서 이 정보를 제공해주기로 하였습니다.

 

scp -i "~/.ssh/ec2-server-kds-keypair.pem" .env ubuntu@ec2-43-202-65-10.ap-northeast-2.compute.amazonaws.com:~/campus_meet/backend/

 

로컬에 있는 .env 파일을 AWS 인스턴스의 ~/campus_meet/backend/ 경로로 scp 명령어를 사용해 복사했습니다. 그러나 이후 docker start 명령어로 기존 컨테이너를 실행했음에도 동일한 에러가 발생했습니다. 이를 서치해보니, docker start 명령어는 기존 컨테이너를 재시작할 뿐, .env 파일의 환경 변수를 반영하지 않는다는 사실을 알게 되었습니다.

 

또한, 현재 Docker Hub에는 FastAPI 소스에 대한 이미지만 푸시되어 있고, 데이터베이스에 대한 정보는 없기 때문에, docker-compose를 사용해 DB 이미지 및 컨테이너 정보를 함께 가져와야 합니다. 따라서 docker run으로 단일 이미지를 컨테이너화해서 실행하는 대신, 배포용으로 새로운 docker-compose 파일을 작성하여, 해당 파일에서 Docker Hub의 이미지를 pull로 가져와 실행하는 방식으로 로직을 구현하는 것이 이후 CI/CD를 고려했을 때 더 효율적이고 관리하기 용이하다고 판단이 들어서 로직을 변경하였습니다.

 

새롭게 docker-compose.prod.yml 을 만들어줍니다.

 

version: '3.8'

services:
  db:
    image: postgres:16
    restart: always
    volumes:
      - app-db-data:/var/lib/postgresql/data/pgdata
    env_file:
      - .env
    environment:
      - PGDATA=/var/lib/postgresql/data/pgdata
    ports:
      - "5432:5432"
    deploy:
      resources:
        limits:
          memory: 1G

  web:
    image: dskang207/campus_meet-main_server:latest
    command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
    ports:
      - "8000:8000"
    depends_on:
      - db
    env_file:
      - .env
    environment:
      - DOMAIN=${DOMAIN}
      - ENVIRONMENT=${ENVIRONMENT}
      - BACKEND_CORS_ORIGINS=${BACKEND_CORS_ORIGINS}
      - SECRET_KEY=${SECRET_KEY?Variable not set}
      - FIRST_SUPERUSER=${FIRST_SUPERUSER?Variable not set}
      - FIRST_SUPERUSER_PASSWORD=${FIRST_SUPERUSER_PASSWORD?Variable not set}
      - SMTP_HOST=${SMTP_HOST}
      - SMTP_USER=${SMTP_USER}
      - SMTP_PASSWORD=${SMTP_PASSWORD}
      - POSTGRES_SERVER=db
      - POSTGRES_PORT=${POSTGRES_PORT}
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER?Variable not set}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set}

volumes:
  app-db-data:

 

web 부분에 build를 없애고 image만 만들어둠으로써, 이미지를 기존 코드에서 새롭게 빌드하는것이 아니라 로컬에 있는지 확인하고 없으면 Docker Hub에서 이미지를 가져와서 실행시키도록 로직을 변경하였습니다.

 

docker-compose.prod.yml 을 실행시키기 위한 명령어는 다음과 같습니다.

 

docker-compose -f docker-compose.prod.yml up -d

 

실행해보면 다음과 같이 컨테이너가 정상적으로 작동하는 모습을 볼 수 있습니다.

 

 

이제 컨테이너가 실행중이니 접속을 해보도록 하겠습니다. EC2 인스턴스의 퍼블릭IP를 활용해 접속해 주겠습니다.

 

 

퍼블릭IP : 8000 으로 접근해보도록 하겠습니다.

 

 

 

포트 번호에도 문제가 없었지만, 정상적으로 서버에 접근되지 않는 것을 확인했습니다. 이는 EC2 인스턴스를 생성할 때 별도의 네트워크 설정을 해주지 않아서 발생한 문제입니다. 아래와 같이 인바운드 규칙을 편집해주면 정상적으로 접근이 가능합니다.

 

 

FastAPI 컨테이너를 생성하면서 지정해준 8000번 포트에 해당하는 인바운드 규칙을 허용해주면 아래와 같이 웹 브라우저에서 정상적으로 서버에 접근할 수 있는 모습을 볼 수 있습니다.

 

 

참고자료

https://velog.io/@mechauk418/FastAPI-Docker-AWS-EC2%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%98%EC%97%AC-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0

 

https://ooeunz.tistory.com/70

 

 

 

'CampusMeet' 카테고리의 다른 글

[AWS] ALB 생성 및 연결  (0) 2024.10.07
[AWS] Private 인스턴스 연결  (2) 2024.10.06
[AutoScaling] 다양한 AutoScaling 전략  (0) 2024.09.19
[DNS] Nginx ingress controller 와 Domain  (0) 2024.09.15
[NKS] NKS를 활용한 배포 (1)  (0) 2024.09.08