오늘은 서버나 웹 개발에 있어 필수적인 도구 중 하나인 Docker의 사용법을 살펴보겠습니다.
Docker는 개발 커뮤니티에서 이미 광범위하게 사용되며, 많은 분들이 이미 알고 계실 것입니다.
Docker의 활용 방법은 사용하는 프레임워크나 환경에 따라 조금씩 차이가 있는데요.
본 글에서는 특히 Node.js 환경에서 Docker를 어떻게 효과적으로 사용할 수 있는지에 대해 살펴보도록 하겠습니다.
1. Docker
1-1. Docker의 의미
Docker는 소프트웨어 개발 세계에서 컨테이너를 사용하는 개념을 도입함으로써,
화물 운송의 컨테이너와 비슷한 역할을 한다는 의미를 내포하고 있습니다.
이러한 Docker의 컨테이너는 각각 독립된 가상 프로세스로,
호스트 시스템에서 격리된 환경에서 실행됩니다.
이들은 자체 파일 시스템과 네트워크를 보유하며,
서로 격리되어 있어 각각 독립된 프로세스처럼 작동합니다.
이 컨테이너의 개념을 실제 화물 운송에 사용되는 컨테이너에 비유해보겠습니다.
컨테이너안에 화물을 넣으면,
배에 실었다가, 기차에 실었다가, 화물 트럭으로 실어서 운송할 수 있습니다.
컨테이너 안에서는 독립적으로 원하는 화물이 들어가 있는데,
어느 운송수단에 넣어도 화물운송이라는 목표를 달성할 수 있습니다.
마찬가지로, Docker 컨테이너는 개발 환경이나 의존성에 구애받지 않고,
어느 환경에서든 동일하게 실행될 수 있는 독립적인 소프트웨어 환경을 제공해 줍니다.
이러한 특성 덕분에 Docker는 소프트웨어 개발과 배포 과정을 효율적이고 일관되게 만들어 주었습니다.
1-2. Docker 와 배포환경의 변화
A. 기존배포환경
예전에는 서버에 애플리케이션을 배포할 때, 다양한 환경 설정을 수동으로 해야 했습니다.
예를 들어, 로컬에서 Node.js로 개발한 앱을 AWS 서버에 배포한다면,
Node.js 설치, NPM 모듈 설정, 환경 설정 등을 모두 다시 해야 했지요.
그래서 A라는 개발자가 세팅하면, B라는 개발자가 다른 서버를 세팅하기가 어려운 측면이 있었습니다.
심지어 A라는 개발자도 시간이 많이 지난 경우에는,
어떻게 했는지 기억을 못하기도 하구요.
B. Docker로 인한 변화
Docker가 등장하면서, 이 모든 과정이 단순화되었습니다.
개발자는 로컬에서 앱을 개발하고 Docker를 사용하여 컨테이너를 빌드합니다.
그런 다음, 이 컨테이너를 그대로 AWS같은 서버에 배포할 수 있습니다.
모든 필요한 설정과 의존성은 컨테이너 안에 포함되어 있으므로, 복잡한 설정 과정이 필요없어졋어요.
예를 들어, 로컬에서 서버개발을 마치고 AWS서버에 올린다고 하겠습니다.
AWS에서는 Docker위에 만들었던 컨테이너를 Deploy 해 주기만 하면 됩니다.
중간에 거쳤던 잡다한 설정은 다 필요없고,
집에서 사용하던 포트와는 다른 AWS의 OUTBOUND포트로 매핑만 시켜주면 끝입니다.
공식 표에 따르면, 서버의 운영(HOST OS) 체제 위에 Docker가 존재하며,
여러 앱의 컨테이너가 Docker 위에 구축됩니다.
이렇게 해주니, 하나의 레이어가 생겨서 컴퓨터의 리소스를 약간은 필요로 하겠지만,
다양한 환경에서 일관된 실행을 보장합니다.
개발했던 환경을 저장해, 다른 서버에 배포해 확장을 빠르게 할 수도 있구요.
다양한 개발환경의 백업용도로 쓸수도 있습니다.
한가지 생각해 볼 점은, 저사양 서버에서의 사용인데요.
Docker 컨테이너는 호스트 시스템의 리소스를 공유하며 동작하므로,
애플리케이션 및 컨테이너의 크기와 개수에 따라 성능이 영향을 받을 수 있기는 합니다.
2. Docker 서비스의 구성요소
Docker는 크게 세 가지 주요 구성 요소로 구분됩니다.
Docker 서버(데몬), 클라이언트, 그리고 Docker Hub인데요.
각 구성 요소에 대해 자세히 알아보겠습니다.
2-1. Docker 서버와 클라이언트
Docker의 핵심은 Docker 서버(또는 Daemon)와 클라이언트로 구성되어 있습니다.
Docker 데몬은 컨테이너 실행 시 필요한 네트워킹 설정과 프로세스 관리를 담당합니다.
반면에 클라이언트는 사용자의 명령을 Docker 데몬에 전달하는 인터페이스 역할을 합니다.
2-2. Docker Hub
Docker Hub는 GitHub와 유사한 서비스로,
Docker 컨테이너 이미지를 저장하고 공유할 수 있는 플랫폼입니다.
사용자는 이곳에서 필요한 컨테이너 이미지를 다운로드하거나 자신이 만든 이미지를 업로드할 수 있습니다.
여기서 Docker의 사용방법을 생각해 볼 수 있는데요.
자신이 직접 컨테이너 이미지를 구성하여 사용할 수도 있고,
다른 사람들이 만들어 놓은 이미지를 수정하여 사용하는 것도 가능합니다.
2-3. 각종 플러그인과 도구
Docker를 보다 효과적으로 사용하기 위한 다양한 플러그인과 도구도 계속 나오고 있습니다.
Docker 사용이 훨신 편리해지고 확장성도 커져가겠지요.
3. Docker의 설치
Docker는 macOS, Windows 뿐만 아니라 Ubuntu, CentOS 등 다양한 Linux 운영 체제 및 AWS, Azure와 같은 대규모 클라우드 서비스에서도 사용할 수 있습니다. 이렇게 다양한 플랫폼 지원한다는 것도 Docker의 장점 중 하나입니다.
이 글에서는 우선 로컬 환경인 macOS와 Windows에서 Docker를 설치하는 방법을 살펴본 후,
Linux 운영 체제에서의 설치 과정에 대해 알아보겠습니다.
3-1. Mac, Windows 에서 설치
Docker는 Mac이나 Windows에 맞는 설치 파일들을 제공해 주고 있습니다.
이를 다운로드 받고 실행해 주기만 하면 됩니다.
설치가 되고 나면, 아래 명령어를 통해서 버전을 확인할 수 있습니다.
docker --version
확인해보니, 20.10.12 버전이군요.
또한, docker info 명령어를 입력하면, 좀 더 자세한 정보를 얻을 수도 있습니다.
docker info
3-2. Linux OS에서 설치
LinuxOS중에서도 Ubuntu버전에 Docker를 설치하는 방법에 대해서 정리해 보겠습니다.
여러가지 설치 패키지를 이용하는 방법이 존재하지만,
공식문서에 나와있는 방법을 이용하려고 합니다.
(https://docs.docker.com/engine/install/ubuntu/)
24년 1월 현재, 지원하는 Ubuntu OS는 64비트 버전인데요.
아래에 나오는 버전 중 하나여야 합니다.
먼저 설치된 Docker라도 update는 필요하므로,
패키지 설치전 update명령어는 습관적으로 하고 시작하겠습니다.
sudo apt-get update
Docker 에서는 아래 명령어로 충돌이 발생할 수 있는 패키지를 제거하고 시작하라고 하는군요.
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
A. Docker Repository Setup 과 Docker Package 설치
먼저 Docker Repository를 setup 해 줍니다.
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
이제 다음 명령어를 이용해 Docker 패키지들을 설치해 줍니다.
예전에는 docker engine 을 따로 설치했었는데, 이제 아래 명령어로 한번에 설치할 수 있습니다.
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
설치가 완료되었다면,
docker --version 으로 설치된 버전을 확인해 봅니다.
정상적으로 설치된 것을 확인할 수 있습니다.
docker engine까지 잘 설치되었다면,
아래 명령어로 'hello-world'를 확인해 볼 수 있습니다.
sudo docker run hello-world
정상적으로 실행되었다면, 아래와 같은 메시지를 볼 수 있습니다.
맥과 같은 GUI를 가진 OS에서는 Docker의 실행 상태를 쉽게 확인하고 조정할 수 있는데요.
AWS와 같은 리눅스 환경에서는,
Docker가 실행 중인지 확인하기 위해 특정 명령어를 사용해야 합니다.
아래의 'systemctl' 명령어는 시스템 서비스 정보를 관리하는 데 사용됩니다
sudo systemctl status docker
실행하면 아래와 같이, active 상태가 나옵니다.
3-3. 리눅스에서 docker 그룹에 등록하기
리눅스 시스템에서 Docker를 사용할 때 root 권한이 필요합니다.
Docker가 Unix 소켓에 바인드되기 때문인데요.
일반 사용자가 Docker 명령어를 사용하려면 다음 두 가지 방법 중 하나를 사용할 수 있습니다:
docker는 Unix Socket에 바인드되어 있기 때문에, 실행시킬 때 root권한이 필요합니다.
따라서 리눅스 일반유저가 docker 명령어를 사용할 때는 다음의 2가지 중 하나를 하는 것이 좋습니다.
첫번째로는, 유저를 sudo그룹에 등록해 놓고, 매번 sudo키워드를 사용해 주는 방법이 있구요.
이것을 피하기 위해서 docker 그룹에 사용자를 등록해 놓고 사용하는 방법이 있습니다.
sudo groupadd docker
다음으로, 아래 명령어를 이용해, 현재 계정을 docker그룹에 포함시킨 후에,
서버를 재시작 해 주면 됩니다.
sudo usermod -aG docker $USER
재시작후에 "docker info" 명령어가 정상적으로 동작한다면 된 것 입니다.
아래에서는 본격적으로 이미지와 컨테이너를 만들어 빌드하는 방법에 대해서 정리해 보겠습니다.
4. 이미지 및 컨테이너의 생성과 실행
Docker 환경이 구성되었으니 무엇을 해야할까요?
위에서 보았던 그림을 다시 보겠습니다.
아래와 같이 Docker위에는 컨테이너들이 올라가야 하는데요.
컨테이너를 만들기 위해 필요한 것이 'Docker 이미지' 이구요.
이 이미지를 생성하려면 Dockerfile이 필요합니다.
개발하는 입장에서 과정을 정리해 보면, 다음과 같습니다.
- Dockerfile 생성과 작성: Docker이미지를 어떻게 만들것인지를 설정
- 이미지 생성: Build 명령어를 이용해 Dockerfile을 기반으로 생성
- 컨테이너 생성: 만들어진 이미지를 가지고 Container를 생성해 실행
- DockerHub에 업로드: Container를 업로드
- DockerHub에서 받아서 실행: 배포하고자 하는 곳에서 DockerHub에서 받아서 실행
그럼 기반이 되는 Dockerfile을 만드는 방법부터 보도록 하겠습니다.
5. Dockerfile 파일 만들기
Docker 프로젝트를 시작하기 위해,
로컬 컴퓨터에서 새 프로젝트 디렉토리를 만들고 이동합니다.
예를 들어, 'mkdir testDocker' 명령어로 디렉토리를 생성한 후,
'cd testDocker' 명령어로 해당 디렉토리로 이동합니다.
이동한 프로젝트 디렉토리에서 확장자 없는 Dockerfile을 생성합니다.
로컬 컴퓨터에서는 텍스트 에디터를 사용하여 파일을 만들 수 는데요.
터미널 환경에서는 'touch Dockerfile' 명령어를 사용하여 파일을 생성할 수 있으며,
아래와 같이 명령어로 파일을 생성해 줍니다.
sudo vi Dockerfile
파일을 생성할 때 주의해야 할 사항은,
'Dockerfile'에서 'D'가 대문자여야 한다는 것입니다.
이제 Dockerfile을 만들고 그 안에 작성할 내용들에 대해 차례대로 살펴보겠습니다.
A. nodeJS 이미지 가져오기
From 키워드는 기본 이미지로 DockerHub에 나와있는 node의 slim버전 이미지를으로 사용하겠다고 하는 것 입니다.
이미지에는 이미 Node.js와 NPM이 설치되어 있으므로, NPM을 따로 설치할 필요는 없습니다.
FROM node:current-slim
NodeJS의 현재 홈페이지로 가보면, 14버전이 LTS의 가장 최신버전임을 알 수 있습니다.
아래와 같이 해주면 LTS버전의 12버전을 DockerHub에 있는 버전의 이미지로 빌드한다는 것 입니다.
아래링크에서 지원하는 최신버전의 NodeJS를 확인할 수 있는데요.
>> https://hub.docker.com/_/node
다행히 14.16.1 이 있는데, 이 버전을 사용하려고 하면 아래와 같이 기술해 주면 됩니다.
FROM node:14.16.1
이로인해서 Docker위에 NodeJS가 올라가도록 할 수 있었습니다.
Docker-hub에 등록된 nodejs의 공식이미지 최신버전을 보면 다음과 같은데요.
21-alpine3.18, 21.7-alpine3.18, 21.7.1-alpine3.18, alpine3.18, current-alpine3.18
21-alpine, 21-alpine3.19, 21.7-alpine, 21.7-alpine3.19, 21.7.1-alpine, 21.7.1-alpine3.19, alpine, alpine3.19, current-alpine, current-alpine3.19
21, 21-bookworm, 21.7, 21.7-bookworm, 21.7.1, 21.7.1-bookworm, bookworm, current, current-bookworm, latest
21-bookworm-slim, 21-slim, 21.7-bookworm-slim, 21.7-slim, 21.7.1-bookworm-slim, 21.7.1-slim, bookworm-slim, current-bookworm-slim, current-slim, slim
21-bullseye, 21.7-bullseye, 21.7.1-bullseye, bullseye, current-bullseye
21-bullseye-slim, 21.7-bullseye-slim, 21.7.1-bullseye-slim, bullseye-slim, current-bullseye-slim
20-alpine3.18, 20.12-alpine3.18, 20.12.0-alpine3.18, iron-alpine3.18, lts-alpine3.18
20-alpine, 20-alpine3.19, 20.12-alpine, 20.12-alpine3.19, 20.12.0-alpine, 20.12.0-alpine3.19, iron-alpine, iron-alpine3.19, lts-alpine, lts-alpine3.19
20, 20-bookworm, 20.12, 20.12-bookworm, 20.12.0, 20.12.0-bookworm, iron, iron-bookworm, lts, lts-bookworm, lts-iron
alpine 이미지는 경량화된 리눅스 배포판 기반으로, 작은 이미지 크기를 원할 경우에 적합합니다.
하지만, Alpine Linux를 기반으로 하므로, apt-get 같은 명령어를 사용할 수는 없습니다.
반면, bullseye나 bookworm 은 Debian 배포판을 기반으로 합니다.
이는 보다 전통적인 리눅스 환경을 선호하는 경우에 적합합니다.
slim 이미지는 기본 이미지에서 일부 파일을 제거하여 크기를 줄인 것으로, 최소한의 기능만을 필요로 할 경우 유용할 수 있습니다.
이런 값들이 너무 복잡하다면, 그냥 숫자버전만을 이용해도 됩니다.
현재 lts버전은 '20.12'버전이므로 위에서는 '20.12'를 사용하면 되겠지요.
B. 작업 디렉토리 명시
WORKDIR이라는 키워드로 작업 디렉토리를 설정할 수 있습니다.
작업디렉토리를 실행한다는 의미는 무엇일까요?
docker가 명령어를 실행할 디렉토리의 위치를 지정한다는 것을 의미하는데요.
RUN, CMD, ENTRYPOINT, COPY and ADD등의 명령어들이,
여기서 지정하는 디렉토리에서 실행된다는 것 입니다.
만약 해당 디렉토리가 존재하지 않을 경우, 해당 디렉토리를 생성해 줍니다.
아래에서는 /usr/src/app 이라는 리눅스에서 많이 쓰는 앱소스 디렉토리를 사용하였습니다.
FROM node:20.12
WORKDIR /usr/src/app
COPY . .
RUN npm install
CMD ["npm", "start"]
위에서 'COPY . .' 명령어를 넣어 주었는데요.
이것은 현재 작업 중인 폴더의 모든 파일과 폴더를 Docker 이미지 안으로 복사하는 명령어입니다.
첫 번째 "."은 현재 작업 중인 폴더를 나타내고, 두 번째 "."는 Docker 이미지 내부의 작업 폴더를 가리킵니다.
참고로, Docker 이미지 안으로 파일을 복사하는 이유는,
애플리케이션을 Docker 컨테이너로 패키징할 때,
필요한 파일들을 포함하기 위함입니다.
Docker 컨테이너는 독립적인 실행 환경을 갖고 있어야 하기 때문이지요.
C. package.json 복사 후 모듈 설치
아래 명령어는 Docker를 호스팅하고 있는 로컬 컴퓨터의 package*.json 파일을,
Docker 이미지 내부의 작업 디렉토리로 복사하는 것 입니다.
COPY package*.json .
이렇게 package.json 파일이 복사되었다면,
RUN 키워드로 npm install 명령어를 실행시켜서,
package.json 에 기술된 모듈들을 복사해 주면 됩니다.
FROM node:20.12
WORKDIR /usr/src/app
# package.json 및 package-lock.json 복사
COPY package*.json ./
RUN npm install
COPY . .
# PM2 설치
RUN npm install pm2 -g
# PM2를 사용하여 애플리케이션 실행
CMD ["pm2-runtime", "app.js"]
참고로 pm2 같은 서비스 매니저들 같은 경우,
Docker 이미지 내부에서 글로벌하게 설치될 필요가 있는데요.
D. ENV 환경설정
환경설정 값들을 여기서 지정할 수 있는데요.
NODE_ENV값이 production을 가르키도록 지정해 줍니다.
다른 값으로는 development가 있는데요.
한가지 주의할 점은, NODE_ENV를 production으로 하면,
package.json에 있는 devDependencies가 설치가 되지 않아서,
MODULE_NOT_FOUND같은 에러가 발생할 수 있습니다.
ENV NODE_ENV production
컨테이너 실행 중 인터랙션이 생기면,
거기에 입력할 수 없기 때문에 아에 그것을 방지해 주어야 하는데요.
아래와 같이 환경값을 넣어줍니다.
ENV DEVIAN_FRONTEND=noninteractive
추가적으로 ENV로 변수값을 만들어서 RUN에서 사용할 수도 있습니다.
지정한 변수값은 {변수}와 같은 형태로 불러올 수 있구요.
F. image파일시스템안에서 실행될 command
아래 명령어로 실행될 명령어를 미리 넣어주고요.
명령어가 여러개 일 경우도 다음 줄에서, RUN으로 시작후 명령어를 넣어주기만 하면 됩니다.
RUN npm install
G. 접속할 포트 번호
앱이 8080포트에 바인딩 되어 있다면, EXPOSE 키워드를 사용해서 docker 데몬에
아래와 같이 해당포트로 매핑해 줍니다.
컨테이너에 접속할 때, 8080포트를 사용한다는 말이지요.
EXPOSE 8080
H. 설치 종료후 실행할 명령어
컨테이너가 위에서 명시된 설치등을 종료후,
런타임을 정의하는 CMD로 앱을 실행하는 명령어를 정의해 주는데요.
nodejs 서버가 실행되도록 npm start를 다음과 같이 입력해 주면 됩니다.
CMD ["npm", "start"]
참고로 npm
참고로, npm start명령어는 package.json파일의 scripts에 있는 start 명령어를 실행해 주는데요.
만약 start 명령어를 따로 설정하지 않았다면 node server.js가 실행되도록 합니다.
npm은 start 명령어에 대해서 'npm run start'와 동일하게 실행해 주는데요.
package.json에 사용자 정의한 명령어라면 중간에 'run'을 넣어주어야 합니다.
예를 들면 아래와 같습니다.
- CMD ["npm", "run", "dev"]
I. 앱의 소스코드 복사
아래 코드로 Docker이미지 안에 앱의 소스코드가 복사되도록 합니다.
COPY . .
J. dockerignore파일
".dockerignore"파일을 이용하면,
Docker 이미지에 로컬 모듈과 디버깅 로그가 복사되는 것을 막아줄 수 있습니다.
node_modules
npm-debug.log
.DS_Store
.git
.gitignore
K. VOLUME
Docker는 가상화 기술을 이용합니다.
각 컨테이너는 독립적인 파일 시스템을 가지고 있고,
컨테이너가 종료되면, 그 상태가 보존되지 않습니다.
컨테이너가 다시 실행될 때마다, 모든 단계를 다시 실행해야 한다는 뜻 입니다.
만약, SQLite같이 파일로 데이터베이스가 저장되는 경우,
Docker 이미지 내부에서 데이터베이스를 저장해 사용하고 있다면,
컨테이너 종료시 데이터베이스도 날아간다는 뜻이 됩니다.
이 때 필요한 것이 VOLUME 키워드 입니다.
SQLite의 DB파일을 컨테이너 내부의 디렉토리에 저장하는 대신,
호스트 컴퓨터의 파일 시스템에 저장하고 싶을 때가 있습니다.
이것을 가능하게 하는 것이 바로 "볼륨 마운트"입니다.
아래코드에서 SQLite3 데이터베이스 파일을 저장할 디렉토리를 마운트 해 주었습니다.
FROM node:14
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# 호스트 시스템의 사용자 홈 디렉토리의 ~/data 폴더를 컨테이너의 /usr/src/app/db 폴더에 마운트
VOLUME ~/data /app/db
CMD ["node", "app.js"]
마운트 된다는 의미를 이해할 필요가 있는데요.
마운트는 Docker이미지를 호스팅 한 컴퓨터의 폴더가,
Dockerfile에 기술된 컨테이너 내의 특정 경로와 연결되어 사용된다는 것을 의미합니다.
즉, 컨테이너 내에서 해당 경로에 접근하면 실제로는 호스트 컴퓨터의 해당 폴더에 접근하는 것이지요.
원래 Docker의 컨테이너는 독립적이므로,
해당 폴더도 독립적이어야 하나,
db파일처럼 상태가 보전되야 하는 경우가 있으니,
외부의 Docker를 호스팅하는 컴퓨터의 폴더를 대신 연결해서 사용하는 것 입니다.
참고로, 위에서 '~/data'라고 docker를 호스팅하는 로컬 컴퓨터에서 데이터를 저장할 폴더를 지정하였는데요.
이렇게 하면 사용자가 root인 경우,
데이터가 호스트 시스템의 루트 디렉토리인 /data에 저장되구요.
사용자이름이 user1과 같은 경우,
/home/user1/data와 같이,
해당 사용자의 홈 디렉토리 내의 data 디렉토리에 저장될 것입니다.
L. ADD
호스트 컴퓨터의 Dockerfile이 있는 디렉토리를 기준으로 해서,
특정한 파일이나 폴더를 컨테이너의 특정 폴더에 추가할 수 있습니다.
아래 예제에서는, hello.txt 파일을 '/app'폴더에 추가하여 줍니다.
FROM ubuntu:20.04
ADD hello.txt /app/
아래는 my_folder라는 폴더를 '/app'폴더에 추가하여 줍니다.
ADD ./my_folder /app/
6. Docker Image만들기
Dockerfile이 준비되었으니, Docker Image를 만들어 줄 차례입니다.
아래와 같이 Build명령어를 사용하면 되는데요.
-t 옵션은 tag를 적어주는 것으로, 아래에서는 이름은 test, tag명은 그냥 tag 고 넣었구요.
마지막에는 빌드 위치를 명시해 주는데,
여기서는 '.'을 사용해 현재위치라고 하였습니다.
docker build -t test:tag .
Dockerfile의 파일이름을 다르게 설정했었다면,
build 명령어 실행시 다음 옵션으로 해당 파일이름을 넣어주면 됩니다.
예를 들어, Dockerfile.dev 파일이라면,
'-f'옵션이나 '--file' 옵션을 사용해서 파일명을 넣어주면 됩니다.
docker build -t test:tag -f Dockerfile.dev .
다 만들어진 Image에 대해서는 아래 명령어로 확인해 볼 수 있는데요.
docker images
실행해 보면 아래와 같이 image가 만들어 진 것을 확인할 수 있습니다.
이렇게 생성한 이미지를 지우고 싶을 때는 아래 명령어를 이용해 주면 됩니다.
docker rmi -f <이미지 ID>
아무래도 이미지 용량이 꽤 큰편이기 때문에,
필요하지 않는 이미지는 바로바로 지워주는 것이 좋습니다.
7. 컨테이너 생성 및 실행 명령어
드디어 컨테이너와 만날 차례입니다.
이미지 파일에 run 명령어를 이용해서 컨테이너를 생성하고 실행할 수 있습니다.
docker run <image 이름>
"-p" 또는 "--publish"옵션을 사용하면, 아웃바운드와 인바운드 포트를 매핑할 수 있는데요.
이 말은, 도커를 띄운 로컬컴퓨터(호스트)와 도커컨테이너 내부의 포트를 연결시켜준다는 것을 뜻합니다.
예를 들어서 아래와 같이 한다면,
로컬 컴퓨터(호스트 컴퓨터)의 바깥에서 80번 포트로 들어온 연결을,
도커 컨테이너 내부의 3000번 포트로 매핑해준다는 것 입니다.
만약, 도커 내부의 NodeJS 컨테이너로 3000번포트를 열었두었다면,
브라우저에서 접속할 경우, 기본적으로 80번포트로 연결되므로,
아래와 같이 해주면 되겠지요.
docker run -p 80:3000 <이미지이름:tag이름>
만약 AWS에서 설정한다고 가정해 보겠습니다.
아래와 같이 로컬로 들어오는 아웃바운드 포트는 80번,
도커의 컨테이너 내부로 들어오는 인바운드 포트는 8080이라고 하겠습니다.
Dockerfile에서 설정한 port와 AWS에서의 열어놓은 port를 연결해 주어야 하겠지요.
AWS의 보안규칙중 인바운드 규칙에서, HTTP의 8080포트를 열어주고요.
AWS에서의 outbound port는 80이라고 해주면 됩니다.
왼쪽이 클라이언트가 접속하는 AWS의 Outbound포트구요.
오른쪽이 도커 컨테이너의 포트입니다.
docker run -p 80:8080 <이미지 이름>
아래와 같이 tag를 붙여 작성해 보았습니다.
특정한 tag를 작성할 경우는 "이름:tag"로 실행시켜 주어야 합니다.
docker run -p 80:8080 test:tag
컨테이너가 올라가고 서버가 정상적으로 동작하는 것을 볼 수 있습니다.
-d로 이미지를 실행하면 분리 모드로 컨테이너를 실행해서 백그라운드에서 컨테이너가 돌아가도록 할 수 있습니다.
docker run -p 80:8080 -d <이미지 이름>
8. 컨테이너 및 이미지 기타 명령어들
8-1. 컨테이너의 Stop과 Start
docker run명령어를 통해서 컨테이너를 생성하고 실행할 수 있다는 것을 알았습니다.
해당 컨테이너 run명령어로 실행된 상태에서 exit를 입력하거나 ctrl+C를 해주면,
컨테이너도 정지가 되어집니다.
이렇게 정지된 컨테이너는 다음 명령어로 시작할 수 있습니다.
docker start <컨테이너 이름 또는 ID>
start를 restart로 대체하면 컨테이너를 재시작할 수도 있습니다.
이렇게 시작된 컨테이너는 run으로 시작할 때처럼 shell이 별도로 보이지 않습니다.
이럴 경우 필요하다면 아래 명령어를 사용해 주면 됩니다.
docker attach <컨테이너 이름 또는 ID>
컨테이너를 정지하는 것은 stop명령어를 이용해 주면 됩니다.
docker stop <컨테이너 이름 또는 ID>
8-2. 컨테이너의 삭제
현재 Docker머신에 존재하는 모든 컨테이너를 리스트 할 수 있는 명령어는 다음과 같습니다.
docker ps -a
-a를 빼면, 현재 동작중인 docker와 매핑된 포트만 볼 수 있습니다.
docker ps -a의 리스트에서 컨테이너 ID를 확인하고,
rm명령어로 컨테이너를 삭제해 줄 수 있습니다.
대신 컨테이너가 삭제가 되더라도 이미지 파일은 남아있게 되는데요.
이것은 images명령어로 확인할 수 있습니다.
docker rm <컨테이너 ID>
현재 동작하는 모든 컨테이너를 삭제하고자 한다면 아래 명령어를 사용해 주면 되는데요.
주의 할 것은 docker앞에 있는 기호는 백틱(`)기호라는 것 입니다.
-f는 강제적으로 컨테이너를 삭제해 주는 옵션입니다.
docker rm -f `docker ps -a -q`
8-3. -it 옵션
아래 명령어를 사용하면, nodejs컨테이너에 가상 터미널로 접속할 수 있습니다.
nodejs runtime이 없는 컴퓨터에서도,
이 명령어 하나면, 접속해서 nodejs컨테이너에서 실행시킬 수 있습니다.
docker run -it nodejs:stable
"-i"는 interactive라는 뜻으로, 표준 입력(stdin)을 유지해서 사용자가 입력하면 컨테이너의 표준입력으로 전달됩니다.
"-t"는 가상 터미널(tty)을 할당해 주는 옵션입니다.
만약 터미널로 접속된 상태에서 무언가를 실행하고자 한다면,
아래와 같이 해주면 됩니다.
docker run -it nodejs:stable bash
8-4. volume 마운트
위에서 Dockerfile에 관해 정리할 때, Volume에 대해 알아보았는데요.
명령어 실행시에도 volume옵션을 넣을 수 있습니다.
'-v' 또는 '--volume'을 넣어주면 되는데요.
아래에서는 '--volume'옵션을 통해서,
호스트 컴퓨터의 /var/www/html 디렉토리를,
컨테이너 내부의 /usr/share/nginx/html 디렉토리와 연결하였습니다.
docker run -p 80:80 --volume /var/www/html:/usr/share/nginx/html nginx
아래에서는 nodejs를 터미널에서 바로 실행하는데,
현재 폴더를 컨테이너 내부의 '/usr/src/app'폴더에 동기화되어서, 사용할 수 있도록 하였습니다.
$(pwd)는 현재 작업 디렉토리의 경로를 반환하는 쉘 명령어 입니다.
docker run -it -v $(pwd):/usr/src/app node:latest
8-5. 환경변수 전달
환경변수도 전달할 수 있는데요.
'--env'라는 옵션을 이용하면 이것이 가능해집니다.
아래에서는 MY_NAME이라는 환경변수를 정의해 전달하였습니다.
docker run -it nodejs:stable --env MY_NAME=la
8-6. 다른 컨테이너에 링크
docker의 컨테이너들을 서로 연결시킬 수도 있는데요.
먼저 nginx의 컨테이너를 아래와 같이 실행시킵니다.
참고로 '--name'은 컨테이너의 이름을 설정할 때 사용하는데요.
아래에서 이 컨테이너에 링크를 할 때, 참조할 이름으로 사용합니다.
docker run -d --name nginx nginx:latest
아래와 같이, '--link' 옵션을 이용하면,
nginx-container를 nodejs컨테이너와,
네트워크 연결을 할 수 있게 됩니다.
즉, nginx와 nodejs 컨테이너 간의 통신이 가능해 지는 것 이지요.
docker run -d --name nodejs --link nginx-container:nginx nodejs:latest
하지만, 이렇게 여러개의 컨테이너를 연결할 때는,
docker 컨테이너들을 하나씩 띄워서 하기보다는,
docker-compose를 사용하는 것이 효율적입니다.
8-7. docker 종료 시 컨테이너 삭제
컨테이너가 종료되고 아무 조치를 취하지 않으면, 컨테이너는 계속 남게 되는데요.
'--rm' 옵션은 Docker 컨테이너가 종료될 때,
해당 컨테이너를 자동으로 삭제하는 옵션입니다.
docker run --rm -d node:21-slim
8-8. 이미지 삭제
컨테이너 삭제 이후에 필요하지 않은 이미지들은,
위에서 했던 것 처럼, 아래 명령어로 삭제해 주면 됩니다.
docker rmi -f `docker images`
이미지는 rmi명령어 이고, 컨테이너를 삭제할 때는 rm이라는 점에 주의하면 되겠네요.
5. DockerHub에 연동
컨테이너를 생성하고 삭제하는 것 까지 알아보았는데요.
DockerHub에 등록하기 전에 보통은,
Github에 해당내용을 Commit 해서 Push를 해 줍니다.
작업이 끝나면, Github에는 Dockerfile과 NodeJS소스코드들이 들어가 있는 상태가 될 텐데요.
5-1. Why DockerHub
그런데 한가지 궁금한 점이, Github에 올렸는데 왜 또 DockerHub에 올려야 되는 것일까요?
그렇게 하는 중요한 이유 중 하나는 DockerHub에서 Github를 참조해서 소스코드가 변경되면,
새로운 이미지가 빌드되도록 할 수 있기 때문입니다.
이것은 특히나, 용량이 큰 이미지를 빌드해서 배포할 곳에 넘기지 않아도 된다는 면에서,
더욱 좋은 기능이 되겠습니다.
5-2. 무료 or 유료
DockerHub는 무료로 쓸 수 있지만,
private한 Repository는 1개만 쓸 수 있습니다.
더 많은 private Repository를 쓰려면 유료 모델을 이용해야 합니다.
이부분은 개인 개발자들한테는 조금 아쉬운 부분일 수 있겠네요.
5-3. Github or Bitbucket 링크
우측 상단의 프로필의 드롭다운 메뉴에서 AccountSettings를 클릭한 다음,
Linked Accounts를 선택한 다음, 하단의 Github혹은 Bitbucket 우측의 Connect를 클릭해서,
로그인을 해 주면 연동이 됩니다.
연동이 되면 아래와 같은 화면을 볼 수 있습니다.
Authorize docker버튼을 누르면 완료가 됩니다.
5-4. Repository 생성
DockerHub에서 회원가입을 한 후에,
우측 상단 메뉴에서 Repository를 클릭하면 생성할 수 있는 버튼이 아래와 같이 나오는데요.
이제 CreateRepository버튼을 눌러서 다음과 같은 화면에서 Repositroy를 만들어 줍니다.
Repository이름을 적어주고, Private을 선택해 준 다음,
하단의 optional에서 위에서 link한 connected라고 써져있는 Github아이콘을 선택해 줍니다.
Github아이콘을 클릭하면, 아래 이미지와 같은 화면을 볼 수 있는데요.
5-5. Create and Build
드롭다운 메뉴에서,
Dockerfile이 들어있는 Github의 계정과 Repository를 선택한 후에,
하단의 BuildRules +를 선택해서,
이름과 TAG등을 정의해 주고 활성화 된, Create & Build 버튼을 클릭해 줍니다.
얼마후에, Repository의 Builds탭을 보시면 아래와 같이 Build가 성공한 것을 볼 수 있습니다.
여기서, Dockerfile의 내용과 Build logs를 아래와 같이 볼 수 있습니다.
이제, DockerHub를 통해서 바로 이미지를 빌드할 수 있게 되었는데요.
Github계정이 계속 링크되어있기 때문에,
Dockerfile을 변경해서 Github에 커밋해주면 계속 변경사항에 따라서,
새로운 이미지가 빌드 되어 집니다.
소스코드 폴더에 이미지를 가지고 있을 필요가 전혀 없다는 것을 알 수 있네요.
대신 한가지 생각해야 할 것은 빌드속도가 로컬 컴퓨터에서 하는 것 만큼 빠르지는 않다는 점과
DockerHub클라우드 서버 상태에 따라서 문제가 발생할 수도 있다는 점 입니다.
5-6. DockerHub에서 내려받기
DockerHub에서 빌드하는 것 까지 해 보았는데요.
이제 그럼 배포하는 곳에서,
내려받아서 사용하도록 하기만 하면 될 텐데요.
우선 DockerHub에서 우측 상단에 보면 아래 이미지와 같이,
push커맨드를 사용할 때 어떻게 해야하는지 나와있는데요.
이 Repository주소와 tag이름등을 이용해서,
pull받을 수 있습니다.
이제 AWS같이 배포하려는 곳에서,
위에서 나온 repository주소와 tag이름을 가지고 아래와 같이 해 주면 pull을 받을 수 있겠지요.
docker login
docker pull <Repository주소:tagname>
실행해 보면 파일이 다운로드 되구요.
"docker images" 명령어를 실행해보면, 저희가 Build하지도 않았는데,
DockerHub의 클라우드에서 빌드된 이미지가 생성되어 있는 것을 알 수 있습니다.
이제 저희는 위에서 정리한 3-3. 컨테이너 생성 및 실행을 참조하여서,
run 명령어로 실행만 해주면 바로 서버가 실행될 수 있겠지요.
한가지 주의할 것은 run명령어로 실행할 이미지 이름은 docker images명령어에서 확인할 수 있는 것처럼,
dockerhub의 Repositroy주소: tagname으로 되어 있습니다.
불편하시면, docker images명령어로 확인한 후 복사해서 사용하면 되겠지요.
5-7. repository의 삭제
해당 repository를 삭제하기 위해서는 아래와 같이,
settings의 가장 하단에서 Delete repository를 해주기만 하면 됩니다.
6. Linux에서 Docker 실행시의 팁
Linux에서 Docker를 항상 실행되도록 하기 위해서는,
부팅시에도 실행되도록 하면 될 텐데요.
ubuntu 14.1 이상버전에서는 systemd를 이용해서 부팅시 실행할 프로그램들을 설정할 수 있습니다.
6-1. 부팅시 Docker 실행
Docker가 서버 부팅시에 실행될 수 있도록 하기 위해서 다음 명령어를 사용하면 됩니다.
sudo systemctl enable docker
반대로 이를 해제하려면 dialble명령어를 아래와 같이 실행해 줍니다.
sudo systemctl disable docker
6-2. Docker내부 Shell에 접속
docker의 컨테이너 내부 쉘에 접속하는 방법에 대해서도 알아보겠습니다.
먼저 빌드된 docker container의 id 를 알아내기 위해서, 아래 명령어를 사용해 줍니다.
docker ps -a
그리고 나서, docker exec 명령어를 -it 을 붙여주고 나서, 위에서 찾은 id를 넣어주고,
마지막 인자로 shell 을 실행시키도록 bash를 넣어줍니다.
docker exec -it <컨테이너 id> /bin/bash
그럼 해당 컨테이너의 root계정으로 쉡에 접속할 수 있습니다.
나올때는 exit를 입력해 주면 됩니다.
7. 정리
Docker와 DockerHub를 사용하는 방법에 대해서 정리해 보았는데요.
여러개의 Docker Container를 실행해도록 도와주는 툴인 Docker Compose에 대한 글은 아래 글을 참조해 주시구요.
>> Docker Compose로 NodeJS, Nginx 를 한번에 설치하기 # YAML
NodeJS에 관한 Best-Practicse에 관해서 정리한 사이트가 있는데,
여기도 참고하면 좋을 것 같습니다.
https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md
또한, DockerFile에 대한 Docker의 공식문서 Reference는
아래링크에서 볼 수 있습니다.
https://docs.docker.com/engine/reference/builder/
ubuntu에 Docker설치하는 방법은 버전이 업데이트되면서 변경될 수 있으므로,
아래 링크를 참조해 주세요.
docs.docker.com/engine/install/ubuntu/
Docekr와 관련된 더 좋은 내용들은 이 글에서 업데이트 하도록 하겠습니다.
'Docker' 카테고리의 다른 글
Docker Compose로 NodeJS, Nginx 설치 및 실행하기 # YAML (13) | 2024.09.13 |
---|---|
Docker 컨테이너 내부의 파일 복사하기: cp 명령어 사용법 (0) | 2024.04.12 |
댓글