본문 바로가기
AI, 논문, 데이터 분석

[Docker] [실습1] 컨테이너 기술과 Docker 개요

by doraemin_dev 2025. 1. 13.

 

[실습] Setup Docker Desktop

#1. Enable WSL 2
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -NoRestart

#2. Enable ‘Virtual Machine Platform’ 
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform 

#3. Linux 커널 업데이트 패키지 다운로드 및 설치
https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi

#4. Set WSL 2 as default
wsl --set-default-version 2

#5. Azure CLI 설치 (앞에서 했으면, 다운로드 할 필요 없다.)
##Windows
# https://docs.microsoft.com/ko-kr/cli/azure/install-azure-cli-windows?tabs=azure-cli

#6. 도커 데스크톱 다운로드
https://hub.docker.com/editions/community/docker-ce-desktop-windows

#7. 도커 데스크톱 설치 --> 다시 시작

 

Windows 기능 켜기/끄기 기능을 명령어로 실행

주의 ) 컴퓨터가 껐다 켜진다는 물음이 나오고, Y를 입력하면 된다. 진짜 껐다 켜진다.

 

Linux 커널 업데이트 패키지 다운로드 및 설치
https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi

 Set WSL 2 as default

도커 데스크톱 다운로드
https://hub.docker.com/editions/community/docker-ce-desktop-windows

주의 ) Close and log out을 누르면, 껐다 켜진다.

 

실행했는데, WSL update failed가 뜬다. 터미널에 업데이트 코드를 입력하자.

 

도커를 실행하면, (skip > skip 누르고 들어가면) Engine running이 뜬다.

다양한 설정을 확인해볼 수 있다.

 

(창을 닫아도 계속 docker는 실행 중이다.)

터미널을 통해, docker가 실행중인 버전을 확인할 수 있다.


컨테이너 기술 개요

  • 개념

컨테이너 : 모듈화 되고 격리된 컴퓨터 환경

→ 가상환경에서의 컨테이너 : 앱과 앱이 서로 독립된 환경에서 구동

  • 컨테이너형 가상화 기술
    • 가상화 SW 없이 OS의 리소스를 격리해 앱을 실행하는 가상 운영 체제 공간을 만드는 기술

더보기

Docker를 사용하는 이유와 해결하려는 문제

Docker는 개발자와 운영자가 겪는 다양한 문제를 해결하기 위해 만들어졌습니다. 아래에서 쉽게 설명해 볼게요.


1. "내 컴퓨터에서는 되는데 서버에서는 안 돼요!"

  • 개발 환경과 서버 환경이 달라서 코드가 작동하지 않는 경우가 많습니다.
    • 예: 내 컴퓨터는 Windows인데 서버는 Linux라서 문제가 생김.
    • 또는 개발자가 설치한 라이브러리 버전과 서버에 설치된 버전이 달라서 에러 발생.

Docker 해결 방법:

  • 애플리케이션 실행에 필요한 모든 파일, 라이브러리, 설정을 컨테이너 안에 묶어서 어디서나 동일하게 실행되도록 만듭니다.
  • 즉, 개발자, 테스트 환경, 서버 환경이 모두 똑같아져요.

2. "새로운 서버에 설치하는 데 시간이 너무 오래 걸려요."

  • 새로운 서버나 테스트 환경을 구축하려면 OS 설치, 라이브러리 세팅, 애플리케이션 설치 등 여러 단계를 거쳐야 함.
    • 이 작업은 몇 시간, 며칠이 걸릴 수도 있어요.

Docker 해결 방법:

  • 미리 만들어 둔 Docker 이미지(컨테이너 실행 파일)를 서버에 복사하고 바로 실행하면 끝!
  • 몇 초 만에 새로운 서버에서 애플리케이션을 실행할 수 있습니다.

3. "서버 자원을 너무 많이 써요."

  • 기존 가상 머신(VM)은 운영 체제(OS) 자체를 통째로 가상화해서 실행하므로, 많은 메모리와 CPU를 사용합니다.
    • 예: 하나의 서버에서 3개의 VM을 실행하려면 각각의 OS가 필요해요.

Docker 해결 방법:

  • Docker는 OS 전체를 가상화하지 않고, 애플리케이션과 필요한 라이브러리만 격리합니다.
  • 같은 OS 커널을 공유하므로 가볍고 빠르게 실행됩니다.
    • 결과적으로 서버 자원을 더 효율적으로 사용할 수 있어요.

4. "운영 중에 업데이트를 안전하게 하고 싶어요."

  • 기존 방식에서는 새 버전을 서버에 설치하다가 문제가 생기면 복구가 어렵습니다.
    • 업데이트 중 문제가 생기면 서비스가 중단될 수도 있어요.

Docker 해결 방법:

  • Docker 컨테이너는 기존 컨테이너를 삭제하고 새 컨테이너로 교체하는 방식으로 업데이트합니다.
  • 문제가 생기면 이전 컨테이너를 바로 다시 실행할 수 있어요.
    • "롤백"이 간단합니다.

5. "여러 애플리케이션을 함께 실행하려는데 충돌이 생겨요."

  • 같은 서버에서 서로 다른 애플리케이션을 실행하려면 라이브러리나 포트가 충돌할 수 있습니다.
    • 예: Python 3.8과 Python 3.9를 동시에 사용해야 하는 상황.

Docker 해결 방법:

  • 각 애플리케이션을 별도의 컨테이너에서 실행하기 때문에 충돌 걱정이 없습니다.
  • 컨테이너끼리는 독립적으로 격리되어 실행됩니다.

Docker가 해결하는 주요 문제 정리

  1. 환경 차이로 인한 문제 → 컨테이너는 어디서나 동일하게 실행됩니다.
  2. 설치 및 배포 시간 → 컨테이너로 몇 초 만에 환경 설정 완료.
  3. 서버 자원 낭비 → 가상 머신보다 가볍고 빠르게 실행.
  4. 안정적인 업데이트 → 컨테이너 교체로 서비스 중단 없이 업데이트.
  5. 애플리케이션 간 충돌 방지 → 컨테이너 격리로 충돌 없음.

쉽게 비유하자면...

  • Docker 없는 세상: "이 레시피는 내 주방에서는 잘 되는데, 친구 집에서는 안 돼요!"
    • 주방 도구, 재료, 오븐 설정이 다르기 때문.
  • Docker 있는 세상: "내가 사용하는 주방 세트를 그대로 옮겨다니는 느낌!"
    • 요리를 어디서든 똑같이 만들 수 있음.

Docker는 이렇게 개발부터 배포까지의 골칫거리를 해결해주는 도구입니다. 😊

  • Docker
    • 컨테이너형 가상화 기술을 구현하기 위한 상주 애플리케이션과 이 애플리케이션을 조작하기위한 명령 줄 도구로 구성된 제품
    • 초기에는 LXC 커널의 가상화 기능 액세스 인터페이스로 사용
    • 0.9x 부터 자체 개발한 libcontainer로 LXC 대체
더보기

Docker: 컨테이너형 가상화 기술

Docker는 애플리케이션을 컨테이너라는 독립된 환경에서 실행할 수 있도록 지원하는 오픈소스 플랫폼입니다. 이 기술은 애플리케이션의 실행 환경과 종속성을 함께 패키징하여 다양한 환경에서 일관된 실행을 가능하게 합니다.


1. 구성 요소

Docker는 두 가지 주요 구성 요소로 이루어져 있습니다:

  1. Docker Daemon (도커 데몬)
    • 컨테이너 관리의 핵심 역할을 하는 상주 프로세스.
    • dockerd라는 프로세스가 백그라운드에서 실행되며, Docker 이미지 빌드, 컨테이너 실행, 네트워크 관리, 볼륨 관리 등의 작업을 처리합니다.
  2. Docker CLI (도커 명령줄 인터페이스)
    • 사용자가 Docker 데몬과 상호작용하는 도구.
    • docker 명령어를 통해 컨테이너를 생성, 실행, 중지, 삭제 등 다양한 작업을 수행할 수 있습니다.

2. 초기 Docker의 동작 방식

Docker는 처음에 **LXC(Linux Containers)**를 기반으로 작동했습니다. LXC는 리눅스 커널의 cgroups와 namespace를 활용하여 프로세스를 격리하고, 컨테이너 환경을 제공합니다. 이를 통해 Docker는 초기부터 컨테이너를 빠르게 실행할 수 있는 기술적 기반을 마련했습니다.


3. libcontainer 도입

  • Docker는 0.9 버전에서 LXC를 대체하는 자체 라이브러리인 libcontainer를 도입했습니다.
  • libcontainer의 도입 이유:
    • LXC의 의존성을 제거하여 Docker가 독립적으로 관리되고, 성능과 확장성을 더 쉽게 최적화할 수 있도록 개선.
    • Docker 팀이 직접 리눅스 커널 기능 (cgroups, namespaces, AppArmor, SELinux, seccomp)을 제어할 수 있게 됨.
  • libcontainer는 이후 OCI(Open Container Initiative)의 표준화 노력과 함께 runc로 발전하였습니다.

4. Docker의 주요 기술적 특징

  1. 컨테이너 격리
    • Docker는 리눅스 커널의 네임스페이스와 cgroups를 사용하여 프로세스, 네트워크, 파일 시스템 등을 다른 컨테이너와 격리합니다.
  2. 이미지 기반 배포
    • 애플리케이션과 필요한 라이브러리, 설정 파일 등을 하나의 이미지로 패키징.
    • 이미지 계층화(layered filesystem)를 활용하여 효율적인 저장과 전송 가능.
  3. 표준화된 포맷
    • OCI(Open Container Initiative)의 컨테이너 이미지 및 런타임 표준을 따름.
  4. 경량화
    • 하이퍼바이저 기반 가상화 기술에 비해 메모리 및 CPU 사용량이 적음.
    • 컨테이너는 OS 커널을 공유하므로 부팅 시간이 매우 짧음.

5. Docker의 장점

  1. 이식성
    • "Write Once, Run Anywhere"를 구현하여 개발 환경과 운영 환경 간 차이를 최소화.
    • 이미지와 컨테이너를 어디에서나 실행 가능.
  2. 빠른 배포
    • 컨테이너는 실행 속도가 빠르고, 새로운 애플리케이션 배포를 자동화할 수 있음.
  3. 자원 효율성
    • 여러 컨테이너가 하나의 호스트 OS 커널을 공유하므로, 하드웨어 자원의 사용량이 감소.
  4. 개발자 생산성 증가
    • 개발, 테스트, 배포 환경의 일관성을 보장.

6. Docker의 발전

Docker는 현재 단순한 컨테이너 도구를 넘어 클라우드 네이티브 애플리케이션 생태계의 핵심 기술로 자리잡았습니다. Kubernetes와 같은 컨테이너 오케스트레이션 도구와의 통합으로 대규모 애플리케이션을 효과적으로 관리할 수 있습니다.


7. Docker의 도전 과제

  1. 보안
    • 컨테이너는 같은 커널을 공유하므로, 취약점이 악용될 가능성이 존재.
    • 이를 해결하기 위해 네임스페이스 격리와 추가적인 보안 도구 사용이 요구됨.
  2. 복잡성
    • Docker Compose, Swarm, Kubernetes 등과 함께 사용될 때, 초기 설정과 학습 곡선이 높아질 수 있음.

8. Docker의 사용 사례

  1. 개발 환경 구축
    • 로컬 개발 환경에서 애플리케이션을 신속히 실행하고, 테스트 가능.
  2. CI/CD 파이프라인
    • Docker를 활용해 지속적 통합 및 배포를 자동화.
  3. 마이크로서비스
    • 각 서비스가 독립된 컨테이너로 배포되고 실행됨.
  4. 하이브리드 클라우드
    • 클라우드 환경 간 애플리케이션 이동성을 보장.

Docker는 가상화와 컨테이너 기술을 결합해 IT 업계의 혁신을 주도하고 있으며, 지속적으로 발전하는 생태계를 형성하고 있습니다.

  • 구성요소
    • Docker Engine(runtime): dockerd docker engine api
    • Docker Client (CLI): docker
    • Docker Objects : Docker container, Docker Image, Docker service
    • Docker Registry
  • 도구
    • Docker Compose: docker-compose, docker-compose.yaml
    • Docker Swarm - docker swarm CLI, docker node CLI

 

  • Dockerfile, Docker Image, Docker Container의 관계

dockerfile에서, 보라색 글자가 instruction, 파란색 글자가 instruction parameter가 ,한 줄씩 실행되면서, 업데이트 된다. 이미지가 구축되어 나간다. 실행이 될 때마다, layer가 attach된다. (나중에 layer를 벗겨낼 수도 있다.)

모든 것이 들어있는, ‘물리적인’ 도커 이미지를 → 생성/실행하면 → 도커 컨테이너에 올린다. 그럼 하나의 애플리케이션 독립 환경을 가지게 된다. (장애가 생기면, 도커 컨테이너만 확인하면 된다. 일반 os랑은 상관없다.)

 

  • Docker 환경 아키텍처

Docker 호스트 : Docker Desktop을 설치한 데스크탑. 여기에 서버와 클라이언트가 설치되었다. api를 호출하여 서버에 명령을 내린다.

Ex) Docker 서버에 특정 이미지를 실행하고자 한다.

  • 호스트 내부 Ubuntu Linux에 있다면, → 곧바로 컨테이너에 올리면 된다.
  • 만약 Docker Hub의 Ubuntu Linux에 있다면, 내부의 이미지 Ubuntu linux로 가져와서 → 컨테이너에 올리면 된다.

[실습] Docker Image 다루기

  • docker search [options] 검색어 : 도커 허브에 등록된 리포지토리 검색 Ex) docker search —limit 5 mysql
  • docker image pull <옵션> <리포지토리[:태그] > : 도커 레지스트리에서 이미지 내려받기 Ex) docker image pull mongo:latest
    • 여러 layer를 각각 내려받는다. (한번에 다 가져오지 않는다.) 네트워크 속도도 유연하게 대응할 수 있고, roll back이 필요한 경우 할 수 도 있다.
  • docker image ls <옵션> <리포지토리[:태그] > : 호스트에 저장된 이미지 목록
  • docker image tag <원본 이미지[:태그]><새 이미지[:태그]> 또는 docker images : 이미지 ID에 붙이는 별명 (쉬운 식별). 태그 하나 당 이미지 하나
#0. sudo 없이 도커 사용
# sudo usermod -aG docker tony
# sudo -su tony
# sudo chmod 666 /var/run/docker.sock
 
#1. 이미지 검색
docker search --limit 5  mysql
docker search steelflea/guestbook:v1

#2. 이미지 내려 받기
##Docker Hub 레지스트리에서 ASP.NET Sample 앱 이미지 검색
# :특정 버젼 명(미입력시 latest)
docker pull mcr.microsoft.com/dotnet/samples:aspnetapp
docker pull mcr.microsoft.com/azuredocs/aci-helloworld

#3. 이미지 목록 확인
docker image ls
docker images

#4. 이미지에 태그(새로운 이름) 추가
docker image tag mcr.microsoft.com/azuredocs/aci-helloworld hello.docker/aci-helloworld:latest
docker tag mcr.microsoft.com/dotnet/samples:aspnetapp hello.docker/samples

[실습] Docker file과 이미지 빌드

Dockerfile 문법

  • FROM <이미지명>:<태그>
  • RUN <도커 컨테이너 안에서 실행할 명령 >; 빌드하는 시점
  • COPY <원본파일/디렉터리> <대상 디렉터리 >; 현재 나의 로컬 소스 코드를 → 대상 디렉터리로 복사. 그곳이 작업 디렉토리가 된다.
  • CMD ; 실행하는 시점
    • '문법 1 - CMD [“명령1 ” “인자1 ” “인자2” …]
    • '문법 2 - CMD <명령> <인자1 > <인자2>
  • ADD ; COPY랑 비슷하다. 하지만, 원본이 다른remote 위치에 있더라도 가져올 수 있는 것은 ADD만 가능하다.
    • 문법 1 一 ADD <원본> <대상>
    • 문법 2(공백이 있는경우) ㅡ ADD [“<원본>”, ”<대상>”]
  • ENV<환경변수명> <값>; Database의 admin 값들
  • EXPOSE <포트1> <포트2>; 실행중인 컨테이너에 액세스 하기 위한 포트 입력
  • WORKER <작업 디렉터리 경로>
  • LABEL <이미지를 만든사람에대한정보>; 큰 의미는 없지만, 자신이 만든 것임을 정보 기록 ex) copyright 이름 연락처

Docker 이미지 빌드

  • docker image build <Dockerfile>
    • 태그 생략 시 → latest
    • [-f<다른파일명>] → Dockerfile이 아닌경우 ; 원래는 위치만 지정하면, 해당 위치에서의 Dockerfile을 가져오긴 한다. 근데 그렇지 않은 경우는 파일 명 입력.
    • [-pull = true] → 베이스 이미지를 매번 다시 받음 ; 이미지 빌드 시마다, 항상 새로운 이미지를 가져온다.
    • 사용자 네임스페이스 추가 → 이미지 명 충돌 방지 ; 내 로컬에서는 충돌이 안 나는데, 서버에 올리다가 이미지 명이 충돌할 수 있는 경우를 방지.

#0. Dockerfile 확인
#1. 도커 이미지 빌드
docker image build ./firstNodeDockerApp -t firstnode-app:1.0
docker build ./guestbook -t guestbook-app # 앞서 baseimage를 가져왔기에, 시간이 더 짧게 걸린다.

#2. 이미지 확인
docker images
더보기
PS C:\lab\day4> #0. Dockerfile 확인  
PS C:\lab\day4> #1. 도커 이미지 빌드
PS C:\lab\day4> docker image build ./firstNodeDockerApp -t firstnode-app:1.0
[+] Building 87.7s (10/10) FINISHED                                                                docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 181B                                                                               0.0s
 => [internal] load metadata for docker.io/library/node:10.16.3                                                    2.9s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [1/5] FROM docker.io/library/node:10.16.3@sha256:f4b6f471cdd4b66b27eef899e7f8423ecd9fbfc863b2cb7a59978a7f64c  81.3s
 => => resolve docker.io/library/node:10.16.3@sha256:f4b6f471cdd4b66b27eef899e7f8423ecd9fbfc863b2cb7a59978a7f64c8  0.0s
 => => sha256:13fb072986dbc2e2b54c5a715cae07beb9c13bccfd154e6440db7137d63e9e48 1.40MB / 1.40MB                     0.6s 
 => => sha256:627a2df1901880015f22dd3f852bfd35c1db188b94b84fa7c9a80df381e7b6a6 292B / 292B                         0.6s 
 => => sha256:aad1e8812bc2dad6ac66f67fdbbc89c64d30ac2a5dedf5ee4a78fbc972b8c79f 20.65MB / 20.65MB                   9.6s
 => => sha256:524be717efb1e5a1cc0998f4ffeb8b3c2613195141ca4aa490da82e0353b235c 4.16kB / 4.16kB                     0.8s
 => => sha256:0bbd8b48260f38a14e012ec233c3e934d9caea30f11ddb2df58577790263c123 214.84MB / 214.84MB                75.8s 
 => => sha256:6ef14aff1139e1065ec0928ae1c07f2cff8c2b35e760f4b463df5c64e6ea1101 50.07MB / 50.07MB                  41.2s 
 => => sha256:f8f0759202953be4b156f44bba90b682b61f985f9bbc60e7262b216f70dabb96 4.34MB / 4.34MB                     1.9s
 => => sha256:db3b6004c61a0e86fbf910b9b4a6611ae79e238a336011a1b5f9b177d85cbf9d 10.80MB / 10.80MB                   3.0s
 => => sha256:9a0b0ce99936ce4861d44ce1f193e881e5b40b5bf1847627061205b092fa7f1d 45.38MB / 45.38MB                  13.2s
 => => extracting sha256:9a0b0ce99936ce4861d44ce1f193e881e5b40b5bf1847627061205b092fa7f1d                          1.0s
 => => extracting sha256:db3b6004c61a0e86fbf910b9b4a6611ae79e238a336011a1b5f9b177d85cbf9d                          0.2s
 => => extracting sha256:f8f0759202953be4b156f44bba90b682b61f985f9bbc60e7262b216f70dabb96                          0.1s
 => => extracting sha256:6ef14aff1139e1065ec0928ae1c07f2cff8c2b35e760f4b463df5c64e6ea1101                          1.2s
 => => extracting sha256:0bbd8b48260f38a14e012ec233c3e934d9caea30f11ddb2df58577790263c123                          3.8s
 => => extracting sha256:524be717efb1e5a1cc0998f4ffeb8b3c2613195141ca4aa490da82e0353b235c                          0.0s
 => => extracting sha256:aad1e8812bc2dad6ac66f67fdbbc89c64d30ac2a5dedf5ee4a78fbc972b8c79f                          0.9s
 => => extracting sha256:13fb072986dbc2e2b54c5a715cae07beb9c13bccfd154e6440db7137d63e9e48                          0.0s
 => => extracting sha256:627a2df1901880015f22dd3f852bfd35c1db188b94b84fa7c9a80df381e7b6a6                          0.0s
 => [internal] load build context                                                                                  0.0s 
 => => transferring context: 20.46kB                                                                               0.0s 
 => [2/5] RUN mkdir -p /app                                                                                        0.8s 
 => [3/5] WORKDIR /app                                                                                             0.0s 
 => [4/5] ADD . /app                                                                                               0.0s 
 => [5/5] RUN npm install                                                                                          1.8s 
 => exporting to image                                                                                             0.5s 
 => => exporting layers                                                                                            0.2s 
 => => exporting manifest sha256:8cc682c7d77df75badbf6c75b4c13700d00f43245ab7d1f859c4b5874f193411                  0.0s 
 => => exporting config sha256:20c05fa278597753483fe88e6bdb63982ab1f4fd1f0c2c4451139db762acad0a                    0.0s 
 => => exporting attestation manifest sha256:27d82e33551d41f8c257e95789b26dee55d8ec0e199bca5b41c6c5829a081a40      0.0s 
 => => exporting manifest list sha256:cc76c98e1990d86d422ad00026d0f81ce76350bcfd73d4db5aa4c6e6dd27986c             0.0s 
 => => naming to docker.io/library/firstnode-app:1.0                                                               0.0s 
 => => unpacking to docker.io/library/firstnode-app:1.0                                                            0.2s
 PS C:\lab\day4> docker build ./guestbook -t guestbook-app # 앞서 baseimage를 가져왔기에, 시간이 더 짧게 걸린다.
[+] Building 4.7s (10/10) FINISHED                                                                 docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 181B                                                                               0.0s 
 => [internal] load metadata for docker.io/library/node:10.16.3                                                    1.0s 
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s 
 => [1/5] FROM docker.io/library/node:10.16.3@sha256:f4b6f471cdd4b66b27eef899e7f8423ecd9fbfc863b2cb7a59978a7f64c8  0.0s 
 => => resolve docker.io/library/node:10.16.3@sha256:f4b6f471cdd4b66b27eef899e7f8423ecd9fbfc863b2cb7a59978a7f64c8  0.0s 
 => [internal] load build context                                                                                  0.0s 
 => => transferring context: 22.46kB                                                                               0.0s 
 => CACHED [2/5] RUN mkdir -p /app                                                                                 0.0s 
 => CACHED [3/5] WORKDIR /app                                                                                      0.0s 
 => [4/5] ADD . /app                                                                                               0.0s 
 => [5/5] RUN npm install                                                                                          3.0s
 => exporting to image                                                                                             0.4s
 => => exporting layers                                                                                            0.2s
 => => exporting manifest sha256:093105a3247a7c3d38596885a7f2c026521bfcb4180b0e1037851a955881e9ab                  0.0s
 => => exporting config sha256:8729399f71e7a3df638c43603381fd56fd150f86f80818f88d285eaaa8ed4e4b                    0.0s 
 => => exporting attestation manifest sha256:f7a5be43e3c632ab252fd1580f91a27107c721259b93c9b77971a0d0b5adc9d3      0.0s 
 => => exporting manifest list sha256:a6efe6193dae25b0bb899cc250a86f8bbecdb6593bfbc4ca5410266673714924             0.0s 
 => => naming to docker.io/library/guestbook-app:latest                                                            0.0s 
 => => unpacking to docker.io/library/guestbook-app:latest                                                         0.1s 
PS C:\lab\day4> docker images
REPOSITORY      TAG       IMAGE ID       CREATED              SIZE
guestbook-app   latest    a6efe6193dae   About a minute ago   1.35GB
firstnode-app   1.0       cc76c98e1990   3 minutes ago        1.35GB

이미지가 잘 다운받아졌다.