가수면
EC2로 Docker를 이용해 배포하기 (feat. Mysql) 본문
개발 환경 설정
문서는 다음 과정을 따른다.
Dockerfile을 생성해 도커 내부로 파일을 옮긴 뒤 도커 이미지를 생성하고,
Docker Compose로 컨테이너를 실행한다. (도커 컴포즈를 사용하지 않고 각각 실행하는 법은 후술)
실행이 완료되는 것을 확인하면 EC2에 적용한다.
예제는 스프링 부트와 Mysql을 포함한 예제를 사용한다.
+ Next 도커 배포 방법 추가
준비물:
1. 도커 설치
2. 도커 허브 회원가입
3. 스프링 부트 프로젝트
도커에서 실행시키기 위한 프로젝트 빌드 준비
1. jar 파일 이름 변경
스프링 부트의 빌드를 위해 편의상 jar파일의 이름을 app.jar로 변경한다.
build.gradle에 아래 코드 추가 후 리프레쉬
bootJar {
archiveFileName = 'app.jar'
}
2. Dockerfile 생성
루트 경로에 스프링 부트를 위한 Dockerfile 생성
FROM openjdk:17 // 실행 환경(node 등)
ARG JAR_FILE=build/libs/app.jar // jar파일 변수로 지정
COPY ${JAR_FILE} ./app.jar // 도커 컨테이너 내부로 카피
ENV TZ=Asia/Seoul
ENTRYPOINT ["java","-jar","./app.jar"] // 실행시킬 명령어(순서대로 명령어 입력됨)
루트 경로에 database폴더 생성 뒤 Dockerfile 생성 (redis 등 데이터베이스 외 추가 컨테이너만들 것이 있다면 같은 원리)
FROM mysql:8.0.34
ENV TZ=Asia/Seoul
도커에서 데이터베이스의 한글이 깨지는 경우를 대비한 설정
/database/config/mysql.cnf
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
skip-character-set-client-handshake
[mysqldump]
default-character-set=utf8mb4
3. application.yml 설정
사용자 프로필을 분리해서 작성한 예제)
spring:
profiles:
active: local
group:
local:
- common
prod:
- common
---
spring:
config:
activate:
on-profile: common
jwt:
secret-key: ${JWT_SECRET_KEY}
token:
expired-time-ms: 2592000000
logging:
level:
org:
springframework: info
---
spring:
config:
activate:
on-profile: local
datasource:
url: jdbc:mysql://localhost:3306/todos
username: jhchoi1182
password: ${SPRING_DATASOURCE_PASSWORD}
jpa:
hibernate:
ddl-auto: update
show-sql: true
todo:
recommendation:
base:
url: http://localhost:8080/dir/
---
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:mysql://todo-database:3306/todos // jdbc:mysql://(mysql의 컨테이너명):3306/(mysql의 데이터베이스 이름)
username: jhchoi1182
password: ${SPRING_DATASOURCE_PASSWORD}
jpa:
hibernate:
ddl-auto: update
show-sql: true
todo:
recommendation:
base:
url: http://52.79.113.40/dir/
application.yml의 env 변수들은 .env에 설정해도 적용되지 않는다. (.env 파일 내 변수들은 도커 컴포즈를 통해 컨테이너로 실행될 때 docker-compose 파일을 통해 application.yml에 주입됨)
로컬 환경에서 환경 변수를 지정하려면 인텔리J IDE 자체에 변수를 설정하면 된다.
도커 컴포즈 구성
1. .env 파일 만들기
.env 파일에 환경 변수를 설정하면 컨테이너로 실행될 때 docker-compose 파일내 변수에 적용된다.
이때 중요한 것은 프로필을 local, prod 나눠서 관리할 경우 'SPRING_PROFILES_ACTIVE=prod'을 설정해줘야 컨테이너 실행 시 프로필이 prod로 스위칭 되어 실행된다.
.env 예시)
SPRING_DATASOURCE_PASSWORD=321321
SPRING_PROFILES_ACTIVE=prod
JWT_SECRET_KEY=aslkaslfmlqwknflkqnlwfnqlwnfqwlnfqlwfnqwf!!@$
2. 도커 컴포즈 파일 만들기
docker-compose-local.yml과 docker-compose.yml로 분리하여 개발 환경에서는 스프링 부트를 컨테이너로 만들지 않고 인텔리J로 로깅을 확인하는 것이 디버깅이나 여러 면에서 좋다.
docker-compose.yml 예시)
docker-compose-local.yml에는 todo-app을 빼면 됨
version: "3.8" # 파일 규격 버전
services: # 이 항목 밑에 실행하려는 컨테이너들을 정의
todo-redis: # 서비스명
container_name: todo-redis # 컨테이너 명
build:
dockerfile: Dockerfile
context: ./redis # 도커 파일 경로
image: jhchoi1182/todo-redis # 도커 허브아이디/사용할 이미지 이름
ports:
- "6379:6379"
todo-database:
container_name: todo-database
build:
dockerfile: Dockerfile
context: ./database
image: jhchoi1182/todo-database
environment:
- MYSQL_DATABASE=todos
- MYSQL_ROOT_PASSWORD=${SPRING_DATASOURCE_PASSWORD}
- MYSQL_USER=jhchoi1182
- MYSQL_PASSWORD=${SPRING_DATASOURCE_PASSWORD}
volumes:
- ./database/config:/etc/mysql/conf.d
- ./database/init:/docker-entrypoint-initdb.d
ports:
- "3306:3306" # 접근 포트 설정 (컨테이너 외부:컨테이너 내부)
todo-app:
container_name: todo-app
build: .
depends_on: # DB, REDIS 컨테이너가 실행된 다음 WEB을 실행시킨다.
- todo-database
- todo-redis
image: jhchoi1182/todo-app
environment: # application.yml에 환경변수 주입
- SPRING_DATASOURCE_PASSWORD=${SPRING_DATASOURCE_PASSWORD}
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE}
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
ports:
- "80:8080"
restart: always # depends on은 실행 순서만 컨트롤 할뿐,
# 컨테이너 안의 서비스가 실행가능한 상태인지까지는 확인 하지 않기 때문에
# DB 또는 Redis가 아직 실행가능한 상태가 아니여서 실패하는 경우 재시작 하도록 설정
# DB가 완전히 실행될 때까지 스프링 부트가 계속 오류가 발생하기 때문에 스프링 부트 실행까지 꽤 오래걸릴 수 있다.
3. 빌드 및 도커 컴포즈 파일 실행
소스 코드의 변화가 있을 때마다 이 부분을 반복하면 된다.
// gradle 캐쉬 삭제 및 빌드
./gradlew clean build
// docker-compose 파일 실행
docker-compose up --build
// (참고) 테스트 케이스 제외하고, jar 파일 빌드만 진행할 경우
./gradlew clean build -x test
// (참고) docker-compose-local 파일 실행
docker-compose -f docker-compose-local.yml up
도커 컴포즈로 생성한 컨테이너 삭제 명령어
docker-compose down
EC2 설정 및 컨테이너 실행
로컬에서 문제없이 작동하는 것을 확인했다면 EC2 서버 설정 후 로컬에서 했던 것처럼 실행시켜주면 된다.
인바운드 규칙 설정
※ ec2 파티션에 스왑 공간 할당하기
프리 티어를 사용할 경우 도커 컨테이너를 여러개 실행시키게 되면 cpu 사용량이 폭발하며 ec2가 렉걸리고 멈추는 현상이 발생한다...
이 경우 스왑 공간을 할당해 RAM을 확보 하여 허접한 메모리 문제를 해결할 수 있다. (렉이 사라진다!)
참고로 프리 티어의 경우 4GB보단 2GB를 추천한다.
// 4GB(128MB x 32)
sudo dd if=/dev/zero of=/swapfile bs=128M count=32
// 2GB(128MB x 16)
sudo dd if=/dev/zero of=/swapfile bs=128M count=16
https://repost.aws/ko/knowledge-center/ec2-memory-swap-file
EC2 인스턴스 생성 후 기본 설정
1. git 설치
#Perform a quick update on your instance:
$ sudo yum update -y
#Install git in your EC2 instance
$ sudo yum install git -y
#Check git version
$ git version
2. 도커 및 도커 컴포즈 설치
//도커 설치
$ sudo yum install docker
$ docker -v
// 도커 컴포즈 설치
$ 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 systemctl start docker
// 실행 권한 적용
$ sudo chmod +x /usr/local/bin/docker-compose
$ sudo chmod 666 /var/run/docker.sock
$ docker-compose -v
만약
Error loading Python lib '/tmp/_MEITz7le8/libpython3.7m.so.1.0': dlopen: libcrypt.so.1: cannot open shared object file: No such file or directory
오류가 발생했을 경우 아래 명령어 입력한 후 docker-compose -v를 다시 해보면 설치된 것을 확인할 수 있다.
sudo dnf install libxcrypt-compat
3. JDK 설치
Amazon에서 제공하는 OpenJDK인 Amazon Coretto를 이용해 설치 진행
// aws coreetto11 다운로드 후 설치
sudo curl -L https://corretto.aws/downloads/latest/amazon-corretto-11-x64-linux-jdk.rpm -o jdk11.rpm
sudo yum localinstall jdk11.rpm
// aws coreetto17 다운로드 후 설치
sudo curl -L https://corretto.aws/downloads/latest/amazon-corretto-17-x64-linux-jdk.rpm -o jdk17.rpm
sudo yum localinstall jdk17.rpm
java -version
4. 프로젝트 clone 따오기
5. .env 파일 생성 후 설정하기
프로젝트 경로로 들어가 .env 생성해서 작성 후 저장
// .env 만들기
vi .env
// 작성 후 저장/나가기
:wq
환경 변수가 잘 주입되었는지 확인하는 법
docker-compose config
6. jar 파일 생성
./gradlew clean build
// ./gradlew clean build에 권한없다는 오류가 발생할 경우 아래 명령어 입력
chmod +x ./gradlew
// 테스트 케이스 제외하고, jar 파일 빌드만 진행할 경우
./gradlew clean build -x test
7. 도커 컴포즈 실행
docker-compose up --build
// 백그라운드에서 실행
docker-compose up --build -d
// 로그 확인
docker logs [컨테이너 ID 또는 이름]
8. 코드 변경 사항 적용
깃허브 원경 저장소에서 pull 이후 아래 명령어를 통해 빌드, 컨테이너 실행 재수행
// ec2 렉걸려서 멈출 경우 먼저 컨테이너 실행 중지
docker-compose stop
./gradlew clean build
docker-compose up --build
// 백그라운드에서 실행
docker-compose up --build -d
//도커 이미지 정리
docker image prune
도커 컴포즈 없이 각각 컨테이너를 실행시키는 경우
도커 컴포즈의 원리는 이렇다.
도커 환경에서 네트워크를 생성한 후 컨테이너를 실행시킬 때 컨테이너들을 해당 네트워크 환경으로 묶어서 실행시킨다.
이 케이스에선 그것을 수동으로 해주는 방법을 살펴본다.
프로젝트의 설정은 도커 컴포즈 설정없이 application.yml 설정만 하면 된다. 이때 주의할 점은 DB 연결 url을 jdbc:mysql://(mysql의 컨테이너명):3306/(mysql의 데이터베이스 이름)으로 설정해야 한다.
1. 네트워크 생성
// 도커 네트워크 생성
docker network create springboot-mysql-net(네트워크 이름)
// 도커 네트워크 확인
docker network ls
2. db 등 컨테이너 생성 및 실행
이때 네트워크 설정을 1번에서 생성한 네트워크로 설정한다.
docker run --detach --env MYSQL_ROOT_PASSWORD=321321 --env MYSQL_USER=jhchoi1182 --env MYSQL_PASSWORD=321321 --env MYSQL_DATABASE=todos --name todo-database --network springboot-mysql-net --publish 3306:3306 mysql:8.0.34
3. Dockerfile 생성
4. 빌드, 도커 이미지 생성, 컨테이너 실행
이때도 역시 스프링 부트의 네트워크 설정을 1번에서 생성한 네트워크로 설정한다.
필요한 경우 --env SPRING_PROFILES_ACTIVE=prod설정을 추가한다.
// 캐쉬 삭제 후 빌드
./gradlew clean build
// 스프링 부트 도커 이미지 생성
docker build -t jhchoi1182/todo-app .
// 스프링 부트 컨테이너 실행
docker run --detach --name springboot-mysql --network springboot-mysql-net --publish 8080:8080 jhchoi1182/todo-app
'웹 개발 > 웹 개발' 카테고리의 다른 글
SQL 쿼리 (0) | 2024.03.27 |
---|---|
쿠키 보안 설정하기 (withCredentials, httpOnly 등) (0) | 2024.02.28 |
IntelliJ 단축키 정리 (1) | 2024.01.09 |
HTTP 상태 코드 정리 (0) | 2023.12.19 |
MySQL (0) | 2023.12.14 |