Kwon's Study Blog !
[Docker] 복잡한 어플을 실제로 배포해보기 (테스트 & 배포) 본문
현재 글은
https://www.inflearn.com/course/%EB%94%B0%EB%9D%BC%ED%95%98%EB%A9%B0-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EB%8F%84%EC%BB%A4-ci/dashboard
을 수강하며 정리한 내용입니다.
문제시 비공개로 처리 하도록 하겠습니다.
따라하며 배우는 도커와 CI환경 - 인프런 | 강의
이 강의를 통해 도커에 대해서 배울 수 있으며, CI 환경을 구성할 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com
저번 글이였던 '복잡한 애플을 실제로 배포해보기(개발 환경 부분)' 와 이번 글의 간략한 개요입니다.
'복잡한 애플을 실제로 배포해보기(개발 환경 부분)' 에선
애플리케이션을 위한 전체적인 소스코드를 작성했고 각각에 맞는 Dockerfile을 작성했으며
그 각각의 컨테이너를 연결시켜주고 간단하게 한번에 실행시켜 줄 수 있는 Docker Compose를 작성했습니다.
이번 글에선 소스코드에 에러가 없는지 테스트를 한 후 테스트에서 성공을 하면 AWS를 통해 배포를 해보는 것 까지 해보겠습니다.
DB를 운영 환경에서는 도커를 이용하는게 아닌 AWS RDS를 이용하므로 이에 맞게 MYSQL 부분을 먼저 정리하도록 하겠습니다.
도커환경의 MYSQL부분 정리하기
docker-compose.yml 파일에서 mysql에 대한 부분을 모두 주석처리 하겠습니다.
# mysql:
# build: ./mysql
# restart: unless-stopped # 개발자가 임의로 멈출 때 빼고
# container_name: app_mysql
# ports:
# - "3306:3306"
# volumes:
# - ./mysql/mysql_data:/var/lib/mysql
# - ./mysql/sqls/:/docker-entrypoint-initdb.d/
# environment:
# MYSQL_ROOT_PASSWORD : password
# MYSQL_DATABASE: myapp
Mysql 이 이제는 도커 안에서 돌아가는게 아닌 AWS에서 돌아가고 있는 것을 이 애플리케이션에 연결만 해 줄 것이니 Mysql을 애플리케이션에 연결해주는 부분 빼고 다 지워줍니다.
Github에 소스 코드 올리기
git init
git add .
git commit -m "first commit"
git remote add origin https://github.com/ksoon1985/docker-fullstack-app.git
git push origin master
하면 github에 올라간 것을 볼 수 있지만
frontend 폴더에 이미 git이 등록된 상태라 -> 계속 modified 상태가 됐습니다...
github에 올라가질 않으므로
이럴 땐 root폴더(docker-fullstack-app)로 와서
git rm -r --cached
위 명령어를 입력하면 남아있는 깃 캐시를 삭제합니다.
그리고 frontend 폴더에서 .git 폴더를 삭제하고
다시 git add -> commit -> push하면 -> github에 잘 올라간 것을 볼 수 있습니다.
Travis CI
Travis CI 가 할 일들은
깃 허브에 코드를 푸시해서 ~ 빌드된 이미지를 도커 허브에 전송하기 까지 과정이 남았습니다.
.travis.yml 파일 작성
# 언어(플랫폼)을 선택
language: generic
# 관리자 권한 갖기
sudo: required
# 도커 환경 구성
services:
- docker
# 스크립트를 실행할 수 있는 환경 구성
# docker build -t <도커 아이디>/<어플 이름> -f <도커파일 경로>
before_install:
- docker build -t ksoon1985/react-test-app -f ./frontend/Dockerfile.dev ./frontend
# 실행할 스크립트(테스트 실행)
script:
- docker run -e CI=true ksoon1985/react-test-app npm run test
# 테스트 성공 후 할일
after_success:
# 각각의 이미지를 build
- docker build -t ksoon1985/docker-frontend ./frontend
- docker build -t ksoon1985/docker-backend ./backend
- docker build -t ksoon1985/docker-nginx ./nginx
# 도커 허브에 로그인
- echo "$DOCKER-HUB-PASSWORD" | docker login -u "$DOCKER_HUB_ID" --password-stdin
# 빌드 된 이미지들을 도커 허브에 push
- docker push ksoon1985/docker-front
- docker push ksoon1985/docker-backend
- docker push ksoon1985/docker-nginx
그리고 테스트하는 부분인 /frontend/src/App.test.js 부분을 수정해줍니다.
기존 App.js 부분이 변경되면서 테스트가 실패하기 때문입니다. (learn react 부분)
test('renders learn react link', () => {
// render(<App />);
// const linkElement = screen.getByText(/learn react/i);
// expect(linkElement).toBeInTheDocument();
});
결과적으로 travis 사이트를 가보면 테스트가 성공하고
그리고 docker hub 사이트를 가보면 build 된 이미지들이 push 된 것을 확인할 수 있습니다.
Docker-Compose 파일 수정
위의 과정까진 컨테이너 안에 DB를 생성해서 데이터를 생성하고 volumes를 이용해 컨테이너가 삭제가 된다 하더라도 데이터들의 영속성을 지켜주었습니다. (개발 환경에서)
운영환경에서는 AWS RDS를 이용할 것이므로 위의 DB를 위한 컨테이너 작업들은 필요하지 않게 됐습니다.
그래서 docker-compose.yml 에서 mysql부분을 주석처리 한 것 입니다.
현재 프로젝트를 보면 각 컨테이너 마다(frontend, backend, nginx) Dockerfile을 가지고 있습니다. Dockerfile이 하나일 땐 Elastic Beanstalk(EB)이 알아서 docker-compose up으로 dockerfile을 빌드하고 컨테이너를 만들어서 실행을 시켜줬습니다. (EB linux 2버전) -> Dockerfile 이 하나일 땐 별다른 설정을 해주지 않아도 됬습니다.
하지만, 이번 애플리케이션은 멀티 컨테이너를 기반으로 하기에 Dockerfile도 여러개 입니다. 이런 상황에선 EB가 특별히 설정을 해주지 않는다면 자동으로 프로세스를 해나갈 수 없기 때문에 특별한 설정을 별도로 필요하게 됩니다.
EB의 linux1버전에선 dockerrun.aws.json파일로 설정을 해주면 됩니다.
하지만 EB의 linux1 버전은 현재 서비스 중지가 되어 가고있는 상태고, linux2버전을 추천으로 하기에 이 버전으로 하는 방법을 알아 보겠습니다.
EB의 linux2 버전에선 별도의 dockerrun.aws.json 파일 없이 docker-compose.yml에 추가적으로 설정을 해주면 됩니다.
현재 개발환경과 운영환경에서의 compose 파일을 구별하기 위해
개발 환경은 docker-compose-dev.yml 로, 운영 및 배포 환경은 docker-compose.yml로 나눠보겠습니다.
docker-compose-dev.yml (개발 환경)
docker-compose-dev.yml 파일은 기존 docker-compose.yml 과 같습니다.
mysql 은 도커 컨테이너에서 운영되기에 주석은 다시 풀어주도록 하겠습니다.
version: '3'
services:
frontend:
build:
dockerfile: Dockerfile.dev
context: ./frontend
volumes:
- /app/node_modules
- ./frontend:/app
stdin_open: true # 리액트 앱을 종료할 때 나오는 버그를 잡아줌
nginx:
restart: always
build:
dockerfile: Dockerfile
context: ./nginx
ports:
- "3000:80"
backend:
build:
dockerfile: Dockerfile.dev
context: ./backend
container_name: app_backend
volumes:
- /app/node_modules
- ./backend:/app
mysql:
build: ./mysql
restart: unless-stopped # 개발자가 임의로 멈출 때 빼고
container_name: app_mysql
ports:
- "3306:3306"
volumes:
- ./mysql/mysql_data:/var/lib/mysql
- ./mysql/sqls/:/docker-entrypoint-initdb.d/
environment:
MYSQL_ROOT_PASSWORD : password
MYSQL_DATABASE: myapp
docker-compose.yml (운영 환경)
version: "3"
services:
frontend:
image: ksoon1985/docker-frontend # 도커 허브에 저장된 이미지를 가져오기 위해
volumes:
- /app/node_modules
- ./frontend:/app
stdin_open: true
mem_limit: 128m # 메모리 사이즈 명시
nginx:
restart: always
image: ksoon1985/docker-nginx
ports:
- "80:80" # 기본적으로 nginx는 80포트에서 애플리케이션을 실행
backend:
image: ksoon1985/docker-backend
container_name: app_backend
volumes:
- /app/node_modules
- ./backend:/app
mem_limit: 128m
environment: # AWS RDB 서비스로 생성된 DB와 애플리케이션 연결을 위한 정보들
- MYSQL_HOST: $MYSQL_HOST
- MYSQL_USER: $MYSQL_USER
- MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD
- MYSQL_DATABASE: $MYSQL_DATABASE
- MYSQL_PORT: $MYSQL_PORT
기존 개발환경에서 추가된 부분은
- image : 도커 이미지를 도커 허브에 이미 저장된 이미지를 가져오기 위해 도커 허브에 저장된 이미지 이름을 명시합니다.
- mem_limit : 메모리 사이즈를 명시합니다.
- AWS RDB 정보 : AWS RDB서비스로 생성된 DB와 애플리케이션 연결을 위한 정보를 RDS 서비스 페이지에서 가져와서 backend 컨테이너의 MYSQL 에 대응하는 키 값을 넣어주면 됩니다.
다중 컨테이너 앱을 위한 Elastic Beanstalk 환경 생성
AWS 로그인 후 Elastic Beanstalk 서비스에 들어갑니다.
새 환경 생성 버튼 클릭 후
웹 서버 환경 생성 ->
애플리케이션 이름에 docker-fullstack-app 입력 ->
플랫폼 선택
애플리케이션 생성 버튼을 클릭하면 EB 가 생성이 됩니다.
EB는 아래 사진처럼 EC2 인스턴스 및 DB, Security 그룹, Auto-Scaling 그룹, 로드 밸런서 등 많은 부분들을 컨트롤을 할 수 있습니다.
VPC(Virtual Private Cloud) 와 Security Group ?
현재 운영환경에서 DB를 위한 것을 아무것도 해주지 않았습니다.
그래서 AWS RDS를 이용하여 MYSQL을 애플리케이션과 연결시켜야 하는데 그것을 하기 위해 VPC 와 Security Group을 설정해줘야 합니다.
VPC 이 무엇?
Amazon Virtual Private Cloud(VPC) 를 사용하면 AWS 클라우드에서 논리적으로 격리된 공간을 프로비저닝(IT 인프라 설정 프로세스를 뜻하며, 데이터와 리소스에대한 엑세스 관리에 필요한 단계) 하여 고객이 정의하는 가상 네트워크에서 AWS 리소스를 시작할 수 있습니다.
즉, 간단하게 이해하자면 제가 AWS에서 만든 EC2 인스턴스나 , EB 인스턴스 혹은 RDS DB를 만들었다면 이러한 인스턴스들을 나의 아이디에서만 접근이 가능하게 논리적으로 격리된 네트워크에서 생성이 되게 해줍니다. 그러기에 다른 아이디로는 접근 하는 것은 물론 보는것도 불가능 합니다.
EB 인스턴스나 RDS를 생성하면 자동적으로 기본 VPC (Default VPC)가 할당이 됩니다.
-> 이는 AWS 웹사이트에서 VPC 대시보드에 들어가면 확인이 가능합니다.
그리고 할당이 될 때는 지역(region)별로 다르게 할당이 됩니다.
Security Group(보안 그룹)이 무엇?
- Inbound: 외부에서 EC2, EB인스턴스로 요청을 보내는 트래픽입니다. HTTP, HTTPS, SSH등이 있습니다.
- Outbound: EC2, EB인스턴스에서 외부로 나가는 트래픽입니다. 파일을 다운로드하거나 Inbound로 들어온 트래픽을 처리하여 응답하는 경우도 포함 됩니다.
결론적으로 Security Group이 Inbound, Outbound를 통제해서 트래픽을 열어줄 수도 있고 닫아 줄 수도 있습니다.
그렇다면 어떻게 해서 VPC와 Securiy Group을 이용해서 EB인스턴스와 RDS가 서로 통신할 수 있게 만들어 줄 수 있을까요 ???
답은 같은 VPC안에 있는 AWS 서비스 간에는 트래픽을 모두 허용할 수 있게 Securiy Group을 허용해주면 됩니다.
MYSQL을 위한 AWS RDS 생성하기
docker-compose.yml 파일을 보면
위와 같이 enviroment 부분에 MYSQL을 위한 환경변수를 지정해주었습니다.
(값에 해당하는 부분은 나중에 AWS 웹사이트 EB 부분에서 환경변수를 지정해주면 됩니다.)
일단 , 위에 해당하는 환경변수를 이용해서 /backend/db.js 파일에서 이용할 수 있게 하려면...
(기존 코드를 보시면 DB 정보들이 노출되는 위험이 있습니다.)
// mysql 모듈을 불러옴
var mysql = require("mysql");
/*
Host, user name, password, db name 을 명시해서
Pool을 생성해줌
그리고 다른 곳에서 사용할 수 있게 export함.
*/
var pool = mysql.createPool({
connectionLimit : 10,
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
port: process.env.MYSQL_PORT
});
exports.pool = pool;
위와 같이 소스코드를 변경하면 docker-compose.yml -> /backend/db.js 파일로 환경 변수를 가져 올 수 있습니다.
이제 AWS RDS 를 생성해보겠습니다.
AWS 웹사이트에서 RDS 서비스를 선택 후 데이터베이스 생성 버튼을 클릭 합니다.
그리고
데이터베이스 생성 방식 선택 (표준 생성) ->
엔진옵션 (MYSQL 선택) ->
템플릿 (프리 티어 선택) ->
설정에서 DB인스턴스 식별자 (docker-fullstack-mysql 입력) ->
자격 증명 설정에서 마스터 사용자 이름과 마스터 암호를 입력 ->
연결에서 VPC 는 default VPC ->
추가 구성에서 초기 데이터베이스 이름 (myapp 입력) ->
데이터 베이스 생성 버튼을 클릭하면
AWS RDS 가 생성이 된 것을 볼 수 있습니다.
Security Group 생성과 적용
Security Group 생성
Elastic Beanstalk 인스턴스와 RDS가 서로 요청을 보낼 수 있게 Securiy Group을 생성하겠습니다.
AWS VPC 대시보드에 들어가 보안 그룹에서 보안 그룹 생성 버튼을 클릭해 줍니다.
기본 세부 정보에서
보안 그룹 이름 (DockerFullstackSecurityGroup),
설명 (allow traffics between services in fullstack docker app),
VPC (기본값으로 설정 돼있음)
설정해주고 보안 그룹 생성 버튼을 클릭해 줍니다.
보안 그룹이 잘 생성이 된 것을 확인할 수 있으며
이제 인바운드 규칙을 설정해 줍니다.
인바운드 규칙 편집을 클릭하고
위와 같이 설정을 해줍니다.
포트 범위는 MYSQL과 통신해야 하므로 MYSQL이 실행되고 있는 포트인 3306을 입력해줍니다.
소스는 사용자 지정으로 방금 만들었던 보안 그룹(sg-09935~ )을 선택하고
규칙 저장버튼을 클릭합니다.
그러면 인바운드 규칙이 추가된 것을 확인할 수 있습니다.
Security Group 적용
방금 EB 인스턴스와 RDS가 서로 요청할 수 있게 Security Group을 생성했는데
이제 이 생성한 Security Group을 사용할 수 있게
EB 인스턴스와 RDS 에 적용을 해보겠습니다.
일단 RDS 먼저 보안 그룹을 적용해 보겠습니다.
AWS 웹사이트 > RDS > DB > MYSQL 인스턴스를 클릭합니다.
그 후 수정버튼을 클릭 후 보안 그룹을 선택해 줍니다.
그 후 계속을 누르고 수정예약에서 즉시 적용을 선택 후 DB인스턴스 수정버튼을 눌러줍니다.
보안 그룹이 적용이 됐습니다.
EB 인스턴스에 보안 그룹을 적용해 보겠습니다.
AWS 웹사이트 > EB > EB 인스턴스를 클릭합니다.
그 후 구성으로 가서 인스턴스 부분에 편집을 눌러줍니다.
그 후 인스턴스 보안 그룹에서 DockerFullstackSecurityGroup을 체크한 후 적용을 하면
보안 그룹이 적용한 것을 확인할 수 있습니다.
EB와 RDS 소통을 위한 환경 변수 설정
먼저 RDS 서비스에 가서 DB > 인스턴스 클릭 후 > 앤드 포인트 부분을 복사합니다.
그 후
AWS 웹사이트 > EB > EB 인스턴스를 클릭합니다.
그 후 구성에서 소프트웨어 부분에서 편집을 눌러줍니다.
그 후
위와 같이 환경 변수를 설정해줍니다.
복사 한 앤드포인트는 -> MYSQL_HOST 부분의 값입니다.
travis.yml 파일에 배포부분 추가
기존 위에서 작성된 .travis.yml 파일에 배포하는 부분을 작성하겠습니다.
...
deploy:
provider: elasticbeanstalk
region: "us-east-1"
app: "docker-fullstack-app"
env: Dockerfullstackapp-env
bucket_name: elasticbeanstalk-us-east-1-681085781418
bucket_path: "docker-fullstack-app"
on:
branch: master
bucket_name 은 EB를 생성하면서 생성된 S3 버킷 네임을 복사해서 붙여넣기 해줍니다.
bucket_path 는 app 이름과 동일하게 입력해 줍니다.
이제 TRAVIS CI 가 AWS에 접근할 수 있게 API Key를 설정 해주겠습니다.
Travis CI 의 AWS접근을 위한 API Key 생성
일단 AWS에서 IAM USER를 생성해 줍니다.
그리고 사용자 > 사용자 추가를 클릭합니다.
사용자 세부 정보 설정 : 사용자 이름 작성 (docker-fullstack-user) , 액세스 유형 (비밀키 액세스키)
-> 사용자 권한 설정 : elastic beanstalk (AdministratorAccess-AWSElasticBeanstalk)
-> 사용자 만들기
이렇게 계정 만들기에 성공을 하면 access key id, access key(비밀키) 가 생성이 됩니다.
이 키들은 한번만 볼수있기때문에 잘 관리해야 합니다. !!
API 키들은 .travis.yml 에 적어두면 노출이 될 위험이 있기때문에
Travis 웹사이트 해당 저장소 대시보드에서 설정을 클릭 후
Enviroment Variables 에서
AWS_ACCESS_KEY : access key id
AWS_SECRET_ACCESS_KEY : access key(비밀키) 로 저장 해두면 .travis.yml 에서 불러올 수 있습니다.
.travis.yml 파일 입니다.
...
deploy:
provider: elasticbeanstalk
region: "us-east-1"
app: "docker-fullstack-app"
env: Dockerfullstackapp-env
bucket_name: elasticbeanstalk-us-east-1-681085781418
bucket_path: "docker-fullstack-app"
on:
branch: master
access_key_id: $AWS_ACCESS_KEY
secret_access_key: $AWS_SECRET_ACCESS_KEY
기존 deploy 에서 access_key_id, secret_access_key 부분만 추가 됐습니다.
이제
git 으로 master branch에 push하면
Travis CI에선 테스트를 잘 성공시키고 이미지들을 도커 허브로 push하는것을 알 수 있습니다.
그러나 ...
AWS Elastic Beanstalk에선
계속 이 오류가 뜨는데 ;;
한참의 삽질을 끝낸 결과
docker-compose.yml 파일을 보면
enviroment 부분에서 ' - ' 이 문자를 빼주면 해결이 잘 됩니다. ^^
환경으로 이동해서 애플리케이션을 동작 시켜도 잘 작동하는 것을 확인할 수 있습니다.
이번 애플리케이션 같은 경우엔 깃헙에 소스코드를 푸시하면 Travis CI가 테스트를 하고
테스트에 성공한다면 각각의 도커파일로 이미지를 만들고 도커 허브에 푸시를 했습니다.
그 다음 AWS S3 버킷에 소스코드를 zip파일로 배포를 하고
EB가 docker-compose.yml 파일을 실행을 시킵니다.
각각의 서비스마다 image : ksoon1985/docker-frontend 처럼 도커 허브에 저장된 이미지를 불러와
실행을 시키고 애플리케이션이 잘 동작하는것을 확인할 수 있습니다.
이번 애플리케이션은 DB를 도커 컨테이너에 내장시킨 것이 아닌 AWS RDS를 이용을 했고
EB 와 RDS가 서로 통신하기 위해선 VPC와 Security Group을 설정해야 했습니다.
nginx서버가 요청 url ('/' , '/api')에 맞게 frontend, backend 서버에 요청을 보내며
frontend에선 정적인 파일을 처리하는 또 하나의 nginx서버를 내장시켰습니다.
끝.
'Docker' 카테고리의 다른 글
[Docker] 복잡한 어플을 실제로 배포해보기 (개발 환경 부분) (0) | 2022.04.04 |
---|---|
[Docker] 간단한 어플을 실제로 배포해보기(테스트 & 배포) (0) | 2022.03.30 |
[Docker] 간단한 어플을 실제로 배포해보기(개발 환경 부분) (0) | 2022.03.29 |
[Docker] Docker Compose (0) | 2022.03.26 |
[Docker] 도커를 이용한 간단한 Node.js 어플 만들기 (0) | 2022.03.25 |