20231019 Docker 기초
VM vs Container
https://kubernetes.io/ko/docs/concepts/overview/what-is-kubernetes/
전통적인 배포 시대: 초기 조직은 애플리케이션을 물리 서버에서 실행했었다. 한 물리 서버에서 여러 애플리케이션의 리소스 한계를 정의할 방법이 없었기에, 리소스 할당의 문제가 발생했다. 예를 들어 물리 서버 하나에서 여러 애플리케이션을 실행하면, 리소스 전부를 차지하는 애플리케이션 인스턴스가 있을 수 있고, 결과적으로는 다른 애플리케이션의 성능이 저하될 수 있었다. 이에 대한 해결책은 서로 다른 여러 물리 서버에서 각 애플리케이션을 실행하는 것이 있다. 그러나 이는 리소스가 충분히 활용되지 않는다는 점에서 확장 가능하지 않았으므로, 물리 서버를 많이 유지하기 위해서 조직에게 많은 비용이 들었다.
가상화된 배포 시대: 그 해결책으로 가상화가 도입되었다. 이는 단일 물리 서버의 CPU에서 여러 가상 시스템 (VM)을 실행할 수 있게 한다. 가상화를 사용하면 VM간에 애플리케이션을 격리하고 애플리케이션의 정보를 다른 애플리케이션에서 자유롭게 액세스 할 수 없으므로, 일정 수준의 보안성을 제공할 수 있다.
가상화를 사용하면 물리 서버에서 리소스를 보다 효율적으로 활용할 수 있으며, 쉽게 애플리케이션을 추가하거나 업데이트할 수 있고 하드웨어 비용을 절감할 수 있어 더 나은 확장성을 제공한다. 가상화를 통해 일련의 물리 리소스를 폐기 가능한(disposable) 가상 머신으로 구성된 클러스터로 만들 수 있다.
각 VM은 가상화된 하드웨어 상에서 자체 운영체제를 포함한 모든 구성 요소를 실행하는 하나의 완전한 머신이다.
컨테이너 개발 시대: 컨테이너는 VM과 유사하지만 격리 속성을 완화하여 애플리케이션 간에 운영체제(OS)를 공유한다. 그러므로 컨테이너는 가볍다고 여겨진다. VM과 마찬가지로 컨테이너에는 자체 파일 시스템, CPU 점유율, 메모리, 프로세스 공간 등이 있다. 기본 인프라와의 종속성을 끊었기 때문에, 클라우드나 OS 배포본에 모두 이식할 수 있다.
컨테이너는 다음과 같은 추가적인 혜택을 제공하기 때문에 인기가 있다.
기민한 애플리케이션 생성과 배포: VM 이미지를 사용하는 것에 비해 컨테이너 이미지 생성이 보다 쉽고 효율적임.
지속적인 개발, 통합 및 배포: 안정적이고 주기적으로 컨테이너 이미지를 빌드해서 배포할 수 있고 (이미지의 불변성 덕에) 빠르고 효율적으로 롤백할 수 있다.
개발과 운영의 관심사 분리: 배포 시점이 아닌 빌드/릴리스 시점에 애플리케이션 컨테이너 이미지를 만들기 때문에, 애플리케이션이 인프라스트럭처에서 분리된다.
가시성(observability): OS 수준의 정보와 메트릭에 머무르지 않고, 애플리케이션의 헬스와 그 밖의 시그널을 볼 수 있다.
개발, 테스팅 및 운영 환경에 걸친 일관성: 랩탑에서도 클라우드에서와 동일하게 구동된다.
클라우드 및 OS 배포판 간 이식성: Ubuntu, RHEL, CoreOS, 온-프레미스, 주요 퍼블릭 클라우드와 어디에서든 구동된다.
애플리케이션 중심 관리: 가상 하드웨어 상에서 OS를 실행하는 수준에서 논리적인 리소스를 사용하는 OS 상에서 애플리케이션을 실행하는 수준으로 추상화 수준이 높아진다.
느슨하게 커플되고, 분산되고, 유연하며, 자유로운 마이크로서비스: 애플리케이션은 단일 목적의 머신에서 모놀리식 스택으로 구동되지 않고 보다 작고 독립적인 단위로 쪼개져서 동적으로 배포되고 관리될 수 있다.
리소스 격리: 애플리케이션 성능을 예측할 수 있다.
자원 사용량: 리소스 사용량: 고효율 고집적.
https://docs.microsoft.com/ko-kr/virtualization/windowscontainers/about/containers-vs-vm
기능 | 가상 머신 | 컨테이너 |
---|---|---|
격리 | 호스트 운영 체제와 기타 VM으로부터 완벽하게 격리합니다. 동일한 서버 또는 클러스터에 있는 경쟁 회사의 호스팅 앱처럼 강력한 보안 경계가 필요할 때 유용합니다. | 일반적으로 호스트 및 기타 컨테이너로부터 어느 정도 격리하지만, VM처럼 강력한 보안 경계를 제공하지는 않습니다. Hyper-V 격리 모드를 사용하여 경량 VM의 각 컨테이너를 분리하는 방식으로 보안을 높일 수 있습니다. |
운영 체제 | 커널을 포함하여 완전한 운영 체제를 실행하므로 더 많은 시스템 리소스(CPU, 메모리 및 스토리지)가 필요합니다. | 운영 체제의 사용자 모드 부분을 실행하며, 앱에 필요한 서비스만 포함하도록 조정하여 시스템 리소스 사용을 줄일 수 있습니다. |
게스트 호환성 | 가상 머신 내의 운영 체제에 대해서만 실행됩니다. | 호스트와 동일한 운영 체제 버전에서 실행됩니다(Hyper-V 격리를 사용하면 경량 VM 환경에서 동일한 OS의 이전 버전을 실행할 수 있음). |
배포 | Windows Admin Center 또는 Hyper-V 관리자를 사용하여 개별 VM을 배포합니다. PowerShell 또는 System Center Virtual Machine Manager를 사용하여 여러 VM을 배포합니다. | 명령줄을 통해 Docker를 사용하여 개별 컨테이너를 배포합니다. Azure Kubernetes Service 같은 오케스트레이터를 사용하여 여러 컨테이너를 배포합니다. |
운영 체제 업데이트 및 업그레이드 | 각 VM에 운영 체제 업데이트를 다운로드하여 설치합니다. 새 운영 체제 버전을 설치하려면 업그레이드가 필요하거나 경우에 따라 완전히 새로운 VM을 만들어야 합니다. 이렇게 하면 시간이 오래 걸리며, 특히 VM 수가 많으면 매우 긴 시간이 걸립니다. | 컨테이너 내의 운영 체제 파일을 업데이트 또는 업그레이드하는 방법은 다음과 같이 동일합니다.
|
영구 스토리지 | 단일 VM의 경우 로컬 스토리지에 VHD(가상 하드 디스크)를 사용하고, 여러 서버에서 공유하는 스토리지에는 SMB 파일 공유를 사용합니다. | 단일 노드에는 로컬 스토리지용 Azure 디스크를 사용하고, 여러 노드나 서버에서 공유하는 스토리지에는 Azure Files(SMB 공유)를 사용합니다. |
부하 분산 | 가상 머신 부하 분산은 실행 중인 VM을 장애 조치(failover) 클러스터의 다른 서버로 이동합니다. | 컨테이너 자체는 이동하지 않습니다. 대신 오케스트레이터는 클러스터 노드의 컨테이너를 자동으로 시작하거나 중지하여 부하 및 가용성의 변화를 관리할 수 있습니다. |
내결함성 | VM은 클러스터의 다른 서버로 장애 조치할 수 있으며, 새 서버에서 VM의 운영 체제가 다시 시작됩니다. | 클러스터 노드에서 오류가 발생하면 오케스트레이터는 해당 노드에서 실행되는 모든 컨테이너를 신속하게 다른 클러스터 노드에 다시 만듭니다. |
네트워킹 | 가상 네트워크 어댑터를 사용합니다. | 가상 네트워크 어댑터의 격리된 보기를 사용하며, 약간 적은 가상화를 제공합니다. 리소스를 적게 사용하지만 호스트의 방화벽을 컨테이너와 공유합니다 자세한 내용은 Windows 컨테이너 네트워킹을 참조하세요. |
여러 머신에서 코드가 제대로 작동하도록 하는 것은 여러 가지 이유로 어려울 수 있습니다.
애플리케이션을 작성하고 테스트한 운영 체제와 애플리케이션이 최종적으로 설치될 운영 체제와 다를 수도 있고, 앱에서 하드웨어 파일이나 기타 속성에 대해 다른 컴퓨터에서는 사실이 아닐 수 있는 가정을 할 수도 있습니다. 또는 앱이 내 컴퓨터에는 있지만 다른 컴퓨터에는 없는 하드웨어에 의존할 수도 있습니다. 이 까다로운 문제를 해결하기 위해 여러 도구가 탄생했습니다. Chef, Ansible, Puppet과 같은 설정 관리 도구는 관리자와 개발자가 머신 그룹을 원하는 상태로 만들 수 있도록 설정을 코드로 사용하여 이 문제를 해결했습니다. 안타깝게도 여기에는 여러 가지 어려움이 따랐습니다. 이러한 도구로 관리되는 머신을 구성하기 전에 해당 도구로 관리되는 머신에 무언가를 설치해야 하는 경우가 많았습니다. 이러한 도구는 루비나 파이썬을 기반으로 했지만, C++를 기반으로 한 도구는 잊어버릴 것입니다. 이미 사용 중인 기술 스택에 더해 마크업 언어와 프레임워크를 배워야 합니다. 해시코프는 개발자가 해시코프 구성 언어인 HCL을 사용하여 미리 만들어진 가상 머신을 불러오고 구성할 수 있는 도구인 Vagrant를 통해 이 문제를 해결했습니다. 이것이 바로 HCL의 약자입니다. 멋지지 않나요? 하지만 가상 머신은 많은 리소스를 차지하고 시작 후 추가 구성이 필요한 경우가 많았습니다. 실제로 많은 Vagrant 사용자는 구성 관리 도구를 사용하여 Vagrant에서 생성한 가상 머신을 구성합니다. 이 모든 것이 코드 작성을 더 번거롭고 시간 소모적으로 만들었습니다.
Docker는 다른 접근 방식을 취했습니다. Docker는 이미지와 컨테이너를 사용하여 어디서나 애플리케이션을 패키징하고 실행합니다. 컨테이너는 제어 그룹과 네임스페이스를 결합합니다. 두 가지 Linux 커널 기능으로 겉으로 보기에 머신의 다른 애플리케이션처럼 보이는 가상 운영 체제 인스턴스를 생성합니다. 이미지는 파일 시스템의 스냅샷으로, 함께 스매싱되어 이를 사용하는 컨테이너에 단일 파일 시스템처럼 보이도록 구성됩니다. 컨테이너와 이미지는 새로운 것이 아닙니다. 진리에서 영역, Linux 컨테이너에 이르기까지 이러한 아이디어는 아주 오래전부터 사용되어 왔습니다.
Docker는 세 가지 주요 방식으로 이러한 개념을 훨씬 더 쉽게 사용하고 이해할 수 있도록 합니다.
첫째, 컨테이너와 이미지는 Docker 파일이라는 경량 구성 파일 형식으로 구성됩니다.
둘째, 이미지를 다른 사람과 공유하고 다른 머신에서 컨테이너 레지스트리로 검색할 수 있습니다.
셋째, 명령줄 클라이언트는 API와 시스템 블랙 매직을 사용하여 컨테이너를 쉽게 생성하고 운영할 수 있습니다.
이 모든 것을 종합하면, 앱을 배포할 머신을 구성하고 최상의 결과를 기대하는 대신 Docker 파일을 사용하여 컨테이너가 생성될 Docker 이미지로 앱을 빌드하고 구성할 수 있습니다. 이러한 이미지에는 앱이 성공적으로 실행되는 데 필요한 모든 것이 포함되어 있으므로 앱이 실행되는 머신에 관계없이 앱이 동일하게 작동합니다.
VM과 Container 주요 차이점
가상 머신은 하이퍼바이저라는 소프트웨어를 사용하여 소프트웨어로 에뮬레이트된 하드웨어와 그 아래에 있는 실제 하드웨어를 연결합니다. 모든 의도와 목적에 있어서 가상 머신은 실제 컴퓨터입니다. 실제 컴퓨터이기 때문에 한 번에 여러 개의 앱을 실행할 수 있으며, 실행 중인 호스트를 보거나 호스트와 상호 작용할 수 없습니다.
앞서 컨테이너는 컨트롤 그룹과 네임스페이스를 사용한다고 언급했습니다. 네임스페이스는 리눅스 시스템에서 컨테이너가 액세스할 수 있는 것을 지정하고, 컨트롤 그룹은 컨테이너가 사용할 수 있는 특정 리소스의 양을 지정합니다.
컨테이너는 컨테이너 런타임과 함께 작동하여 이러한 속성을 구성합니다. 컨테이너는 하드웨어를 에뮬레이션하지 않습니다. 컨테이너는 머신의 다른 앱과 동일한 하드웨어를 사용합니다. 따라서 컨테이너는 잠재적으로 호스트와 상호 작용할 수 있지만, 컨테이너가 호스트와 상호 작용하는 방식을 제어할 수 있다는 것입니다.
<출처> https://github.com/docker/labs/blob/master/beginner/chapters/alpine.md
1. 첫 컨테이너 실행하기
Alpine Linux(경량 리눅스 배포판)를 실행하고 도커 실행 명령어를 사용해 보겠습니다.
202310-DockerBasic# docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
96526aa774ef: Pull complete
Digest: sha256:eece025e432126ce23f223450a0326fbebde39cdf496a85d8c016293fc851978
Status: Downloaded newer image for alpine:latest
202310-DockerBasic# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest 8ca4688f4f35 2 weeks ago 7.34MB
zeroacrdemo.azurecr.io/appmod-contapp latest 8629e2fcf972 7 months ago 121MB
zeroacrtf.azurecr.io/appmod-contapp latest 8629e2fcf972 7 months ago 121MB |
pull 명령은 Docker 레지스트리에서 알파인 이미지를 가져와 시스템에 저장합니다. docker images 명령을 사용하여 시스템의 모든 이미지 목록을 볼 수 있습니다.
Docker 아키텍처 및 용어
Images - 컨테이너를 생성하는 데 사용되는 애플리케이션의 파일 시스템 및 구성입니다. Docker 이미지에 대해 자세히 알아보려면 docker inspect alpine을 실행하세요.
Containers - Docker 이미지의 인스턴스 실행 - 컨테이너는 실제 애플리케이션을 실행합니다. 컨테이너에는 애플리케이션과 애플리케이션의 모든 종속성이 포함됩니다. 컨테이너는 다른 컨테이너와 커널을 공유하며 호스트 OS의 사용자 공간에서 격리된 프로세스로 실행됩니다.
Docker client - 사용자가 Docker 데몬과 상호 작용할 수 있는 명령줄 도구입니다.
Docker daemon - 호스트에서 실행되는 백그라운드 서비스로, Docker 컨테이너의 빌드, 실행 및 배포를 관리합니다.
Docker Registry - Docker 이미지의 레지스트리입니다.
이미지 빌드 요청이 전송되거나(docker 빌드) 미리 빌드된 이미지를 사용하라는 요청이 전송됩니다(
docker pull, docker run).
두 번째 단계는 실행할 이미지를 찾거나 실제로 빌드하는 것 입니다.
호스트 머신에서 이미지를 사용할 수 없는 경우, 도커 데몬은 구성된 레지스트리(기본값은 http://docker.io )에서 온라인에서 이미지를 찾으려고 시도합니다. Docker 레지스트리에는 바로 사용할 수 있도록 미리 빌드된 이미지가 포함되어 있습니다. 이미지를 다운로드하거나 빌드한 후에는 3단계를 계속 진행합니다.
호스트 머신에서 이미지를 사용할 수 있는 경우에는 온라인 레지스트리를 무시하고 3단계로 바로 이동합니다.
이미지를 빌드해야 하는 경우 컨텍스트("Dockerfile" 옆의 폴더 트리)가 데몬으로 전송됩니다.
이미지가 확인 및 검증(체크섬 비교)된 후 호스트 머신에 저장되어 Docker 데몬에서 사용할 수 있게 됩니다.
이 부분은 Docker 실행이 명시적으로 실행될 때만 발생합니다. 2단계와 3단계의 이미지로 Docker 컨테이너가 빌드되며, 호스팅 OS에 관계없이 호스트 머신에서 실행됩니다.
Docker 실행
이제 이 이미지를 기반으로 Docker 컨테이너를 실행해 보겠습니다. 이를 위해 docker 실행 명령을 사용합니다.
202310-DockerBasic# docker run alpine ls -l
total 56
drwxr-xr-x 2 root root 4096 Sep 28 11:18 bin
drwxr-xr-x 5 root root 340 Oct 19 01:51 dev
drwxr-xr-x 1 root root 4096 Oct 19 01:51 etc
drwxr-xr-x 2 root root 4096 Sep 28 11:18 home
drwxr-xr-x 7 root root 4096 Sep 28 11:18 lib
drwxr-xr-x 5 root root 4096 Sep 28 11:18 media
drwxr-xr-x 2 root root 4096 Sep 28 11:18 mnt
drwxr-xr-x 2 root root 4096 Sep 28 11:18 opt
dr-xr-xr-x 245 root root 0 Oct 19 01:51 proc
drwx------ 2 root root 4096 Sep 28 11:18 root
drwxr-xr-x 2 root root 4096 Sep 28 11:18 run
drwxr-xr-x 2 root root 4096 Sep 28 11:18 sbin
drwxr-xr-x 2 root root 4096 Sep 28 11:18 srv
dr-xr-xr-x 11 root root 0 Oct 19 01:51 sys
drwxrwxrwt 2 root root 4096 Sep 28 11:18 tmp
drwxr-xr-x 7 root root 4096 Sep 28 11:18 usr
drwxr-xr-x 12 root root 4096 Sep 28 11:18 var |
Docker 클라이언트가 Docker 데몬에 연결합니다.
도커 데몬은 로컬 스토어에서 이미지(이 경우 알파인)를 로컬에서 사용할 수 있는지 확인하고, 그렇지 않은 경우 도커 스토어에서 다운로드합니다. (이전에 docker pull alpine을 발급했기 때문에 다운로드 단계는 필요하지 않습니다.)
Docker 데몬이 컨테이너를 생성한 다음 해당 컨테이너에서 명령을 실행합니다.
Docker 데몬은 명령의 출력을 Docker 클라이언트로 스트리밍합니다.
docker run alpine을 실행할 때 명령(ls -l)을 제공했으므로 Docker가 지정된 명령을 시작하고 목록을 볼 수 있습니다.
좀 더 흥미로운 것을 시도해 봅시다.
202310-DockerBasic# docker run alpine echo "hello from alpine"
hello from alpine |
이 경우, Docker 클라이언트는 알파인 컨테이너에서 echo 명령을 실행한 다음 종료했습니다. 이 모든 일이 매우 빠르게 진행되었습니다. 가상 머신을 부팅하고 명령을 실행한 다음 종료한다고 상상해 보세요. 이제 컨테이너가 빠르다고 말하는 이유를 알 수 있습니다!
다른 명령을 시도합니다.
it 플래그를 사용하여 실행 명령을 실행하면 컨테이너의 대화형 tty에 연결됩니다. \
실행에 대해 자세히 알아보려면 docker run --help를 사용하여 지원하는 모든 플래그 목록을 확인하세요. 더 진행하면서 도커 실행의 몇 가지 변형을 더 보게 될 것입니다.
2. 정적 웹 컨테이너 실행하기
Dockerfile 작성
Docker 빌드
Docker 실행
결과 확인
Docker 로그 확인
Docker 종료