본문 바로가기
AWS Cloud School 8기/도커(docker)

컨테이너 가상화

by YUNZEE 2025. 3. 18.
728x90

📌 컨테이너(Container)란?

컨테이너는 애플리케이션과 그 실행에 필요한 모든 라이브러리, 종속성, 설정 파일 등을 하나의 패키지로 묶어 독립적으로 실행할 수 있도록 하는 가상화 기술임

특징

  • 경량성: 운영체제(OS)를 가상화하는 기존 VM(Virtual Machine) 보다 훨씬 가벼움
  • 이식성(Portability): 개발 환경과 운영 환경이 동일하여 "내 PC에서는 되는데 서버에서는 안 돼요" 같은 문제가 적어짐
  • 격리성(Isolation): 각 컨테이너는 독립적으로 실행되어 충돌을 방지함
  • 빠른 배포: 컨테이너는 가볍고 실행 속도가 빠르므로 DevOps 및 CI/CD 환경에서 유용

📌도커(Docker)란?

도커(Docker)는 컨테이너를 쉽게 생성하고 관리할 수 있도록 도와주는 컨테이너 관리 플랫폼

도커의 역할

  • 애플리케이션을 컨테이너로 패키징
  • 컨테이너 실행 및 관리
  • 이미지(컨테이너 템플릿) 생성 및 배포

도커 주요 개념

  • 이미지(Image): 컨테이너를 실행하기 위한 템플릿(설정 및 애플리케이션 포함)
  • 컨테이너(Container): 이미지를 기반으로 실행되는 독립적인 환경
  • 도커 허브(Docker Hub): 이미지 저장소 (GitHub 같은 역할)
  • 도커 파일(Dockerfile): 컨테이너 이미지를 생성하기 위한 설정 파일
  • 도커 컴포즈(Docker Compose): 여러 개의 컨테이너를 한꺼번에 관리하는 도구

도커 사용 시 장점

  • 실행 환경 일관성 유지 (개발-PC, 스테이징, 운영 서버 간 환경 차이 해결)
  • 배포 속도 향상 (빠르게 컨테이너 실행 가능)
  • 확장성 (AWS ECS, Kubernetes와 연동 가능)

📌하이퍼바이저 가상화 (Hypervisor Virtualization)

하이퍼바이저는 가상 머신을 관리하고, 각 가상 머신은 독립된 운영 체제와 애플리케이션을 실행함.

하이퍼바이저 종류

  1. 타입 1 하이퍼바이저 (Bare-metal Hypervisor):
    • 직접 하드웨어 위에서 실행되며, 운영 체제가 필요 없음
    • 예: VMware ESXi, Microsoft Hyper-V, Xen
  2. 타입 2 하이퍼바이저 (Hosted Hypervisor):
    • 호스트 운영 체제 위에서 실행됨
    • 예: VMware Workstation, Oracle VirtualBox

하이퍼바이저의 특징

  • 물리적 하드웨어를 가상화하여 각 VM에 독립적인 운영 체제와 하드웨어 리소스를 할당함
  • 리소스 격리가 매우 강력하며, 가상 머신은 서로 독립적이고 다른 가상 머신과 상호작용하지 않음
  • 하이퍼바이저는 하드웨어 리소스를 관리하고 각 VM에 가상화된 자원을 할당함

📌 하이퍼바이저 가상화& 컨테이너 가상화(Docker)

  하이퍼바이저 가상화 컨테이너 가상화 
격리수준 하드웨어 수준 프로세스 수준
자원 오버헤드가 높음 오버헤드가 낮음
보안 컨테이너 가상화에 비해 높음 하이퍼바이저에 비해 낮음
프로비저닝 속도 상대적으로 느림
guest VM부터 구성
상대적으로 빠름, 호스트 H/W와 커널까지 공유하고 있음. 따로 guest VM과 Guest Kernel을 구성할 필요가 없음.
호환성 원하는 OS 설치 가능 호스트에 종속 됨
리눅스 기반이 컨테이너만 가능

- 컨테이너의 최상위 디렉토리를 변경하여, 컨테이너 내부에서 접근 가능한 디렉터리(공간)를 제한함.

📌chroot

📌Cgroup(Control Group)

- Cgroup(Control Group): 자원을 컨트롤.

-> 컨테이너에 호스트의 자원을 '제한'

-> 하이퍼바이저 VM의 경우엔 자원을 '할당' (2 core, 2GB)하는 개념이라면 컨테이너의 경우 '제한'을 하는 개념(램을 2GB까지 쓸 수 있음 )

-> 호스트의 모든 자원을 하나의 컨테이너가 끌어다 쓰는 것을 제한

 

컨테이너 가상화의 종류

- Docker(컨테이너를 관리하는데 최적화, 앞으로 K8s를 할 때도 계속 사용): 도커에서는 이미지화하는 것이 중요하고, 이걸 어떻게 활용할지 생각을 잘해야 됨.

- Containerd( K8s를 구성하는 런타임으로 쓸 예정)

- CRIO

- LXC(Linux Container)

실습 환경 구성

도커 설치

더보기

- ubuntu_tem을 clone 해서 host vm 만들기

- hostname - host, IP - 211.183.3.100 /24

root@ubun-tem:/home/user1# vi /etc/netplan/00-installer-config.yaml

# ubuntu 20.04의 네트워크 설정 파일. ip를 수정할 수 있음

root@ubun-tem:~# netplan apply

#systemctl restart network이랑 비슷

curl -fsSL https://get.docker.com -o get-docker.sh

 

  • curl: **curl**은 데이터 전송 도구로, HTTP, FTP와 같은 프로토콜을 통해 웹 서버에서 데이터를 가져올 수 있음. 이 명령어는 Docker 설치 스크립트를 다운로드하는 데 사용됨.
    • -f: fail silently 옵션으로, HTTP 요청이 실패하면 오류 메시지를 출력하지 않고 종료됨.
    • -s: silent 옵션으로, 출력 메시지를 최소화함.
    • -S: show error 옵션으로, 오류가 발생하면 오류 메시지를 출력함.
    • -L: location 옵션으로, 리다이렉트된 URL을 자동으로 따라가서 데이터를 다운로드함.
  • https://get.docker.com: Docker를 설치하기 위한 설치 스크립트 URL임
  • -o get-docker.sh: 다운로드한 데이터를 **get-docker.sh**라는 파일로 저장

root@host:~# chmod +x get-docker.sh

  • chmod: 파일 권한을 변경하는 명령어
  • +x: 파일에 실행 권한을 부여
  • get-docker.sh: 권한을 부여할 파일명

root@host:~# ./get-docker.sh

- 잘 설치된 걸 볼 수 있음!

- 네임도 변경시켜주기

- ip add 했을 때 보이는 docker0를 잘 확인해 주는 게 중요!

- 어떤 네트워크 안에 있어야 됨, docker0라는 NIC를 통해서, 어떤  네트워크가 구성되어 있음

- 지금 보이는 캡처 본은 정상적인 상태인데, 여기서 ip가 안 보이면 비정상적인 상태임

- 나중에 컨테이너는 실행이 되는데, 컨테이너끼리나 외부로 통신이 안되면 ip add를 쳐서 docker0이라는 인터페이스의 아이피가 잘 올라왔는지 확인을 하자

- 아이피가 없으면 systemctl restart docker를 실행

- nat 대역과 비슷한 docker 대역?

- 리눅스 측면에서 바라본 컨테이너 네트워크(172.16.0.0/16)

 

docker run nginx

- 원래는 docker container run nginx라고 해야 되지만 생략가능

- 왜냐면 run 될게 container 말고 없음

- 도커 허브에서 nginx를 설치함?

root@host:~# docker run nginx

-> nginx라는 이미지를 동작시켜줘라는 명령어
Unable to find image 'nginx:latest' locally

-> 가장 최근 nginx이미지를 설치해 줌
latest: Pulling from library/nginx
7cf63256a31a: Pull complete 
bf9acace214a: Pull complete 

-> 하나만 다운 받는 것이 아니라 여러 개 다운을 받음

-> pull 했다는 것은 다운로드했다는 의미임 <-> push

-> 여러 개의 layer가 층을 쌓아 nginx를 실행시킴

 

container image

"nginx: latest" => tag를 생략하면 :latest가 디폴트 값

 

docker run에 어떤 의미가 내포되어 있냐라고 생각했을 때, create랑 start라는 의미가 담겨있음.

- 뭘 입력할 수 없는 상태는 foreground 상태임

- 컨테이너가 잘 동작하려면 반드시 foreground 상태인 프로세스가 존재해여함

ex) 자바컨테이너: 자바가 잘 동작하기 위한 명령을 실행해둬야 함

java -jar app.jar

=> 아주 기본적이면서 제일 중요한 개념임.

- sweet_wozniak라는 컨테이너 이름이 생성됨

ctrl + c를 하면 나가짐 

- docker가 끝난 상태에서는 정상동작중인 컨테이너를 찾을 수 없음

- 컨테이너 내부에서 포어그라운드로 동작하던 프로세스가 종료되자 컨테이너가 중지 됐음. 이 차이를 이해하고 해결하는 게 컨테이너 가상화에서 필수이자 가장 기본이 되는 능력임.

docker ps -a

-> 모든 도커 상태를 볼 수 있음 

-> 중지된 컨테이너는 -a 옵션으로 볼 수 있음

컨테이너 지우기

docker rm -f sweet_wozniak

-f 강제로 sweet_wozniak이라는 컨테이너를 지우겠다는 의미임.

 

root@host:~# docker run --name test centos:7

- name: test

- centos는 7로 할 것

- centos:7 이미지로 test라는 이름을 갖는 컨테이너를 띄우겠다는 의미임

 - 하지만 

 - 이렇게 실행되고 있지 않은 게 보임

- COMMAND: 컨테이너가 실행 시 수행되는 명령어

-  /bin/bash이 명령으로는 특정한 프로세스가 실행가 없네 

- 다시 말해, centos:7이라는 이미지는 처음에 설계 당시, '이 컨테이너를 실행하면 bin/bash라는 명령을 수행해라'라고 만들어졌음. 따라서 우리가 docker run 명령으로 컨테이너를 실행 시키면서 /bin/bash라는 명령어 수행됐을 것 임. 하지만 컨테이너가 '정상 동작'하려면 반드시 컨테이너 내부에서 포어그라운드로 프로세스가 실행되어야하기때문에 그 조건을 충족시키지 못해서 결국 컨테이너는 stop된 상태로 존재함.

 

root@host:~# docker run --name test centos:7 sleep 60

- 강제로 실행된 모습을 볼 수 있음

- 컨테이너 실행시 = command -> 수행할 명령어

-> 보통은 컨테이너를 foreground로 유지하는 것이 목적임

- 60초가 지나니까 자연스럽게 off 된 걸 볼 수 있음 

- ststus가 up 상태이면 잘 실행된 상태인 걸 알 수 있음

 

docker start : 실행

docker restart : 재실행.

docker stop : 중지.

 

질문) 컨테이너의 경우엔 꼭 운영체제가 아닌, nginx나 python, java 같은 앱도 컨테이너로 만들 수 있다 = 이미 호스트의 커널을 공유 중이기 때문.

- 원래 있던 건 실행이 안됨

- kvm의 이미지와 컨테이너 이미지가 어떤 차이가 있는지 이해하면 좋음

- 컨테이너 이미지로 컨테이너를 띄우더라도 원래 이미지는 그대로 존재함.

- vm은 많은 리소스로 무겁고, 컨테이너는 적은 리소스로 가벼움

detach (-d, 매우 중요)

더보기

- -d는 detach 된 상태라는 걸 의미함

- detach는 백그라운드로 컨테이너를 실행

- 컨테이너의 세부 정보를 알 수 있음

root@host:~# docker inspect ntest | grep -i ipa

- 컨테이너의 ip를 조회할 수 있음

- 내부에 실제로 컨테이너가 생긴 걸 확인할 수 있음

- ntest안에 nginx가 있을 거고 그 앱은 foreground 한 상태일 것 임

- 80번/ 172.17.0.2를 통해서 빠져나감?

- curl을 쳤을 때 잘 나오는 걸 확인할 수 있음

- 리눅스에서 컨테이너로 통신이 잘 되는 걸 확인할 수 있음.

- logs는 컨테이너의 로그, 주로 컨테이너가 잘 동작하지 않을 때 볼 수 있음

- docker logs: 일반적으로 컨테이너를 실행시켰을 때 내부의 프로세스가 적절하게 동작하지 않아 컨테이너가 중지되는 경우가 있음. 왜 중지됐는지 여부를 어느 정도 확인이 가능함

- 지금 우리는 3개의 대역대가 있음

- 차이가 있다면 DRIVER의 차이임

- NAT대역이랑 똑같은 bridge 대역

 

root@host:~# docker image ls

- 컨테이너 이미지 목록

- REPOSITOTY는 책장

- TAG는 책

- 우리가 pull 해온 두 개의 이미지 (nginx:latest, centos:7은 도커허브에서 가져온 오피셜 이미지)

root@host:~# docker exec ntest ls

- 컨테이너 내부에서 명령을 수행

ntest: 명령을 수행할 컨테이너 이름

ls: 명령

 

root@host:~# docker rm -f ntest

- 지우고

- it: 컨테이너 접속

더보기

root@host:~# docker run -it --name cent centos:7

- i: interactive, 상호작용 -t: terminal

해당 컨테이너와 터미널로 상호작용하겠다 = 해당 컨테이너 접속

 

 - cent라는 컨테이너를 실행과 동시에 위에 입력한 해당 컨테이너 안으로 들어감

- 나로 인해서 잘 동작하고 있음

- 내가 연 터미널이 bash

- 몰래 빠져나가는 거 ctrl +pq: 조심스럽게 빠져나갈 수 있음?

- 내가 열었던 터미널이 유지되어 컨테이너가 잘 동작됨.

 

- cent 컨테이너 삭제 후

- ctrl + d

- 포어그라운드로 동작하는 프로세스가 없기 때문에, 컨테이너는 중지 상태가 됨.

실습 1) myhttpd라는 이름을 갖는 컨테이너를 띄워보세요. 이 컨테이너를 백그라운드로 실행시키고, 컨테이너 이미지는 httpd:latest  이걸로 하세요. 그리고 컨테이너의 ip를 조회해서 curl을 쳐보세요.

더보기


root@host:~# docker run -d --name myhttpd httpd:latest

-d가 백그라운드를 의미함

- 어떤 특정 컨테이너가 있는데, docker ps를 쳤는데 잘 나타나면서 막상 연동이 오래 걸려서 안될 수도 있음

 

root@host:~# docker inspect myhttpd | grep -i ipa

root@host:~# curl 172.17.0.2

 - bash라는 command에 의해서는 외부에서 활성화되지 않음

- test라는 컨테이너는 실행되면서 bash라는 명령이 수행이 됨

- 근데 이 명령만으로는 내부에서 httpd 프로세스가 정상적으로 동작하지 않음

- 컨테이너를 foreground로 httpd:latest라는 유지시키는 명령어

- 컨테이너 내부에서 httpd-foreground라는 명령을 수행하면 외부에서 curl이 잘 됨.

 

 ctrl +pq 하고 나오기

- 내부로 진입해서 명령을 치겠다는 의미 = 콘솔 접속하는 느낌

- httpd라는 컨테이너이미지의 웹루트디렉터리

실습 2) httpd:latest 이미지로 이름이 index_http라는 컨테이너를 만들고 이 컨테이너의 주소로 curl 했을 때 hello라는 기본페이지가 보이도록 해보세요.

더보기

방법 1) 컨테이너 접속

root@host:~# docker run -d --name index_http httpd:latest

root@host:~# docker inspect index_http | grep -i ipa
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAMConfig": null,
                    "IPAddress": "172.17.0.2",
root@host:~# docker exec -it index_http bash
root@517173ca3a88:/usr/local/apache2# cd htdocs/
root@517173ca3a88:/usr/local/apache2/htdocs# echo hello > index.html

-  컨테이너 내부에서 curl을 쳤을 때 안 나가는 것이 아니라 curl이 설치가 안되어 있어서 그런 거임. 그래서 curl을 설치하고 curl치면 외부로 나감 localhost든 ip든 잘 html파일을 불어오는 걸 알 수 있음

- 명령어가 최소한으로 들어있기 때문에 사소한 예를 들어 curl이란 명령어나 ip add이라는 명령어가 없음. 그러니까 사소하게 필요한 것들을 다 설치해 줘야 됨

 

방법 2) 호스트에서 echo 명령 수행

root@host:~# docker exec index_http sh -c 'echo hello2 > /usr/local/apache2/htdocs/index.html'
root@host:~# curl 172.17.0.2
hello2

-> 특문이 없었으면 명령을 그대로 수행해도 되는데, 특문(리다이렉션)이 있었기 때문에 sh -c를 통해서 내가 수행할 명령어를 묶어줌.

실습 3) nginx:latest 이미지로 이름이 index_nginx라는 컨테이너를 만들고 이 컨테이너의 주소로 curl 했을 때 hello_nginx라는 기본페이지가 보이도록 해보세요.

더보기

방법 1)

root@host:~# docker run -d --name index_nginx nginx:latest

root@host:~# docker exec -it index_nginx bash

# 직접 들어가서 echo

 

방법 2) 호스트에서 exec 실행

root@host:~# docker exec index_nginx sh -c 'echo hello_nginx2 > /usr/share/nginx/html/index.html'

 

- 모든 컨테이너 삭제 = docker rm -f $(docker ps -qa)

 

root@host:~# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
5a983f0210b2   bridge    bridge    local : 외부와 통신 가능

d1b03fbcbfa4   host      host      local: 호스트 내에서만 통신
11c9b4e084bc   none      null      local: 네트워크 없음

volume (-v, 매우 중요)  

docker volume: 컨테이너와 호스트를 마운트 개념

- 컨테이너의 경우어떤 식으로든 컨테이너가 삭제되면, 내부의 데이터나 로그 같은 것들도 같이 삭제가 됨. 데이터베이스 컨테이너를 띄웠는데, 갑자기 컨테이너가 날아가면, 데이터도 다 날아감. 따라서 내가 영구적으로 저장시키고 싶은 데이터가 았다면 -v(볼륨)을 통해 host에 영구 저장을 시킬 수 있음

* 여러 번 사용 가능.

더보기

root@host:~# mkdir /host-txt

root@host:~# docker run -d --name vol_nginx -v /host-txt:/txt nginx:latest

root@host:~# echo vol-test > /host-txt/test.txt

root@host:~# docker exec vol_nginx cat /txt/test.txt
vol-test

root@host:~# docker exec -it vol_nginx bash

- 내부에서 삭제가 되면 

- 외부에서도 삭제가 됨.

실습 4) -it 옵션이나 exec를 쓰지 않고 nginx:latest 이미지로 컨테이너를 생성했을 때 vol_test라는 문구가 뜨도록 해보세요.

더보기

root@host:~# cd /
root@host:/# cd host-txt
root@host:/host-txt# ls
root@host:/host-txt# echo vol_test > index.html
root@host:/host-txt# ls
index.html

 

root@host:/host-txt# docker run -d --name my_nginx -v/host-txt:/usr/share/nginx/html nginx:latest

 

root@host:/host-txt# docker inspect my_nginx | grep -i ipa
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAMConfig": null,
                    "IPAddress": "172.17.0.2",
root@host:/host-txt# curl 172.17.0.2
vol_test

 

root@host:/host-txt# docker run -it --name test nginx:latest bash
root@edc9e008ab85:/# cd /usr/share/nginx/html/
root@edc9e008ab85:/usr/share/nginx/html# ls
50x.html  index.html

# 새로 nginx 컨테이너를 띄워서 내부로 진입하여, /usr/share/nginx/html로 가보니, 이미 index.html이 존재하더라.

# => 기존에 파일이 존재하면, 호스트의 파일이 우선순위가 높음

 

root@host:~# docker exec -it my_nginx bash
root@0cabd61818fc:/# ls /usr/share/nginx/html
index.html

 

# 50x.html이 존재하지 않는 상황.  컨테이너 내부의 /usr/share/nginx/html이 바라보는 대상이 호스트의 /host-txt라는 경로로 변경됨

Publish (-p, 매우중요)

- 컨테이너를 외부에 노출, 배포(publish) 시키는 옵션

더보기

root@host:~# cd /
root@host:/# cd host-txt
root@host:/host-txt# docker run -d --name pub -p 8080:80 nginx:latest

-p : publish

8080: 호스트의 포트

80: 컨테이너의 포트

 - 컨테이너 내부에서 동작하는 프로세스가 서비스를 제공하는 포트임

- 일반적으로 웹서버는 80. python  장고는 8000, node.js 기반은 3000번 등등... 중요한 건 컨테이너 내부에서 어떤 포트로 서비스가 제공됨 

 

- 실습, 테스트 할 때는 되도록이면 호스트의 윌노운 포트는 쓰지 않는 게 좋다. 만약에 최종적으로 웹서버를 배포하는 거면 80번 포트 같은 웰노운포트를 쓰는 게 당연히 좋음

실습) 호스트의  /host_vol 이라는 경로에는 간단한 댕댕이 템플릿이 존재한다. 이 무료템플릿을 호스트의 7979번 포트로 퍼블리쉬해보세요.

더보기

풀이) httpd:latest로 해보겠습니다.

 

root@host:/# apt update -y && apt install -y unzip wget

 

root@host:~# wget https://www.free-css.com/assets/files/free-css-templates/download/page8/dogcare.zip

# 템플릿 다운

 

root@host:~# unzip dogcare.zip

 

root@host:~# cd dogcare/

root@host:~/dogcare# mkdir /host_vol

root@host:~/dogcare# cp -r ./* /host_vol

root@host:~/dogcare# cd /host_vol/

 

root@host:/host_vol# docker run -d --name web -v /host_vol:/usr/local/apache2/htdocs -p 7979:80 httpd:latest

root@host:/host_vol# docker run -d --name web -v /host_vol:/usr/local/apache2/htdocs -p 7979:80 httpd:latest
2084946d521f04156cafe2c8b8743a98beb818b5803c7e6f314599946d2508d1
root@host:/host_vol# ls
about.html  blog.html  css  fonts  images  index.html  js  shop.html
root@host:/host_vol# echo cptest > cptest.txt
root@host:/host_vol# 

docker cp (중요도 하)

더보기

1. 호스트의 파일을 컨테이너에 복사

root@host:/host_vol# docker cp web:/cptest.txt ./
Successfully copied 2.05kB to /host_vol/./
root@host:/host_vol# ls
about.html  blog.html  cptest.txt  css  fonts  images  index.html  js  shop.html
root@host:/host_vol# cat cptest.txt
cptest

- 최상위 디렉터리에 cptest.txt가 존재하는 걸 확인가능

- 컨테이너의 파일을 호스트에 복사

환경 변수 지정: -e (중요도: 중)

더보기

root@host:/host_vol# docker run -d --name envcon -e ENVTEST=test nginx:latest
2e4b8f2033da2b70c44f76c10d738566d0ebd9c1fcab08e1723f506ddeac54e9

root@host:~# docker exec -it envcon bash
root@2e4b8f2033da:/# echo $ENVTEST

test

-> 데이터베이스 컨테이너 생성 시 많이 사용함

-> 데이터베이스 컨테이너의 경우엔 적어도 root패스워드를 설정해야 컨테이너가 잘 동작함

작업디렉터리지정: -w(나중에 중요함)

더보기

root@host:~# docker run -d --name workdir nginx:latest

root@host:/host_vol# docker exec -it workdir bash
root@84275b56ac90:/# pwd
/

- 컨테이너에 진입했을 때의 경로 = 현재작업디렉터리(pwd)

root@host:/host_vol# docker run -d --name workdir1 -w /usr nginx:latest

- 작업디렉터리를 /usr로 지정

 

root@host:~# systemctl restart docker

- 도커 엔진 재시작 시 동작중이던 컨테이너들이 멈추게 됨

 

root@host:~# docker rm -f $(docker ps -qa)

 

root@host:~# docker run -d --name retest --restart=always nginx:latest

- 원래는 도커가 정지 상태가 맞지만 restart always를 사용했기 때문에 restart를 해줘도 도커가 안 꺼지는 걸 확인할 수 있음.

- -- restart=always: 컨테이너가 중지가 돼도 항상 재시작

과제)

# 1. 호스트에 파일이 존재하는 경우

# 2. 마운트 포인트가 이미 존재하고 파일도 있는 경우.

-> 두 개가 어떤 차이가 있고, 어떻게 되는지 한 번 생각해 보세요.

-> 호스트 파일이 존재하고 파일도 이미 있음. 가려진 상태이지 없어진 건 아님

728x90