본문 바로가기
{시리즈}/Docker

4. 실습 - Docker Hub에서 node 특정 버전을 찾아서 내려 받기 실행하기

by jay2022 2023. 12. 4.

실습의 목적

Docker 연습을 위한 실습에는 아무 이미지나 테스트해도 상관없다.
하지만 프로젝트 환경이나 운영환경에서는 목적에 맞는 '이미지'를 찾는게 중요하다.

'이미지'마다 정의되어 있는 환경은 다르며, 모든 이미지에 대해 설명할 수 없다.
그래서 실습을 통해 이미지를 찾는 과정에 대해 설명하려 한다.

실습 환경

공식 레지스트리: Docker Hub
공식 실습 환경: Docker에서 제공하는 playground

1. 이미지 검색

1.1. docker search node 명령 이미지를 검색

ex_docker_cli_search-img

  • docker search node 명령으로도 이미지를 검색은 가능하지만 깊이 있는 검색은 지원하지 않는다.
  • 어떤 이미지가 공식인지 스타를 많이 받았는지 등의 정보만 제공된다.

1.2. Docker Hub에서 node 이미지 검색

ex_docker-hub_1

  • 일반적인 환경이라면 Official 라벨이 붙은 이미지를 우선하여 본다.
  • 그 다음에 Start 개수를 우선하여 선택한다.
  • 이제 찾은 이미지를 바로 사용하는 것이 아닌 이미지 상세의 정보를 토대로 원하는 환경을 찾아야 한다.

2. Node 18.19.0 버전 찾기

ex_docker-hub_2

  • 어떤 버전의 이미지를 지원하는지 대략적인 정보가 있다.
  • 아쉽게도 모든 버전에 대한 이미지를 지원하지 않는다.
    • (23년 4분기)현재는 node의 장기 지원 버전인 18버전 이후 정보만 나온다.
    • 더 과거의 버전은 공식 저장소를 통해 찾을 수 있다.
  • 이미지에는 버전만 정의된 것이 아닌 뒤에 alpine, sime 등등 다양한 정보가 붙는다.

2.2. 'How to use this image' 섹션을 보고 이미지명 의미를 알아낸다.

ex_docker-hub_3

  • node 이미지의 경우 크게 3가지로 나뉜다는 것을 알 수 있다.
    • node:<version> : 가장 무거운 이미지로 범용적인 환경을 지원한다.
    • node:<version>-alpine : 가장 최소한의 실행환경만 포함한다.
    • node:<version>-slim: alpine 보다는 더 넓은 범위의 실행 환경을 포함한다.
  • <version>은 'Supported tags and respective Dockerfile links' 섹션에 확인하여 찾는다.

node:<version>

가장 무거운 이미지로 가장 범용적일 수 있도록 많은 패키지와 환경을 포함한다.
해당 리눅스 환경은 ubuntu의 기반이 되는  Debian 버전을 사용한다.

  • node 18.19.0 버전 설치
$ docker pull node:18.19.0
  • 가장 최신의 안정적인 버전 설치
$ docker pull node:latest

node:<version>-alpine

node 이미지 이외에도 alpine 라벨은 Alpine Linux 프로젝트를 기반으로 하는 이미지다.
Alpine Linux는 매우 제한적인 환경에서 실행가능한 리눅스 환경을 지원한다.
때문에 alpine 라벨이 붙는 이미지는 매우 최소한의 실행환경만 포함한다.

  • node 18.19.0 버전 설치
$ docker pull node:18.19.0-alpine

node:<version>-slim

기본 태그(node:<version>)에 포함된 일반 패키지가 포함되어 있지 않은 경량화된 이미지이다.
정의되어 있는 설명을 보면 배포 환경이나 제한적인 환경에 적합하며, 이미지 내부에서 node로 작업을 하는 경우에는 적합하지 않다고 한다.

  • node 18.19.0 버전 설치
$ docker pull node:18.19.0-slim

3. 찾은 Node 18.19.0 버전 이미지 테스트 해보기

3.1. 실제 이미지 크기 확인하기

3가지 버전 이미지 설치

  • 이미지 설치 명령어: pull
$ docker pull node:18.19.0

$ docker pull node:18.19.0-alpine

$ docker pull node:18.19.0-slim

설치된 이미지 용량 확인

  • 이미지 확인 명령어: images
$ docker images
  • 결과
    ex_docker_images_1
  • 이미지 크기를 보면 엄청 많이 차이가 나는 것을 볼 수 있다.
  • 이미지 크기는 node:<version> > node:<version>-slim > node:<version>-alpine 이다.

3.2. 이미지 차이를 확인하기 위해 컨테이너 실행하기

node 이미지는 실행될 소스코드가 없는 경우 실행되고 바로 종료된다.

  • 이미지로 컨테이너 생성 실행 명령: run
# docker run --name <컨테이너명> <이미지명:TAG>
$ docker run --name node-18.19.0 node:18.19.0
  • 도커 실행중인 컨테이너 확인: ps
$ docker ps -a
  • 결과
    ex_docker_ps

node 컨테이너 종료 방지 세팅

컨테이너 종료를 방지하기 위해 컨테이너 실행 완료 시 실행될 코드를 정의한다.

const http = require('node:http');

const hostname = '127.0.0.1';const port = 3000;
const server = http.createServer((req, res) => { 
    res.statusCode = 200; 
    res.setHeader('Content-Type', 'text/plain'); 
    res.end('Hello World');
});
server.listen(port, hostname, () => { 
    console.log(`Server running at http://${hostname}:${port}/`);
});
  • 터미널에서 vim을 통해 app.js를 정의 한다.
$ vim app.js # 위의 코드를 붙여 넣고 저장한다.
  • 컨테이너를 실행시 정의한 소스코드가 실행되도록 CLI를 입력한다.
# docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
$ docker run -d \            # -d: 백그라운드로 실행
--name node-18.19.0 \      # --name:<이름>: 컨테이너 이름 정의
-v $(pwd)/app.js:/app.js \ # -v <호스트:컨테이너>: 호스트의 파일을 컨테이너와 동기화 시킨다.
node:18.19.0 \             # 사용할 이미지 선택 
node /app.js               # 컨테이너가 실행된 후 바로 실행할 명령을 입력
  • 동일한 방법으로 나머지 이미지도 실행한다.
# node:18.19.0-alpine
$ docker run -d --name node-18.19.0-alpine \
-v $(pwd)/app.js:/app.js \
node:18.19.0-alpine node /app.js 

# node:18.19.0-slim
$ docker run -d --name node-18.19.0-slim \
-v $(pwd)/app.js:/app.js \
node:18.19.0-slim node /app.js 
  • 실행중인 컨테이너 확인
$ docker ps
  • 결과
    ex_docker_images_2

3.3. 실행 중인 컨테이너에 명령어 입력하기 - 기본

case1) ps 명령어

  • 호스트에서 실행중인 컨테이너로 명령어 실행: exec
# docker exec <컨테이너명> <명령어>
$ docker exec node-18.19.0 ps aux
  • 결과
    ex_docker_exec_1

case2) 내부 쉘에 접근

  • 호스트에서 실행중인 컨테이너의 쉘 실행하고 연결(표준 입출력 연결)
$ docker exec -it node-18.19.0 /bin/bash
  • 결과
    ex_docker_exec_2
  1. CONTAINER ID 가 컨테이너의 host가 된 것을 확인 할 수 있다.
  2. 컨테이너 생성시 전달한 app.js가 있는 것을 볼 수 있다.
  3. 컨테이너 쉘은 ssh로 접속과 동일하게 exit로 나갈 수 있다.

3.4. 실행중인 컨테이너에 명령어 입력하기 - alpine

case1) ps 명령어

  • 호스트에서 실행중인 컨테이너로 명령어 실행: exec
$ docker exec node-18.19.0-alpine ps aux
  • 결과
    ex_docker_exec_1-alpine

case2) 내부 쉘에 접근

  • 호스트에서 실행중인 컨테이너의 쉘 실행하고 연결(표준 입출력 연결)
$ docker exec -it node-18.19.0-alpine /bin/bash
  • 결과(에러): alpine의 경우 /bin/bash가 존재하지 않는다.
    ex_docker_exec_1

OCI runtime exec failed: exec failed: unable to start container process: exec: "/bin/bash": stat /bin/bash: no such file or directory: unknown

  • alpine의 경우 상대적으로 가벼운 /bin/sh 이 설치되어 있다.
$ docker exec -it node-18.19.0-alpine /bin/sh
  • 결과
    ex_docker_exec_4

3.5. 실행중인 컨테이너에 명령어 입력하기 - slim

case1) ps 명령어

  • 호스트에서 실행중인 컨테이너로 명령어 실행: exec
$ docker exec node-18.19.0-slim ps aux
  • 결과(에러): ps가 설치되어 있지 않다.
    ex_docker_exec_1-slim

OCI runtime exec failed: exec failed: unable to start container process: exec: "ps": executable file not found in $PATH: unknown

case2) 내부 쉘에 접근

  • 호스트에서 실행중인 컨테이너의 쉘 실행하고 연결(표준 입출력 연결)
$ docker exec -it node-18.19.0-slim /bin/bash
  • 결과

4. 실습종료 - 자원 제거하기

  • 실행중인 컨테이너 확인
$ docker ps
  • 결과
    ex_docker_images_2

  • 이미지 확인 명령어: images

$ docker images
  • 결과
    ex_docker_images_1

4.1. 컨테이너 종료

  • 컨테이너 종료: stop
# docker stop <컨테이너ID>
$ docker stop 5e8dbc873129 a82e917b25f2 c580ffa7fae9
  • 결과
    ex_docker_stop_1

4.2. 컨테이너 제거

  • 컨테이너 종료: rm
# docker rm <컨테이너ID>
$ docker rm 5e8dbc873129 a82e917b25f2 c580ffa7fae9
  • 결과
    ex_docker_rm_1

  • -f 옵션으로 컨테이너 종료와 제거 동시에 수행

$ docker rm -f 5e8dbc873129 a82e917b25f2 c580ffa7fae9

4.3. 이미지 제거

  • 이미지 제거: rmi
# docker rmi <이미지ID>
$ docker rmi 5c0278530100 44b36f415b2c 3f3cd58c287a
  • 결과
    ex_docker_rmi

5. 결론

여기까지 실습을 통해 환경에 맞는 이미지를 찾고 어떤 점이 다른지 찍먹을 해보았다.
결론은 "상황에 맞는 이미지를 맞춰 적절한 이미지를 선정하자" 이다.

node:<version> 처럼 모든 게 있는 이미지를 쓰면 얼마나 좋을까?
하지만 배포가 자주 있는 운영에 1.1GB의 이미지를 사용하는 것은 좋은 선택이 아니라고 생각한다.

나의 경우 현업에서 많은 프로젝트를 alpine 이미지로 처리했다.
하지만 경험상 alpine 이미지의 사용법은 일반적인 리눅스와 차이가 있다.
운영에서 alpine을 사용하려면 장애가 났을 때 당황하지 않을 만큼 정보를 얻고 사용했으면 한다.