article thumbnail image
Published 2024. 2. 3. 19:42

이미지와 컨테이너란

컨테이너
  • 애플리케이션을 실행하는 전체 환경 등 무엇이든 포함하는 작은 패키지
결국 컨테이너에는 소프트웨어 실행 유닛이 존재하고 우리는 그것을 단순히 실행시키는 것
 
이미지
  • dissolver로써 템플릿으로써 컨테이너의 블루 프린트로 활용
  • 코드와 코드를 실행하는데 필요한 도구를 포함
  • 그런 다음 컨테이너가 실행되어 코드를 실행시킨다.
  • 한 번만 정의하면 다른 시스템과 다른 서버에서 여러번 실행 가능
 
즉 이미지는 모든 설정 명령과 모든 코드가 포함된 공유 가능한 패키지이며 컨테이너는 그러한 이미지의 구체적인 실행 인스턴스이다. 이미지를 기반으로 컨테이너를 실행하는게 핵심
 
 

이미지를 생성하고 가져오는 방법

  1. 이미 존재하는 이미지를 가져와서 실행하는 방식
Docker Hub 혹은 커뮤니티에서 만든 이미지를 가져와서 사용할 수 있습니다.
공식 도커 이미지를 찾을 수 있습니다.
# 로컬에서 찾을 수 없다면 도커 허브에서 해당 이미지를 찾은 뒤 컨테이너를 만듭니다.
docker run node
 
기본적으로 컨테이너는 주변 환경과 격리되어 있기 때문에 내부에 실행 중인 인터렉티브 쉘이 있다고해서 사용자가 사용할 수 없습니다. 컨테이너에 의해 자동으로 노출되지 않습니다.
$ docker ps -a
CONTAINER ID   IMAGE     COMMAND                   CREATED         STATUS                     PORTS     NAMES
0ec115cf471f   node      "docker-entrypoint.s…"   2 minutes ago   Exited (0) 2 minutes ago             determined_dirac
 
하지만 도커에게 컨테이너 내부에서 대화형 세션을 사용하겠다는 것을 전달하면 사용가능합니다.
 
docker run -it node
해당 명령어를 입력하면 컨테이너에서 실행 중인 노드와 연결이 가능합니다.
 
 
다시 정리하면 이미지는 컨테이너에 필요한 모든 논리와 모든 코드, 이것들을 실행하는 설정 값 등 실행과 관련된 여러가지를 포함하고 있고 run 명령을 사용하여 이미지를 기반으로 이미지의 인스턴스를 만듭니다.
 
그렇게 만들어진 인스턴스는 컨테이너에 실려 사용됩니다.
 
  1. Dockerfile을 사용한 자체 이미지
앞서 적용해봤던 공식 베이스 이미지를 기반으로 구축한 다음 그 위에 코드를 추가하여 커스텀한 이미지를 만들어서 사용합니다.
 
Dockerfile로 자체 이미지를 생성하려고할 때 실행하고자하는 명령이 포함됩니다.
따라서 Dockerfile에는 자체 이미지 구축에 대한 설정 명령이 포함되어 있습니다.
 
이론적으로 도커 이미지를 처음부터 빌드할 수 있지만 코드에 필요한 기타 도구와 같은 운영 레이어가 필요합니다.
 
Dockerfile을 기반으로 이미지를 생성하는 순간 이미지에 있는 설정들이 다운로드 되어 로컬에 저장됩니다.
# 도커가 node라는 이름의 이미지가 존재하며 그것을 찾을 수 있음
FROM node

# 작업 디렉토리를 생성하고 설정함
WORKDIR /app

# 첫 번째 경로는 이미지의 외부 경로로 이미지로 복사되어야하는 파일들이 있는 곳
# 두 번째 경로는 이미지 내부의 경로로 자체 내부 파일 시스템에 저장되는 곳(도커 컨테이너 내부에 존재)
COPY . /app

# 도커 컨테이너 및 이미지의 작업 디렉토리에서 실행됨(생성한 디렉토리로 이동해야함)
RUN npm install

# 해당 명령어는 이미지가 빌드되기만해도 실행됨
# 하지만 이미지는 컨테이너의 템플릿으로 활용되어함
# RUN node server.js

# 도커는 내부 환경과 자체적인 내부 네트워크로 격리되어 있기 때문에 포트를 노출시키지 않으면 내부에서만 수신 대기를 함
# 하지만 여기서 사용하는 EXPOSE는 단순히 문서/주석의 느낌이고 실제로는 -p를 통해 로컬 포트와 매칭시켜야 합니다.
EXPOSE 80

# 이미지를 기반으로 컨테이너가 실행될 때마다 컨테이너 node 명령어로 server.js파일을 실행하도록 명령
CMD ["node", "server.js"]
docker run -p <Local Port>:<Docker External Port> <image ID>
 
 
-p 를 함으로써 도커에게 어떤 로컬 포트가 있는지 알려줄 수 있습니다.
⇒ 사용자 로컬 머신의 어떤 포트가 내부의 도커 특정 포트에 엑세스 할 수 있는지 알려줍니다.
 
쉽게 도커의 노출된 포트와 내부 로컬 머신의 포트를 매핑시켜주는 역할
# 도커 이미지를 실행할 때 모든 이미지ID를 붙여넣을 필요 없이 부분만 붙여넣어도 실행됨

$ docker run -p 3000:80 sha256:af5c9e01fc071fb2a23cbe5c523fe738d14c24ca850d6583e5548bf9131fc91e
$ docker run -p 3000:80 sha256:af5c9e01fc07
 
 
  • Docker container stop
$ docker ps
CONTAINER ID   IMAGE          COMMAND                   CREATED              STATUS              PORTS                  NAMES    
58972211dc2a   af5c9e01fc07   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:3000->80/tcp   bold_chaum

$ docker stop bold_chaum
bold_chaum
 
 

이미지 설정

  1. 이미지는 읽기 전용
소스 코드를 이미지에 복사하는데, 기본적으로 복사한 시점에서 소스 코드의 스냅샷을 생성합니다.
따라서 업데이트된 소스 코드를 새 이미지로 복사하기 위해선 이미지를 다시 빌드 해야하며 이미지의 모든 것은 읽기 전용으로 사용됩니다.  COPY . /app 
 
이렇게 이미지를 다시 빌드하게 되면 이미지는 완전히 새로운 이미지로 만들어지기 때문에 새로운 이미지 이름을 갖게 됩니다.
 
새로운 이미지는 정상적으로 반영되었습니다.
 
 
  1. 이미지 레이어
이미지는 레이어 기반
 
 
이미지를 빌드하거나 이미지를 다시 빌드할 때 변경된 부분의 명령과 그 이후의 모든 명령이 재평가됩니다.
$ docker build .
[+] Building 33.4s (10/10) FINISHED                         docker:default
 => [internal] load build definition from Dockerfile                  0.0s
 => => transferring dockerfile: 1.28kB                                0.0s
 => [internal] load .dockerignore                                     0.0s
 => => transferring context: 2B                                       0.0s
 => [internal] load metadata for docker.io/library/node:latest        2.5s
 => [auth] library/node:pull token for registry-1.docker.io           0.0s
 => [1/4] FROM docker.io/library/node@sha256:3af9f785cb8fc1a9c60a77  26.9s
 => => resolve docker.io/library/node@sha256:3af9f785cb8fc1a9c60a77c  0.0s
 => => sha256:3af9f785cb8fc1a9c60a77c7b31b1ba7f5c74a 1.21kB / 1.21kB  0.0s 
 => => sha256:8d6c41e6504f6b9d7a8c2a82803a4d158660ae 7.34kB / 7.34kB  0.0s 
 => => sha256:6a299ae9cfd996c1149a699d36cdaa76fa33 49.58MB / 49.58MB  3.5s 
 => => sha256:2cc34c90ae08e9a7447d01513af15a1f37193b 2.00kB / 2.00kB  0.0s 
 => => sha256:e08e8703b2fb5e50153f792f3192087d2697 24.05MB / 24.05MB  2.6s 
 => => sha256:68e92d11b04ec0fe48e60d59964704aca234 64.14MB / 64.14MB  3.8s 
 => => sha256:5b9fe7fef9befda786bc8e1dd1ae42ffd8 211.11MB / 211.11MB  9.3s 
 => => sha256:278d467c182fb935287ed6f4be3d44f8ac2714 3.37kB / 3.37kB  3.8s 
 => => extracting sha256:6a299ae9cfd996c1149a699d36cdaa76fa332c8e9d6  5.2s
 => => sha256:aab6430b55a2372670b8797fbe6eae0e3652 49.30MB / 49.30MB  6.7s
 => => sha256:b6f92f4f624059d4cf4ea96bc1f2512f74d63e 2.23MB / 2.23MB  4.2s
 => => sha256:2e24338062e4bb2910914b5ba45c6bad6985b6f31a 451B / 451B  4.5s 
 => => extracting sha256:e08e8703b2fb5e50153f792f3192087d26970d26280  0.8s 
 => => extracting sha256:68e92d11b04ec0fe48e60d59964704aca234084f87a  3.5s 
 => => extracting sha256:5b9fe7fef9befda786bc8e1dd1ae42ffd8b9c37a4cc  9.1s 
 => => extracting sha256:278d467c182fb935287ed6f4be3d44f8ac2714264bc  0.0s 
 => => extracting sha256:aab6430b55a2372670b8797fbe6eae0e36526282a1b  3.3s 
 => => extracting sha256:b6f92f4f624059d4cf4ea96bc1f2512f74d63ebabcc  0.1s 
 => => extracting sha256:2e24338062e4bb2910914b5ba45c6bad6985b6f31a6  0.0s 
 => [internal] load build context                                     0.0s 
 => => transferring context: 2.40kB                                   0.0s 
 => [2/4] WORKDIR /app                                                1.3s 
 => [3/4] COPY . /app                                                 0.0s 
 => [4/4] RUN npm install                                             2.2s 
 => exporting to image                                                0.2s 
 => => exporting layers                                               0.2s 
 => => writing image sha256:7e90540ed7ec87a1c8ca0dd57f09d6026872cc57  0.0s 

What's Next?
  View a summary of image vulnerabilities and recommendations → docker scout quickview
 
 
  • CACHED
 => CACHED [2/4] WORKDIR /app                                         0.0s 
 => CACHED [3/4] COPY . /app                                          0.0s 
 => CACHED [4/4] RUN npm install
 
동일한 작업 디렉토리에서 변경사항이 없으니 명령을 다시 실행될 필요가 없다는 것을 인지한 것입니다.  각 Dockerfile 명령이 이미지의 별도 레이어를 생성하며, 빌드 과정에서 Docker는 이러한 레이어를 캐시하여 재빌드 시 변경되지 않은 레이어에 대해 다시 실행할 필요가 없게 합니다.
 
 
대신 이미지를 빌드할 때마다 도커는 모든 명령 결과를 캐시하고 이미지를 다시 빌드 할 때 명령을 다시 실행할 필요가 없다면 캐시된 결과를 사용합니다.
 
 
이것을 레이어 기반 아키텍쳐라고 하며 Dockerfile의 모든 명령은 각각의 레이어를 나타냅니다.
 
따라서 정리하면 이미지는 도커의 다양한 명령을 기반으로 여러 레이어를 읽기 전용으로 구성하며, 레이어를 생성하고 생덩된 레이어를 캐싱합니다.
 
이미지를 기반으로 컨테이너를 실행하면 Dockerfile에 지정한 명령을 실행한 결과로 코드를 실행 중인 환경 위에 새로운 추가 레이어를 구성합니다.
 
그리고 도커 컨테이너를 실행할 때, 이 이미지 위에 새로운 가장 상위 레이어가 추가됩니다. 이 새로운 레이어는 "쓰기 가능 레이어"(writeable layer)라고 부르며, 컨테이너 내에서 발생하는 모든 파일 시스템의 변경사항(파일 추가, 수정, 삭제 등)을 저장합니다. 이렇게 구성함으로써, 실행 중인 컨테이너가 필요로 하는 데이터 변경을 가능하게 하면서도, 기반 이미지의 읽기 전용 레이어들은 변경되지 않고 그대로 유지됩니다.
 
또한 각 컨테이너는 자체의 쓰기 가능 레이어를 가지지만 기반 이미지 레이어는 공유됩니다.
 
하지만 모든 레이어가 변경된 부분에 대한 세세한 판단을 하지 않기 때문에 하나의 레이어가 변경된다면 후속 모든 레이어도 변경되게 됩니다.
 

최적화(참고)


Node.js에서는 추가적인 패키지가 없다면 굳이  npm install을 실행할 필요가 없지만, 레이어의 특성 상 소스 코드가 수정되게 된다면, 캐시되어있어 필요없는 패키지 설치과정이 반복됩니다.
 
따라서 최적화를 진행한다면 아래와 같이 먼저 package.json을 통해 패키지 정보를 가져오고 이상이 없다면 소스 코드의 변경사항을 입력하는 방식으로 최적화를 진행할 수 있습니다.
# 도커가 node라는 이름의 이미지가 존재하며 그것을 찾을 수 있음
FROM node

# 작업 디렉토리를 생성하고 설정함
WORKDIR /app

# package.json을 먼저 불러와 변경된 패키지가 있는지 확인
# 굳이 소스 코드만 변경되었음에도 패키지를 설치할 필요가 없음
COPY package.json /app

# 도커 컨테이너 및 이미지의 작업 디렉토리에서 실행됨(생성한 디렉토리로 이동해야함)
RUN npm install

# 첫 번째 경로는 이미지의 외부 경로로 이미지로 복사되어야하는 파일들이 있는 곳
# 두 번째 경로는 이미지 내부의 경로로 자체 내부 파일 시스템에 저장되는 곳(도커 컨테이너 내부에 존재)
COPY . /app

# 해당 명령어는 이미지가 빌드되기만해도 실행됨
# 하지만 이미지는 컨테이너의 템플릿으로 활용되어함
# RUN node server.js


# 도커는 내부 환경과 자체적인 내부 네트워크로 격리되어 있기 때문에 포트를 노출시키지 않으면 내부에서만 수신 대기를 함
# 하지만 여기서 사용하는 EXPOSE는 단순히 문서/주석의 느낌이고 실제로는 -p를 통해 로컬 포트와 매칭시켜야 합니다.
EXPOSE 80

# 이미지를 기반으로 컨테이너가 실행될 때마다 컨테이너 node 명령어로 server.js파일을 실행하도록 명령
CMD ["node", "server.js"]
 

첫 번째 개념 정리

  • 이미지
    • 우리는 애플리케이션을 구성하는 코드를 이미지라 불리우는 곳에 집어넣습니다.
    • 우리의 코드 뿐만 아니라 코드를 실행하는데 필요한 도구인 실행 환경도 넣습니다.
    • 이러한 이미지를 만들기 위해 Dockerfile을 생성하며, 세부적인 명령을 재공하고 무엇을 넣고, 어떤 이미지를 사용하며, 어떤 코드와 종속성, 최종적으로 외부 컨테이너에서 단계까지 정리하여 작성합니다.
    • 컨테이너의 블루프린트이자 템플릿입니다.
  • 컨테이너
    • 이미지에 기반한 여러 개의 컨테이너 실행 가능합니다.
    • 컨테이너는 단순히 이미지 위에 추가된 레이어입니다.
    • 일단 실행되면 다른 컨테이너와 독립적인 환경으로 실행됩니다.
    • 중요한건 컨테이너가 이미지에서 코드와 환경을 복사하는게 아니라 이미지에 저장된 환경을 사용하고, 컨테이너는 단순히 최상단에 레이어를 추가하여 사용하는 방식입니다.
    • 따라서 이미지의 레이어를 공유하고 각 컨테이너에 독립적인 실행 환경을 제공하는 방식이라고 생각하여 정리하였습니다.

컨테이너 중지 & 재시작

  • 이미지를 통한 컨테이너 시작
    • docker run
  • 기존 중지되어있는 컨테이너를 다시 시작
    • docker start <container name>
 
 

Attached & Detached 컨테이너

app.post("/store-goal", (req, res) => {
  const enteredGoal = req.body.goal;
  console.log(enteredGoal);
  userGoal = enteredGoal;
  res.redirect("/");
});
 
 

 

  • Attached
    • docker run으로 시작하면 attached가 기본으로 시작됩니다.
    • 컨테이너와 연결하여 출력 결과를 수신
    • docker attach <container name or id>
    • $ docker run -p 3000:80 sha256:da6989592eeb5a0585305f2c87758599aa3e77f5aea897bbf601ea39a5addd0d
      Docker Gosu
       
  • Detached
    • docker start⁠ or docker run -d⁠ 명령으로 시작하면 detached가 기본으로 시작됩니다.
 
로그 메시지를 보기 위해 사용되며 docker logs <container id or name>⁠ 을 통해 볼 수 있습니다.
$ docker logs interesting_villani
Docker Gosu

# attach로 계속해서 로그 메시지를 확인할 수 있습니다.
$ docker logs -f interesting_villani
 
중요한건 attached, detached든 컨테이너는 게속해서 실행 중이라는 사실이며, 컨테이너 내부의 정보가 필요한 경우 사용하게 됩니다.
 
 
 

인터렉티브 모드

  • 컨테이너는 포그라운드와 백그라운드 환경을 구분합니다.
  • 컨테이너나 컨테이너로 실행되는 애플리케이션에는 어떠한 명령어도 입력할 수 없습니다.
$ docker run 9c38aec534e5
Please enter the min number: Traceback (most recent call last):
  File "/app/rng.py", line 3, in <module>
    min_number = int(input('Please enter the min number: '))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
EOFError: EOF when reading a line
 
  • 컨테이너에 접속하여 무언가를 입력하기 위해선 추가적인 옵션이 필요합니다.
    • -i 또는 --interactive는 Docker 컨테이너의 표준 입력(STDIN)을 열어두어, 사용자의 입력을 컨테이너에 전달할 수 있게 합니다. 하지만 이 옵션만 사용할 경우, 컨테이너와의 상호작용은 텍스트 기반의 입력에 국한되며, 커맨드 라인의 입력/출력을 완전히 제어할 수는 없습니다.
    • -t 또는 --tty는 가상 터미널을 할당합니다. 이 옵션은 컨테이너와의 상호작용을 더욱 풍부하게 만들어주며, 사용자가 컨테이너 내부에서 실행되는 애플리케이션과 마치 실제 터미널을 사용하는 것처럼 상호작용할 수 있게 해줍니다. -t 옵션은 컨테이너 내부의 명령어 실행 시 터미널의 표준 출력과 에러를 적절하게 포맷팅하는 데 유용합니다.
$ docker run --help
  -i, --interactive                    Keep STDIN open even if not attached
  -t, --tty                            Allocate a pseudo-TTY
# it를 결합하면 무언가를 입력할 수 있으며 , 컨테이너가 무언가를 입력받을 수 있습니다.

$ docker run -it 9c38aec534e5
Please enter the min number: 1
Please enter the max number: 2
2

# 컨테이너를 다시 시작합니다.
# -t 옵션은 기존 run에서 있었던 환경을 유지하며 실행됩니다.
$ docker start -a -i tender_pasteur
Please enter the min number: 1
Please enter the max number: 5
4
 
이를통해 도커가 단순히 웹 서버나 애플리케이션 같은 장기적으로 실행되는 프로세스에만 적용되는게 아닌 간단히 유틸리티 애플리케이션을 도커화하는데 사용할 수 있습니다.
 
위 내용처럼 단순히 입력이 필요하고 무언가를 출력하는 애플리케이션을 만들 수 있습니다.
정리하자면 필요에 따라 컨테이너와 통신할 수 있습니다.
 

이미지 & 컨테이너 삭제하기

  • 컨테이너 제거하기
    • 선행적으로 중지되어있어야 합니다.
    • docker rm <container name> 
  • 이미지 제거하기
    • 기본적으로 이미지 내부의 모든 레이어를 삭제합니다.
    • 이미지가 컨테이너에 의해 사용되지 않는 경우만 삭제할 수 있습니다.
    • docker rmi <image id>⁠ 
  •  모든 이미지 제거하기
    • 현재 실행 중인 컨테이너에서 사용되지 않는 이미지들이 포함됩니다.
    • docker image prune⁠ 
 

중지된 컨테이너 자동으로 제거하기

  • 컨테이너가 중지되면 자동으로 제거할 수 있습니다.
$ docker run --help
--rm                             Automatically remove the container

$ docker run -p <local>:<container> -d --rm <image id>
 
 
 

이미지 검사

이미지가 환경과 컨테이너를 포함하기 때문에 용량이 매우 크게 잡혀있습니다.
 
하지만 실행 중인 컨테이너는 매우 크지 않은데, 앞선 레이어에서 알 수 있듯이 이미지 레이어 위에 추가된 작은 레이어기 때문입니다.
 
따라서 이미지는 복사되지 않고 컨테이너는 이미지를 기반으로 빌드된다고 할 수 있습니다.
동일한 이미지로 실행되는 여러 컨테이너는 이미지 내부의 코드를 공유합니다.
오직 컨테이너 레이어에서만 쓰기 작업을 할 수 있습니다.
$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED             SIZE
<none>       <none>    9c38aec534e5   48 minutes ago      1.02GB
<none>       <none>    da6989592eeb   About an hour ago   1.11GB
 
  • 이미지 정보 확인하기  docker image inspect <image id>
    • ⁠날짜, 버전, 환경 변수 등등 여러 이미지 환경 정보를 알 수 있습니다.
$ docker image inspect da6989592eeb
[
    {
        "Id": "sha256:da6989592eeb5a0585305f2c87758599aa3e77f5aea897bbf601ea39a5addd0d",
        "RepoTags": [],
        "RepoDigests": [],
        "Parent": "",
        "Comment": "buildkit.dockerfile.v0",
        "Created": "2024-02-03T05:53:05.664255152Z",
        "Container": "",
        "ContainerConfig": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": null,
            "Cmd": null,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "DockerVersion": "",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "80/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "NODE_VERSION=21.6.1",
                "YARN_VERSION=1.22.19"
            ],
            "Cmd": [
                "node",
                "server.js"
            ],
            "ArgsEscaped": true,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "/app",
            "Entrypoint": [
                "docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 1109012962,
        "VirtualSize": 1109012962,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/a9ac9rjzl1zxdqgccrumb7s1f/diff:/var/lib/docker/overlay2/6qsby0k3l2cebtbqprnohpsfj/diff:/var/lib/docker/overlay2/jx19xnlau71usuwczexarz2yd/diff:/var/lib/docker/overlay2/a7d9673ea1a995f423d9a8d8ded0192a3fd6bb71ec2f5008cd9f9749dd264cfe/diff:/var/lib/docker/overlay2/107e2a7aba3dcc149ac136bb38cbebfbf5b489fb7213027fb2cc9610977ffb30/diff:/var/lib/docker/overlay2/a89bd169688ca99c639d7046eb6e0b81bcc93ca478c1854e11bf25804909cf32/diff:/var/lib/docker/overlay2/13203f4e6364f033c96441c5306720cbf3ee3ae7cd339d4d01b6e1b1de9f5797/diff:/var/lib/docker/overlay2/20e942d2e19e4e7951e32bd797d77e78a4bbbb67ea295d2d4e1b60f1240b20c4/diff:/var/lib/docker/overlay2/88301b54245f728935b4d692d2c4655a358c08024bf8d99f319dde2394e9ef0a/diff:/var/lib/docker/overlay2/8e2e822830c6a0a1c2669bf4d5955b335e0c090fa501dc4204b505cdce5c0976/diff:/var/lib/docker/overlay2/31647be9a8c6d231e08a387b3039818f99348cafbeac5665ad4ac331416e58ad/diff",
                "MergedDir": "/var/lib/docker/overlay2/36hc1orifmsrgf35j3hnfx3hb/merged",
                "UpperDir": "/var/lib/docker/overlay2/36hc1orifmsrgf35j3hnfx3hb/diff",
                "WorkDir": "/var/lib/docker/overlay2/36hc1orifmsrgf35j3hnfx3hb/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:1dae5147cd293b16e7b8c93f778dbf7ceff5c81c2b2704d3e5a98d331cdbe0ab",
                "sha256:bcd354c940e1b2a5c23b23b76d7d6d1df242de7e222aae8ceb32cc6ff1a19673",
                "sha256:9f843c569746b6fc06f17d77a368d723b7a702eb9dd9df8e2c86371cf05f7737",
                "sha256:d43f876e6f21d85f0e43c25ff4aaf25c9a2a47de6e91b5fb54dac2b74682c820",
                "sha256:e27cf095612475d2087f75327a9f8bd2103f1e82ef2531a9d650d33abcff2930",
                "sha256:9c297f4ae6351c6799194a950c70c53e269f60e22790575e83babe759ca8d81a",
                "sha256:56553c49b3a39be1b328e492a9431053ffacb135451e071c35c9f7eedead8cd6",
                "sha256:621685762c1642715d68257b5d839adf306f15153323c3ff4a708073a432c1d6",
                "sha256:a718020434adbfe225b785989227719e6b5c915e59256365d89c0ab27002b094",
                "sha256:695a4d105c49c683c1dcd271d6ed95a0f674dc08c922e3fd3fd1d7b12de1fc65",
                "sha256:13a097e914c6a9ccd0a89b68313425ed1de92d7267255ea695b0473c36eddabd",
                "sha256:a8ce279add73a7f63e79540d00311790c0b588cda79b8b567908aea02041575f"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]
 
 

컨테이너 검사 및 파일입출력

  • 실행 중인 컨테이너로 혹은 실행 중인 컨테이너 밖으로 파일 또는 폴더를 복사할 수 있습니다.
    • docker cp
 
# /c/docker-course/dummy/sample.txt
today: 2024-02-03
hello!
 
 
# docker cp sourceDir <container name>:<dir>
$ docker cp dummy/. musing_elion:/test
Successfully copied 2.56kB to musing_elion:/test

# 반대로 container에서 로컬로 파일 가져오기
$  docker cp musing_elion:/test dummy
Successfully copied 2.56kB to C:\docker-course\dummy
 
컨테이너를 다시 시작하고 이미지를 다시 작성하지 않고도 컨테이너에 무언가를 추가할 수 있습니다.
하지만 변경 사항을 추적하기 어렵기 때문에 중요 파일을 수정하지 않도록 해야합니다.
 
 

컨테이너와 이미지에 이름 & 태그 지정하기

컨테이너와 이미지에 고유한 이름과 태그를 추가할 수 있습니다.
 
  • Container 고유한 이름 추가하기
$ docker run --name <name> <image id>
--name string                    Assign a name to the container

CONTAINER ID   IMAGE          COMMAND                   CREATED         STATUS         PORTS                  NAMES
8b0f84d141a7   da6989592eeb   "docker-entrypoint.s…"   6 seconds ago   Up 5 seconds   0.0.0.0:3000->80/tcp   docker-app
 
  • image 고유한 이름 추가하기
    • 이미지에도 이름을 추가할 수 있으며 이를 태그라고 부릅니다.

 
이미지 태그는 두 부분으로 구성됩니다.
  • name(REPOSITORY) : tag⁠ / FROM node:14
    • name
      • 여러 개의 특정화된 이미지 그룹을 만들 수 있습니다.
    • tag
      • 이미지의 특정화된 버전을 정의할 수 있습니다.
      • 태그를 통해 특정 버전, 특정 구성을 사용할 수 있게 됩니다. 
      • Docker hub에는 다양한 태그를 지닌 특정한 이미지들이 있고, 해당 이미지를 사용하여 자체 컨테이너 환경에서 더 정확한 구성을 사용할 수 있습니다.
  • 이미지의 다른 구성이나 버전이 있는 경우라면, 이름과 태그를 결합할 수 있습니다.
    • 이미지에 특정화된 버전과 고유 식별자를 언제나 가지게 됩니다.
    • 태그가 없다면, 이름만으로도 고유한 식별자를 가지게 됩니다.
    • 태그는 단어 혹은 1,2,3과 같이 숫자도 될 수 있습니다.
 
$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
<none>       <none>    9c38aec534e5   2 hours ago   1.02GB
docker-app   latest    da6989592eeb   2 hours ago   1.11GB
 
이미지 뒤에 적절한 태그를 추가함으로써 식별할 수 있는 특정화된 다양한 버전의 이미지를 생성할 수 있습니다.
 
 

이미지 공유하기

이미지가 있는 모든 사람은 이미지를 기반으로 컨테이너를 만들 수 있습니다.
이는 실제로 컨테이너를 누구와도 공유하지 않고 이미지만을 공유한다는 것을 의미합니다.
 
공유 방식에는 두 가지가 있습니다
  1. Dockerfile 공유
    • Dockerfile과 애플리케이션에 관한 소스 코드를 제공하면 자체 이미지를 빌드하고 컨테이너를 실행할 수 있습니다.
    • Dockerfile은 빌드되어야 컨테이너로 사용할 수 있게 되며 해당 Dockerfile에서 필요한 코드와 환경 또한 공유될 필요가 있습니다.
  2. 빌드된 Image 공유
    • 모든 것이 이미지로 구성되어 있기 때문에 빌드 과정 없이 즉시 컨테이너로 실행할 수 있습니다.
 
 

Docker hub에 이미지 push하기

  • Docker Hub
    • https://hub.docker.com/
    • 들어오는 이미지를 처리하는 방법과 이미지를 저장, 배포하는데 사용할 수 있는 다양한 서비스가 존재합니다.
    • 공개 혹은 비공개로 이미지를 저장할 수 있습니다.
  • Private Registry
    • 많은 공급자가 있으며, 도커 이미지를 지원하는 공급자를 사용할 수도 있습니다.
    • 제공업체에 따라 이미지 공개범위가 다릅니다.
 
공통적으로는 이미지를 푸시하여 다른 사용자들과 공유를 할 수 있다는 장점이 있습니다.
docker push , docker pull⁠ 명령이 있으며 해당 명령에 공급자의 URL이 포함되어야 합니다.
 
 
Docker hub를 만들었다고 하더라도, 이미지의 로컬 설정이 해당 저장소를 인지하지 못합니다.
An image does not exist locally with the tag: chois6j/node-app
$ docker push chois6j/node-app
Using default tag: latest
The push refers to repository [docker.io/chois6j/node-app]
An image does not exist locally with the tag: chois6j/node-app
 
하지만 Docker Hub ID를 prefix로 사용하여 다시 빌드하게 된다면 로컬에서 Docker hub 저장소를 인지하고 Push가 가능하게 됩니다.
# 이미지 빌드를 통한 설정
$ docker build -t chois6j/node-app .

# 기존 이미지 재설정 <tag>는 생략 가능
$ docker tag <target image name>:<tag> <new image name>:<tag>

# tag로 만들면 이전 이미지의 복제본으로 생성되게 됩니다.
$ docker tag docker-app chois6j/node-app:1.5
REPOSITORY         TAG       IMAGE ID       CREATED       SIZE
<none>             <none>    9c38aec534e5   2 hours ago   1.02GB
chois6j/node-app   1.5       da6989592eeb   3 hours ago   1.11GB
docker-app         latest    da6989592eeb   3 hours ago   1.11GB
 
하지만 push의 경우 해당 계정을 소유한 경우에만 가능하기 때문에 push 하려는 Docker hub repository account
로그인이 필요합니다. docker login⁠ / docker logout  
$ docker push chois6j/node-app:1.5
The push refers to repository [docker.io/chois6j/node-app]
a8ce279add73: Layer already exists
13a097e914c6: Layer already exists
695a4d105c49: Layer already exists
a718020434ad: Layer already exists
621685762c16: Layer already exists
56553c49b3a3: Layer already exists
9c297f4ae635: Layer already exists
e27cf0956124: Layer already exists
d43f876e6f21: Layer already exists
9f843c569746: Layer already exists
bcd354c940e1: Layer already exists
1dae5147cd29: Layer already exists
errors:
denied: requested access to the resource is denied
unauthorized: authentication require
 
$ docker login
Log in with your Docker ID or email address to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com/ to create one.
You can log in with your password or a Personal Access Token (PAT). Using a limited-scope PAT grants better security and is required for organizations using SSO. Learn more at https://docs.docker.com/go/access-tokens/

Username: chois6j
Password: 
Login Succeeded
 
 
  • 정상적으로 푸시가 된 것을 확인할 수 있습니다.
    • 이미지 업로드는 전체 이미지를 업로드하지 않고 의존하는 이미지에 대한 연결을 생성하여 필요한 부분만 업로드하게 됩니다.
    • 동일한 리포지토리에 다른 태그를 부여하여 업로드하더라도 여러 태그를 포함하여 저장할 수 있습니다.
$ docker push chois6j/node-app:1.5
The push refers to repository [docker.io/chois6j/node-app]
a8ce279add73: Layer already exists
13a097e914c6: Layer already exists
695a4d105c49: Layer already exists
a718020434ad: Layer already exists
621685762c16: Layer already exists
56553c49b3a3: Layer already exists
9c297f4ae635: Layer already exists
e27cf0956124: Layer already exists
d43f876e6f21: Layer already exists
9f843c569746: Layer already exists
bcd354c940e1: Layer already exists
1dae5147cd29: Layer already exists
1.5: digest: sha256:80e4841fa6d1f0dc94a111745303b6819d7d637cf0a12e2223675bb655e9c0de size: 2836
 
 

공유 이미지 가져오기(pull) & 사용하기

docker pull  명령으로 이미지를 가져올 수 있습니다.
공개되어 있다면 로그인 할 필요없이 모든 사람이 가져갈 수 있습니다.
$ docker pull chois6j/node-app:1.5
1.5: Pulling from chois6j/node-app
6a299ae9cfd9: Already exists
e08e8703b2fb: Already exists
68e92d11b04e: Already exists
5b9fe7fef9be: Already exists
278d467c182f: Already exists
aab6430b55a2: Already exists
b6f92f4f6240: Already exists
2e24338062e4: Already exists
3b72273561c4: Already exists
d6f4e22209a7: Already exists
56e0e64c0f3c: Already exists
6b685fa59969: Already exists
Digest: sha256:80e4841fa6d1f0dc94a111745303b6819d7d637cf0a12e2223675bb655e9c0de
Status: Downloaded newer image for chois6j/node-app:1.5
docker.io/chois6j/node-app:1.5

What's Next?
  View a summary of image vulnerabilities and recommendations → docker scout quickview chois6j/node-app:1.5

$ docker run -p 3000:80 --rm -d chois6j/node-app:1.5
ecba6b57821ecbd3af73f4aecb3c28ecdfccf247f828beced687a3c2afbb1644
 
 
Docker에서 이미지를 실행하기 위해 docker run 명령을 사용할 때, 해당 이미지가 로컬 컴퓨터에 존재하지 않는 경우, Docker는 자동으로 Docker Hub 또는 지정된 다른 레지스트리에서 이미지를 찾아서 다운로드(즉, docker pull을 내부적으로 실행)한 후 컨테이너를 실행합니다. 이 과정은 사용자가 명시적으로 docker pull 명령을 사용하지 않아도 자동으로 수행됩니다.
 
따라서 docker pull chois6j/node-app:1.5⁠⁠⁠ 명령을 사용하지 않고 바로 docker run chois6j/node-app:1.5⁠ 명령을 사용한다면 docker run⁠ 은 로컬 컴퓨터에서 이미지를 찾지 못하고 Docker hub에 접근하여 이미지를 가져오게 됩니다.
 
하지만 로컬에 해당 이미지가 존재한다면 그것이 최신 버전이든 상관없이 해당 이미지를 실행시키게 됩니다.
 
정리하면 현재 로컬에 이미지가 존재하지만, 이미지가 업데이트 되어 Docker hub에 다시 푸시되었다면 docker run 명령 만으로는 최신 업데이트 이미지를 제공받지 못합니다. 그렇기 때문에 docker pull⁠ 과정이 수행되어야합니다.
복사했습니다!