0. 강의 소개
1 ~ 3강: 이론
4 ~ 5강: 실습
6 ~ 7강: react
8 ~ 9강: 실무. 풀스택 개발 후 배포.
소스코드: https://github.com/jaewonhimnae/docker-codes
도커 도표자료: https://drive.google.com/drive/folders/1VLD6GVa2E60qM1nFDgrYXjrl_OjJIOc_
1. 도커와 AWS 업데이트로 인해 변화된 것들
1) Buildkit
이 강의를 더 쉽게 따라가기 위해서는 Buildkit을 비활성화 하시면 됩니다.
이 강의에서는 Buildkit 을 비활성화하라고 하지만
찾아보니 Buildkit 이 속도향상에 많은 역할을 해 비활성화하지 않도록 합시다.
Buildkit 비활성화 하는 방법
- 오른쪽 위 톱니바퀴 모양 (Settings) 클릭
- Docker Engine 탭 클릭
- buildkit 값을 false 로 변경
{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"experimental": false,
"features": {
"buildkit": false
}
}
2) IAM 권한
IAM 유저 권한을 주는 부분에 ElasticBeanstalkFullAccess -> AdministratorAccess-AWSElasticBeanstalk
3) AWS 플랫폼 브랜치
현재까지 저희가 일래스틱 빈스톡 환경을 생성할 떄 플랫폼 브랜치를 선택 할 때
단일 컨테이너 애플리케이션을 이용할 떄(6-7강)는 Docker running on 64bit Amazon Linux를 사용했으며
멀티 컨테이너 애플리케이션을 이용할 때(8-9강)는 Multi-container Docker running on 64but Amazon Linux를
사용했습니다.
하지만 AWS에서 20201년 7월 8일 부로 앞서 말한 두개의 플랫폼 브랜치의 사용 중단(deprecation)을 발표했습니다.
그리고 2022년 6월 30일 까지만 사용할 수 있게 공지했습니다.
그래서 현재도 6-7강과 8-9강에서 한 부분이 문제 없이 작동하지만,
AWS에서 새롭게 추천하는 방법이 아니기 때문에 AWS에서 추천하는 플랫폼 브랜치인 Docker running on 64bit Amazon Linux2를 사용해서 저희가 만든 애플리케이션을 다시 배포해보겠습니다.
플랫폼 브랜치의 교체로 가장 크게 변화가 된 것은 Linux2 플랫폼 브랜치에서는
Dockerrun.aws.json 대신에 docker-compose.yml을 이용한다는 것입니다.
원래는 Dockerrun.aws.json 파일을 이용해서 도커 허브에 있는 이미 빌드되어있는 이미지를 가져오고 메모리 사이즈나 포트 맵핑 및 볼륨 설정까지 해주었습니다.
그리고 도커 컴포즈 파일은 개발 환경에서 애플리케이션을 쉽게 실행하기 위해서 사용했습니다.
하지만 이제는 Dockerrun.aws.json 파일에 명시해놓은 메모리 사이즈 도커 허브에 있는
도커 이미지등을 도커 컴포즈 파일에 명시해주겠습니다.
- 먼저는 Dockerrun.aws.json파일과 docker-compose.yml 파일을 비교합니다.
비교하므로써 Dockerrun.aws.json에 명시되어 있는 설정들을 docker-compose.yml으로 옮길수 있습니다.
- 먼저 메모리 사이즈를 명시해주겠습니다. 메모리 사이즈를 docker-compose.yml에서 명시할 때는 mem_limit : <메모리 사이즈> 로 주시면 됩니다. 각 컨테이너 마다 주겠습니다.
- 도커 이미지를 도커 허브에 이미 저장된 이미지를 가져오기 위해서 도커 허브에 저장된 이미지 이름을 명시합니다.
- nginx 컨테이너에서 포트맵핑은 80:80으로 해주겠습니다. 그 이유는 nginx를 사용할 때는 기본적으로 3000번 포트가 아닌 80번 포트에서 애플리케이션이 실행되기 때문입니다.
- AWS RDS서비스로 생성된 데이터베이스와 애플리케이션을 연결하기 위해서 MySQL HOST 정보를 RDS 서비스 페이지에서 가져와서 backend 컨테이너의 MYSQL_HOST 키에 대응하는 값으로 넣어주시면 됩니다.
공식 설명서는 아래의 링크에 나와있습니다.
2. 도커 기본
1) 도커의 정의
도커란 ?
응용프로그램을 더 쉽게 만들고 배포하고 실행할 수 있도록 설계된 도구.
컨테이너 기반의 오픈소스 가상화 플랫폼이며 생태계이다.
컨테이너 이미지란 ?
응용프로그램을 실행하는데 필요한 모든 것을 포함하는 소프트웨어 패키지.
인프라에 관계없이 항상 동일하게 실행된다.
프로그램을 실행하는데 필요한 설정을 갖고있으며 도커 이미지를 통해 컨테이너를 생성하며 도커 컨테이너를 이용해 프로그램을 실행한다.
도커 이미지 -> 컨테이너 -> 프로그램
2) 도커 설치하기
Windows Home 이 아닌 OS 도커 설치
- https://www.docker.com/
- Get Started 클릭
- Docker Desktop -> OS 에 맞게 다운로드
- 회원가입
- 도커 실행해서 로그인
- 도커 설치확인; 터미널에서
docker version
입력
Windows Home 유저를 위한 도커 설치
Docker Community Edition for Windows 를 사용하려면 Hyper-V 라는 것이 필요한데 Home 버전에서는 Hyper-V 라는 것을 지원하지 않는다.
일반 윈도우 버전 도커를 사용할 수 없으므로 Docker Toolbox 를 설치해서 사용해야 한다.
Toolbox 도 이제 없어졌다고 합니다.
메뉴얼: https://docs.docker.com/desktop/windows/
다운로드: https://github.com/docker-archive/toolbox/releases
이제 Toolbox 를 다운로드 받을 수 없다.
3) 도커를 사용할 때의 흐름
도커 Client (CLI) - 도커 Server (Daemon)
docker run hello-world
"Hello from Docker!" 를 출력해주는 이미지. 이미지가 없으면 도커 허브에서 가져온다.
4) 도커와 기존의 가상화 기술과의 차이를 통한 컨테이너 이해
가상화 기술 전 -> 가상화 (하이퍼 바이저) -> 도커
하이퍼바이저
- 네이티브 하이퍼 바이저: 하드웨어를 직접 제어. 설치 어렵다.
- 호스트형 하이퍼 바이저: 호스트 OS 위에 실행. 오버헤드가 크다. 게스트 OS 종류에 대한 제약이 없고 구현이 다소 쉽다.
일반적으로 많이 이용하는 방법이다.
하이퍼 바이저 기반의 VM 구조
도커 컨테이너: 컨테이너가 전체 OS 를 내장할 필요가 없어 매우 가볍다.
가상머신: 비교적 사용법이 간단하지만 느리다.
5). 이미지로 컨테이너 만들기
- 컨테이너가 시작 될 때 실행디는 명령어 ex) run kakaotalk
- 파일 스냅샷 ex) 컨테이너에서 카카오톡을 실행하고 싶다면 카카오톡 파일 스냅샷
- 파일 스냅샷은 디렉토리나 파일을 카피한 것
이미지로 컨테이너 만드는 순서
- Docker 클라이언트에 docker run <이미지> 입력
- 파일 스냅샷을 하드 드라이버로 옮겨줌
- 이미지에서 가지고 있는 명령어를 이용해서 카카오톡을 실행시켜줌
6). C-group, 네임스페이스를 도커 환경에서 쓸 수 있는 이유
도커는 리눅스 환경에서 돌아가고 있다.
그래서 리눅스에서만 사용할 수 있는 C Group 과 네임스페이스를 사용할 수 있다.
3. 기본적인 도커 클라이언트 명령어 알아보기
1) 도커 이미지 내부 파일 구조 보기
2) 컨테이너들 나열하기
docker
도커 클라이언트 언급ps
process status
- container id: 컨테이너의 고유한 아이디 해쉬값. 실제로는 더 길다.
- image: 컨테이너 생성 시 사용한 도커 이미지.
- command: 컨테이너 시작 시 실행 될 명령어. 내장되어 있어 별도 설정이 필요없다.
- created: 컨테이너가 생성된 시간.
- status: 컨테이너의 상태
- 실행중: Up
- 종료: Exited
- 일시정지: Pause
- ports: 컨테이너가 개방한 포트와 호스트에 연결한 포트.
특별한 설정을 하지 않은 경우 출력되지 않는다. - names: 컨테이너 고유한 이름
컨테이너 생성 시 --name 옵션으로 이름 설저앟지 않으면 도커엔진이 임의로 형용사와 명사를 조합해 설정
id 와 마찬가지로 중복이 안되고 docker rename 명령어로 이름을 변경할 수 잇다.docker rename original-name changed-name
3) 도커 컨테이너의 생명주기
docker start -a 아이디
-a 옵션
attach 의 줄임말.
4) Docker Stop vs Docker Kill
도커의 생명주기 중에서 중지 부분
docker stop 과 docker kill 로 중지할 수 있다.
docker stop 아이디
Gracefully 하게 중지시킨다.docker kill 아이디
바로 중지시킨다.
- sigterm: Grace Period 정리하는 시간
5) 컨테이너 삭제하기
docker rm
중지된 컨테이너 삭제docker rm 'docker ps -a -q'
모든 컨테이너 삭제docker rmi <이미지ID>
이미지를 삭제docker system prune
모든 컨테이너, 이미지, 네트워크 삭제. 실행중인 컨테이너에는 영향을 주지 않는다.
prune: 가지치다. 축소하다.
6) 실행 중인 컨테이너에 명령어 전달
docker exec 아이디 명령어
docker run 아이디 명령어 와 비슷하지만 이미 실행중인 컨테이너에 사용한다.
7) 레디스를 이용한 컨테이너 이해
docker run redis
레디스 서버를 실행한다.redis-cli
레디스 클라이언트도 컨테이너 안에서 실행을 시켜야 한다.
8) 실행 중인 컨테이너에서 터미널 생활 즐기기
- sh
- bash
- zsh
- powershell
Q. it 의 뜻 ?
A.
- docker exec -it 아이디 sh:
- touch: 파일생성
- export: 환경변수 지정
- echo: 값 반환
- 예) echo $hello
Ctrl D: 쉘 환경에서 빠져나온다.
4. 직접 도커 이미지를 만들어 보기
1) 도커 이미지 생성하는 순서
- docker create 이미지이름: 도커 생성
2) Dockerfile 만들기
# 베이스 이미지를 명시해준다.
FROM baseImage
# 추가적으로 필요한 파일들을 다운로드 받는다.
RUN command
# 컨테이너 시작 시 실행될 명령어를 명시해준다.
CMD ["executable"]
- FROM: 이미지 생성 시 기반이되는 이미지 레이어
- 이미지이름:태그 형식으로 작성
- 태그를 안붙이면 자동적으로 가장 최신것으로 다운받음
- ex) ubuntu:14.04
- RUN: 도커 이미지가 생성되기전에 수행할 쉘 명령어
- COM: 컨테이너가 시작되었을 때 실행할 실행파일 또는 셸 스크립트
- 해당 명령어는 DockerFile 내 1회만 쓸 수 있습니다.
# 베이스 이미지를 명시해준다.
FROM alpine
# 추가적으로 필요한 파일들을 다운로드 받는다.
# RUN command
# 컨테이너 시작 시 실행될 명령어를 명시해준다.
CMD ["echo", "hello"]
3) 도커 파일로 도커 이미지 만들기
도커파일에 입력된 것들이 도커 클라이언트에 전달되어서 도커 서버가 인식하게 하여야 합니다.
Build 명령어
- 해당 디렉토리 내에서 dockerfile 이라는 파일을 찾아서 도커 클라이언트에 전달시켜준다.
docker build .
만들었던 이미지를 도커에 전달한다.
4) 내가 만든 이미지 기억하기 쉬운 이름 주기
- docker run -it 104849
`devconnor/hello:0.1
5. 도커를 이용한 간단한 Node.js 어플 만들기
순서
Node.jsp APP 만들기 -> 도커 이미지 생성 후 컨테이너에서 실행
1) Node.js 앱 만들기
- package.json: 프로젝트의 정보와 프로젝트에서 사용중인 패키지의 의존성을 관리하는 곳
- server.js: 시작점 (Entry Point) 으로서 가장 먼저 시작되는 파일
Node.js 를 다운로드 받습니다.
- npm init: package.json 을 생성합니다.
package.json 에서 dependencies 의 버전을 자동완성을 하려면 Node.js 모듈을 받으시면 됩니다.
package.json
{
"name": "nodejs-docker-app",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"express":"4.17.3"
},
"author": "",
"license": "ISC"
}
강사님은 express 를 4.17.1 버전으로 했으나 버전을 찾을 수 없다고 나와 본인은 4.17.3 버전으로 했습니다.
server.js
const express = require('express');
const PORT = 8080;
// APP
const app = express();
app.get('/', (req, res) => {
res.send("Hello World")
});
app.listen(PORT);
console.log("Server is running")
2) Dockerfile 작성하기
- npm: Node.js 로 만들어진 모듈을 웹에서 받아서 설치하고 관리해주는 프로그램
Dockerfile
FROM node:10
RUN npm install
CMD ["node", "server.js"]
docker build .
3) Package.json 파일이 없다고 나오는 이유
- Node 베이스이미지로 임시 컨테이너를 생성한다.
- npm install: 어플리케이션에 필요한 종속성을 다운받을 때 package.json 을 보고 다운받는다.
COPY 를 이용해서 Package.json 을 컨테이너 안으로 넣어주어야 한다!!!
Dockerfile
FROM node:10
COPY package.json .
RUN npm install
CMD ["node", "server.js"]
docker build -t devconnor/nodejs .
Cannot find module '/server.js'
server.js 가 컨테이너에 없기 때문에 COPY 키워드에 작성하도록 한다.
FROM node:10
COPY . .
RUN npm install
CMD ["node", "server.js"]
- docker run devconnor/nodejs
Server is running 은 잘 출력되지만 'localhost:포트번호' 로 접근할 수는 없다.
다음 강의에서 확인해보자.
4) 생성한 이미지로 어플리케이션 실행 시 접근이 안 되는 이유
docker run -p 5000:8080 nodejs
-p
옵션으로 도커를 실행해야 한다.
5) Working Directory 명시해주기
Dockerfile
FROM node:10
WORKDIR /usr/src/app
COPY . .
RUN npm install
CMD ["node", "server.js"]
Terminal
docker build -t nodejs .
워크디렉토리 확인
Dockerfile node_modules package-lock.json package.json server.js
docker run -it nodejs ls
이제 루트경로에 있는 파일들이 더 이상 보이지 않는 것을 볼 수 있다.
6) 어플리케이션 소스 변경으로 다시 빌드하는 것에 대한 문제점
docker run -d -p 5000:8080 nodejs
- -d: detach 의 약어.
7) 어플리케이션 소스 변경으로 재빌드 시 효율적으로 하는 법
COPY package.json .
RUN npm install
COPY . .
COPY 를 두 부분으로 나누면 소스코드가 변경되면 종속성을 다시 받지 않는다.
8) Docker Volume에 대하여
명령어
맥: -v $(pwd):/usr/src/app
윈도우: -v %cd%:/usr/src/app
PWD 란 ?
명령어
맥: -v $(pwd):/usr/src/app
윈도우: -v %cd%:/usr/src/app
docker run -d -p 5000:8080 -v /usr/src/app/node_modules -v %cd%:/usr/src/app nodejs
Volume 이 작동이 되지 않는다면 VSCODE 의 터미널을 powershell 에서 cmd 로 바꿔보세요!!
stop 이나 kill 은 이미지이름이 아닌 ID 로 정지시킨다.
6. Docker Compose
1) Docker Compose란 무엇인가?
다중 컨테이너 도커 어플리케이션을 정의하고 실행하기위한 도구이다.
2) 어플리케이션 소스 작성하기
{
"name": "docker-compose-app",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"express":"4.17.3",
"redis":"4.0.4"
},
"author": "",
"license": "ISC"
}
start 와 dependencies 를 추가해줍니다.
"start": "node server.js"
npm run start 만으로 노드를 실행할 수 있다.
server.js
const express = require("express");
const redis = require("redis");
const app = express();
app.listen(8080);
console.log('Server is running');
- 레디스 클라이언트를 생성해준다.
- 하지만 여기서 redis server 가 작동하는 곳과 Node.js 앱이 작동하는 곳이 다른 곳이라면 host 인자와 port 인자를 명시해주어야 한다.
도커환경에서 레디스 클라이언트를 생성 시 docker-compose.yml 의 이름과 매치시킨다.
server.js
const express = require("express");
const redis = require("redis");
// 레디스 클라이언트 생성
const client = redis.createClient({
host:"",
port: 6379
})
const app = express();
// 숫자는 0 부터 시작합니다.
client.set("number", 0);
app.get('/', (req, res) => {
client.get("number", (err, number) => {
// 현재 숫자를 가져온 후에 1씩 올려줍니다.
client.set("number", parseInt(number) + 1)
res.send("숫자가 1씩 올라갑니다. 숫자: " + number)
})
})
app.listen(8080);
console.log('Server is running');
3) Dockerfile 작성하기
FROM node:10
WORKDIR /usr/src/app
COPY . .
RUN npm install
CMD ["node", "server.js"]
이전과 동일하게 작성합니다.
4) Docker Containers 간 통신할 때 나타나는 에러
레디스 클라이언트가 작동하려면 레디스 서버가 켜져있어야 한다.
docker run redis
docker build -t compose .
docker run compose
위 코드를 실행하면 UnhandledPromiseRejectionWarning: Error: The client is closed 와 같은 에러가 나온다.
서로 다른 컨테이너에 있으므로 Node.js 앱에서 redis 서버에 접근할 수 없어서 그렇다.
멀티 컨테이너 상황에서 쉽게 네트워크를 연결해주기 위해서 Docker Compose 를 이용합니다.
5) Docker Compose 파일 작성하기
YAML
- ain't markup language 의 약자
- XML 이나 json 포맷으로 많이 쓰였지만 좀 더 사람이 읽기 쉬운 포맷으로 나타난게 yaml 이다.
- version: 도커 컴포즈의 버전
- services: 이곳에 실행하려는 컨테이너들을 정의
- redis-server: 컨테이너 이름
- image: 컨테이너에서 사용하는 이미지
- node-app: 컨테이너 이름
- build: 현 디렉토리에 있는 Dockerfile 사용
- ports: 포트매핑 - 로컬 포트: 컨테이너 포트
Docker-compose.yml
version: '3'
services:
redis-server:
image: "redis"
node-app:
build: .
ports:
- "5000:8080"
docker-compose up
어떤 에러인지 redis 에 연결할 수가 없다. 나중에 다시 확인하자.
전체코드
docker run redis
docker build -t compose .
docker run compose
docker-compose up
6) Docker Compose 로 컨테이너를 멈추기
- docker compose down: 한꺼번에 중단한다.
- -d: detached 모드로서 앱을 백그라운드에서 실행시킨다. 앱에서 나오는 output 을 표출하지 않는다.
- docker compose up -d (--build)
- docker-compose up --build: 이미지가 있든 없는 이미지를 빌드하고 컨테이너 시작
7. 간단한 어플을 실제로 배포해보기(개발 환경 부분)
1) 리액트 앱 설치하기
Node.js 가 다운받아져 있어야 한다.
cmd 에서 node -v 라고 입력해 버전을 확인한다.
노드가 받아져있다면 아래와 같은 명령어를 입력하여 리액트를 설치한다.
npx create-react-app .
npm run start
React 를 실행합니다.
npm run test
테스트합니다.
npm run build
빌드합니다. build 폴더를 생성합니다.
2) 도커를 이용하여 리액트 앱 실행하기
Dockerfile 을 개발단계를 위한 것과 실제 배포 후를 위한 것을 따로 작성하는게 좋습니다.개발단계는 Dockerfile.dev 라는 파일로 작성하겠습니다.
Dockerfile.dev
FROM node:alpine
WORKDIR /usr/src/app
COPY package.json .
RUN npm install
COPY . .
CMD ["npm", "run", "start"]
package.json 과 같은 위치에 생성합니다.
도커파일 빌드
docker build -f Dockerfile.dev -t react-app .
파일명의 대소문자를 구분합니다.
도커파일의 이름이 다른경우 이렇게 빌드한다.
도커환경에서 리액트 앱을 실행한다면
굳이 로컬에 node_modules 이 필요없으며 오히려 빌드시간이 늘어납니다.
npm install 에서 이미 생성되었는데 COPY . . 에서 작업이 중복된다.
그래서 로컬의 node_modules 은 삭제하도록 한다.
3) 생성된 도커 이미지로 리액트 앱 실행해보기
docker build -f Dockerfile.dev -t react-app .
docker run -it -p 3000:3000 react-app
- -i: 상호 입출력
- -t: tty 를 활성화하여 bash 쉘을 사용
http://localhost:3000/
리액트는 포트 3000 을 사용한다.
4) 도커 볼륨을 이용한 소스 코드 변경
앞에서 했던 볼륨을 리액트에 적용해보자.
docker run -it -p 3000:3000 -v /usr/src/app/node_modules -v %cd%:/usr/src/app react-app
5) 도커 컴포즈로 좀 더 간단하게 앱 실행해보기
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
6) 리액트 앱 테스트 하기
docker run -it 이미지이름 npm run test
Learn React 라는 원래 글자로 변경 후 빌드하면 테스트가 문제없이 완료된다.
node_modules 폴더가 없어서 로컬에서는 테스트할 수 없다.
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
version: "3"
services:
react:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- /usr/src/app/node_modules
- ./:/usr/src/app
environment:
- CHOKIDAR_USEPOLLING=true
stdin_open: true
tests:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- /usr/src/app/node_modules
- ./:/usr/src/app
command: ["npm", "run", "test"]
7) 운영환경을 위한 Nginx
웹서버 Nginx 가 왜 필요할까 ?
개발서버는 소ㅗ스를 변경하면 자동으로 다시 빌드해서 변경소스를 반영해주는 것처럼 개발환경에 특화된 기능들이 있다.
Nginx 는 그런 불필요한 기능들이 없어 더 깔끔하고 빠르기에 운영시에 Nginx 를 웹서버로 사용한다.
8) 운영환경 도커 이미지를 위한 Dockerfile 작성하기
디폴트로 /usr/share/nginx/html 에 복사하도록 하자.
FROM node: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
nginx 의 기본 포트는 80 이다.
docker run -p 8080:80 react-app
Nginx 서버 잘 기동된다.
현재까지 안되는 것: Volume, docker-compose Volume
8. 간단한 어플을 실제로 배포해보기(테스트 & 배포 부분)
1) Travis CI 설명
GitHub 에 push 합니다.
Travis CI
GitHub 에서 진행되는 오픈소스 프로젝트를 위한 지속적인 통합 (Continuous Integration) 서비스이다.
2011년에 설립되었고 처음에는 Ruby 언어만 지원했엇다.
Travis CI 를 이용하면 GitHub repository 에 있는 프로젝트를 특정 이벤트에 따라 자동으로 테스트, 빌드하거나 배포할 수 있다.
Private repository 는 유료이다.
흐름
- GitHub 저장소에 push 한다.
- push 되면 Travis CI 에게 소스가 push 되었다 알린다.
- Travis CI 는 업데이트된 소스를 GitHub 에서 가져온다.
- 깃헙에서 가져온 소스의 테스트코드를 실행한다.
- 테스트가 성공하면 AWS 같은 호스팅사이트로 보내서 배포를 한다.
2) Travis CI 이용 순서
- GitHub 아이디로 회원가입 후 로그인
- Sync account 로 GitHub 레포지토리를 가져온다.
3) .travis.yml 파일 작성하기 (테스트까지)
- sudo: 관리자 권한갖기
- language: 언어 (플랫폼) 을 선택
- services: 도커환경 구성
- before_install: 스크립트를 실행할 수 있는 환경 구성
- script: 실행할 스크립트 (테스트 실행)
- after_success: 테스트 성공 후 할 일
- -t: 태그
- -f: 파일
sudo: required
language: generic
services:
- docker
before_install:
- echo "start creating an image with dockerfile"
- docker build -t react-app -f Dockerfile.dev .
script:
- docker run -e CI=true react-app npm run test -- --coverage
after_success:
- echo "Test success"
Dashboard 에서 Trigger a build 를 눌러야 깃허브의 push 를 감지해서 테스트합니다.
빌드가 정상적으로 되었다.
4) AWS 알아보기
EC2 (Elastic Compute Cloud)
클라우드에서 확장식 컴퓨팅을 제공
가상서버를 구축하고 보안 및 네트워크 구성과 스토리지 관리가 가능
한 대의 컴퓨터를 임대한다고 생각하면 된다.
1대의 컴퓨터를 하나의 EC2 인스턴스라고 부른다.
EB (Elastic BeanStalk)
Apache, Nginx 같은 친숙한 서버에서 Java, NET, PHP, Node.js, Python, Ruby, Go 및 Docker 와 함께 개발된 웹 응용 프로그램 및 서비스를 배포하고 확장하기 쉬운 서비스입니다.
EB 안에 EC2 인스턴스와 데이터베이스, Security 그룹, Auto-Scaling 그룹, 로드 밸런서 등을 관리할 수 있다.
5) Elastic Beanstalk 환경 구성하기
- 새 환경 생성
- 웹 서버 환경 선택
- 플랫폼: Docker
- 브랜치: Docker running on 64bit Amazon Linux
- 버전: 2.15.2
6) .travis.yml 파일 작성하기 (배포 부분)
Elastic Beanstalk 을 생성하면 S3 버킷도 함께 생성된다.
deploy:
provider: elasticbeanstalk
region: "ap-northeast-2"
app: "docker-react-app"
env: "Dockerreactapp-env"
bucket_name: "elasticbeanstalk-ap-northeast-2-615258856034"
bucket_path: "docker-react-app"
on:
branch: "main"
region
elasticbeanstalk 의 물리적 주소app
애플리케이션 이름env
elasticbeanstalk 의 환경이름bucket_name
S3 버킷의 이름bucket_path
app 과 동일하게branch
GitHub 의 메인 브랜치
7) Travis CI의 AWS접근을 위한 API 생성
IAM (Identity and Access Management)
AWS 리소스에 대한 엑세스를 안전하게 제어할 수 있는 웹 서비스
- 좌측 액세스 관리 탭의 사용자
- 사용자 추가
- 액세스 키 – 프로그래밍 방식 액세스
- 기존 정책 직접 연결
- elasticbean 을 검색하면 AdministratorAccess-AWSElasticBeanstalk 선택
- Travis CI 사이트
- More options
- Settings
- 내리면 Environment Variables (환경변수)
- name: AWS_ACCESS_KEY
- value: IAM 의 액세스 키
- 예) AKIAY6QC ~~
- Add 를 누른다.
- name: AWS_SECRET_ACCESS_KEY
- value: IAM 의 비밀 액세스 키
deploy:
provider: elasticbeanstalk
region: "ap-northeast-2"
app: "docker-react-app"
env: "Dockerreactapp-env"
bucket_name: "elasticbeanstalk-ap-northeast-2-615258856034"
bucket_path: "docker-react-app"
on:
branch: "main"
access_key_id: $AWS_ACCESS_KEY
secret_access_key: $AWS_SECRET_ACCESS_KEY
Dockerreactapp-env 아래의 링크를 누르거나 좌측의 '환경으로 이동' 을 누르면 배포된 사이트로 이동할 수 있다.
작업의 환경종료를 눌러야 더 이상 과금이 일어나지 않는다.
배포에 성공했다.
9. 복잡한 어플을 실제로 배포해보기(개발 환경 부분)
1) 섹션 설명
Proxy 설계
URI 로 요청구분
정적파일 설계
포트번호로 요청구분
2) Node JS 구성하기
- npm init
package.json
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.17.3",
"mysql": "^2.18.1",
"nodemon": "2.0.15",
"body-parser": "1.19.2"
},
"author": "",
"license": "ISC"
}
db.js
const mysql = require("mysql");
const pool = mysql.createPool({
connectionLimit: 10,
host: 'mysql',
user: 'root',
password: 'johnahn',
database: 'myapp'
});
exports.pool = pool;
server.js
const express = require("express");
const bodyParser = require('body-parser');
const db = require('./db');
const app = express();
app.use(bodyParser.json());
// 테이블 생성하기
db.pool.query(`CREATE TABLE lists (
id INTEGER AUTO_INCREMENT,
value TEXT,
PRIMARY KEY (id)
)`, (err, results, fileds) => {
console.log('results', results)
})
// DB lists 테이블에 있는 모든 데이터를 프론트 서버에 보내주기
app.get('/api/values', function(req, res) {
// 데이터베이스에서 모든 정보 가져오기
db.pool.query('SELECT * FROM lists;',
(err, results, field) => {
if (err)
return res.status(500).send(err)
else
return res.json(results)
})
})
// 클라이언트에서 입력한 값을 데이터베이스 lists 테이블에 넣어주기
app.post('/api/value', function(req, res, next) {
// 데이터베이스에 값 넣어주기
db.pool.query('INSERT INTO lists (value) VALUES ("${req.body.value}")'),
(err, results, fields) => {
if(err)
return res.status(500).send(err)
else
return res.json({success: true, value: req.body.value})
}
})
app.listen(5000, () => {
console.log('애플리케이션이 5000번 포트에서 되었습니다.')
})
3) React JS 구성하기
- npx create-react-app frontend
- cd frontend
- npm run start
- useState: react 라이브러리에서 가져옴
- lists: DB 저장된 값 가져와 넣어둠
- value
- useEffect
package.json
"axios": "0.26.1"
dependency 에 추가
App.js
import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
function App() {
// useEffect(() => {
// axios.get('/api/hi')
// .then(response => {
// console.log('response', response)
// })
// }, [])
useEffect(() => {
// 여기서 데이터베이스에 있는 값을 가져온다.
axios.get('/api/values')
.then(response => {
console.log('response', response)
setLists(response.data)
})
}, [])
const [lists, setLists] = useState([])
const [value, setValue] = useState("")
const changeHandler = (event) => {
setValue(event.currentTarget.value)
}
const submitHandler = (event) => {
event.preventDefault();
axios.post('/api/value', { value: value })
.then(response => {
if (response.data.success) {
console.log('response', response)
setLists([...lists, response.data])
setValue("");
} else {
alert('값을 DB에 넣는데 실패했습니다.')
}
})
}
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<div className="container">
{lists && lists.map((list, index) => (
<li key={index}>{list.value} </li>
))}
<br />
안녕하세요.
<form className="example" onSubmit={submitHandler}>
<input
type="text"
placeholder="입력해주세요..."
onChange={changeHandler}
value={value}
/>
<button type="submit">확인.</button>
</form>
</div>
</header>
</div>
);
}
export default App;
4) 리액트 앱을 위한 도커 파일 만들기
Dockerfile.dev
FROM node:alpine
WORKDIR /app
COPY package.json ./
RUN npm install
COPY ./ ./
CMD ["npm", "run", "start"]
Dockerfile
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
default.conf
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
5) 노드 앱을 위한 도커 파일 만들기
backend 폴더에도 Dockerfile 과 Dockerfile.dev 를 만들어준다.
Dockerfile.dev
FROM node:alpine
WORKDIR /app
COPY ./package.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
Dockerfile****
FROM node:alpine
WORKDIR /app
COPY ./package.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "start"]
6) DB에 관해서
AWS RDS (Relational Database Service)
7) MYSQL을 위한 도커 파일 만들기
mysql 폴더 내
Dockerfile
FROM mysql:5.7
ADD ./my.cnf /etc/mysql/conf.d/my.cnf
initialize.sql
DROP DATABASE IF EXISTS myapp;
CREATE DATABASE myapp;
USE myapp;
CREATE TABLE lists (
id INTEGER AUTO_INCREMENT,
value TEXT,
PRIMARY KEY (id)
)
my.cnf
[mysqld]
character-set-server=utf8
[mysql]
default-character-set=utf8
[client]
default-character-set=utf8
8) NGINX를 위한 도커 파일 만들기
nginx 폴더 내
default.conf
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";
}
}
Dockerfile
FROM nginx
COPY ./default.conf /etc/nginx/conf.d/default.conf
9) Docker Compose 파일 작성하기
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
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: johnahn
MYSQL_DATABASE: myapp
10) Docker Volume을 이용한 데이터 베이스 데이터 유지하기
- docker-compose up: 테스트를 해봅시다.
에러
Module not found: Error: Can't resolve 'axios' in 'C:\Users\Connor\Desktop\docker-lecture\docker-fullstack-app\frontend\src'
- 로컬에 axios 모듈이 깔려있지 않으므로 'npm install axios' 명령어를 통해 모듈을 설치한다.
10. 복잡한 어플을 실제로 배포해보기(테스트 & 배포 부분)
1) 섹션 설명
2) 도커 환경의 MYSQL부분 정리하기
docker-compose.yml 에서 mysql 주석처리하자.
3) Github에 소스 코드 올리기
backend/.gitignore
node_modules
mysql/.gitignore
mysql_data
4) Travis CI Steps
Travis CI 에서 Trigger a build 클릭
5) .travis.yml 파일 작성하기
.travis.yml
language: generic
sudo: required
services:
- docker
before_install:
- docker build -t react-test-app -f ./frontend/Dockerfile.dev ./frontend
script:
- docker run -e CI=true react-test-app npm run test
after_success:
- docker build -t $DOCKER_HUB_ID/docker-frontend ./frontend
- docker build -t $DOCKER_HUB_ID/docker-backend ./backend
- docker build -t $DOCKER_HUB_ID/docker-nginx ./nginx
- echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_ID" --password-stdin
- docker push $DOCKER_HUB_ID/docker-frontend
- docker push $DOCKER_HUB_ID/docker-backend
- docker push $DOCKER_HUB_ID/docker-nginx
도커허브: https://hub.docker.com/
Trtavis CI 에서 도커허브 아이디와 비밀번호의 Environment Variables (환경변수) 를 설정합니다.
환경변수에 넣는 값에 특수문자가 있다면 이스케이프 문자 ' \ ' 를 앞에 꼭 넣도록 합니다.
frontend/src/App.test.js
test('renders learn react link', () => {
});
다음과 같이 test 의 중괄호 내의 코드를 모두 지운다.
- git add - commit - push
- docker push: 도커허브에 이미지를 올리는 명령어이다.
에러
GitHub 에서 frontend 가 화살표 폴더 (충돌폴더) 로 표시되는 경우
- frontend 폴더 안에 .git 폴더 삭제
- 루트폴더에서 'git rm --cached . -rf'
- 다시 푸시
6) Dockerrun.aws.json 파일 작성하기
새로운 Linux2 플랫폼에서는 json 파일을 사용하지 않고 docker-compose.yml 에 함께 작성하도록 합니다.
docker-compose.yml
version: "3"
services:
frontend:
build:
dockerfile: Dockerfile.dev
context: ./frontend
image: devconnor/docker-frontend
volumes:
- /app/node_modules
- ./frontend:/app
stdin_open: true
mem_limit: 128m
nginx:
restart: always
build:
dockerfile: Dockerfile
context: ./nginx
image: devconnor/docker-nginx
ports:
- "80:80"
mem_limit: 128m
links:
- frontend
- backend
backend:
build:
dockerfile: Dockerfile.dev
context: ./backend
image: devconnor/docker-backend
container_name: app_backend
volumes:
- /app/node_modules
- ./backend:/app
environment:
MYSQL_HOST: docker-fullstack-mysql.cjix2ohrkuio.ap-northeast-2.rds.amazonaws.com
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: johnahn777
MYSQL_DATABASE: myapp
MYSQL_PORT: 3306
mem_limit: 128m
강사님 코드에는 build 가 모두 지워져있다.
7) VPC(virtual private cloud)와 Security Group 설정하기
Docker running on 64bit Amazon Linux2 로 Elastic beanstalk 생성
VPC 란 ?
Amazon Virtual Private Cloud 의 약어.
AWS 클라우드에서 논리적으로 격리된 공간을 프로비저닝하여 고객이 정의하는 가상 네트워크에서 AWS 리소스를 시작할 수 있다.
EC2 인스턴스나 EB 인스턴스, RDS DB 를 만들었다면 나의 아이디에서만 접근할 수 있게 해준다.
EB 인스턴스를 생성할 때 VPC 가 같이 생성된다.
일래스틱 빈스탁 을 생성할 때 EC2 가 생성되며 VPC 가 생성되었다.
AWS 의 도구들
- Elastic Beanstalk: 개발환경 (EC2 + VPC)
- RDS: DB
- EC2: 컴퓨터 대여
- IAM: 계정
- S3: 파일서버
- VPC: 보안. 울타리.
8) MYSQL을 위한 AWS RDS 생성하기
docker-compose.yml
version: "3"
services:
frontend:
build:
dockerfile: Dockerfile.dev
context: ./frontend
image: devconnor/docker-frontend
volumes:
- /app/node_modules
- ./frontend:/app
stdin_open: true
mem_limit: 128m
nginx:
restart: always
build:
dockerfile: Dockerfile
context: ./nginx
image: devconnor/docker-nginx
ports:
- "80:80"
mem_limit: 128m
backend:
build:
dockerfile: Dockerfile.dev
context: ./backend
image: devconnor/docker-backend
container_name: app_backend
volumes:
- /app/node_modules
- ./backend:/app
environment:
MYSQL_HOST: mysql
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: johnahn777
MYSQL_DATABASE: myapp
MYSQL_PORT: 3306
mem_limit: 128m
backend 의 environment 의 MYSQL 부분을 추가해준다.
db.js
const mysql = require("mysql");
const pool = mysql.createPool({
connectionLimit: 10,
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABSE,
port: process.env.MYSQL_PORT
});
exports.pool = pool;
RDS
DB 생성
- MySQL 과 프리티어 선택
- 마스터 사용자 이름: MY_SQL_USER 와 동일하게 설정
- 마스터 암호: MYSQL_ROOT_PASSWORD 와 동일하게 설정
- 추가구성의 초기 데이터베이스 이름: MYSQL_DATABASE 와 동일하게 설정
9) Security Group 생성하기
- 왼쪽 보안 탭의 보안 그룹 클릭
- 보안 그룹 생성
- 인바운드 규칙 편집 클릭 후 규칙 추가
- 포트 범위: 3306
- 소스: 보안그룹 - 생성한 보안그룹 선택
- Inbound: 외부에서 EC2 인스턴스나 EB 인스턴스로 요청을 보내는 트래픽
- HTTP, HTTPS, SSH 등이 있다.
- Outbound: 외부로 나가는 트래픽
- 파일을 다운로드 하거나 inbound 로 들어온 트래픽을 처리하여 응답하는 경우도 포함된다.
10) Security Group 적용하기
RDS
- DB 클릭해서 수정
- 연결의 보안 그룹을 생성한 보안 그룹으로 선택
- 계속
- 수정 사항 요약: 즉시 적용
- DB 인스턴스 수정
Elastic Beanstalk
- 왼쪽에서 생성한 EB 선택 후 구성 클릭
- 인스턴스에서 편집 클릭
- 인스턴스 보안 그룹에서 생성한 보안그룹 체크해서 적용
11) EB와 RDS 소통을 위한 환경 변수 설정하기
EB 가 MySQL 의 환경변수를 인식하지 못하므로 EB 에 환경변수를 설정하자.
- 구성 - 소프트웨어
- 환경 속성
- docker-compose.yml 의 environment 의 값을 넣어준다.
- MYSQL_HOST: RDS 에서 생성한 DB 의 엔드포인트
- 예) docker-fullstack ~~ amazonaws.com
- MYSQL_USER
- MYSQL_ROOT_PASSWORD
- MYSQL_DATABASE
- MYSQL_PORT
12) travis.yml 파일 작성하기 (배포 부분)
travis.yml 에 배포부분을 추가해보자.
- region: EB 의 지역으로 준다.
- env: EB 의 환경이름과 동일하게 준다.
- bucket_name: S3 이름과 동일하게 준다.
- bucket_path: app 이름과 동일하게 준다.
13) Travis CI의 AWS 접근을 위한 API key 생성
AWS 에서 제공해주는 Secret key 를 Travis 에 저장합니다.
AWS 가 Travis CI 에 접근하기 위해서는 API key 가 필요하다.
Secret, Access API Key 받는 순
- EB 권한을 가진 IAM 계정 생성
- Travis CI 저장소 대쉬보드에 액세스키와 비밀 엑세스키 저장
- Travis 에는 총 4개의 환경변수가 저장되게 된다.
- AWS_ACCESS_KEY
- AWS_SECRET_ACCESS_KEY
- DOCKER_HUB_ID
- DOCKER_HUB_PASSWORD
AWS_SECRET_ACCESS_KEY 를 환경변수에 저장할 때
꼭 특수문자 앞에 백슬래쉬 (이스케이프 문자) 를 붙여주도록 하자!
.travis.yml
language: generic
sudo: required
services:
- docker
before_install:
- docker build -t react-test-app -f ./frontend/Dockerfile.dev ./frontend
script:
- docker run -e CI=true react-test-app npm run test
after_success:
- docker build -t $DOCKER_HUB_ID/docker-frontend ./frontend
- docker build -t $DOCKER_HUB_ID/docker-backend ./backend
- docker build -t $DOCKER_HUB_ID/docker-nginx ./nginx
- echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_ID" --password-stdin
- docker push $DOCKER_HUB_ID/docker-frontend
- docker push $DOCKER_HUB_ID/docker-backend
- docker push $DOCKER_HUB_ID/docker-nginx
# 추가
deploy:
provider: elasticbeanstalk
region: "ap-northeast-2"
app: "docker-fullstack-app"
env: "Dockerfullstack-env"
bucket_name: elasticbeanstalk-ap-northeast-2-615258856034
bucket_path: "docker-fullstack-app"
on:
branch: main
access_key_id: $AWS_ACCESS_KEY
secret_access_key: $AWS_SECRET_ACCESS_KEY
docker-compose.yml
version: "3"
services:
frontend:
# build:
# dockerfile: Dockerfile.dev
# context: ./frontend
image: devconnor/docker-frontend
volumes:
- /app/node_modules
- ./frontend:/app
stdin_open: true
mem_limit: 128m
nginx:
restart: always
# build:
# dockerfile: Dockerfile
# context: ./nginx
image: devconnor/docker-nginx
ports:
- "80:80"
mem_limit: 128m
links:
- frontend
- backend
backend:
# build:
# dockerfile: Dockerfile.dev
# context: ./backend
image: devconnor/docker-backend
container_name: app_backend
volumes:
- /app/node_modules
- ./backend:/app
environment:
MYSQL_HOST: docker-fullstack-mysql.cjix2ohrkuio.ap-northeast-2.rds.amazonaws.com
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: johnahn777
MYSQL_DATABASE: myapp
MYSQL_PORT: 3306
mem_limit: 128m
# 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: johnahn
# MYSQL_DATABASE: myapp
운영환경에서는 빌드를 주석처리했다.
Travis CI 에러
The request signature we calculated does not match the signature you provided. Check your key and signing method. (Aws::S3::Errors::SignatureDoesNotMatch)
비밀번호에 백슬러쉬를 제대로 하지 않아 그랬다. IAM 계정을 지우고 새로 입력하니 작동한다.
Travis 에서 문제를 잡으니 AWS 에서 502 Bad way 문제가 뜬다.
AWS 에러
aws Process default has been unhealthy for 8 minutes (Target.FailedHealthChecks).
백엔드 코드의 문제인 것 같다. 쌤님 코드로 하니 잘 작동한다.
배포가 잘 되어 휴대폰으로도 잘 접속이 된다.
'Backend > 노트' 카테고리의 다른 글
Tucker 의 Go 언어 프로그래밍 (0) | 2022.08.14 |
---|---|
한 번에 끝내는 Node.js 웹 프로그래밍 초격차 패키지 Online (0) | 2022.07.06 |
🍀스프링부트 (0) | 2022.03.29 |
DB 연결 (0) | 2022.03.19 |
Spring Boot를 이용한 RESTful Web Services 개발 (0) | 2022.03.10 |