Project를 배포 해보자 #2

장준혁·2024년 2월 29일

AWS-EC2

목록 보기
2/2
post-thumbnail

docker-compose로 project의 작동을 확인 해 봤다.
▶Project를 배포 해보자 #1 (docker-compose)

project를 배포하기 위해 EC2 인스턴스를 생성 해줘야 한다.

🔧 EC2 설정

📌 인스턴스 생성


인스턴스 명과 사용할 AMI(Amazon Machine Image)를 선택해야 한다.

AMI로 Ubuntu를 선택 했으며 기호에 맞게 변경하면 좋을 것 같다.


미리 만들어둔 키 페어가 존재하지 않았기에 새로 생성을 해서 적용 했다.


네트워크는 딱히 수정할 필요 없이 default로 설정

인스턴스 생성이 정상적으로 완료 되었음을 확인 할 수 있다.

인스턴스를 생성할 때 항상 새 IP를 할당한다.

인스턴스를 중지하고 재시작하면 새로운 IP가 할당되기 때문에 고정적인 IP를 가질수 있도록 탄력적 IP 주소를 할당해 줘야 한다.
안 해줘도 실행 하는데 문제는 없다.

**
탄력적 IP를 만들고 ec2에 연결해주지 않으면 과금이 되기때문에 반드시 연결해야 한다.

생성에는 과금이 되지 않으며 생성 후 방치 하면 요금이 부과 되는 것 같다.


생성한 고정 IP주소를 인스턴스와 연결

정상적으로 IP 주소가 고정 됨을 확인 했다.

📌 Spring boot port 개방


생성한 인스턴스 정보를 통해 보안그룹 이름을 확인 할 수 있으며, 보안그룹 이름을 통해 인바운드 규칙을 설정한다.

9091포트를 사용하는 spring boot 프로젝트에 접속 하기 위해 9091포트를 인바운드 설정 해줬다.

본인이 사용하는 spring boot port 번호에 맞게 변경하여 개방 해주면 된다.

📌 인스턴스 연결


인스턴스가 중지 된 상태라면 시작 상태로 변경 해주면 연결이 가능하다.

📌 인스턴스 용량 문제

프리티어로 사용 할 수 있는 t2.micro는 RAM 1GB(GiB)의 제한이 있기 때문에 프로젝트를 실행 할 때 뻗는 경우가 있었다.

CPU 점유율이 99 ~ 100이 되면 자체적으로 ec2에서 연결을 끊어버리기 때문에 용량을 늘릴 필요가 있었다.

docker-compose로 실행 하려는 프로젝트는 기본 용량을 한참 넘어서기 때문에 어쩔 수 없이 대안을 찾아 봐야 했다.

💾 Swap 메모리

ec2 램 메모리가 가득 찰 경우 실제 디스크의 용량을 이용하여 부족한 메모리를 대체할 공간을 생성 할 것이며 이를 swap memory(스왑 공간)이라 한다.

swap메모리는 ec2 램을 대체 하는 것은 아니기 때문에 램에 직접 접근 하는 것 보다 속도가 느려지지만 테스트 목적 이기 때문에 감당 하기로 했다.

📝 Swap 파일에 메모리를 할당 한다.

sudo dd if=/dev/zero of=/swapfile bs=128M count=16

sudo : 'Super User Do'의 줄임말로, 관리자 권한으로 명령을 실행하는 데 사용
dd : 파일을 복사하거나 변환하는 데 사용되는 명령어
if=/dev/zero : /dev/zero를 사용, /dev/zero는 모든 위치에서 읽을 때마다 null 바이트를 생성하는 특수 파일이다.
of=/swapfile : 출력 파일로 /swapfile을 사용한다는 의미
bs=128M count=16 : 2GB(128MB x 16)의 용량을 할당 한다.

📝 Swapfile에 접근할 수 있는 권한을 설정한다.

sudo chmod 600 /swapfile

읽기, 쓰기가 가능하도록 chmod 600으로 설정 해줬다.

📝 swap 공간으로 생성

sudo mkswap /swapfile

mkswap : /swapfile을 스왑 공간으로 설정.

📝 swap 공간으로 생성

sudo swapon /swapfile

swapon : /swapfile 스왑 공간을 즉시 활성화 한다.

📝 /etc/fstab 파일 수정

sudo vi /etc/fstab 명령어를 입력 후 /etc/fstab vi 에디터 열어주고 파일의 맨 아래에 /swapfile swap swap defaults 0 0 를 추가 해준다.

📌 인스턴스 Docker, Compose 사전 설정

ec2환경에서 docker와 compose를 작동하기 위해 docker 설치 및 docker compose 사전 작업을 해주자.

🐳 Docker

  • 패키지 업데이트
    sudo apt update

  • https관련 패키지
    sudo apt install apt-transport-https ca-certificates curl software-properties-common

  • docker repository gpg 키 설정
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

  • docker repository 등록
    sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"

  • 패키지 업데이트
    sudo apt update

  • 도커 설치
    sudo apt install docker-ce

  • 확인
    docker --version

🐙 Docker-compose

sudo docker-compose up -d 명령어 실행 시에

ubuntu@ip-172-31-32-137:~/mere-dev-merona$ sudo docker-compose up
sudo: docker-compose: command not found

와 같은 에러가 발생 한다면

  • docker-compose 설치
    sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

  • 실행 권한 부여
    sudo chmod +x /usr/local/bin/docker-compose

🐋 Docker-hub

📝 Dockerfile-server.yml

Spring Boot를 jar파일로 변환 해주기 위해서 Dockerfile-server.yml 파일을 작성 한다.

FROM openjdk:8-jdk
EXPOSE 9091
ARG JAR_FILE=./build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

Dockerfile-server.yml 파일을 작성 후 dokcer-compose.yml 파일과 동일한 경로에 위치 하게 둔다.

📝 Docker-hub image upload

server:
    build:
      context: .
      dockerfile: Dockerfile_server
    container_name: dev_merona_spring_boot ## 컨테이너 이름
    restart: always # 매번 프로젝트를 다시 실행한다.
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/{database 명}?useSSL=false&allowPublicKeyRetrieval=true
      SPRING_DATASOURCE_USERNAME: {USERNAME}
      SPRING_DATASOURCE_PASSWORD: {PASSWORD}
      SPRING_REDIS_HOST: redis
      SPRING_REDIS_PORT: 6379
      SPRING_RABBITMQ_HOST: rabbitmq
      SPRING_RABBITMQ_PORT: 5672
      SPRING_RABBITMQ_USERNAME: guest
      SPRING_RABBITMQ_PASSWORD: guest
      spring.profiles.active: local ## 스프링 active profile 설정
    ports:
      - 9091:9091
    depends_on:
      - mysql
      - redis
      - rabbitmq
    networks:
      - dev_merona

  rabbitmq:
    image: rabbitmq:3.12.11-management
    hostname: rabbitmq
    container_name: dev_merona_rabbitmq
    restart: unless-stopped
    environment:
      - RABBITMQ_DEFAULT_USER=guest
      - RABBITMQ_DEFAULT_PASS=guest
    ports:
      - 5672:5672
      - 15672:15672
    networks:
      - dev_merona
  # rabbit image 버전 3.12.11
  # 5672작동 , 15672 시각화

  mysql:
    container_name: dev_merona_mysql
    image: mysql:8.0.22
    environment:
      MYSQL_DATABASE: {database 명}
      MYSQL_ROOT_PASSWORD: {PASSWORD}
      MYSQL_ROOT_HOST: '%'
      # MySQL root 계정이 어떤 호스트에서든지 접근 가능하도록 설정
      TZ: 'Asia/Seoul'
      # 컨테이너의 시간대를 'Asia/Seoul'로 설정.
    ports:
      - 3306:3306
      # 호스트의 3306 포트와 컨테이너의 3306 포트를 연결
    volumes:
      - ./mysql-file/dev_merona_mere.sql:/docker-entrypoint-initdb.d/dev_merona_mere.sql
      #mysql 덤프 파일
    networks:
      - dev_merona
    restart: always

  redis:
    container_name: dev_merona_redis
    image: redis:6.2.5
    command: redis-server
    restart: always
    networks:
      - dev_merona
    ports:
      - 6379:6379

volumes:
  elasticsearch:


networks:
  dev_merona:
    #네트워크 명
	#동일한 네트워크를 설정
    driver: bridge

docker-compose 파일 경로로 이동해서 docker-compose up 명령어를 작동 한다면 container와 image가 생성 될 것이다.


원래 계획은 git hub를 통해서 프로젝트를 통째로 내려받은 후 docker-compose 파일을 작동 시켜 ec2환경에서 그대로 돌려버릴 생각 이였다.

하지만 막상 ec2환경에서 docker-compose 파일을 실행 시켜보니 spring boot Dockfile이 정상 작동 하지 않았다.

git ignore로 위험성이 있는 폴더 들을 제외 했었기 때문에 해당하는 폴더 및 파일들은 git에 업로드가 되지 않았으며 jar파일의 폴더도 포함되어 있었다.

ignore된 project를 git에 업로드 했기 때문에 git 프로젝트를 그대로 받은 ec2 project 역시 ignore된 상태 였던 것이다.

git ignore를 전부 수정 하려고도 시도 해봤지만 워낙 많은 설정들이 되어 있어 수정하기 난감 했다.

프로젝트를 수정하지 않고 ec2환경에 내려받기 위해 Docker hub에 spring boot image를 업로드 하기로 했다.

hub에 image를 업로드 후 docker-compose 파일로 해당 이미지를 다운 받을 수 있도록 설정 한다면 가능 할 것 같았다.

먼저 repository를 생성 해준다.


cmd창 또는 teminal에 접속 후 docker login 명령어 입력을 통해 docker에 접속한다.

만약 username , password를 요구한다면 docker hub 가입 시 입력했던 값을 넣어 주면 될 듯 하다.

현재 image들을 전부 확인 후 hub에 업로드 하길 원하는 spring boot server image id를 기억해둔다.

docker image tag {이미지 id} {변경할 이미지명} 명령어를 통해 이미지 tag명을 변경 해 복사 한 후
docker push {변경 된 이미지 명} 명령어를 통해 hub에 push한다.


업로드 할 이미지 명을 repository명 과 동일하게 설정 해줬기 때문에 정상적으로 push 되었다면 repository명으로 pull 할 수 있다.

📄 파일 설정

📝 application.yml (Spring Boot)

기존의 Spring Boot 프로젝트를 local 환경에서 작성 해왔기 때문에 모든 url이 local로 잡혀 있었다.
url: jdbc:mysql://localhost:3306/mere_test?useSSL=false&serverTimezone=Asia/Seoul

mysql, rabbitmq, redis 등 container가 분리된 환경이라
url: jdbc:mysql://mysql:3306/mere_test?useSSL=false&serverTimezone=Asia/Seoul 와 같이 host를 지정 해줘야 한다고 생각했었다.

host를 지정한 application.yml 파일과 지정하지 않은 파일을 비교 해보니 정상 작동 했었다.
host를 지정하지 않아도 정상 작동 한 원인은 동일한 network를 설정 했기에 container간 local 접근이 가능 하기 때문이다.

📝 docker-compose.yml (최종)

version: '3.2'

services:

  server:
    image: {docker hub에 업로드한 이미지 명}
    container_name: dev_merona_spring_boot ## 컨테이너 이름
    restart: always # 매번 프로젝트를 다시 실행한다.
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/{database 명}?useSSL=false&allowPublicKeyRetrieval=true
      SPRING_DATASOURCE_USERNAME: {USERNAME}
      SPRING_DATASOURCE_PASSWORD: {PASSWORD}
      SPRING_REDIS_HOST: redis
      SPRING_REDIS_PORT: 6379
      SPRING_RABBITMQ_HOST: rabbitmq
      SPRING_RABBITMQ_PORT: 5672
      SPRING_RABBITMQ_USERNAME: guest
      SPRING_RABBITMQ_PASSWORD: guest
      spring.profiles.active: local ## 스프링 active profile 설정
    ports:
      - 9091:9091
    depends_on:
      - mysql
      - redis
      - rabbitmq
    networks:
      - dev_merona

  rabbitmq:
    image: rabbitmq:3.12.11-management
    hostname: rabbitmq
    container_name: dev_merona_rabbitmq
    restart: unless-stopped
    environment:
      - RABBITMQ_DEFAULT_USER=guest
      - RABBITMQ_DEFAULT_PASS=guest
    ports:
      - 5672:5672
      - 15672:15672
    networks:
      - dev_merona
  # rabbit image 버전 3.12.11
  # 5672작동 , 15672 시각화

  mysql:
    container_name: dev_merona_mysql
    image: mysql:8.0.22
    environment:
      MYSQL_DATABASE: {database 명}
      MYSQL_ROOT_PASSWORD: {PASSWORD}
      MYSQL_ROOT_HOST: '%'
      # MySQL root 계정이 어떤 호스트에서든지 접근 가능하도록 설정
      TZ: 'Asia/Seoul'
      # 컨테이너의 시간대를 'Asia/Seoul'로 설정.
    ports:
      - 3306:3306
      # 호스트의 3306 포트와 컨테이너의 3306 포트를 연결
    volumes:
      - ./mysql-file/dev_merona_mere.sql:/docker-entrypoint-initdb.d/dev_merona_mere.sql
      #mysql 덤프 파일
    networks:
      - dev_merona
    restart: always

  redis:
    container_name: dev_merona_redis
    image: redis:6.2.5
    command: redis-server
    restart: always
    networks:
      - dev_merona
    ports:
      - 6379:6379

volumes:
  elasticsearch:


networks:
  dev_merona:
    #네트워크 명
	#동일한 네트워크를 설정
    driver: bridge

💻 작동 확인


ec2 puplic ip에서 정상적인 swagger의 작동을 확인 할 수 있었으며 CPU 사용률 역시 swap메모리를 적용한 이후 안정된 모습을 보여줬다.

📗 정리

프로젝트가 마무리 되어가는 이 시점에서, 지금까지 미루고 있던 EC2 배포 작업에 손을 대게 되었다.
배포를 시도하는 과정에서 Docker 설정과 관련된 에러가 발생하여 상당히 많은 시간을 소비하게 되었는데 원인은 Docker에 대한 기본적인 이해가 부족하여 필요한 설정을 보충할 필요성이 있었기 때문이다.

YouTube와 Google 등에서 많은 정보를 얻을 수 있었지만, 그 정보를 프로젝트에 맞게 적용하고 이해하는 과정은 여전히 험난했다.

지금 생각해 보면 docker hub를 사용하지 않고도 spring boot 프로젝트를 통째로 옮겨준 다음 jar로 변환 해줄 수도 있었을 것 같다.
당시에는 ec2 환경이 익숙하지 않아 local에서 최대한 많은 작업을 한 후 ec2에서 실행만 하려 했었디.
지금도 ec2 환경이 익숙하지는 않다.

프리티어를 사용하는 t2.micro 용량으로는 프로젝트 규모를 감당하지 못했다.
sudo docker-compose up -d 명령어를 실행하면 서버가 뻗어버리고 명령어 입력도 되지 않아 인스턴스 삭제 및 재생성도 굉장히 많이 했다.

아직 팀원들의 작업이 모두 완료되지 않아 모든 작업물을 배포해 보지는 못해서 아쉽지만 배포를 경험했다는 것에 의미를 두고 싶다.

참고

EC2 : https://velog.io/@song22861/ec2-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1%ED%9B%84-%EC%84%9C%EB%B2%84-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0

docker hub upload : https://yeko90.tistory.com/entry/how-to-upload-image-in-docker-hub

swap : https://kth990303.tistory.com/361

profile
wkd86591247@gmail.com

0개의 댓글