소프트웨어 유지 보수를 위해 애플리케이션 전체 생애주기를 담당하는 DevOps를 도입할 수 있다.
운영: Bash, Nagios, Powershell 등
개발: Make, Maven, NuGet, MSBuild 등
서로 다른 기술을 사용하더라도 Dockerfile과 Docker compose script를 사용해 같은 기술과 도구로 팀을 통일할 수 있게 된다.
Jenkins나 CALMS라는 DevOps 프레임워크를 사용할 수도 있다.
Culture(문화) : 아래 기능을 공유하는 장소
Automation(자동화) : 컨테이너 환경의 핵심
Lean(린) : 분산 애플리케이션은 린 원칙에 따라 만들어짐
Metric(측정) : 배포 프로세스와 운영 로그로부터 얻은 측정치 활용
Sharing(공유) : 도커 허브는 이미 있는 것을 재사용 가능하게 하는 공유 기능 가능
✒️ 실습 환경 초기화 명령
도커는 컨테이너나 애플리케이션을 자동으로 정리해주는 기능이 없다. 데스크톱을 종료하거나 서비스를 정지시키면 모든 컨테이너가 종료되어 CPU나 메모리를 사용하지는 않지만, 없어지지는 않으므로 실습 환경을 초기화하는 것이 편리하다.
docker container rm -f $(docker container ls -aq)
다음 명령을 사용하면 내려받은 이미지가 차지한 디스크 용량을 모두 회수할 수 있다.
docker image rm -f $(docker image ls -f reference=''diamol/*' -q)
2. 기본적인 Docker 사용법
📌 컨테이너로 Hello World 실행하기
docker container run diamol/ch02-hello-diamol
run 명령을 실행하면 애플리케이션 패키지 'diamol/ch02-hello-diamol'로부터 컨테이너 실행
Host PC에 해당 패키지(이미지)가 없으므로 설치(Pull)한다.
패키지를 사용해 컨테이너를 실행한다. (로그는 애플리케이션이 출력한 내용)
컴퓨터 이름 : 여기선 'e32240490a23', Container id에 해당한다.
운영체제 종류 : Linux 5.10.102.1-microsoft-standard-WSL2 x86_64
네트워크 주소 : 172.17.0.2
해당 정보는 컨테이너를 실행할 때마다 바뀌며, 운영체제는 Host 운영체제를 따른다.
✒️ Docker의 핵심 워크 플로우
https://www.ahnlab.com/kr/site/securityinfo/secunews/secuNewsView.do?seq=30533 ① 빌드 : 애플리케이션을 컨테이너에서 실행할 수 있도록 패키징 ② 공유 : 다른 사람이 패키지를 사용할 수 있도록 공유 ③ 실행 : 패키지를 내려받은 사람이 컨테이너를 통해 애플리케이션을 실행
아무리 복잡한 애플리케이션이라도 모두 위 과정을 거친다. 도커를 실행할 수 있는 환경이라면 어디서든 실행할 수 있도록 도커 이미지를 만들 수 있다. 이런 방법으로 도커의 핵심 이점인 이식성이 확보된다.
참고로 위의 컨테이너를 다시 실행하면 아래처럼 스크립트가 실행된다.
컴퓨터 이름(컨테이너 ID)과 IP 주소가 실행할 때마다 바뀔 확률이 굉장히 높다.
📌 Container
각 컨테이너는 가상의 호스트명, IP 주소, 파일 시스템을 가진다.
컨테이너 안에서는 밖의 환경을 볼 수 없다. 따라서 고립되어 있다.
하나의 컴퓨터에는 여러 개의 컨테이너를 실행할 수도 있다. 그리고 모든 컨테이너는 독립적이다.
컨테이너는 컴퓨터의 CPU와 메모리, 운영체제를 공유한다. → 리소스 경감
격리(isolation)와 밀집(density)를 동시에 만족한다.
격리 : 런타임 버전 차이, 호환되지 않는 버전의 라이브러리, 리소스 부족 등으로 인한 독립된 환경 필요
밀집 : 컴퓨터에 CPU와 메모리가 허용하는 한 되도록 많은 수의 애플리케이션을 실행하는 것
✒️ Virtual Machine 방식의 문제점
• 각각의 가상 머신은 자신만의 운영체제를 별도로 갖는다 • 애플리케이션이 사용할 CPU와 메모리 자원을 상당량 차지한다. • 운영체제 라이선스와 업데이트 부담이 추가 발생한다. • 따라서, 가상 머신은 격리는 달성할 수 있지만 밀집은 제공하지 못한다.
반면, 컨테이너는 호스트 컴퓨터의 운영체제를 공유하므로 리소스가 크게 경감된다. 그만큼 실행도 빠르고, 가상 머신에 비해 더 많은 수(일반적으로 약 5배)의 애플리케이션 실행이 가능하다. 또한 컨테이너는 외부와 독립된 환경이므로 밀집과 격리가 동시에 달성되는 것이다.
📌 컨테이너를 원격 컴퓨터처럼 사용하기
💡 run으로 애플리케이션을 실행하고, logs로 로그, top으로 프로세스 목록, inspect로 컨테이너 상세정보 확인
컨테이너를 실행하고 원격 컴퓨터에 접속하듯, 대화식 컨테이너를 실행할 수도 있다.
docker container run --interactive --tty diamol/base
--interactive: 컨테이너에 접속된 상태가 된다.
--tty: 터미널 세션을 통해 컨테이너를 조작한다.
컨테이너 내부에 접속된 터미널 세션의 명령 프롬프트가 뜬다.
접속한 컴퓨터는 현재 실행 중인 컨테이너다.
원격 컴퓨터의 터미널 세션은 로컬 운영체제에 따라 달라진다. (호스트 운영체제를 공유하므로)
호스트 컴퓨터가 리눅스 머신이라면 리눅스 쉘이 뜬다.
윈도우 머신이라면 윈도우 명령 프롬프트가 뜬다.
도커 자체는 호스트 컴퓨터의 아키텍처나 운영체제와 상관없이 동일하게 동작한다.
하지만 컨테이너에 들어 있는 애플리케이션은 운영체제나 아키텍처를 가릴 수 있다.
결국 컨테이너의 내용물이 무엇이든 컨테이너를 다루는 방법은 환경과 상관없이 동일하다.
✒️ 컨테이너는 Host의 운영체제를 공유하는데 독립적이라고?
이 부분이 정말 이해하는데 오래 걸렸다. 컨테이너를 구동하기 위해서 Host의 운영체제를 공유하고, 실제로 터미널 세션이 호스트 환경에 지배된다면 컨테이너가 독립적이라고 부를 수 있을까?
여기에 대한 정답은 "컨테이너가 Host Kernel의 기능(리소스 관리, 네트워킹, 프로세스 스케줄링)을 공유하는 것이지, 직접적으로 커널 자원을 공유하는 것은 아니기" 때문이다.
컨테이너는 애플리케이션 실행에 필요한 라이브러리, 실행 환경, 종속성 등을 포함하는 격리된 공간이다. 컨테이너 이미지에는 애플리케이션 실행을 위한 모든 구성 요소와 파일 시스템이 포함되어 있고, 패키징된 컨테이너 이미지는 호스트 컴퓨터에서 실행될 때 자체적으로 필요한 운영체제 및 아키텍처에 대한 의존성을 해결한다.
즉, 컨테이너는 고립된 파일 시스템, 네트워크, 프로세스 공간 등을 가지므로 격리는 성립한다. 하지만 동작하는데 있어 호스트 운영체제의 커널을 활용하여 애플리케이션을 실행하는 것이다.
ex. HTTP 요청마다 로그를 생성하는 웹 애플리케이션이 동작 중이면, HTTP 요청에 대한 로그만 출력
4️⃣ 대상 컨테이너의 상세 정보를 출력해라
docker container inspect cd
애플리케이션에 발생한 문제를 추적하는 데 유용한 정보를 제공한다.
JSON 포맷으로 출력
📌 컨테이너를 사용해 웹 사이트 호스팅하기
✒️ 컨테이너는 자동으로 사라지지 않는다.
• 컨테이너 내부 애플리케이션이 실행 중이어야 컨테이너 상태도 실행 중이 된다. ∘ 애플리케이션 프로세스가 종료되면 컨테이너는 Exited가 된다. • 컨테이너가 종료돼도 컨테이너는 사라지지 않는다. ∘ 컨테이너를 다시 실행하거나, 로그 확인, 컨테이너의 파일 시스템에 새로운 파일 복사 혹은 외부로 복사 가능 ∘ 컨테이너 파일 시스템이 그대로 남아 있어, 컴퓨터 디스크 공간을 계속 점유한다.
그렇다면 컨테이너를 실행하고 백그라운드에서 계속 동작하게 만드는 방법은 무엇일까?
실제로 도커를 사용하는 주목적은 웹 사이트, 배치 프로세스, 데이터베이스 같은 서버 애플리케이션일 것이다.
docker container run --detach --publish 8088:80 diamol/ch02-hello-diamol-web
--detach : 컨테이너를 백그라운드에서 실행하며 컨테이너 ID를 출력한다.
--publish : 컨테이너 포트를 호스트 컴퓨터에 공개한다.
아파치 웹 서버와 간단한 HTML 페이지를 담고 있다.
✒️ publish의 원리
컨테이너는 기본적으로 외부 환경에 노출되지 않는다. 컨테이너가 가지는 고유의 IP 주소는 도커가 관리하는 내부 가상 네트워크에 불과하다. (외부에서 접근 불가) 포트를 공개한다는 것은 도커가 호스트 컴퓨터의 Network Layer의 트래픽에 끼어들어 필요한 것을 가로챌 수 있다. 앞선 예제의 경우 Host PC 8088 포트로 들어온 트래픽을 Docker가 가로채서 컨테이너의 80번 포트로 전달한다.
http://localhost:8088에 HTTP 요청은 로컬 컴퓨터에서 보내고, 응답은 컨테이너로부터 나온 것이다.
우리가 docker 명령을 사용(run, build, pull)할 때 호출하는 API가 도커 CLI에 해당한다.
원격 컴퓨터에서 실행 중인 도커를 조작할 수 있도록 CLI가 요청을 전달하는 곳을 변경할 수 있다.
도커 API는 명세가 공개되어 있기 때문에 도커 CLI 외의 다른 클라이언트를 사용할 수도 있다.
도커의 세부 구조를 알 필요는 없지만, 아래 내용 정도는 알아두면 좋다.
도커 엔진은 containerd라는 컴포넌트를 통해 컨테이너를 실제로 관리한다.
containerd는 호스트 운영체제가 제공하는 기능을 통해 컨테이너, 즉 가상 환경을 만든다.
containerd는 CNCF에서 관리하는 오픈 소스 프로젝트다.
컨테이너는 개방형 컨테이너 이니셔티브(Open Container Initiative, OCI)라는 이름으로 공개된 개방형 표준이다.
📌 연습 문제: 컨테이너 파일 시스템
웹 사이트 컨테이너를 실행하고 index.html 파일을 교체해 웹 페이지의 내용을 수정해라. 컨테이너도 자신만의 파일 시스템을 가지므로, 이 웹 페이지 파일 역시 컨테이너 파일 시스템 안에 담겨 있다.
• docker container 명령 사용 시, 컨테이너를 대상으로 할 수 있는 일의 목록을 볼 수 있다. • 모든 docker 명령에 --help 플래그를 추가하면 명령의 도움말을 볼 수 있다. • 도커 이미지 diamol/ch02-hello-diamol-web 안에서 웹 페이지 파일이 위치한 경로는 `/usr/local/apache2/htdocs`다.