도커 - 복잡한 테스트 배포 부분
in Docker
1. 전체적 구성도
- 짤린 부분은 이미지 생성 후 Docker Hub으로 전달
- AWS RDB 사용
- 운영 환경에서는 MYSQL이 도커에서 돌아가는게 아니라 AWS에서 돌아가게 한다.
- 앞서 docker compose에서 작성했던 mysql 코드를 전부 주석 처리
2. Travis CI Steps
# .travis.yml
language: generic
sudo: required
services:
- docker
before_install:
- docker build -t backtony/react-test-app -f ./frontend/Dockerfile.dev ./frontend
script:
- docker run -e CI=true backtony/react-test-app npm test
after_success:
- docker build -t backtony/docker-frontend ./frontend
- docker build -t backtony/docker-backend ./backend
- docker build -t backtony/docker-nginx ./nginx
- echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_ID" --password-stdin
- docker push backtony/docker-frontend
- docker push backtony/docker-backend
- docker push backtony/docker-nginx
- 테스트하기 전에 Dockerfile.dev 설정으로 이미지 빌드하고 테스트 진행
- 성공하면 운영버전으로 이미지 빌드
- Travis CI에 Settings에서 변수로 docker hub 아이디, 비밀번호 세팅
- echo를 이용해서 로그인
- docker hub에 이미지 푸시
3. Dockerrun.aws.json
- 노드, Mysql, Nginx 등을 위한 Dockerfile이 여러 개 있는 경우 Elastic Beanstalk이 어떤 파일을 먼저 실행하고 어떤 행동을 취해야하는지 자동으로 처리할 수 없기 때문에 임의로 설정이 필요한데 이때 사용하는 파일
- Docker 컨테이너 세트를 Elastic Beanstalk 애플리케이션으로 배포하는 방법을 설명하는 Elastic Beanstalk 고유의 JSON 파일
- Elastic Beanstalk이 컨테이너를 어떻게 실행해야 하는지 모르기 때문에 Task에 어떻게 컨테이너를 실행해야 하는지 정의하는 것은 Task Definition(작업 정의)라고 하고, 작업 정의를 등록할 때는 Container Definition을 명시해야 하는데 이 Container Definition을 Dockerrun.aws.json에 넣는다. 이는 도커 데몬으로 전달된다.
// Dockerrun.aws.json
{
"AWSEBDockerrunVersion": 2,
"containerDefinitions": [
{
"name": "frontend",
"image": "backtony/docker-frontend",
"hostname": "frontend",
"essential": false,
"memory": 128
},
{
"name": "backend",
"image": "backtony/docker-backend",
"hostname": "backend",
"essential": false,
"memory": 128
},
{
"name": "nginx",
"image": "backtony/docker-nginx",
"hostname": "nginx",
"essential": true,
"portMappings": [
{
"hostPort": 80,
"containerPort": 80
}
],
"links": ["frontend", "backend"],
"memory": 128
}
]
}
- AWSEBDockerrunVersion : dockerrun 버전
- containerDefinitions : 컨테이너 정의 시작
- name : 컨테이너 이름
- image : Docker hub에 올린 이미지 이름
- hostname : 호스트이름으로 다른 컨테이너에서 해당 컨테이너로 접근할 때 사용하는 이름
- essential : 값이 true이면 해당 컨테이너가 죽으면 다른 작업도 모두 중지, 작업에 있어서 필수적인 부분에 true
- memory : 한 컨테이너에서 얼만큼의 메모리를 사용할지 명시
- portMappings : 포트 매핑
- links : 연결할 컨테이너의 목록, 연결된 컨테이너는 서로 검색하고 안전하게 통신 가능
cf) Task Definition(작업 정의)에서 지정할 수 있는 것들
- 작업의 각 컨테이너에 사용할 도커 이미지
- 각 작업 또는 작업 내 각 컨테이너에서 사용할 CPU 및 메모리의 양
- 사용할 시작 유형으로서 해당 작업이 호스팅되는 인프라 결정
- 작업의 컨테이너에 사용할 도커 네트워킹 모드
- 작업에 사용할 로깅 구성
- 컨테이너가 종료 또는 실패하더라도 작업이 계속 실행될지 여부
- 컨테이너 시작 시 컨테이너가 실행할 명령
- 작업의 컨테이너에서 사용할 데이터 볼륨
- 작업에서 사용해야 하는 IAM 역할
4. VPC와 Security Group
- EB(elastic beanstalk) 인스턴스에서 많은 컨테이너들이 싫행되고 이것과 RDS를 연결시켜줘야 하는데 기본적으로 설정되어있지 않아서 통신할 수 있게 연결시키기 위해 필요한 것이 VPC와 Security Group
VPC
- Amazon Virtual Private Cloud
- AWS 클라우드에서 논리적으로 격리된 공간을 제공하여 고객이 정의하는 가상 네트워크에서 AWS 리소스를 시작할 수 있도록 제공하는 서비스
- 간단하게 설명하면, 내가 만든 EC2 인스턴스, EB 인스턴스, RDS 데이터 베이스에는 나의 아이디에서만 접근이 가능하게 논리적으로 격리된 네트워크에서 생성되게 해준다. 따라서 다른 아이디로는 접근, 보기가 불가능
- EB 인스턴스나 RDS를 생성하면 자동적으로 기본 VPC가 할당되는데 지역별로 다르게 할당
같은 VPC안에 있지만 기본적으로 통신할 수 없게 되어 있어서 서로 통신이 불가능한 상태이다.
Security Group
- 보안그룹으로 방화벽이라고 부름
- Inbound : 외부에서 EC2 인스턴스나 EB 인스턴스로 요청을 보내는 트래픽
- HTTP, HTTPS, SSH 등등
- Outbound : EC2인스턴스나 EB인스턴스등에서 외부로 나가는 트래픽
- 파일을 다운로드 하거나 inbound로 들어온 트래픽을 처리하여 응답하는 경우도 포함
- 결론적으로 Security Group이 inbound와 outbound를 통제해서 트래픽을 열수도 닫을 수도 있음
연결시키기
- 같은 VPC 안에 있는 AWS 서비스 간에는 트래픽을 모두 허용할 수 있게 Security Group을 허용
- 간단하게 말하면, 같은 VPC 안에 있는 경우 원래는 방화벽 때문에 소통이 안됬지만 방화벽 없애서 자유롭게 소통이 가능하도록 한게 설정하면 된다는 뜻
5. RDS 관련 설정
# docker-compose.yml
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
environment:
MYSQL_HOST: mysql
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: myapp
MYSQL_PORT: 3306
- docker compose에서 기존에 mysql로 따로 서비스로 작성했던 내용을 지우고 백엔드 부분에 environment로 작성
- backend폴더 DB연결 코드에서는 연결시키는 부분에서 이제부터 process.env.MYSQL_HOST 와 같은 변수로 사용이 가능
- RDS는 AWS에서 직접 생성 -> 강의 참고
- RDS 생성하고 Security Group 생성한뒤 Security Group 을 통해 inbound와 outboud에 RDS 포트(mysql 3306)포트를 열어준다.
- elasticbeanstalk과 RDS 둘다 설정에서 만든 Security group을 추가해줌으로 인해 같은 VPC에서 오는 트래픽은 모두 허용하게 되어 서로 소통이 가능해 짐
- elasticbeanstalk에는 컨테이너가 많은데 MYSQL과 소통할 때 mysql의 환경변수 부분을 인식하지 못하므로 elasticbeanstalk에 mysql 관련 환경변수 설정 추가(위에 작성한 environment 내용을 웹사이트에서 설정)
6. 배포
# .travic.yml
language: generic
sudo: required
services:
- docker
before_install:
- docker build -t backtony/react-test-app -f ./frontend/Dockerfile.dev ./frontend
script:
- docker run -e CI=true backtony/react-test-app npm run test
after_success:
- docker build -t backtony/docker-frontend ./frontend
- docker build -t backtony/docker-backend ./backend
- docker build -t backtony/docker-nginx ./nginx
- echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_ID" --password-stdin
- docker push backtony/docker-frontend
- docker push backtony/docker-backend
- docker push backtony/docker-nginx
deploy:
provider: elasticbeanstalk
region: "ap-northeast-2"
app: "docker-fullstack-app"
env: "Dockerfullstackapp-env"
bucket_name: elasticbeanstalk-ap-northeast-2-****
bucket_path: "docker-fullstack-app"
on:
branch: master
access_key_id: $AWS_ACCESS_KEY
secret_access_key: $AWS_SECRET_ACCESS_KEY
- deploy 부분을 추가
- provider : 외부 서비스 표시(s3, elaticbeanstalk, firebase 등등)
- region : 현재 사용하고 있는 AWS의 서비스가 위치하고 있는 물리적 장소
- 한국이라면 ap-northeast-2
- app : 생성된 애플리케이션의 이름
- env : 추가적으로 설정하지 않으면 app의름을 붙여서 자동으로 생성됨
- 웹사이트에 적혀있으니 그것 참고해서 작성
- bucket_name : elasticbeanstalk 을 위한 s3 버켓 이름
- Travis CI에서 AWS elasticbeanstalk을 통해 소스 배포과정에서 바로 elastic으로 가는게 아니라 travis에서 가지고 있는 파일을 압축해서 먼저 S3로 보내므로 필요함
- elasticbeanstalk을 생성할 때 S3가 자동적으로 생성 됨, 웹사이트에서 참고하여 작성
- bucket_path : 애플리케이션 이름과 동일
- on branch : 어떤 브랜치에 push를 할때 AWS에 배포할 것인지
- access key : AWS와 Travis CI를 연동하기 위해 필요한 키
- AWS IAM을 통해서 키를 발급받고 노출되면 안되므로 Travis CI Setting에서 환경변수로 작성하고 $를 통해 가져와서 사용