react로 간단한 애플리케이션을 만들고 테스트해보고 배포해보겠다. 전체적인 흐름은 소스 코드를 작성한 후 local에서 테스트한 후 travis CI를 통해 전체 소스를 AWS에 보내 Elasticbeanstalk에서 이미지를 만들어 컨테이너를 실행해 배포하는 방식이다.
react app 설치 : 현재 디렉토리에 react-app을 설치하는 명령어다. 정상적으로 설치가 되면 디렉토리 안에 여러 폴더들이 생긴다.
npx create-react-app ./
react app 실행 : react 앱을 실행한다. 아래와 같은 화면이 뜬다.
npm run start
테스트 실행
npm run test
빌드
npm run build
1. Dockerfile 작성하기
현재까지는 Dockerfile을 그냥 한 개만 작성해서 사용했지만 Dockerfile은 개발 환경과 운영 환경을 위한 것으로 따로 작성하는 것이 좋다. 아래는 개발 환경을 위한 Dockerfile이다. 파일 이름을 편의상 Dockerfile.dev라 하겠다. 이제껏 만들었던 Dockerfile과 별 다를 바 없다.
Dockerfile.dev
FROM node:alpine
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY ./ ./
CMD ["npm", "run", "start"]
2. 이미지 빌드
빌드할 Dockerfile 이름을 -f 옵션으로 명시해주자.
docker build -f Dockerfile.dev -t strangehoon/docker-react-app ./
3. local의 node_modules 폴더 삭제
npm install 명령어를 입력하면 package.json의 dependencies의 파일들이 다운받아져서 node_modules 폴더 안으로 들어간다. local 환경에서 react 앱을 실행하려면 당연히 node_modules 폴더가 필요하지만, docker 환경에서 리액트 앱을 실행하려면 굳이 local의 node modules가 필요하지 않다. local의 node_modules가 있으면 오히려 빌드 시간이 늘어나는데 COPY ./ ./
명령어 실행 시 이미 컨테이너에 node_modules가 있는데 한번더 복사가 되어 중복되기 때문이다.
4. 컨테이너 실행
리액트쪽 업그레이드로 인하여 -it를 붙여줘야 한다.
docker run -it -p 3000:3000 strangehoon/docker-react-app
5. Docker Volume 이용해서 컨테이너 실행
docker run -it -p 3000:3000 -v /usr/src/app/node_modules -v ${PWD}:/usr/src/app strangehoon/docker-react-app
6. Docker Compose로 간편화하기
docker-compose.yml
version: "3"
services:
react:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- /usr/src/app/node_modules
- ./:/usr/src/app
stdin_open: true
stdin_open
: 리액트 앱을 끌 때 필요
docker compose로 컨테이너 실행
docker-compose up
7. 테스트
docker run -it strangehoon/docker-react-app npm run test
테스트 실행 코드를 docker compose 안에 넣어주면 더 좋다. 아래 코드를 docker-compose.yml에 넣어주자.
tests:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- /usr/src/app/node_modules
- ./:/usr/src/app
command: ["npm", "run", "test"]
개발환경에서 리액트가 실행되는 과정 : 리액트 컨테이너 안에 개발 서버가 있고 정적 파일들이 있다. 브라우저에서 http 요청을 리액트 애플리케이션에 보내면 개발 서버를 통해 브라우저에서 요청한 정적 파일들을 개발 서버가 제공해준다.
앞서 작성한 dockerfile.dev는 개발환경 전용이므로 운영환경 전용 dockerfile을 작성해보자.
Dockerfile
FROM node:16-alpine as builder
WORKDIR '/usr/src/app'
COPY package.json .
RUN npm install
COPY ./ ./
RUN npm run build
FROM nginx
EXPOSE 80
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
이전에 작성했던 docker-compose.yml의 파일명을 docker-compose-dev.yml로 바꾸고 운영환경 전용 compose 파일을 다시 작성해보자.
docker-compose.yml
version: "3"
services:
react:
build:
context: .
dockerfile: Dockerfile
ports:
- '80:80'
volumes:
- /usr/src/app/node_modules
- ./:/usr/src/app
stdin_open: true
먼저 앞서 작성한 React 애플리케이션 코드들을 Github repository에 push하자. repository 이름은 docker-react-app으로 하겠다. CICD 툴로 Github actions, Jenkins 등이 있지만 여기서는 Travis CI를 사용하겠다.
Travis CI는 Github에서 진행되는 오픈소스 프로젝트를 위한 지속적인 통합 CI 서비스다. Travis CI를 이용하면 Github repository에 있는 프로젝트를 특정 이벤트에 따라 자동으로 테스트, 빌드하거나 배포할 수 있다,
먼저 Travis CI 웹 사이트에 들어가 자신이 관리하고자 하는 Github repository를 연동하자.
이제부터는 Github에서 Travis CI로 소스를 어떻게 전달 시킬거며 전달 받은 것을 어떻게 Test 하며 그 테스트가 성공했을 때 어떻게 AWS에 전달해서 배포할 것인지를 설정해줘야 한다. 이러한 설정을 위해서 Travis CI에서는 .travis.yml 파일이 필요하다.
.travis.yml
# sudo : 관리자 권한 갖기
sudo: required
# language : 언어(플랫폼 선택)
language: generic
# services : 도커 환경 구성
services:
- docker
# before_install : 스크립트를 실행할 수 있는 환경 구성
before_install:
- echo "start creating an image with dockerfile"
- docker build -t strangehoon/docker-react-app -f Dockerfile.dev .
# script : 실행할 스크립트(테스트 실행)
script:
- docker run -e CI=true strangehoon/docker-react-app npm run test -- --coverage
.travis.yml 파일을 작성하고 다시 깃허브에 커밋 푸시하자. 푸시가 끝나고 travis에 들어가보면 정상적으로 테스트가 완료되고 이미지 빌드가 수행되었음을 확인할 수 있다.
AWS Elastic Beanstalk는 Apache, Nginx같은 친숙한 서버에서 Java, NET, PHP, Node.js, Python, Ruby, Go 및 Docker와 함께 개발된 웹 응용 프로그램 및 서비스를 배포하고 확장하기 위한 서비스다. 아래서 보는 도표와 같이 Elastic Beanstalk은 EC2 인스턴스나 데이터베이스 같이 많은 것들을 포함한 "환경"을 구성하며 만들고 있는 소프트웨어를 업데이트할 때마다 자동으로 이 환경을 관리해준다.
AWS에 가서 Elastic Beanstalk 환경을 생성하자. 그리고 앞서 .travis.yml에 아래 코드를 추가하자.
.travis.yml
deploy:
edge: true
# provider : 외부 서비스 표시(s3, elasticbeanstalk, firebase 등)
provider: elasticbeanstalk
# region : 현재 사용하고 있는 AWS의 서비스가 위치하고 있는 물리적 장소
region: "ap-northeast-2"
# app : 생성된 애플리케이션의 이름
app: "docker-react-app"
# 생성된 애플리케이션 env의 이름
env: "Docker-react-app-env-3"
# 해당 elasticbeanstalk을 위한 s3 버킷 이름
bucket_name: "elasticbeanstalk-ap-northeast-2-624450342306"
# 애플리케이션 이름과 동일
bucket_path: "docker-react-app"
# 어떤 브랜치에 Push할때 AWS에 배포할 건지
on:
branch: master
흥미로운 점은 Elastic Beanstalk 환경을 생성할 때 자동으로 S3 버킷 1개가 생성되었다는 점이다. S3 버킷은 Elastic Beanstalk 환경의 구성과 애플리케이션 버전에 관련된 파일을 저장하는 데 사용된다. 각각의 Elastic Beanstalk 환경은 고유한 버킷을 가지며, 이 버킷에는 배포된 애플리케이션의 코드와 로그 파일들이 저장된다. 따라서 Elastic Beanstalk의 운영을 단순화하고, 애플리케이션 배포 및 관리를 편리하게 해줌과 동시에 애플리케이션 배포 시 소스 코드를 유지하고 복원할 수 있으며, 로그 파일을 중앙 집중식으로 저장하여 추후 분석이나 모니터링에 활용할 수 있다.
현재까지 Travis CI에서 AWS에 어떤 파일을 전해줄지, AWS에서 어떤 서비스를 이용할건지 .travis.yml 파일에 명시하였다. 하지만 Travis CI와 AWS가 실질적으로 소통할 수 있게 인증하는 부분은 설정해주지 않았다.
인증을 위해서는 AWS에서 제공해주는 key가 필요하다. key는 IAM USER를 생성함으로써 얻을 수 있다. 참고로 일상적인 작업이든 관리 작업이든 Root 사용자를 사용하는 것은 보안상 좋지 않기 때문에 IAM User를 생성하여 적당한 권한을 부여해주는 것이 좋다. 여기서는 AdministratorAccess-AWSElasticBeanstalk라는 정책 권한을 부여했다.
이렇게 얻어진 key들은 보안상 노출되면 안 되기 때문에 .travis.yml에 적으면 안된다. 대신 Travis 웹 사이트 해당 저장소 대시보드의 Environment Variables탭에 보관하고 필요할 때마다 local 환경에서 가져올 수 있게 해줘야 한다.
local 환경에서 Travis CI에서 보관중인 key들을 가져올 수 있게 아래 코드를 .travis.yml에 추가해주자.
deploy:
edge:true
provider: elasticbeanstalk
region: "ap-northeast-2"
app: "docker-react-app"
env: "Docker-react-app-env-3"
bucket_name: "elasticbeanstalk-ap-northeast-2-624450342306"
bucket_path: "docker-react-app"
on:
branch: master
access_key_id: $AWS_ACCESS_KEY
secret_access_key: $AWS_SECRET_ACCESS_KEY
다시 빌드하고 푸시한다음 aws의 Elastic Beanstalk탭에서 환경으로 이동 버튼 or 도메인을 클릭하면 정상적으로 react 애플리케이션이 실행되었음을 확인할 수 있다.