스터디원들과 함께 스터디하기로 정한 책입니다.
스터디하고 혼자, 혹은 같이 공부한 내용 작성했습니다.
이 책에 대한 예제 파일은
https://github.com/wikibook/docker-ci
여기서 확인할 수 있습니다.
01. 도커란 ?
1.1 도커 설치하기
- 도커 데스크탑 설치 (Mac)
brew install --cask docker
윈도우는 apt get 으로 설치하거나 홈페이지에서 설치하면 된다.
설치하는데 꽤나 오래걸린다.
도커 어플리케이션을 Spotlight 로 실행 후에는
1.2 도커와 기존 가상화 기술의 차이점
Cgroup 과 네임스페이스
Cgroup 과 네임스페이스는 다른 프로세스 사이에 벽을 만드는 리눅스 커널의 기능입니다.
Cgroup 은 CPU, 메모리, 네트워크 대역폭, HD I/O 등 프로세스 그룹의 시스템 리소스 사용량을 관리합니다.
네임스페이스는 하나의 시스템에서 프로세스를 격리시킬 수 있는 가상화 기술로 별개의 독립된 공간을 사용하는 것처럼 격리된 환경을 제공하는 경량 프로세스 가상화 기술입니다.
02. 기본적인 도커 클라이언트 명령어
이번 장에서는 정말 많은 도커 명령어 중에서 가장 기본적인 도커 클라이언트 명령어를 살펴보겠습니다.
2.1 도커 이미지의 내부 파일 구조 보기 - docker run ls
첫 번째로 도커 이미지의 내부 파일 구조를 볼 수 있는 명령어를 알아보겠습니다.
도커 이미지의 내부 파일 구조를 보려면 해당 이미지로 컨테이너를 실행한 다음 그 컨테이너에 어떤 파일들이 있는지 살펴보면 됩니다.
따라서 먼저 도커 컨테이너를 실행해야 합니다.
<이미지 이름> 뒤에 ls 명령어를 입력하면 컨테이너를 실행할 때 그 안에 무슨 파일이 있는지 알 수 있습니다.
실습 2-1 에서는 alpine 이라는 도커 이미지를 이용해 이 도커 이미지가 어떠한 파일 구조를 가지고 있는지 살펴보겠습니다.
- 도커 이미지 내부의 파일구조 확인
docker run alpine ls
모든 도커 이미지에서 ls 명령어를 실행할 수 있는 것은 아닙니다. 그러면 어떻게 alpine 이미지에서 ls 명령어를 사용할 수 있는 것일까요?
그 이유는 alpine 이미지의 파일 스냅숏 안에 ls 명령어를 사용할 수 있는 파일이 들어 있기 때문입니다.
2.2 컨테이너 나열하기 - docker ps
두 번째로 살펴볼 명령어는 현재 어떤 컨테이너를 실행 중인지 나열하는 명령어입니다.
docker ps
ps 는 process status 의 약자입니다.
- 컨테이너 실행
여기서 ping google.com 을 입력한 이유는 docker run alpine 만 입력하면 컨테이너가 실행 후 바로 꺼지기 때문에 ping 명령어로 컨테이너가 켜져 있는 상태를 유지하기 위해서입니다.
터미널을 하나 더 켜서 (우클릭 - split pane horizontally)
- 실행된 컨테이너 확인
도커 컨테이너를 나열할 때 원하는 항목만 보기
docker ps --format 'table {{.Names}} \t table {{.Image}}'
실행 중인 컨테이너 뿐만 아닌 중단된 컨테이너 모두를 보고 싶을 때는 -a 옵션을 추가해 docker ps -a 를 입력합니다.
-a 는 all 을 의미합니다.
docker ps -a
2.3 도커 컨테이너의 생성과 실행
도커의 생명주기
- docker create <이미지 이름> 생성
- docker start <컨테이너 아이디/이름> 시작 및 실행
- docker run <이미지 이름> 생성 - 시작 - 실행
- docker stop <컨테이너 아이디/이름> 중지
- docker rm <컨테이너 아이디/이름> 삭제
docker run = docker craete + docker start
docker run 을 주로 사용하므로 docker run 만 알아두자.
2.4 도커 컨테이너 멈추기
도커를 중지할 때는 docker stop 명령어와 docker kill 두 가지 명령어를 이용해 중지할 수 있습니다.
이 두 명령어는 어떤 차이가 있을까요 ?
- stop 은 우아하게 (Graceful) 컨테이너를 중지시킵니다.
- 하던 작업을 모두 마치고 컨테이너를 중지합니다.
- kill 은 stop 과 달리 어떠한 것도 기다리지 않고 즉시 컨테이너를 중지시킵니다.
2.5 도커 컨테이너 삭제하기 - docker rm
rm 은 remove 의 약자입니다.
rm 명령어를 사용해 도커 컨테이너를 삭제할 때는 이미 그 컨테이너가 중지된 상태여야 합니다.
만약 실행 중인 컨테이너를 삭제하려고 하면 오류가 발생합니다.
중지된 모든 컨테이너 삭제하기
docker rm 'docker ps -a -q'
도커 이미지 삭제하기
docker rmi <이미지 id>
- 사용하지 않는 데이터 삭제하기
docker system prune
중지된 모든 컨테이너와 네트워크, 이미지 등이 삭제되며, 삭제된 목록들과 삭제함으로써 확보한 공간의 사이즈를 알려줍니다.
여기서 prune 은 가지를 치다 라는 뜻을 가지고 있습니다.
실행 중인 컨테이너에 명령어 전달하기
docker exec
2.6 실행 중인 컨테이너에 명령어 전달하기 - docker exec
도커를 사용하다보면 컨테이너를 시작하면서 명령어를 전달해야 할 때도 많지만, 컨테이너를 실행하는 중에 명령어를 전달해야 하는 경우도 많습니다.
이번 절에서는 실행중인 컨테이너에 어떻게 명령어를 전달하는지 알아보겠습니다.
첫 번째 터미널에서
두 번째 터미널에서
docker exec <컨테이너ID> ls
2.7 레디스를 이용해 도커 컨테이너 이해하기
첫 번째 터미널에 레디스 서버 실행
레디스 클라이언트는 레디스 서버가 실행되고 있을 때 redis-cli 명령어로 실행할 수 있는데, 레디스 클라이언트도 도커 컨테이너 안에서 실행해주면 됩니다.
두 번째 터미너릉ㄹ 열고 docker ps 로 컨테이너 id 를 확인합니다.
exec 명령어를 이용해 레디스 서버가 실행 중인 컨테이너에 redis-cli 명령어를 전달해 레디스 클라이언트를 실행합니다.
이렇게 해서 에러 없이 레디스 클라이언트까지 실행했습니다.
여기서 -it 옵션은 i (interactive) 옵션과 t (terminal) 옵션이 합쳐진 옵션입니다.
앞으로 -it 옵션은 많은 부분에서 사용할 것입니다.
2.8 실행 중인 컨테이너에서 터미널 사용하기 - docker exec sh
이번 절에서는 컨테이너에 셸이나 터미널 환경으로 접속해 사용하는 방법을 살펴보겠습니다.
셸 환경으로 접속하기
첫 번째 터미널에서 컨테이너를 실행 후
두 번째 터미널에서
한 가지 더 알아두면 좋은 점은 exec 명령어 대신 run 명령어로도 컨테이너에 셸 환경으로 접근할 수 있다는 점입니다.
셸 환경에서 벗어날 때는 Control + D 키를 눌러 나올 수 있습니다.
지금까지는 모든 실습에서 다른 사람이 만들어둔 도커 이미지를 도커 허브에서 내려받아 사용했습니다.
이어서 다음 장인 3장에서는 도커 이미지를 직접 만들어 보겠습니다.
03. 직접 도커 이미지 생성하기
3.1 도커 이미지를 생성하는 순서
도커 이미지를 생성하는 순서를 알아보겠습니다.
- 도커 이미지는 컨테이너를 만드는 데 필요한 설정이나 종속성을 가진 소프트웨어 패키지입니다.
- 도커 이미지를 이용해 도커 컨테이너를 생성할 수 있습니다.
- docker create <이미지 이름>
- 이 때 다른 사람이 만든 도커 허브에 올려둔 도커 이미지를 내려받아 이용할 수도 있고, 직접 도커 이미지를 만들어서 사용할 수도 있습니다. 또한, 직접 만든 도커 이미지를 도커 허브 (Docker Hub) 에 업로드할 수도 있습니다.
도커 이미지는 어떻게 생성할까요 ?
도커 이미지를 생성하는 순서
- 도커 파일 (Dockerfile) 이라는 파일을 작성합니다.
- 도커 파일이란 도커 이미지를 만들기 위한 설정 파일입니다. 컨테이너가 어떻게 행동해야 하는지에 대한 설정을 정의합니다.
- 도커 파일 (Dockerfile) 에 입력한 명령들이 도커 클라이언트에 전달됩니다.
- 도커 클라이언트에 전달된 명령들을 도커 서버에서 처리해 도커 이미지를 만들어 줍니다.
이러한 순서로 도커 이미지를 생성합니다.
3.2 도커 파일 (Dockerfile) 만들기
이번 절에서는 도커 파일이 무엇인지 알아보고 도커 파일을 작성해 보겠습니다.
도커 파일이란 ?
도커 이미지를 만들기 위한 설정 파일로, 도커 컨테이너가 어떻게 행동해야 하는지에 대한 설정을 정의하는 곳입니다.
도커 파일은 도커 이미지를 만들기 위한 설정 파일이기 때문에 도커 파일을 작성할 때는 도커 이미지 안에 무엇이 필요할지 생각하면서 도커 파일을 작성해야 합니다.
가상으로 코코아톡이라는 프로그램을 실행할 수 있는 코코아톡 이미지가 있다고 가정하겠습니다.
여기서 코코아톡 이미지 안에 필요한 것은 크게 2가지입니다.
하나는 코코아톡 이미지로 컨테이너를 생성했을 때 코코아톡 애플리케이션을 실행하는 데 필요한 명령어입니다.
그리고 다른 하나는 코코아톡 실행에 필요한 파일 스냅숏입니다.
도커 파일 작성 방법
- 베이스 이미지를 명시합니다.
- 베이스 이미지 이외에 코코아톡 애플리케이션을 실행하는 데 필요한 파일들을 이미지 안에 내려받기 위한 명령어를 명시합니다. (파일 스냅숏에 해당하는 부분입니다.)
- 마지막으로 컨테이너 시작 시에 실행할 명령어를 명시합니다. (애플리케이션 실행을 위한 명령어에 해당합니다.)
베이스 이미지란 무엇인가 ?
앞서 도커 파일을 작성하는 과정에서 베이스 이미지를 명시해야 한다고 했는데, 베이스 이미지가 무엇인지 알아보겠습니다.
도커 이미지는 여러 개의 레이어로 구성돼 있습니다.
이때 레이어는 중간 단계의 이미지라고 생각하면 되고, 레이어를 이미지에 추가하는 것을 레이어 캐싱이라고 부릅니다.
도커 파일 작성하기
이제 실습을 통해 도커 파일을 작성하는 방법을 배워보겠습니다.
도커 파일을 작성할 때는 목표로 하는 도커 이미지가 있어야만 그 목표에 맞게 도커 파일을 작성할 수 있습니다.
그래서 우선 기능이 최ㅐ한 간단한 도커 이미지를 만들어 보겠습니다.
실습에서는 hello 라는 문구를 출력해주는 애플리케이션을 실행하는 도커 이미지를 만드는 것을 목표로 하겠습니다.
- 도커 파일을 만들 폴더 하나를 생성합니다.
- 이 책에서는 새로 생성한 폴더의 이름을 'dockerfile-folder' 로 변경했습니다.
- 소스코드 편집기를 실행한 다음 방금 생성한 폳더를 불러옵니다.
이 책에서는 책과 동일하게 비주얼 스튜디오 코드를 사용하는 것을 추천하고 있다.
혹시 vscode 가 깔려있지 않다면 홈페이지나 홈브류로 깔아보자.
- 홈페이지에서 설치
https://code.visualstudio.com/
- 홈브류로 설치 (for Mac)
brew install --cask visual-studio-code
3. 새 파일을 눌러 파일이름을 Dockerfile 로 해서 생성합니다.
FROM
- 이미지 생성 시 기반이 되는 이미지 레이어를 명시합니다.
- <이미지 이름>:<태그> 형식으로 작성합니다.
- 태그를 붙이지 않으면 자동으로 가장 최신 버전으로 내려받습니다.
- 예) ubuntu: 14.04
RUN
- 도커 이미지가 생성되기 전에 수행할 셸 명령어입니다.
CMD
- 컨테이너가 시작됐을 때 실행할 실행 파일 또는 셸 스크립트입니다.
- 이 명령어는 도커 파일 내에서 한 번만 쓸 수 있습니다.
5. 앞서 작성한 기본적인 토대에서 FROM 부분의 baseImage 를 실제 값으로 변경합니다.
베이스 이미지에는 ubuntu 나 centos 등을 넣어도 됩니다. 하지만 hello 라는 문구를 출력하는 간단한 기능을 이용하는 데 용량이 큰 베이스 이미지를 사용할 필요가 없기 때문에 용량이 아주 작은 alpine 이라는 베이스 이미지를 사용하겠습니다.
FROM alpine
6. 이어서 RUN 부분을 작성할 차례입니다.
hello 라는 문구를 출력하는 데 필요한 파일은 이미 alpine 베이스 이미지에 들어 있기 때문에 굳이 추가로 파일을 내려받지 않아도 됩니다.
따라서 RUN 부분은 생략하겠습니다.
7. 마지막으로 CMD 부분에는 컨테이너를 시작할 때 실행할 명령어를 명시합니다.
CMD ["echo", "hello"]
3.3 Buildkit 비활성화하기
앞서 만든 도커 파일을 이용해 도커 이미지를 생성하기 전에 한 가지 설정을 변경하겠습니다. 변경하고자 하는 설정은 도커 버전 18.09 부터 도커 이미지 빌드를 할 때 기본적으로 지원하는 Buildkit 입니다.
Buildkit 이란 무엇인가요 ?
도커 버전 18.09 부터는 도커 파일을 이용해 도커 이미지를 빌드할 때 Buildkit 을 기본적으로 사용하게 됐습니다.
이 Buildkit 은 도커 파일을 빌드할 때 더 빠른 속도로, 더 효율적으로 빌드할 수 있게 해줍니다.
따라서 실제로 도커를 사용할 때는 도커 Buildkit 을 사용하는 게 좋습니다.
Buildkit 을 이용했을 때의 차이점
Buildkit 을 이용했을 때 다른점은 도커 파일을 빌드할 때 나타납니다. Buildkit 을 이용해 빌드하면 도커 파일을 빌드하는 과정에서 Buildkit 을 사용하지 않았을 때와는 다른 출력 문구를 내보냅니다.
그 중에서 가장 주요한 차이점은 빌드 프로세스 마지막 부분에 나오는 이미지 ID 입니다.
이 책에서는 조금 더 쉽게 강의를 따라할 수 있게 Buildkit 을 비활성화한 다음 실습을 진행하겠습니다.
Buildkit 비활성화하기
Buildkit 을 비활성화하기 위해서는 도커 데스크탑에서 소스 코드를 변경해야 합니다.
도커 데스크탑에 들어가보겠습니다.
Docker Engine 으로 들어가 buildkit 을 true -> false 로 변경합니다.
3.4 도커 파일로 도커 이미지 만들기
- 도커 이미지를 만드는 순서
도커파일 -> 도커 클라이언트 -> 도커 서버 -> 이미지
도커 파일에 입력한 명령어들을 도커 클라이언트로 전달해 도커 서버가 인식하게 해야 합니다.
이를 위해 Dockerfile 이 있는 디렉터리로 이동한 다음 명령어를 입력합니다.
- 도커 빌드
docker build .
빌드 과정 자세히 살펴보기
빌드 명령어를 입력하면 이미지를 빌드하는 동안 Step 1 부터 2 까지 올라가는 모습을 볼 수 있습니다.
Step 1/2 에서는 alpine 이미지를 가져옵니다.
Step 2/2 에서는 임시 컨테이너를 생성하고, 컨테이너를 시작할 때 사용할 명령어를 임시 컨테이너에 포함시킵니다.
그 다음 방금 생성한 임시 컨테이너를 지우고 새로운 이미지를 만듭니다.
3.5 내가 만든 이미지에 기억하기 쉬운 이름 붙여주기
생성된 컨테이너를 실행해봅시다.
인터넷에서 IP 주소를 기억하기 어렵기 때문에 도메인 이름을 만들어서 이용하는 것처럼 우리가 만든 도커 이미지에도 이름을 붙여줄 수 있습니다.
도커 이미지에 이름 붙여주기 - docker build -t
도커 이미지에 이름을 붙이려면 -t 태그를 이용합니다. t 는 tag 의 약자이며, -t 뒤에 도커 ID/프로젝트 이름을 작성하고 그 뒤에 쌍점 (:) 과 버전을 작성합니다. 이름은 자신이 원하는 이름으로 정해도 되지만 관습적으로 도커 ID 와 프로젝트의 이름을 이용해 지어 줍니다.
버전도 임의로 정합니다.
이렇게 이름을 지정하면 도커 컨테이너를 실행할 때 임의로 생성된 이미지의 ID 가 아닌 별도로 지정한 도커 이미지의 이름을 이용해 컨테이너를 실행할 수 있습니다.
04. 도커를 이용한 간단한 Node.js 애플리케이션 만들기
4.1 Node.js 애플리케이션 만들기
Node.js 애플리케이션을 도커 환경에서 실행하는 법은 Node.js 의 공식 홈페이지에도 나와 있지만, 이 책에서는 더 깊게 살펴보겠습니다.
완성된 도커 파일을 살펴보겠습니다.
FROM node:14
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]
도커를 이용해 Node.js 애플리케이션을 만드는 순서
Node.js 애플리케이션을 만들려면 가장 먼저 두 가지 파일이 필요합니다.
바로 package.json 파일과 server.js 파일입니다.
컴퓨터에 Node.js 가 설치돼 있다면 npm init 명령어로 생성할 수 있습니다.
Node.js 앱 만들기
- 폴더 이름을 nodejs-docker-app 으로 지정
- VSCODE 로 실행
3. 추가한 NODEJS-DOCKER_APP 디렉터리에 package.json 파일을 생성합니다.
npm init 대신 npm init -y 명령어를 입력하면 자동으로 프로젝트의 이름과 메인 파일이 설정됩니다.
대신 메인 파일이 index.js 로 설정되므로 server.js 로 바꿔줘야 합니다.
4. index.js 를 server.js 로 변경합니다.
5. 프로젝트에 필요한 종속성을 추가합니다.
- express 를 디펜던시에 추가
"dependencies": {
"express": "^4.17.1"
}
npm install 로 설치해버리면 빌드 시 엄청 오래걸리니 직접 추가해주자.
- server.js
const express = require("express");
const PORT = 8080;
const app = express();
app.get("/", (req, res) => {
res.send("반갑습니다.");
});
app.listen(PORT, () => {
console.log("애플리케이션이 실행됐습니다.");
});
4.2 도커 파일 작성하기
- Dockerfile
FROM node:10
RUN npm install
CMD ["node", "server.js"]
왜 FROM 부분에 alpine 베이스 이미지가 아닌 node 이미지를 사용할까요 ?
alpine 이미지에는 npm 이 없는 최소한의 경량화된 파일만 들어있습니다.
4.4 생성한 이미지로 애플리케이션 실행 시 접근이 안 되는 이유
- dockerfile
FROM node:10
COPY ./ ./
RUN npm install
CMD ["node", "server.js"]
- 도커 이미지 생성
docker build -t devconnor/node-app ./
- 실행
docker run -p <로컬 호스트 포트>:<컨테이너 속 포트> <이미지 이름>
docker run -p 5000:8080 devconnor/node-app
이제 5000 을 호출해봅시다.
4.5 작업 디렉터리 명시하기
이번에는 도커 파일에 작성할 수 있는 WORKDIR 이라는 지시자를 살펴 보겠습니다. 지금까지는 WORKDIR 지시자 없이도 문제 없었지만, 더 복잡한 애플리케이션을 도커를 이용해 실행할 때는 여러 이유로 인해 WORKDIR 지시자를 추가해야 합니다. 그래서 이번 절에서는 WORKDIR 지시자는 왜 추가해야 하는지 자세히 알아보겠습니다.
WORKDIR 지시자는 무엇인가요?
WORKDIR 지시자는 도커 파일 (Dockerfile) 에서 뒤에 오는 모든 지시자 (RUN, CMD, COPY, ADD 등) 에 대한 작업 디렉터리를 설정합니다. 리눅스 명령어의 cd 와 비슷한 역할을 합니다. 따라서 WORKDIR 지시자를 사용해 작업 디렉터리를 별도로 지정하면 로컬에 있는 파일들이 도커 컨테이너로 볼사될 때 WORKDIR 지시자에 정의한 디렉터리로 들어갑니다.
COPY 지시자로 컨테이너 안으로 복사한 폴더와 파일
COPY 로 복사한 파일은 다음과 같습니다.
- Dockerfile
- package.json
- package-lock.json
- node_module
이렇게 파일과 폴더가 한 디렉터리에 섞이면 문제가 되나요?
- 원래 최상위 폴더 안에 들어 있던 파일 및 폴더의 이름이 COPY 지시자로 복사한 파일 및 폴더의 이름과 같다면 원래 있던 파일을 덮어쓰게 됩니다.
- 모든 파일이 한 디렉터리에 들어 있으면 정리정돈이 되지 않아서 복잡해집니다.
이 책에서는 경로를 /usr/src/app 으로 지정했는데, 다른 경로를 지정해도 무방합니다.
- Dockerfile
FROM node:10
WORKDIR /usr/src/app
COPY ./ ./
RUN npm install
CMD ["node", "server.js"]
- 이미지 빌드
docker build -t devconnor/node-app ./
이렇게 작업 디렉터리를 설정하면 컨테이너를 더 깔끔하게 관리할 수 있습니다.
4.6 애플리케이션의 소스 코드 변경으로 다시 빌드할 때의 문제점
이번 절에서는 변경한 소스 코드를 애플리케이션에 반영하는 방법을 알아보겠습니다.
도커 환경에서 애플리케이션을 실행하는 순서
소스코드를 조금 수정한 후에 브라우저에서 변경된 부분을 확인하려면 도커 이미지의 생성부터 컨테이너의 실행까지 다시 해야하는 번거로움이 있습니다.
server.js 를 열고 반갑습니다. 에서 안녕하세요. 로 소스코드를 변경합니다.
이미지를 새로 빌드하면서 생긴 2가지 문제점
첫 번째 문제점은 소스코드를 변경한 파일은 server.js 뿐인데 'COPY ./ ./' 로 인해서 node_modules 에 있는 종속성까지 모두 다시 내려받는 것입니다.
두 번째 문제점은 소스코드를 조금 변경했을 뿐인데 도커 이미지를 다시 생성하고, 컨테이너를 다시 실행하는 과정이 너무 번거롭습니다.
4.7 애플리케이션의 소스 코드를 변경했을 때 이미지를 효율적으로 다시 빌드하기
이 문제를 해결하려면 도커 파일에서 COPY 지시자 부분을 수정해야 합니다.
COPY 지시자 부분을 바꾼 이유는 무엇일까요?
COPY 지시자 부분을 바꾼 이유는 'npm install' 로 모듈을 설치하는 과정에서 불필요한 다운로드를 피하기 위해서입니다.
- dockerfile
COPY package.json ./
RUN npm install
COPY ./ ./
이렇게 코드를 변경하면 종속성을 내려받을 때 'RUN npm install' 전 단계의 COPY 에서 조금이라도 변화가 있다면 npm install 에서 종속성을 다시 내려받고, 아무런 변화가 없었다면 캐시 (cache) 를 이용해 이 과정을 생략합니다.
COPY 지시자를 RUN 전후로 나눠 효율적으로 빌드하기
- before
- after
4.8 도커 볼륨
비효율적으로 모듈을 다시 내려받는 문제는 해결했습니다.
하지만 아직도 소스코드를 변경할 때마다 변경된 소스코드를 복사한 다음 이미지를 다시 빌드하고, 컨테이너를 다시 실행해야만 변경된 소스 코드가 화면에 반영됩니다.
이 문제는 도커 볼륨 (Docker Volume) 을 사용해 해결할 수 있습니다.
도커 볼륨 (Docker Volume) 은 무엇인가요?
COPY 지시자는 로컬 호스트의 디렉토리에 있는 파일을 도커 컨테이너로 그대로 복사하는 방식입니다.
그리고 도커 볼륨은 도커 컨테이너에서 호스트 디렉토리에 있는 파일들을 참조해서 사용하는 방식입니다.
이렇게 복사하지 않고 도커 컨테이너에서 로컬 호스트의 디렉토리에 있는 파일을 계속 참조해서 사용하므로 소스 코드를 변경해도 다시 COPY 를 사용해 이미지를 빌드할 필요가 없습니다.
도커 볼륨은 어떻게 이용하나요?
도커 볼륨은 도커 컨테이너를 실행할 때 -v 옵션으로 지정할 수 있습니다.
하지만 명령어가 조금 복잡합니다. 다음 그림을 보면 -v 옵션이 있는데, 뒤에 있는 부분 (pwd 라고 쓰인 부분) 부터 자세히 알아보겠습니다.
docker run -p 5000:8080 -v /usr/src/app/node_modules -v $(pwd):/usr/src/app <이미지 아이디>
PWD (print working directory)
PWD 는 현재 작업 중인 디렉토리의 절대 경로를 출력하는 명령어입니다. 실제로 이 명령어를 사용해 보겠습니다.
윈도우에서는 %cd% 라고 합니다.
도커 볼륨은 컨테이너 안에서 호스트 디렉토리에 있는 파일이나 폴더들을 참조하는 것이므로 pwd 명령어를 이용해서 출력된 절대 경로의 파일과 폴더에서 쌍점 뒤에 있는 작업 디렉토리를 참조해 애프리케이션을 실행한다는 의미입니다.
도커 볼륨을 사용해 애플리케이션 실행하기
앞서 살펴본 명령어를 토대로 도커 볼륨을 사용해 애플리케이션을 실행해 보겠습니다.
docker run -p 5000:8080 -v /usr/src/app/node_modules -v $(pwd):/usr/src/app devconnor/node-app
- 컨테이너를 백그라운드에서 실행하는 명령어
docker run -d -p 5000:8080 -v /usr/src/app/node_modules -v $(pwd):/usr/src/app devconnor/node-app
여기서 -d 는 detach 의 약자이다.
이미지를 다시 빌드하지 않고
문구가 바뀐 모습을 볼 수 있다.
- server.js
const express = require("express");
const PORT = 8080;
const app = express();
app.get("/", (req, res) => {
res.send("반가워요");
});
app.listen(PORT, () => {
console.log("애플리케이션이 실행됐습니다.");
});
- Dockerfile
FROM node:10
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY ./ ./
CMD ["node", "server.js"]
- package.json
{
"name": "nodejs-docker-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1"
}
}
컨테이너를 내렸다가 올리면
docker run -p 5000:8080 -v /usr/src/app/node_modules -v $(pwd):/usr/src/app devconnor/node-app
코드의 수정사항이 반영된다.
05. 도커 컴포즈
이번 장에서는 도커 컴포즈 (Docker Compose) 를 살펴보겠습니다. 우선 도커 컴포즈는 다중 컨테이너인 도커 애플리케이션을 정의하고 실행하기 위한 도구입니다. 하지만 이렇게 정의만 봐서는 도커 컴포즈가 도대체 무엇인지 감이 잘 오지 않습니다.
이번 장에서는 도커 컴포즈를 이용해 새로운 애플리케이션을 만들면서 도커 컴포즈에 대해 깊게 배워보는 시간을 가지겠습니다.
5.1 이번 장에서 만들 애플리케이션
이번 장에서 만들 애플리케이션은 페이지를 새로고침할 때마다 숫자가 0부터 1씩 올라가는 간단한 애플리케이션입니다.
이 애플리케이션은 Node.js 애플리케이션을 위한 컨테이너 하나와 레디스를 위한 컨테이너 하나로, 총 두 개의 컨테이너로 구성된 애플리케이션입니다.
5.2 Node.js 와 레디스 구현하기
- package.json 생성
npm init -y
- dependencies 설치
npm install express@4.17.1
npm install redis@3.0.2
npm install nodemon
- package.json 수정
"main": "server.js",
"start": "nodemon server.js"
main 파일과 서버실행 스크립트를 작성해줍니다.
- 생성된 package.json
{
"name": "the-docker-and-ci-environment-by-copying",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon server.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/dev-connor/The-Docker-and-CI-environment-by-copying.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/dev-connor/The-Docker-and-CI-environment-by-copying/issues"
},
"homepage": "https://github.com/dev-connor/The-Docker-and-CI-environment-by-copying#readme",
"dependencies": {
"express": "^4.17.1",
"nodemon": "^2.0.22",
"redis": "^3.0.2"
}
}
- server.js
// Express 모듈 불러오기
const express = require('express')
// Express App 생성하기
const app = express()
app.get('/', (req, res) => {
res.send('안녕하세요')
})
// Express App 시작하기
app.listen(8080, () => {
console.log('애플리케이션이 실행됐습니다.')
})
이렇게 Node.js 의 기본적인 부분을 완성했습니다. 이어서 레디스가 무엇인지 살펴보고, 방금 만든 Node.js 애플리케이션에 레디스를 연동하겠습니다.
레디스란 무엇인가요?
레디스 (Redis, REmote Dictionary Server) 는 메모리 기반의 키-값 구조 데이터 관리 시스템입니다.
모든 데이터를 메모리에 저장하므로 빠르게 읽고 쓸 수 있으며, 비 관계형 데이터베이스 (NoSQL) 입니다.
레디스의 장점은 무엇인가요?
레디스는 데이터를 메모리에 저장하기 때문에 MySQL 과 같이 하드디스크에 데이터를 저장하는 데이터베이스보다 훨씬 빠르게 데이터를 저장하고 불러올 수 있습니다.
또한 데이터를 메모리에 저장하지만, 영속적으로도 보관할 수 있습니다.
즉, 서버를 재부팅해도 데이터를 유지할 수 있는 장점이 있습니다.
Node.js 환경에서 레디스를 사용하는 방법
먼저 redis-server 를 작동시키고, redis-server 가 작동하는 곳에서 레디스 클라이언트를 생성해 작동시킵니다.
이렇게 레디스 클라이언트를 생성하려면 레디스 모듈을 이용해야 하는데, 앞서 package.json 에 이미 레디스 모듈을 추가했습니다.
따라서 레디스 모듈에서 제공하는 createClient() 함수를 이용해 레디스 클라이언트를 생성하기만 하면 됩니다.
이때 레디스 서버가 작동하는 곳과 Node.js 애플리케이션이 작동하는 곳이 다르다면 레디스 서버가 작동하는 곳의 host 와 port 를 명시해야 합니다.
- server.js
// 레디스 클라이언트 생성
const client = redis.createClient({
host: 'redis-server',
port: 6379,
})
예를 들어 레디스 서버가 작동하는 곳이 redis-server.com 이라면 Node.js 의 소스코드에서 레디스 클라이언트를 생성할 때 host 옵션을 위 코드처럼 'redis-server' 로 지정합니다. 그리고 레디스의 기본 포트는 6379 이므로 포트번호를 6379 로 지정했습니다.
도커 환경에서 레디스 클라이언트를 생성할 때 주의할 점
보통 도커를 사용하지 않는 환경에서는 레디스 서버가 작동되고 있는 곳의 URL 을 이용해 host 옵션을 설정하면 되지만, 도커 컴포즈를 사용할 때는 다음과 같이 host 옵션을 docker-compose.yml 파일에 명시한 서비스 이름으로 설정합니다.
만약 docker-compose.yml 파일에서 서비스 이름을 redis-server 로 지정했다면 host 옵션값을 redis-server 로 지정합니다.
Node.js 애플리케이션에 레디스 코드 추가하기
server.js 파일에 레디스 모듈을 사용해 레디스 클라이언트를 생성하겠습니다.
// Express 모듈 불러오기
const express = require('express')
const redis = require('redis')
// 레디스 클라이언트 생성
const client = redis.createClient({
host: 'redis-server',
port: 6379,
})
// Express App 생성하기
const app = express()
// 숫자는 0부터 시작합니다.
client.set('number', 0)
app.get('/', (req, res) => {
client.get('number', (err, number) => {
res.send('숫자가 1씩 올라갑니다. 숫자: ', +number)
client.set('number', parseInt(number) + 1)
})
})
// Express App 시작하기
app.listen(8080, () => {
console.log('애플리케이션이 실행됐습니다.')
})
레디스 기본 명령어
set text hello
get text
set text bye
del text
5.3 Node.js 를 실행할 컨테이너의 도커 파일 작성하기
애플리케이션의 소스코드는 모두 구현했으므로 이제 애플리케이션을 도커화 (Dockerize) 할 시간입니다.
- Dockerfile
FROM node:10
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY ./ ./
CMD ["nodemon", "server.js"]
5.4 통신할 때 나타나는 에러
애플리케이션 실행 순서
- 레디스 서버 실행
docker run redis
- 컨테이너 이미지 생성
docker build -t devconnor/node-app ./
만약 다음과 같은 에러가 발생한다면
Dockerfile 에 아래 코드를 넣어서 다시 이미지를 생성해보자.
RUN npm install -g nodemon
- Dockerfile
FROM node:10
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
RUN npm install -g nodemon
COPY ./ ./
CMD ["nodemon", "server.js"]
왜 에러가 나는 걸까요?
컨테이너는 격리된 상태로 생성되고 기본적으로 외부와 통신할 수 없게 설정돼 있습니다.
이때 필요한 게 바로 도커 컴포즈입니다.
이어서 다음 절부터는 도커 컴포즈에 대해서 배우면서 이번 절에서 발생한 문제를 해결해 보겠습니다.
5.5 도커 컴포즈 파일 작성하기
도커 컴포즈 파일은 확장자가 yaml 이나 yml 인데 이것은 무슨 파일인가요?
YAML 은 Ain't Markup language 의 약자이며, 일반적으로 구성 파일 및 데이터가 저장되거나 전송되는 응용 프로그램에서 사용합니다.
원래는 XML 이나 JSON 포맷으로도 많이 쓰였지만, 좀 더 사람이 읽기 쉬운 포맷으로 나타난 형식이 yaml 입니다.
확장자로는 yaml, yml 둘 중 하나를 이용합니다.
도커 컴포즈의 파일 구조
도커 컴포즈 파일의 이름은 docker-compose.yml 혹은 docker-compose.yaml 입니다.
그리고 도커 컴포즈 파일은 규격 버전이 있으며, 파일의 규격에 따라 지원하는 옵션이 다릅니다.
이 책에서 사용할 버전은 버전 3 인데 이것은 3 으로 시작하는 최신 버전을 사용한다는 의미입니다.
version // 도커 컴포즈의 버전
services // 이곳에 실행하려는 컨테이너들ㅇ르 정의
redis-server // 컨테이너 이름
image // 컨테이너에서 사용하는 이미지
node-app // 컨테이너 이름
build // 현 디렉토리에 있는 Dockerfile 사용
ports // 포트 매핑 - 로컬포트:컨테이너 포트
기존에 Node.js 컨테이너를 실행할 때 명령어를 이용해 포트매핑을 했던 부분을 도커 컴포즈 파일에 명시했기 때문에 이제는 명령어를 길게 입력하지 않아도 됩니다.
- docker-compose.yml
version: "3"
services:
redis-server:
image: "redis"
node-app:
build: .
ports:
- "5000:8080"
- 도커 컴포즈로 애플리케이션 실행
docker-compose up
코드를 수정하고 다시 실행할 때는 docker-compose up --build 옵션을 주어야 한다.
--build 옵션이 없으면 이미지가 없을 때만 이미지를 빌드하고 컨테이너를 시작하며
옵션이 있다면 무조건 이미지를 빌드하고 컨테이너를 시작한다.
06. 단일 컨테이너를 활용한 애플리케이션 만들기
지금부터 6장부터 9장에 걸쳐 두 가지 애플리케이션을 만들어 보겠습니다.
첫 번째 애플리케이션은 리액트 (React.js) 만을 이용해 개발하며, 개발부터 배포까지 도커를 활용해 만들어 보겠습니다.
그리고 두 번째 애플리케이션은 리액트와 Node.js 그리고 데이터베이스를 이용해 개발하며, 마찬가지로 개발부터 배포까지 도커를 활용하면서 도커를 배우는 시간을 갖겠습니다.
6.1 리액트 설치 및 애플리케이션 생성
docker-react-app 폴더에 터미널에서 다음 명령어를 입력합니다.
- 리액트 설치
npx create-react-app ./
길게는 1~2분 정도의 시간이 걸립니다.
리액트를 실행해보자.
- 리액트 어플리케이션 실행
npm run start
이어서 이번 장에서 리액트를 사용하는 데 필요한 3가지 명령어를 살펴보겠습니다.
- 앱 시작하기 npm run start
- 앱 테스트하기 npm run test
- 앱 빌드하기 npm run build
리액트 애플리케이션 생성하기
애플리케이션을 테스트할 때는 `npm run test` 명령어를 사용합니다.
테스트는 원하는 만큼 더 만들 수 있습니다.
npm run build
이렇게 해서 리액트 애플리케이션 설치를 완성하고 간단하게 리액트 애플리케이션의 사용법을 알아봤습니다.
6.2 리액트 애플리케이션을 위한 도커 파일 작성하기
이번에는 두 개의 도커파일을 작성해보겠습니다.
하나는 개발 환경을 위한 도커 파일이고, 다른 하나는 운영 환경을 위한 도커 파일입니다.
개발환경의 도커파일은 지금까지 작성한 도커 파일과 거의 비슷합니다.
하지만 이후에 작성할 운영 환경의 도커파일에는 새로운 내용이 많이 포함될 것입니다.
먼저 개별환경을 위한 도커파일을 만들어보겠습니다.
개발환경을 위한 도커파일 작성하기
- 먼저 도커파일을 생성합니다. 개발환경이므로 `Dockerfile.dev` 로 지정합니다.
- 다음과 같이 작성합니다.
- Dockerfile.dev
FROM node:10
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY ./ ./
CMD ["npm", "run", "start"]
- WORKDIR 의 경로는 임의의 경로로 지정하면 됩니다.
- package.json 을 먼저 복사하는 이유는 소스코드를 변경했을 때 변경되지 않은 종속성까지 내려받는 문제를 해결하기 위해서입니다.
docker build -f Dockerfile.dev ./
빌드할 때 오래걸리므로 로컬머신의 node_modules 폴더와 build 폴더는 삭제해주는 것이 좋습니다.
6.3 생성된 도커 이미지로 로컬에서 리액트 실행하기
이번 절에서는 이전 절에서 생성한 도커 이미지로 컨테이너를 실행하고, 리액트 애플리케이션을 실행해 보겠습니다.
docker build -f Dockerfile.dev -t devconnor/docker-react-app ./
노드 이미지를 12로 올려서 사용하자.
- Dockerfile.dev
FROM node:12
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY ./ ./
CMD ["npm", "run", "start"]
docker run -p 3000:3000 devconnor/docker-react-app
책에는 -it 옵션을 주라고 하는데 나 왠지 잘 된다.
6.4 도커 볼륨을 이용한 소스 코드 변경
이번 리액트 애플리케이션에서도 이전과 같이 소스코드를 변경했을 때 이미지를 새로 빌드하지 않아도 변경한 소스코드가 애플리케이션에 반영되도록 도커볼륨을 적용해 보겠습니다.
도커 볼륨을 사용해 애플리케이션 실행하기
docker run -p 3000:3000 -v /usr/src/app/node_modules -v $(pwd):/usr/src/app devconnor/docker-react-app
6.5 도커 컴포즈로 좀 더 간단하게 애플리케이션 실행하기
앞서 리액트 앱을 실행할 때 너무나 긴 명령어를 입력해야 했습니다.
명령어가 길면 입력하기에도 불편하고, 실수가 생기기도 쉽습니다.
이러한 불편을 해소하기 위해 도커 컴포즈를 이용해 리액트 애플리케이션을 다시 실행해 보겠습니다.
도커 컴포즈를 이용해 애플리케이션 실행하기
- docker-compose-dev.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 -f docker-compose-dev.yml up --build
이렇게 도커 컴포즈를 이용해 애플리케이션을 더 쉽게 실행할 수 있게 됐습니다.
6.6 리액트 애플리케이션 테스트하기
리액트를 테스트할 때는 'npm run test' 명령어를 사용한다고 했습니다.
먼저 이 명령어를 이용해 테스트를 진행해 보겠습니다.
도커를 이용한 리액트 애플리케이션에서 테스트를 진행하려면
App.js 의 글자를 다시 'Learn React' 로 바꾼 후
이미지를 생성해 봅시다.
- 이미지 생성
docker build -f Dockerfile.dev -t devconnor/docker-react-app ./
- 테스트
docker run -it devconnor/docker-react-app npm run test
이번에는 소스코드를 변경하면 변경된 소스코드가 자동으로 화면에 반영되는 것 처럼 테스트 소스 코드를 추가하면 바로 반영되게 만들어 보겠습니다. 지금 상태에서는 App.test.js 에 테스트 코드를 하나 더 추가해도 아무런 변화가 없습니다.
테스트를 하나 더 추가했을 때 바로 반영되게 하려면 소스코드를 실시간으로 변경하기 위해 도커 볼륨을 이용했던 것 처럼 이번에도 도커 볼륨을 이용해야 합니다. 다음과 같이 도커 컴포즈 파일에 test 서비스를 만들고, 테스트를 위한 컨테이너를 추가합니다.
- docker-compose-dev.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
# 서비스 이름
tests:
# 현 디렉토리에 있는 Dockerfile 사용
build:
# 도커 이미지를 구성하기 위한 파일들이 있는 위치
context: .
# 도커 파일이 어떤 것인지 지정
dockerfile: Dockerfile.dev
# 로컬 머신에 있는 파일을 매핑
volumes:
- /usr/src/app/node_modules
- ./:/usr/src/app
# 테스트 컨테이너가 시작될 때 실행되는 명령어
command: ["npm", "run", "test"]
이렇게 도커 컴포즈 파일을 구성하면 컨테이너를 실행할 때 두 개의 컨테이너가 실행됩니다. 먼저 리액트 애플리케이션을 실행하는 컨테이너가 실행되고, 이어서 애플리케이션을 테스트하는 컨테이너가 실행됩니다.
도커 컴포즈를 실행해 두 개의 컨테이너를 모두 실행해 보겠습니다.
- 도커 컴포즈 실행
docker-compose -f docker-compose-dev.yml up --build
두 개의 컨테이너가 잘 떴다면 이제 테스트 부분의 소스코드를 변경해 보겠습니다.
App.test.js 에서 테스트 케이스를 하나 더 추가합니다.
- App.test.js
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();
});
test("renders learn react link", () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
6.7 운영 환경을 위한 엔진엑스
이제부터는 배포 후 운영환경을 위한 부분을 하나씩 다뤄보겠습니다.
먼저 운영환경에 필요한 Nginx 를 살펴볼 텐데, Nginx 가 왜 필요한지부터 알아보겠습니다.
운영환경에서 리액트 애플리케이션이 실행되는 구조
운영환경에는 개발환경과 달리 개발서버가 없습니다. 개발서버가 없다면 어떻게 브라우저의 요청에 맞게 정적파일들을 제공할 수 있을까요?
운영환경에서 개발서버 대신에 정적파일을 제공해주는 역할을 하는 게 바로 Nginx 웹 서버입니다.
왜 개발환경의 서버와 운영환경의 서버가 다른가요?
개발에서 사용하는 서버는 소스코드를 변경하면 자동으로 전체 애플리케이션을 다시 빌드해서 변경된 소스코드를 반영하는 것 처럼 개발환경에 특화된 기능이 있습니다. 따라서 개발 중에는 이러한 기능이 없는 Nginx 서버보다 개발서버가 더욱 적합합니다.
반대로 운영환경에서는 소스코드를 변경할 때 다시 반영할 필요가 없으므로 Nginx 처럼 더 깔끔하고 빠른 서버를 웹서버로 사용합니다.
6.8 운영 환경의 도커 이미지를 위한 도커 파일 작성하기
이번 절에서는 운영환경에서 개발서버 대신에 정적 파일을 제공하기 위해 Nginx 웹 서버가 필요하다는 것을 알아봤습니다.
개발환경의 도커파일과 운영환경의 도커파일 비교
개발환경에서는 빌드 명령어를 입력하는 과정 없이 애플리케이션을 실행할 수 있지만, 운영환경에서는 빌드를 해야 합니다.
따라서 'npm run build' 명령어를 추가해 운영환경에서 배포할 빌드 파일을 생성합니다.
그다음 Nginx 를 실행하고, 빌드된 파일들을 Nginx 가 처리하도록 합니다.
운영환경의 도커파일 자세히 보기
첫 번째는 빌드 파일을 생성합니다.
두 번째는 Nginx 를 가동하고, 생성된 필드폴더의 파일들을 웹 브라우저의 요청에 따라 제공합니다.
BUILDER STAGE
FROM
먼저 FROM 에 있는 AS 는 현재 FROM 부터 다음 FROM 까지 모두 빌드단계라는 것을 명시합니다.
FROM node:alpine as builder
WORKDIR
RUN
Nginx 를 가동하고,
COPY
Nginx 가 빌드파일들을 제공할 수 있게 Nginx 가 제어할 수 있는 디렉토리로 빌드파일을 복사합니다.
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
RUN STAGE
COPY
다른 스테이지에 있는 파일을 복사하기 위해 --from=builder 와 같이 스테이지를 명시합니다.
즉 builder 단계에서 빌드된 파일을 가져오는 것임을 명시합니다.
그리고 이 빌드파일을 컨테이너의 /usr/share/nginx/html 디렉토리로 복사합니다.
이 경로는 Nginx 가 정적파일을 브라우저에 제공할 수 있게 기본적으로 설정된 장소입니다. 즉 모든 빌드파일을 Nginx 가 제어할 수 있는 경로인 /usr/share/nginx/html 로 복사하는 것입니다.
이 경로는 다른 디렉토리로 변경할 수 있지만 그러할 필요가 없기 때문에 기본으로 설정된 경로를 그대로 사용하겠습니다.
지금까지 설명한 내용을 토대로 운영환경의 도커파일을 생성하고, 운영환경에서 리액트 애플리케이션을 실행해 보겠습니다.
운영환경에서 리액트 애플리케이션 실행하기
- Dockerfile
FROM node:alpine as builder
WORKDIR '/usr/src/app'
COPY package.json .
RUN npm install
COPY ./ ./
RUN npm run build
FROM nginx
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
작성한 도커파일로 이미지를 생성합니다.
- 도커파일로 이미지 생성
docker build -t devconnor/docker-react-app ./
- 이미지로 애플리케이션 실행
docker run -p 8080:80 devconnor/docker-react-app
6.9 운영 환경의 도커 컴포즈 파일 작성하기
이번 절에서는 운영환경에서 이용할 도커 컴포즈 파일을 작성해 보겠습니다.
도커 컴포즈 파일을 개발환경과 운영환경의 파일로 나누는 이유는 애플리케이션을 배포할 때 AWS 의 일래스틱 빈스톡이라는 환경을 이용하는데 그 환경이 컨테이너를 실행할 때 도커 컴포즈 파일 (docker-compose.yml) 을 보고 이미지를 빌드한 후에 컨테이너를 실행하기 때문입니다.
개발환경을 위한 도커 컴포즈 파일만 있다면 운영환경을 위한 컨테이너를 실행할 때 에러가 발생하기 때문에 운영환경을 위한 도커 컴포즈 파일을 따로 만들어 주겠습니다.
운영환경의 도커 컴포즈 파일 작성
개발환경의 도커 컴포즈 파일과 거의 흡사하기 때문에 복사하여 진행합니다.
이 상태에서 tests 서비스 부분을 제거합니다.
리액트를 테스트하는 부분은 개발환경에서만 필요하기 때문에 테스트를 위한 컨테이너는 필요하지 않습니다.
리액트의 운영환경에서는 웹서버로 Nginx 를 사용하기 때문에 개발환경의 기본포트인 3000 이 아닌 Nginx 의 기본포트인 80번에서 실행됩니다.
version: "3"
services:
react:
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80"
volumes:
- /usr/src/app/node_modules
- ./:/usr/src/app
stdin_open: true
07. 단일 컨테이너를 활용한 애플리케이션의 테스트와 배포
7.1 깃허브에 소스 코드 올리기
이번 장에서는 6장에서 작성한 소스코드를 깃허브에 올리고, 깃허브에 올린 소스코드를 Travis CI 로 가져가 Travis CI 에서 소스코드가 문제없이 동작하는지 테스트하겠습니다. 그리고 테스트 케이스에 성공하면 AWS 에 소스코드를 보내서 배포하는 과정까지 살펴보겠습니다.
7.2 Travis CI 에서 테스트하기
이번 절에서는 Travis CI 에 대해서 알아보겠습니다.
Travis CI 란 ?
프로젝트를 빌드하고 테스트할 수 있는 호스팅 통합 서비스입니다.
Travis CI 를 이용하면 깃허브 저장소에 있는 프로젝트를 특정 이벤트에 따라 자동으로 테스트 및 빌드하거나 배포할 수 있습니다.
public 저장소는 무료로 사용할 수 있으며, private 저장소는 일정 금액을 지불하고 사용할 수 있습니다.
Travis CI 의 역할
로컬 Git -> Github -> Travis CI -> AWS
Travis CI: https://www.travis-ci.com/
- Travis CI 에 로그인합니다.
도커에서는 도커 컴포즈 파일 (docker-compose.yml) 에 무엇을 할지 작성했다면,
Travis CI 에서는 .travis.yml 파일에 무엇을 할지 작성합니다.
테스트를 위한 Travis CI 설정파일 작성하기 - travis.yml
이번 절에서는 Travis CI 를 이용해 테스트코드를 실행하고 애플리케이션을 배포하는 방법을 살펴보겠습니다.
그러기 위해서는 먼저 Travis CI 의 설정파일인 travis.yml 파일을 작성해야 합니다.
- Travis CI 에서도 도커파일을 이용해 도커 이미지 생성
- 어떻게 테스트할 것인지 설정
- 어떻게 AWS 소스코드를 배포할 것인지 설정
travis.yml 파일 자세히 살펴보기
- travis.yml
# 관리자 권한 갖기
sudo: required
# 언어 (플랫폼) 를 선택
language: generic
# 도커 환경 구성
services:
- docker
# 스크립트를 실행할 수 있는 환경 구성
before_install:
- echo "start creating an image with dockerfile"
- docker build -t devconnor/docker-react-app -f Dockerfile.dev .
- sudo: required
- Travis CI 를 제어할 때 관리자권한으로 제어하기 위해
- language
- 언어의 플랫폼을 선택하는 부분. Node.js 나 파이썬 등 다양한 언어를 사용할 수 있지만, 이 책에서는 generic 이라는 보편적인 언어 플랫폼을 사용하겠습니다.
- services
- 리액트 애플리케이션을 도커환경에서 실행하기 위해
- before_install
- 스크립트를 실행할 수 있는 환경을 구성
이어서 테스트를 진행하는 소스코드를 작성합니다.
# 실행할 스크립트 (테스트 실행)
script:
- docker run -e CI=true devconnor/docker-react-app npm run test -- --coverage
# 테스트 성공 후에 할 일
after_success:
- echo "Test Success"
script
- 리액트 애플리케이션을 테스트하는 부분입니다.
- '-e CI=true' 부분이 없으면 에러가 발생합니다.
- '--coverage' 부분은 테스트한 결과를 더욱 상세하게 보기 위해서 추가한 옵션입니다.
after_success
- 테스트가 성공한 이후에 어떠한 일을 할 것인지 지정
travis.yml 전체코드
# 관리자 권한 갖기
sudo: required
# 언어 (플랫폼) 를 선택
language: generic
# 도커 환경 구성
services:
- docker
# 스크립트를 실행할 수 있는 환경 구성
before_install:
- echo "start creating an image with dockerfile"
- docker build -t devconnor/docker-react-app -f Dockerfile.dev .
# 실행할 스크립트 (테스트 실행)
script:
- docker run -e CI=true devconnor/docker-react-app npm run test -- --coverage
# 테스트 성공 후에 할 일
after_success:
- echo "Test Success"
travis.yml 파일을 모두 작성했따면 리액트 애플리케이션 소스코드를 깃허브에 다시 배포해서 Travis CI 가 잘 처리하는지 확인해보겠습니다.
7.3 AWS 알아보기
7.4 일래스틱 빈스톡 환경과 애플리케이션 만들기
7.5 애플리케이션을 배포하기 위한 Travis CI 설정 파일 작성하기
7.6 Travis CI 에서 AWS 에 접근하기 위한 API 생성
08. 다중 컨테이너를 활용한 애플리케이션의 개발 환경 구축
8.1 다중 컨테이너를 활용해 만들 애플리케이션의 구조
8.2 Node.js 로 애플리케이션의 백엔드 서버 구현하기
8.3 React.js 로 애플리케이션의 프런트엔드 구현하기
8.4 리액트 애플리케이션을 위한 도커 파일 만들기
8.5 노드 애플리케이션을 위한 도커 파일 만들기
8.6 개발 환경과 운영 환경의 데이터페이스 구성
8.7 MySQL 을 위한 도커 파일 만들기
8.8 엔진엑스를 위한 설정 파일과 도커 파일 만들기
8.9 개발환경을 위한 도커 컴포즈 파일 작성하기
8.10 볼륨을 이용한 데이터베이스의 데이터 유지하기
09. 다중 컨테이너를 활용한 애플리케이션의 배포
9.1 테스트 및 배포 순서 살펴보기
9.2 도커 환경의 MYSQL 정리하기
9.3 깃허브에 소스 코드 올리기
9.4 Travis CI 에서 테스트하기
9.5 다중 컨테이너 애플리케이션을 위한 일래스틱 빈스톡 환경 생성
9.6 VPC 와 보안 그룹 설정하기
9.7 AWS RDS 를 이용한 데이터베이스 생성하기
9.8 데이터베이스 정보를 도커 컴포즈 파일에 명시해주기
9.9 보안 그룹의 생성과 적용
9.10 애플리케이션을 배포하기 위한 Travis CI 설정 파일 작성하기
9.11 Travis CI 에서 AWS 에 접근하기 위한 API 키 생성
9.12 애플리케이션이 잘 실행되는지 테스트하기
'Backend > 노트' 카테고리의 다른 글
고랭 애플로그인 구현 (golang apple login) (0) | 2023.05.06 |
---|---|
도커 환경에서 디버그하기 (0) | 2023.04.30 |
쉽고 빠른 Go 시작하기 (0) | 2023.02.27 |
Gin (0) | 2022.10.15 |
Hands-On Full stack development with Go (1) | 2022.10.07 |