2020-11-7 도커 강의정리 멀티컨테이너 배포하기
실제배포하기(복잡한)
- 리액트, 노드, 데이터베이스 함께 배포해야한다. 멀티컨테이너
Nginx의 Proxy를 이용한 설계
-
운영환경을 위한 FrontDocker
-
FROM node:alpine as builder WORKDIR /app COPY ./package.json ./ RUN npm install COPY . . RUN npm run build FROM nginx EXPOSE 3000 COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf COPY --from=builder /app/build /usr/share/nginx/html
-
운영환경에서는 개발서버가 NGINX로 대체된다.
-
server { listen 3000; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; => 정말중요. } }
- listen 3000 => nginx가 listen하고 있는 PORT를 명시
- location / => location /를 할때 어떤일을 할지 명시.
- root /usr/share/nginx/html => HTML 파일이 위치할 루트 설정. 루트도 변경할 수 있다. 기본은 /usr/share/nginx/html에 있다.
- index index.html index.htm => 사이트의 index페이지로 할 파일명 작성
- try_files $uri $uri/ /index.html => React Router를 사용해서 페이지간 이동할때 필요. React가 SPA기 때문에 index.html 하나의 정적파일만 가지고 있다. {URL}/home으로 이동하려고 해도 index.html에 접근해서 라우팅을 해야된다. nginx에서는 이 부분을 할 수 없기때문에 /home로 접속할때 매칭되는 것이 없어서 대안으로 index.html을 제공하고 /home으로 라우팅할 수 있게 임의로 설정하는 부분이다.
- /etc/nginx/conf.d/default.conf는 컨테이너안에 있는 nginx설정파일의 경로
-
./nginx/default.conf는 nginx설정 파일을 복사한다.
-
Nginx를 가동하고, 빌더스테이지에서 만들어진 build파일들을 nginx폴더로 이동한다.
- COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf는 우리가 작성한 nginx설정파일을 /etc/nginx/conf.d/default.conf로 덮어씌우는거다.
-
데이터베이스 도커구성
- 개발환경에서는 도커환경을 이용
- 운영환경에서는 AWS RDS를 이용
- nginx, front, backend는 ElasticBeansTalk에 있지만 DB에 대해서만 RDS를 이용
FROM mysql:5.7
ADD ./my.cnf /etc/mysql/conf.d/my.cnf
- mysql컨테이너안으로 ./my.cnf파일을 /etc/mysql/conf.d/my.cnf로 덮어 씌어야한다.
// my.cnf
[mysqld]
character-set-server=utf8
[mysql]
default-character-set=utf8
[client]
default-character-set=utf8
- 한글이 깨질 수 있어서 my.cnf를 추가해야한다.
// sql/initialize.sql
DROP DATABASE IF EXISTS myapp;
CREATE DATABASE myapp;
USE myapp;
CREATE TABLE lists (
id INTEGER AUTO_INCREMENT,
value TEXT,
PRIMARY KEY (id)
);
Nginx 구성
-
Proxy에서 요청 처리를 하는 Nginx 컨테이너를 구성한다.
-
운영환경과 개발환경의 차이가 없어서 Dockerfile을 1개만 만든다.
-
이 프로젝트에서 Nginx는 Proxy를 처리하는 부분과 Static파일을 처리하는 부분으로 구성된다.
-
request가 /api로 시작되면 서버로. 그 외에는 프론트로 요청을 보낸다.
-
upstream frontend { server frontend:3000; } upstream backend { server backend:5000; } server { listen 80; location / { proxy_pass http://frontend; } location /api { proxy_pass http://backend; } location /sockjs-node { proxy_pass http://frontend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; } }
-
upstream frontend => frontend가 3000번 포트에서 돌아가고 있다고 명시
-
upstream backend => backend가 5000번 포트에서 돌아가고 있다고 명시
-
frontend, backend는 docker-compose에서 서비스 아래 컨테이너이름을 명시해야한다.
-
listen 80 => 서버 포트 80번을 열어둔다.
-
location에는 우선순위가 있다.
- location /는 우선순위가 가장 낮다. 우선은 /api로 시작되는걸 찾고 없으면 /이 시작되고 frontend로 요청을 보낸다.
- /api로 시작되는건 backend로 요청을 보낸다.
- /sockjs-node가 없으면 에러가 발생한다. 리액트 개발환경에서 필요.
도커 컴포즈 파일 작성하기
version: "3"
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
ports:
- 3000:3000
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: 12345
MYSQL_DATABASE: test
- nginx restart: nginx재시작정책(no, always, on-failure, unless-stopped)
- no : 재시작 안한다.
- always : 항상 재시작
- on-failure 에러코드와 함께 컨테이너가 멈출때만 재시작
- unless-stopped : 개발자가 임의로 멈출때 빼고 항상 재시작
Volume을 이용하여 데이터베이스 데이터 유지하기
- 원래 컨테이너를 지우면 컨테이너에 저장된 데이터들이 지워진다. 데이터가 컨테이너 안에 저장되기 때문
- 영속성이 필요한 데이터들을 위해 volume을 이용한다.
- 호스트 파일시스템을 볼륨으로 활용하여 도커 컨테이너가 지워지더라도 데이터가 유지되도록 한다.
- 컨테이너에서 변화된 데이터가 컨테이너에 저장되는 것이 아니라 호스트 파일시스템에 저장
Dockerrun.aws.json
-
Docker container세트를 EB 고유의 JSON파일이다. 멀티 컨테이너 환경에서 사용한다.
- 이 파일이 있어야 AWS ElasticBeansTalk에서 App을 작동시킬 수 있다.
- Dockerfile이 하나인 경우, EB가 알아서 빌드된 이미지를 실행시켜서 어플리케이션을 실행했다.
- Node, React, Nginx 등 여러개의 Dockerfile을 돌리는 경우 EB가 어떤 파일을 먼저 실행하고 행동을 해야되는지 알 수 없기때문에 Dockerrun.aws.json에 명시를 해야한다.
- Task Definition과 Container Definition을 해야한다.
- Dockerrun.aws.json에 Container Definition을 명시
- Container Definition을 명시하면 Docker Daemon으로 전달되어 Docker daemon이 실행한다.
- Task Definition에서 지정할 수 있는것
- 작업의 각 컨테이너에 사용할 도커 이미지
- 컨테이너에서 사용할 CPU, 메모리양
- 호스팅되는 인스파결정
- 도커 네트워킹 모드
- 로깅구성
- 컨테이너가 종료, 실패되도 작업 계속할지
- 컨테이너 시작 시 컨테이너가 실행할 명령
- 컨테이너가 사용할 데이터 볼륨
- IAM역할
{
"AWSEBDockerrunVersion": 2,
"containerDefinitions": [
{
"name": "frontend",
"image": "jimmy/docker-frontend",
"hostname": "frontend",
"essential": false,
"memory": 128
},
{
"name": "backend",
"image": "jimmy/docker-backend",
"hostname": "backend",
"essential": false,
"memory": 128
},
{
"name": "nginx",
"image": "jimmy/docker-nginx",
"hostname": "nginx",
"essential": true,
"portMappings": [
{
"hostPort": 80,
"containerPort": 80
}
],
"links": ["frontend", "backend"],
"memory": 128
}
]
}
- containerDefinitions안에 컨테이너들을 정의
- name : 컨테이너 이름
- image : Docker 레포지토리의 도커 이미지 => 도커허브에 업로드된 이미지
- hostname : 호스트이름, 이 이름을 활용하여 도커컴포즈를 이용하여 다른 컨테이너에서 접근가능
- essential : 컨테이너가 실패하고 작업을 중지해야되면 true
- memory : 컨테이너 인스턴스의 메모리
- portMapping : 컨테이너에 있는 네트워크 지점을 호스트에 있는 지점으로 매핑
- links : 연결할 컨테이너 목록. nginx가 frontend와 backend 컨테이너를 연결한다.
다중컨테이너 ElasticBeansTalk 환경구성
- ElasticBeansTalk 환경구성을 기존 Single Continaer에서 Multi Container환경으로 재구성해야한다.
VPC
- RDS의 MySQL와 애플리케이션을 연결시키기 위해서는 VPC와 Security Group이 필요하다.
- EB 인스턴스와 RDS 함께 사용을 많이하는데 기본적으로 연결되어있지 않아서 통신할 수 없다. 통신할 수 있도록 연결해줘야한다.
- VPC(Virtual Private Cloud)로 AWS에서 논리적으로 격리된 공간을 프로비저닝하여 사용자가 정의한 가상네트워크에서 AWS 리소스를 실행할 수 있다. EC2, EB, RDS 등 내가 생성한 인스턴스를 나만이 접근할 수 있도록 네트워크를 논리적으로 구성하게된다. 다른아이디에서는 접속, 조회가 안된다.
- EB, RDS를 생성하면 기본VPC(default VPC)가 할당된다. 할당될때는 리전별로 다르게 할당
- AP-Northeast-2에 할당된 기본 VPC에 EB인스턴스와 RDS가 함께 존재하지만 통신을 할 수 없다. 통신하기 위해서는 따로 설정.
Security Group
- 인바운드 : 외부에서 EC2, EB인스턴스로 요청을 보내는 트랙픽. HTTP, HTTPS, SSH 등이 있다.
- 아웃바운드 : EC2, EB 등에서 나가는 트래픽. 파일을 다운로드 하거나 인바운드 트래픽을 처리하여 응답하는 경우
- Security Group이 인바인드와 아웃바운드를 통제해서 트래픽을 열어줄수도 닫을수도 있다.
- RDS를 위해 Security Group에서 inbound 3306을 허용해야한다.
- Security Group, VPC를 설정해도 EB와 MySQL이 소통할때 환경변수 부분을 인식하지 못한다. EB에서 환경변수를 설정해야한다.
- EB의 환경-구성-소프트웨어편집에서 환경변수를 설정해야한다. MYSQL_ROOT_PASSWORD, MYSQL_HOST 등등
배포코드
deploy:
provider: elasticbeanstalk
region: "ap-northeast-2"
app: "docker-multicontainer"
env: "DockerMulticontainer-env"
bucket_name: "elasticbeanstalk-ap-northeast-2-194829314461"
bucket_path: "docker-react-app"
on:
branch: master
access_key_id: $AWS_ACCESS_KEY
secret_key_key: $AWS_SECRET_ACCESS_KEY
Reference
- 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/lecture/52087?tab=curriculum&speed=1.25
Written on November 7, 2020