이번 장에서 하게 될 것
- 컨테이너에서 애플리케이션을 실행하기
- 정확히 컨테이너가 무엇인지 알기
- 왜 컨테이너가 앱을 실행하는 방법 중 경량화 된 방법인지 알기
1. 컨테이너로 "Hello World" 실행하기
우선 Docker를 사용해 어떻게 애플리케이션을 실행시킬 수 있는지 직접 해보자.
그리고 난 뒤, 어떻게 동작하는지 알아볼 것이다.
예제
- 커맨드
docker container run diamol/ch02-hello-diamol
- 실행 결과
위 커맨드는 다음와 같은 의미로 사용이 되었다.
docker container run <이미지를 가져올 패키지 이름>
(이미지를 가져오는 패키지가 어디에 위치하는지는 나중에 다루기 때문에 지금은 그냥 넘어가도록 하자.)
여기에서 말하는 이미지(Image)란,
어떤 특정 이름으로 패키징되어 있는 하나의 애플리케이션 단위라고 보면 된다.
(예시에서는 "diamol/ch02-hello-diamol")
위 커맨드를 처음 실행시킨다면,
실행시킨 컴퓨터 내에 'diamol/ch02-hello-diamol'이라는 이미지가 없기 때문에 어딘가로부터 다운로드를 하게 된다.
이를 도커에서는 풀링(Pulling)이라고 한다.
출력된 내용 중, "Unable to find image ... locaaly"라는 내용은 그 때문에 출력이 된 것이다.
그 다음 내용으로는 "lastest ..."의 내용은 다운로드 상태를 나타내며,
"Status: ..." 라인은 어떤 이미지를 다운로드 했는지를 보여준다.
그리고나면 도커는 풀링한 이미지를 사용해 컨테이너를 실행시킨다.
이미지는 애플리케이션의 내용을 이미 포함하고 있기 때문에,
어떻게 애플리케이션을 시작해야하는지 도커에게 알려주게 되고
그에 기반해 컨테이너가 실행된다.
(예시에서 실행된 애플리케이션은 "Hello from Chapter 2!", "Im running on ..., My address is: ..." 문자열이 출력되었다.)
여기에서 중요한 점은
어떤 복잡한 애플리케이션이라도 "docker container run <이미지 이름>" 이라는 커맨드로
컨테이너를 실행시킨다는 워크플로우는 항상 동일하다는 점이다.
여기에서 다시 한번 동일한 커맨드를 입력해보면,
- 실행 결과
앞서 실행 했던 결과와 다르게 풀링하는 과정 없이
바로 문자열이 출력되는 것을 확인할 수 있다.
이는 기존에 한번 풀링했던 과정에서 다운로드 된 이미지는
다시 재사용된다는 것을 의미한다.
하지만 실행된 컨테이너의 이름("My name is의 값")이나 IP의 출력 값이 다르다.
왜 다르게 나타나는 것일까?
2. 컨테이너란 무엇인가?
바로 얘기해보자면,
컨테이너란 애플리케이션이 담긴 물리적인 박스와 같은 개념이다.
박스 내부에서 애플리케이션은 그 자체로 컴퓨터인 것처럼,
자체적인 이름과 IP 주소, 자체 디스크 드라이브를 가지는 것처럼 동작한다.
실제로 이것은 도커에 의해서 만들어지는 가상 리소스이다.
이 가상 리소스들이 결합되어 애플리케이션을 구동할 수 있는 환경을 만들게 된다.
각각의 박스는 서로 분리되어 있으며, 박스 내부에서는 외부를 볼 수 없다.
그리고 하나의 컴퓨터에서는 여러개의 박스를 실행시킬 수 있으며,
실행시킨 컴퓨터의 CPU나 메모리, OS를 공유하게 된다.
아래 그림에서 동일한 컴퓨터 내에서 독립적으로 분리되어 있다는 것을 볼 수 있다.
이렇게 컨테이너가 분리되는 것이 왜 중요할까?
분리하게 되면 격리와 밀도, 이 두 가지 문제를 개선할 수 있다.
- 밀도: 보유하고 있는 모든 메모리와 프로세서를 사용해서 얼마나 많은 애플리케이션을 실행시킬 수 있는지를 의미
- 격리: 호환되지 않는 도구나 라이브러리들로 인해 애플리케이션들이 서로 잘 동작하지 않을 수 있기 때문에 분리의 필요성이 있음
기존에는 이 두 가지 문제를 VM(Virtual Machine)을 통해서 해결하고자 했다.
여기서 모든 VM은 각각 자신의 OS를 필요로 하고,
그 OS는 몇 기가바이트의 메모리와
애플리케이션을 작동하기 위한 컴퓨팅 파워와
많은 CPU 연산 시간을 사용하는 등의 리소스를 사용한다.
이는 밀도를 줄이게 되고 격리를 제공하게 된다.
하지만 Docker의 컨테이너는 밀도와 격리 둘 다 제공한다.
각 컨테이너를 실행하고 있는 컴퓨터의 OS를 공유하므로
컨테이너들을 극도로 경량화시킬 수 있다.
또한, 컨테이너들은 빠르고 간결하게 실행할 수 있기 때문에
같은 하드웨어 상에서도 더 많은 컨테이너를 실행할 수 있다.
이것이 도커가 지닌 또 다른 핵심 기능인 효율성이라고 할 수 있다.
3. 컨테이너를 원격 컴퓨터처럼 사용하기
예제
- 커맨드
docker container run --interactive --tty diamol/base
- 실행 결과
"docker container run" 커맨드와 더불어 몇 가지 플래그를 통해
실행 중인 컨테이너와 연결된 터미널 세션으로 상호작용을 할 수 있다.
- --interactive
- 컨테이너와 현재 터미널을 연결하기 위한 플래그
- --tty
- 컨테이너 내부 터미널 세션과 현재 터미널을 연결하기 위한 플래그
커맨드를 실행하면, 도커가 이미지를 풀링하고 커맨드 프롬프트 입력을 대기할 것이다.
여기서의 커맨드 프롬프트는 컨테이너 내부의 터미널 세션이다.
연결된 프롬프트창에 "hostname"과 "date"을 입력하면 연결된 컨테이너에 대한 정보를 확인할 수 있다.
컨테이너를 실행한 컴퓨터의 OS를 공유하기 때문에 리눅스 프롬프트창으로 확인할 수가 있다.
도커 그 자체는 어떤 OS, 프로세스를 사용하는 것과 관계없이 동일하게 동작한다.
새 터미널을 연 다음, 아래 커맨드를 통해 실행중인 모든 컨테이너들의 세부사항을 볼 수 있다.
docker container ls
앞에서부터 차례대로
컨테이너 ID, 이미지 이름, 도커가 실행했던 명령어, 생성 시간, 현재 컨테이너의 상태
를 목록으로 확인할 수 있다.
목록에 있는 컨테이너 ID가
컨테이너 내부의 프롬프트에서 "hostname"으로 실행하여 확인했던 ID와
"83dc8f9853f6"을로 동일하다는 것을 확인할 수 있다.
도커는 컨테이너를 생성할 때, 무작위 ID를 할당하고 ID의 일부를 "hostname"으로 사용한다.
"docker container <COMMAND>" 커맨드의 다수가 특정 컨테이너와 상호작용하기 위해서 사용하게 되는데,
할당받는 컨테이너 ID의 일부를 사용해서 식별한다.
다음은 컨테이너 ID를 사용해 실행 중인 컨테이너의 프로세스 정보를 확인할 수 있다.
예제
- 커맨드
docker container top 83dc8f9853f6
- 실행 결과
컨테이너에서 프로세스를 여러 개 사용하고 있다면 그 목록을 전부 보여준다.
컨테이너 ID의 일부로도 조회가 가능하다. ex) "docker container top 83dc8"
다음으로는 컨테이너에서 출력된 로그를 확인해보자.
예제
- 커맨드
docker container logs 83dc8f9853f6
- 실행 결과
해당 커맨드를 실행함으로써
컨테이너 내부 애플리케이션으로부터 출력된 로그들을 보여준다.
이를 활용하여 애플리케이션의 문제를 추적할 때 용이하다.
연결된 컨테이너의 프롬프트창은
"exit"을 입력하여 종료시킬 수 있으며, 그와 동시에 컨테이너는 종료된다.
4. 컨테이너로 웹 사이트 호스팅하기
우선 실행 여부와 관계없이 모든 컨테이너를 확인해보자.
예제
- 커맨드
docker container ls --all
- 실행 결과
- --all
- 컨테이너의 실행 여부와 관계 없이 모든 컨테이너의 목록을 보여주는 플래그
이전의 연결되었던 컨테이너 내부의 프롬프트창에
"exit"를 입력하여 컨테이너의 실행을 종료시켰다면,
해당 컨테이너의 STATUS는 "Exited" 상태를 가지고 있을 것이다.
여기서 알 수 있는 2가지는
1. 컨테이너는 오직 컨테이너 내부의 애플리케이션이 실행 중인 동안에만 작동한다.
2. 컨테이너의 내부 애플리케이션 실행이 종료되어 컨테이너 작동이 중단되어 "Exited" 상태가 되어도 사라지지 않는다.
는 것이다.
여기서 의문을 가질 수 있다.
"컨테이너로 실행한 프로세스를 유지하기 위해서 터미널을 계속 실행해두어야 할까?" 이다.
도커에서는 백그라운드에서 내부 애플리케이션을 작동시키고 유지할 수 있도록 또 다른 기능을 제공한다.
예제
- 커맨드
docker container run --detach --publish 8080:80 diamol/ch02-hello-diamol-web
- 실행 결과
위 실행 결과를 확인해보면,
커맨드 실행 후, "docker container ls" 커맨드로 실행 중인 컨테이너를 확인했을 때,
터미널 세션을 점유하고 있지 않는데도 여전히 컨테이너가 실행중인 것을 볼 수 있다.
위 커맨드의 구조는 아래와 같다.
docker container run [--detach, -d] [--publish, -p] [게시할 포트번호:컨테이너 내부 포트번호] [이미지 이름]
- --detach, --d
- 컨테이너를 detach 모드(백그라운드)로 실행하는 플래그
- --publish, --p [게시할 포트 번호:컨테이너 내부 포트 번호]
- 컨테이너 내부 포트와 컨테이너를 실행한 컴퓨터의 포트를 연결하는 플래그
도커 컨테이너를 실행할 때는 attach, detach 모드가 있으며
컨테이너를 실행했을 때, 실행한 터미널 세션을 점유하여 다른 작업을 하지 못하게 하는 attach 모드가 기본값이다.
반대로 detach 모드는 백그라운드로 해당 컨테이너를 실행시켜 실행 후에도 다른 작업을 이어 갈 수 있다.
"--publish" 플래그를 통해
컨테이너와 컨테이너 외부를 연결할 수 있는 포트를 서로 연결시킬 수 있어,
외부에서도 특정 포트를 통해 컨테이너가 실행 중인 내부 애플리케이션을 확인할 수 있다.
이것은 도커를 설치할 때, 도커가 컴퓨터의 네트워크 계층으로 자신을 주입시켜
컴퓨터 안으로 들어오는 트래픽을 가로채 컨테이너 안의 트래픽으로 보내기 때문에 가능하다.
인터넷 브라우저에 "http://localhost:8080"을 입력하여 확인해보면
아래와 같이 컨테이너에서 실행된 애플리케이션을
"--publish"플래그와 함께 지정한 포트로 접근이 가능하다.
이렇게 실행된 컨테이너는
컴퓨터의 자원을 얼마나 많이 사용하고 있을지 궁금해 할 수 있다.
아래 예제를 통해 확인해보자.
예제
- 커맨드
docker container stats
- 실행 결과
CPU 점유율, 메모리 사용량 등,
현재 실행 중인 컨테이너가 얼마나 많은 리소스를 사용하고 있는지
다양한 정보를 확인할 수 있다.
앞서 살펴봤던 것처럼
컨테이너 내부 애플리케이션의 프로세스가 끝나면,
컨테이너는 "Exited" 상태가 되고, 더 이상 CPU나 메모리를 사용하지 않게 된다.
하지만 컨테이너의 파일시스템이 컴퓨터의 디스크 공간을 여전히 차지하고 있기 때문에
수동으로 삭제할 필요가 있다.
컨테이너를 삭제하기 위해서는 아래 예제의 커맨드를 실행하면 된다.
(물론, 도커에서는 컨테이너의 실행이 종료되고 자동으로 제거해주는 기능이 있다. 나중에 나오니 인내심을 가져보자!)
예제
- 커맨드
docker container rm [--force] [컨테이너 ID]
- 실행 결과
앞서 실행 했던 컨테이너를 제거하려
위 커맨드를 실행해보면 에러가 발생하여 제거되지 않는다.
기본적으로 컨테이너가 실행 중일 때는 제거 할 수 없다.
그렇기 때문에 "--force" 플래그를 통해
실행 중인 컨테이너도 제거할 수 있다.
5. 도커가 컨테이너를 실행하는 원리
도커 엔진은
도커를 관리하는 요소로, 로컬 이미지 캐시를 관리한다.
필요할 때 이미지를 다운로드하고, 이미 다운로드한 이미지이면 캐시로부터 재사용한다.
또한 OS와 함께 작동하여 컨테이너, 가상 네트워크 및 기타 도커 리소스를 만든다.
도커 엔진은 항상 실행되는 백그라운드 프로세스다.
커맨드를 실행하게 되면, 도커 CLI는 도커 API로 명령을 보내고, 도커 엔진이 그 작업을 수행하게 된다.
'도서 > Learn Docker in a month of lunches' 카테고리의 다른 글
3장. 도커 이미지 빌드하기 (0) | 2023.09.19 |
---|---|
1장. 시작하기 전에 (1) | 2023.09.16 |