컨테이너 가상화(Docker)
하나의 호스트 위에 여러 컨테이너를 적재
서버(하이퍼바이저) 가상화
보안 수준 유리
컨테이너 가상화
자원의 오버헤드 측면, 프로비저닝이 필요없음, 자원/속도 유리
하이퍼바이저 가상화 | 컨테이너 가상화 | |
격리수준 | 하드웨어 수준 | 프로세스 수준 |
자원 | 오버헤드 높음 | 오버헤드 낮음 |
보안 | 컨테이너 가상화보다 높음 | 하이퍼바이저보다 낮음 |
프로비저닝 속도 | 상대적으로 느림 guest VM부터 구성 |
상대적으로 빠름 호스트 H/W와 커널까지 공유하기 때문 따로 Guest VM과 Quest 커널을 구성할 필요없음 |
호환성 | 원하는 OS 설치 가능 | 호스트에 종속된다. 리눅스 기반의 컨테이너만 가능 |
컨테이너 가상화에 필요한 리눅스 기반 기술
1. ch root
컨테이너의 최상위 디렉토리 변경
컨테이너 내부에서 접근 가능한 디렉토리(공간)를 제한
2. Cgroup(Control Group)
자원을 컨트롤, 컨테이너에 호스트의 자원을 제한
- 하이퍼바이저 VM의 경우, 자원을 '할당'하는 개념(2core, 2GB)
- 컨테이너의 경우, '제한'하는 개념(램을 2GB까지)
- 호스트의 모든 자원을 하나의 컨테이너가 끌어다 쓰는 것을 제한
컨테이너 가상화의 종류
- Docker: 컨테이너를 관리하는데 최적화, 앞으로 K8s를 할때도 계속 사용
- Containerd: K8s를 구성하는 런타임으로 사용할 예정
- CRIO
- LXC(Linux Container)
실습환경 구성
ubuntu 20.04에 도커 설치 → ubuntu-tem clone
hostname: host, RAM: 4GB, IP: 211.183.3.100 /24
- IP: 211.183.3.100 /24
root@ubun-tem:~# vi /etc/netplan/00-installer-config.yaml
root@ubun-tem:~# netplan apply
- hostname: host
root@ubun-tem:~# hostnamectl set-hostname host
root@ubun-tem:~# su
도커 설치
- 최상위 디렉토리 위치에 설치 스크립트 다운
root@host:~# curl -fsSL https://get.docker.com -o get-docker.sh
- 권한 부여 후 실행
root@host:~# chmod +x get-docker.sh
root@host:~# ./get-docker.sh
- 설치 및 버전 확인
root@host:~# docker -v
Docker version 28.0.1, build 068a01e
# 컨테이너는 실행되는데, 컨테이너끼리나 외부로 통신이 안되면 docker0 인터페이스 IP가 잘 올라왔는지 확인하기
# IP가 없으면 systemctl restart docker 실행하기
- IP 확인
root@host:~# ip add
- IP 안뜰경우 실행
root@host:~# systemctl restart docker
리눅스 측면에서 바라본 컨테이너 네트워크 (172.16.0.0/16)
도커 기본 명령어
docker run
- nginx라는 이미지로 컨테이너를 run(생성+동작)
root@host:~# docker run nginx
# 컨테이너가 잘 동작하려면 반드시 foreground 상태인 프로세스가 존재해야 한다.
- 자바컨테이너 : java -jar app.jar
(자바가 잘 동작하기위한 명령을 실행해줘야 한다.) - 파이썬컨테이너 : python manager.py runserver
- nginx컨테이너 : nginx -g "daemon off;"
- httpd컨테이너 : httpd -D FOREGROUND
→ 기본적이고 중요한 개념
터미널 창을 하나 더 열어서 docker ps를 쳐보면,
- docker ps : 컨테이너 상태
- Ctrl + C (Cancel)로 종료 후 실행 중인 컨테이너 확인
# 내가 포어그라운드로 점유하던 프로세스가 종료됐다. 세션이 닫힘
# 컨테이너 내부에서 포어그라운드로 동작하던 프로세스가 종료되자 컨테이너가 중지됐다. 이 차이를 이해하고 해결하는게 컨테이너 가상화에서 필수이자 가장 기본이 되는 능력이다.
- docker ps -a : 모든 컨테이너 확인
- docker rm : 컨테이너 삭제
- -f 강제삭제: 실행중인 경우에도 삭제
- centos:7 이미지로 test라는 이름을 갖는 컨테이너 생성+동작
root@host:~# docker run --name test centos:7
# centos:7이라는 이미지는 처음에 설계당시, '이 컨테이너를 실행하면 /bin/bash라는 명령을 수행해라'라고 만들어졌다. 따라서 우리가 docker run 명령으로 컨테이너를 실행시켰을때 /bin/bash라는 명령이 수행됐을것이다. 하지만, 컨테이너가 '정상동작'하려면 반드시 컨테이너 내부에서 포어그라운드로 프로세스가 실행되어야하기 때문에 그 조건을 충족시키지 못해 결국 컨테이너는 중지상태가 된다.
- 삭제
- 재생성
# 이미지를 기준으로 왼쪽은 컨테이너 run에 필요한 옵션을 적어주고 오른쪽에는 이 컨테이너가 동작시 실행된 명령어(COMMAND)를 '하나' 적어준다. 컨테이너 동작시 60초 동안 포어그라운드 상태로 만들기 위해 sleep 60이라는 명령을 수행하도록하자
# 60초가 지난후 docker ps를 쳐보면 컨테이너 내부에서 foreground를 유지시켜주던 'sleep 60'이라는 명령어 종료로 인해 foreground 프로세스가 사라지면서 컨테이너는 중지된다.
# 컨테이너가 정상동작하고 있다면 pid가 1인 프로세스가 반드시 존재한다.
# status=up의 의미
- docker start : 실행
- docker restart : 재실행
- docker stop : 중지
질문) 컨테이너의 경우엔 꼭 운영체제가 아닌, nginx나 python, java같은 앱도 컨테이너로 만들 수 있다
→ 이미 호스트의 커널을 공유중이기 때문
# kvm의 이미지와 컨테이너이미지가 어떤 차이가 있는지 이해하면 좋다.
# 컨테이너이미지로 컨테이너를 띄우더라도 원래 이미지는 그대로 존재한다.
docker run 옵션
- -d(detach): 백그라운드로 컨테이너 실행
root@host:~# docker run -d --name ntest nginx:latest
- 컨테이너의 세부정보
root@host:~# docker inspect ntest
- NIC 확인
- 리눅스(host)에서 컨테이너로 통신이 잘 되는 것 확인
root@host:~# curl 172.17.0.2
- 컨테이너의 로그 확인
root@host:~# docker logs ntnest
# docker logs: 일반적으로 컨테이너를 실행시켰을 때, 내부의 프로세스가 적절하게 동작하지 않아 컨테이너가 중지되는 경우가 있는데, 왜 중지됐는지 여부를 어느정도 확인할 수 있다. (db연동이 안되거나, command에 문제가 있는 경우, 내부에서 프로세스가 동작 안할거임)
- 컨테이너 네트워크 목록
root@host:~# docker network ls
- 컨테이너 이미지 목록
root@host:~# docker image ls
- pull했던 두개의 이미지(nginx:latest, centos:7은 도커허브에서 가져온 오피셜 이미지)
- docker exec : 컨테이너 내부에서 명령을 수행
root@host:~# docker exec ntest ls
root@host:~# docker rm -f ntest
- 해당 컨테이너와 터미널로 상호작용하겠다 = 해당 컨테이너 접속
root@host:~# docker run -it --name cent centos:7
- -i : interactive, 상호작용
- -t : terminal
# cent라는 컨테이너를 실행과 동시에 안으로 들어옴
- Ctrl + PQ: 빠져나오고 컨테이너 동작 유지
# Ctrl + PQ로 빠져나오면 내가 열었던 터미널이 유지되어 컨테이너가 잘 동작한다.
- 삭제
- Ctrl + D: 프로세스 유지하지 않고 나옴
- 포어그라운드로 동작하는 프로세스가 없기때문에, 컨테이너는 중지상태
myhttpd라는 이름을 갖는 컨테이너를 띄워보세요. 이 컨테이너를 백그라운드로 실행시키고, 컨테이너 이미지는 httpd:latest 이걸로 하세요.
root@host:~# docker run -d --name myhttpd httpd:latest
- -d : detach, 백그라운드로 동작(호스트 입장에서)
이 컨테이너의 ip를 조회해서 curl을 해보세요.
root@host:~# docker inspect myhttpd | grep -i ipa
root@host:~# curl 172.17.0.2
- bash라는 command로는 내부에서 httpd라는 프로세스가 정상적으로 동작하지 않음
root@host:~# docker run -it --name test httpd:latest bash
# test라는 컨테이너는 실행(run)되면서 bash라는 명령이 수행이 됐다. 근데 이 명령만으로는 내부에서 httpd라는 프로세스가 정상적으로 동작하지 않는다.
- bash로 foreground가 동작하지 않음
root@host:~# docker run -it --name test httpd:latest bash
root@7d656a9c05bd:/usr/local/apache2#
root@host:~# curl 172.17.0.2
curl: (7) Failed to connect to 172.17.0.2 port 80: Connection refused
- 내부에서 foreground 명령을 통해 동작시킴
root@host:~# docker run -it --name test httpd:latest bash
root@7d656a9c05bd:/usr/local/apache2# httpd-foreground
root@host:~# curl 172.17.0.2
<html><body><h1>It works!</h1></body></html>
# 컨테이너 내부에서 httpd-foreground라는 명령을 수행하면 외부에서 curl이 잘 된다.
- 이미 동작중인 애한테 들어가겠다
root@host:~# docker exec -it test bash
# 이미 동작중인 컨테이너 내부로 진입해서 명령을 치겠다 = 콘솔접속하는느낌
실습)
httpd:latest 이미지로 이름이 index_http 라는 컨테이너를 만들고 이 컨테이너의 주소로 curl했을때 hello라는 기본페이지가 보이도록 해보세요.
방법1) 컨테이너 접속
root@host:~# docker run -d --name index_httpd httpd:latest
root@host:~# docker exec -it index_http bash
root@9260ca7f2409:/usr/local/apache2# cd htdocs/
root@9260ca7f2409:/usr/local/apache2/htdocs# echo hello > index.html
root@host:~# curl 172.17.0.2
방법2) 호스트에서 echo 명령 수행
root@host:~# docker exec index_httpd sh -c 'echo hello2 > /usr/local/apache2/htdocs/index.html'
# 특문(리다이렉션)이 없었으면 명령을 그대로 수행해도 되는데, 특문이 있어서 sh -c를 통해 수행할 명령어를 묶어줌.
실습2)
nginx:latest 이미지로 이름이 index_nginx 라는 컨테이너를 만들고 이 컨테이너의 주소로 curl했을때 hello라는 기본페이지가 보이도록 해보세요.
웹루트디렉토리는 검색해보세요!
- /usr/share/nginx/html/index.html
방법1) 직접들어가서 echo
root@host:~# docker run -d --name index_nginx nginx:latest
e999d59ad03d2aba656c16106099d471c523af2be8338d566f66a618dc369108
root@host:~# docker exec -it index_nginx bash
root@e999d59ad03d:/# echo hello > /usr/share/nginx/html/index.html
방법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 rm -f $(docker ps -qa)
docker volume (-v, ★★★★★)
컨테이너와 호스트를 마운트 개념
컨테이너의 경우, 어떤식으로든 컨테이너가 삭제되면 내부 데이터나 로그 등도 같이 삭제됨
→ 볼륨으로 호스트에 파일을 업로드, 백업
ex. 데이터베이스 컨테이너를 띄웠는데, 갑자기 컨테이너가 날아가면 데이터도 다 날라감
→ 따라서 영구적으로 저장하고 싶은 데이터가 있으면 -v(볼륨)을 통해 host에 영구 저장할 수 있음
- 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
# 호스트 및 컨테이너 내부에 디렉토리가 없으면 자동으로 생성함
과제)
1. 호스트에 파일이 존재하는 경우
2. 마운트 포인트가 이미 존재하고 파일도 있는 경우
→ 두개가 어떤 차이가 있고, 어떻게 되는지 한번 생각해보세요.
실습)
-it 옵션이나 exec를 쓰지 않고 nginx:latest 이미지로 컨테이너를 생성했을때 vol_test라는 문구가 뜨도록 해보세요.
root@host:~# cd /host-txt
root@host:/host-txt# ls
root@host:/host-txt# echo vol_test > index.html
root@host:/host-txt# docker run -d --name my_nginx -v /host-txt:/usr/share/nginx/html nginx:latest
292bd0dfce16ce267caf13bc708a960b1a4af99d2516f5c27bdf0dccb60580d5
root@host:/host-txt# docker inspect my_nginx | grep -i ipa
root@host:/host-txt# curl 172.17.0.2
root@host:/host-txt# docker run -it --name test nginx:latest bash
root@f981c26d04f1:/# cd /usr/share/nginx/html
root@f981c26d04f1:/usr/share/nginx/html# ls
# 새로 nginx 컨테이너를 띄워서 내부로 진입하여, /usr/share/nginx/html로 가보니, 이미 index.html이 존재함
→ 기존에 파일이 존재하면, 호스트의 파일이 우선순위가 높다.
# 50x.html이 존재하지 않는 상황. 컨테이너 내부의 /usr/share/nginx/html이 바라보는 대상이 호스트의 /host-txt 라는 경로로 변경 됐다.
- 전부 삭제
root@host:/# docker rm -f $(docker ps -qa)
Publish (-p, ★★★★★)
컨테이너를 외부에 노출, 배포(publish)시키는 옵션
root@host:/# docker run -d --name pub -p 8080:80 nginx:latest
# 일반적으로 웹서버는 80, python 장고는 8000, nodejs기반은 3000번 등등... 중요한건 컨테이너 내부에서 실제로 어떤포트로 서비스가 제공되는지가 제일 중요하고, 그 포트를 찾아서 publish해야 함
root@host:/host-txt# curl localhost:8080
# 실습, 테스트할때는 되도록이면 호스트의 웰노운포트는 쓰지 않는게 좋다. 만약에 최종적으로 웹서버를 배포하는거면 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
docker cp (★)
1. 호스트의 파일을 컨테이너에 복사
- 컨테이너의 파일을 호스트에 복사
환경변수 지정 (-e, ★★★)
- -it 말고, 처음에 생성할때 -d로 하는 것이 좋음
root@host:~# docker run -d --name envcon -e ENVTEST=test nginx:latest
root@host:~# docker exec -it envcon bash
root@4d58eebc87ad:/# echo $ENVTEST
# 데이터베이스 컨테이너의 경우엔 적어도 root 패스워드를 설정해야 컨테이너 잘 동작
작업디렉토리지정 (-w, ★★★★★)
root@host:~# docker run -d --name workdir nginx:latest
root@host:~# docker exec -it workdir bash
# 컨테이너에 진입했을때의 경로 = 현재작업디렉토리(pwd)
root@host:~# docker run -d --name workdir1 -w /usr nginx:latest
# 작업디렉토리를 /usr로 지정
root@host:~# docker exec -it workdir1 bash
- 도커 엔진 재시작
root@host:~# systemctl restart docker
root@host:~# docker rm -f $(docker ps -qa)
- --restart=always : 컨테이너 중지 시 항상 재시작
root@host:~# docker run -d --name retest --restart=always nginx:latest
'AWS Cloud School 8기 > Docker' 카테고리의 다른 글
58일차) 2025-03-24(docker-compose) (0) | 2025.03.24 |
---|---|
57일차) 2025-03-21(앱배포-python, nginx) (0) | 2025.03.21 |
56일차) 2025-03-20(앱 배포-java/js, 컨테이너화, dockerignore) (0) | 2025.03.20 |
55일차) 2025-03-19(우분투 용량 추가, was-db, 사설저장소) (0) | 2025.03.19 |
54일차) 2025-03-18(도커 이미지, Dockerfile, DockerRegistry) (0) | 2025.03.18 |