AWS 서버 환경 구축
초기 인프라 구축의 경우 AWS의 EC2, ElastiCache, RDS 서비스를 이용해 인프라를 구축했다.
API 서버의 경우 컨테이너 기반의 환경을 구성하기 위해 Docker, docker-compose를 이용했으며 토큰 관리를 위해 ElastiCache의 Redis를 이용했다. RDS는 프리티어에서 사용할 수 있으며 가장 쉽게 접할 수 있는 MySQL를 이용해 초기 서버 환경을 구축 했다.
EC2 서버 스팩
아래 EC2 서버 스팩의 경우 Free Tier이기도 하며 SpringBoot 프로젝트 배포 시 최소한의 스팩에 맞도록 인스턴스 유형을 설정
OS | Amazon Linux 2 |
인스턴스 유형 | t2.micro |
vCPU | 1(1 Core) |
Memory | 1GB |
Network | Low to Moderate |
RDS 스팩
Free Tier 기준으로 초기 RDS 인스턴스 구성
DBMS | MySQL 8.0.33 |
인스턴스 유형 | db.t3.micro |
vCPU | 2 |
Memory | 1GB |
Network | 최대 2,085Mbps |
스토리지 유형 | 20GiB(최대 60 IOPS) |
API 서버 환경 구축
Docker 설치
1
2
3
4
5
6
7
8
| # yum 업데이트
$ sudo yum update -y
# amazon linux extras 저장소에서 Docker 설치한다.
$ sudo amazon-linux-extras install docker
# Docker 서비스 실행
$ sudo service docker start
|
초기에 Docker를 설치하면 docker 그룹의 유저만 Docker를 관리할 수 있다. ec2-user도 Docker를 관리할 수 있도록 하기 위해 docker 그룹에 ec2-user를 추가해준다.
1
| $ sudo usermod -a -G docker ec2-user
|
설치 및 유저 수정 후 도커 컨테이너 정보를 확인하다보면 아래와 같이 권한 오류가 발생한다면 ssh를 재접속하면 된다.
1
2
| # Docker 정보 확인
$ docker info
|
Docker 자동실행
시스템 부팅 시 Docker 서비스가 자동으로 시작되도록 설정한다.
1
| $ sudo systemctl enable docker
|
Docker Compose 설치
amazon linux extras에서 Docker Compose를 관리하지 않기에 아래와 같이 링크를 통해 설치한다.
1
| $ sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
|
설치가 완료되면 Docker Compose에 대한 권한이 없기에 아래와 같이 설치한 docker-compose 디렉토리의 권한을 변경해준다.
1
2
3
4
5
| # docker-compose 권한 변경
$ sudo chmod +x /usr/local/bin/docker-compose
# docker-compose 버전 확인
$ docker-compose version
|
Nginx 테스트
docker-compose 설정
1
2
3
4
5
6
7
8
9
| services:
nginx:
image: nginx:latest
container_name: nginx # 사용할 컨테이너 이름
environment:
- TZ=Asia/Seoul
ports:
- 80:80 # 외부 포트 : 내부 포트
restart: always
|
위와 같이 docker-compose.yml를 작성하면 nginx 이미지를 pull 한 뒤 컨테이너가 실행된다.
그렇지만, 브라우저로 접속하게되면 실행되지 않는다. 아래 명령어를 통해 docker 내부에 접속해 conf 파일을 보면
1
2
3
4
| # nginx container 접속
$ docker exec -it nginx /bin/bash
$ cd /etc/nginx
|
Nginx Container 내부에 접속해 conf.d 내부를 보면 비어있을 것이다. 그리고 nginx.conf 내부를 보면 아래 코드가 있다.
1
2
3
4
5
| ...
include /etc/nginx/conf.d/*.conf;
...
|
위 부분은 server 설정 정보를 읽어오는 부분이고 관련된 파일이 없어 실행되지 않는다. 해결방법으로 기본 nginx 컨테이너를 실행하여 접속 후 관련 파일들을 가져온다.
1
2
3
4
5
6
7
8
| # 기본 nginx 이미지를 기반으로 컨테이너 실행
$ docker run -d --name temp-nginx nginx
# temp-nginx의 nginx.conf 파일을 서버에 복사
$ docker cp temp-nginx:/etc/nginx/nginx.conf {nginx.conf 파일을 복사할 경로}
# temp-nginx의 conf.d 디렉토리를 서버에 복사
$ docker cp temp-nginx:/etc/nginx/conf.d {conf.d 디렉토리을 복사할 경로}
|
위 명령어를 실행하고 서버 경로에 들어가면 default.conf 파일을 볼 수 있다. 우선 기본 파일로 두고 위 파일 및 경로를 실제 운영할 Nginx 컨테이너 볼륨으로 연결해준다.
1
2
3
4
5
6
7
8
9
10
11
12
| services:
nginx:
image: nginx:latest
container_name: nginx # 사용할 컨테이너 이름
environment:
- TZ=Asia/Seoul
ports:
- 80:80 # 외부 포트 : 내부 포트
volumes: # 외부(서버) 경로 : 컨테이너 내부 경로
- {conf.d 디렉토리가 복사된 경로}/conf.d:/etc/nginx/conf.d
- {nginx.conf 파일을 복사된 경로}/nginx.conf:/etc/nginx/nginx.conf
restart: always
|
이 후 컨테이너를 실행하면 아래와 같이 정상적으로 실행되는 것을 볼 수 있다.
1
| $ docker-compose up -d # -d : 백그라운드로 실행
|
http://{호스트 DNS or IP} 로 접속하면 아래와 같이 Nginx가 정상적으로 구동되는 것을 확인할 수 있다.
Docker Compose 환경변수 파일 설정
환경변수 파일 생성 및 설정
docker-compose.yml 파일이 있는 디렉토리에 .env
파일 생성 후 필요한 파라미터 값을 설정해준다.
1
2
3
4
| # .env 파일
CPUS_LIMIT=1
MEMORY_LIMIT=128m
MEMORY_RESERVE=32m
|
docker-compose 파일에서 .env 파일에 설정한 변수를 가져와 사용할 수 있다.
1
2
3
4
5
6
7
| # docker-compose.yml
services:
nginx:
cpus: ${CPUS_LIMIT}
mem_limit: ${MEMORY_LIMIT}
mem_reservation: ${MEMORY_RESERVE}
...
|
환경변수 파일 설정 후 아래 명령어를 통해 정상적으로 설정되었는지 확인 할 수 있다.
1
| $ docker-compose config
|
커스텀 환경변수 파일 생성 및 설정
만약 docker-compose.yml과 다른 폴더 혹은 다른 파일명으로 환경설정 파일을 사용하기 위해서는 아래와 같이 직접 명령어를 통해 컨테이너를 실행해야 한다.
1
2
| $ docker-compose --env-file {적용할 환경변수 파일 경로}{적용할 환경변수 파일명} up # 컨테이너 시작
$ docker-compose --env-file {적용할 환경변수 파일 경로}{적용할 환경변수 파일명} down # 컨테이너 종료
|
별도 환경변수 파일 설정 후 아래 명령어를 통해 정상적으로 설정되었는지 확인 할 수 있다.
1
| $ docker-compose --env-file {적용할 env 파일 경로}{적용할 env 파일명} config
|
컨테이너 시작/종료 명령어를 통해 설정하지 않고 docker-compose 파일에 env_file
옵션을 이용해서 서비스별 환경변수를 설정할 수 있다고 하는데 테스트 해보았을 때 적용되지 않는다…
Docker Compose Container 자동실행
시스템 부팅 시 docker-compose로 실행하는 컨테이너를 자동으로 시작되도록 설정한다.
서비스 파일 생성 및 설정
1
2
3
4
| # 시스템 서비스 관리 경로로 이동
$ cd /etc/systemd/system
# 실행할 컨테이너 서비스 파일 생성
$ sudo vi {서비스명}.service
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| [Unit]
Description={서비스 파일 설명}
Requires=docker.service
After=docker.service
[Service]
RemainAfterExit=yes
Restart=on-failure
RestartSec=5s
WatchdogSec=30s
WorkingDirectory=/my-app
ExecStart=/usr/local/bin/docker-compose up
ExecStop=/usr/local/bin/docker-compose down
[Install]
WantedBy=multi-user.target
|
- [Unit]
- Requires : Docker 서비스 필수 설정
- After : Docker 서비스 실행 후 실행
- [Service]
- RemainAfterExit : docker-compose를 백그라운드로 실행(docker-compose up)하지 않아도 실행 종료 후 active 상태로 유지
- Restart : 서비스가 비정상 종료(exit code ≠ 0, 시그널 종료 등)된 경우에만 재시작
- RestartSec : 서비스가 종료된 후, 재시작되기 전에 대기 시간 설정 (단위 : min, s, ms)
- WatchdogSec : 서비스가 응답하지 않거나 오류 상태에 빠졌을 때 systemd가 이를 자동으로 감지하고 서비스를 재시작 (단위 : min, s, ms)
- WorkingDirectory : docker-compose.yml 파일 디렉토리 경로
- ExecStart : docker-compose 실행 명령어
- ExecStop : docker-compose 중지 명령어
- [Install]
- WantedBy=multi-user.target : 시스템이 멀티 사용자 모드로 부팅될 때 해당 서비스가 자동으로 시작되도록 설정
Elasticache - Redis
Refresh Token의 경우 고유 방문자수가 최대 886,440명 기준 약 300MB 정도의 크기가 스토리지가 필요하다. Refresh Token 저장소로 RDB 혹은 In-Memory DB가 있다. 처음에는 아래의 이유로 RDB에 저장하려고 했었다.
- RDB 여유 공간 이용 가능
- In-Memory DB 사용시 별도 서비스 이용으로 비용 발생
여러 개발 블로그를 찾아보았지만, 대부분 Redis와 같은 In-Memory DB에 저장하는 것을 권장한다. 이유는 아래와 같다.
- 데이터의 만료일을 지정
- 뛰어난 조회 성능으로 Reissue 호출 빈도가 높아지면 병목현상 방지
- 특정 토큰 탈취의 경우 블랙리스팅의 필요성
이러한 이유로, In-Memory DB인 Redis를 구성하기로 했다.
Token 관리용 Redis 서버
최대 886,440명의 사용자 기준으로 최소 300MB정도의 메모리가 필요하다. AWS에서 In-Memory DB 서비스로 ElastiCache, Amazon MemoryDB for Redis가 있다.
Elasticache | cache.t2.micro | 1 | 0.555 | USD 0.026 | USD 18.72 |
Amazon MemoryDB for Redis | db.t4g.small | 2 | 1.37 | USD 0.07 | USD 50.4 |
필요한 최소 메모리 기준 및 낮은 비용으로 ElastiCache를 사용하기로 했으며, ElastiCache 구성시에 우선 Refresh 토큰 저장을 목적으로 두고 있기에 별도 Slave 노드는 구성하지 않고 Master 노드만 구성했다.
Redis 접속을 위한 Redis CLI 설정
Redis CLI 설치
1
2
3
4
5
| $ sudo wget https://download.redis.io/redis-stable.tar.gz
$ tar xvzf redis-stable.tar.gz
$ cd redis-stable
$ make
$ sudo cp /src/redis-cli /usr/bin/
|
Redis CLI 접속
1
| $ redis-cli -h {ElastiCache Redis OSS 기본 엔드포인트(포트제외)} -p 6379
|
redis-cli 접속 후 ping 입력 → PONG 응답이 보이면 된다.
Docker Project 배포
Local → Docker Hub 배포
- 프로젝트 루트 경로 이동 후 빌드
1
| $ ./gradlew clean build
|
$ ./gradlew clean build -x test
→ test를 Skip하고 Build
- Local Docker에 이미지 생성
1
| $ docker build --build-arg DEPENDENCY=build/dependency -t {Docker 사용자}/{이미지명}:{TAG} --platform linux/amd64 .
|
- Docker Push
1
| $ docker push {Docker 사용자}/{이미지명}:{TAG}
|
프로젝트 컨테이너 실행
- Docker Project Image 가져오기
1
| $ docker pull {Docker 사용자}/{이미지명}:{TAG}
|
- docker-compose 작성
1
2
3
4
5
6
7
8
| services:
서비스명:
container_name: {컨테이너 명}
image: {Docker 사용자}/{이미지명}:{TAG}
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE={SPRING_PROFILES_ACTIVE 환경변수}
|
- 컨테이너 실행
Nginx Proxy 설정
- default.conf 파일 수정
1
2
3
4
5
6
| location / {
proxy_pass http://{호스트 DNS 혹은 IP}:8080;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
|