[Docker in Action] Running Software in containers (1)

2022. 4. 9. 15:14DevOps/Docker & Kubernetes

 

 

 

Controlling containers: Building a website monitor

이번 포스팅에서는 아래와 같은 웹 서비스 구조를 Docker Container를 통해 설계해보며, Docker Container에 대한 이해도를 높여보려고 합니다. 아래의 서비스 구조는 다음과 같이 동작합니다.

 

  1. NGINX 이미지로부터 컨테이너를 생성합니다. 이 컨테이너에서 웹 서비스를 호스팅 합니다.
  2. NGINX 컨테이너에 문제가 생긴 경우 (Service Down) Watcher Agent는 이를 감지하고 있다가 mailer에게 알려줍니다.
  3. mailer는 컨테이너에 문제가 생겼다는 사실을 Watcher Agent로부터 받아서 관리자의 계정으로 메일을 보냅니다.

 

 

Creating and starting a new container

위에서 Web container는 "NGINX" 이미지로부터 생성되었습니다. 앞선 포스팅에서도 이야기했듯, 도커에서 "이미지"라는 개념은 "소프트웨어 프로그램을 실행하기 위해 필요한 모든 파일과 명령어들의 조합"을 의미합니다. 프로그램이 실행되기 위한 모든 조건들을 다 갖추고 있기 때문에 하나의 이미지로부터 여러 개의 프로그램을 실행하는 것이 가능하며, 도커에서 프로그램은 격리된 환경인 "컨테이너" 안에서 실행됩니다. 즉, 하나의 이미지로부터 "여러 개의 컨테이너"를 실행하는 것이며, 각각의 컨테이너 안에서 소프트웨어 프로그램이 동작하고 있는 것입니다.

 

https://docs.microsoft.com/en-us/dotnet/architecture/microservices/container-docker-introduction/docker-containers-images-registries

 

 

 NGINX 이미지는 trusted repository 이므로 만약 해당 이미지가 로컬 머신에 저장되어있지 않은 경우, Docker Hub의 public repository로부터 해당 이미지를 가져오게 됩니다. (아래 이미지에서는 테스트하는 제 로컬 머신에 NGINX 이미지가 이미 저장되어 있으므로 별도의 원격 요청 없이 로컬 저장소의 이미지를 그대로 사용하게 됩니다.)

 

 

위의 명령어를 실행하면 Unique 한 문자열이 생성되는데, 이는 컨테이너의 Unique Identifier입니다. docker run 명령어를 사용해 컨테이너를 새로 만들 때마다, 이렇게 컨테이너는 Unique 한 ID를 갖게 됩니다. 해당 커맨드가 정상적으로 실행되었음에도 터미널에는 아무런 표시가 없는데, 이는 해당 컨테이너는 '--detach (-d)' 모드로 실행시켰기 때문입니다. detach 모드란 "프로그램이 실행되었지만, Terminal에는 Attach 되지 않았음"을 의미합니다. 대부분의 서버 인스턴스들은 Terminal에서 상호작용하는 것보다, 백그라운드에서 네트워크 통신을 통해 상호작용하는 경우가 많기 때문에 이렇게 detach 모드로 동작하는 경우가 많습니다. 이러한 타입의 프로그램을 daemon이나 service라고 합니다.

 

dockerinaction/ch2_mailer는 로컬 머신에 존재하는 이미지가 아니므로 이렇게 원격 저장소에서 가져와 프로그램을 실행하게 됩니다.

 

Running Interactive Containers

위에서 detach 모드를 통해 컨테이너를 실행시키는 방법을 살펴보았습니다. 이와 반대로 Terminal과 interactive 해야 하는 프로그램의 경우 터미널의 input stream과 output stream에 해당 컨테이너를 binding 해주는 방법도 있는데 "--interactive(-i)"과 "--tty(-t)" 명령어를 사용하여 이를 구현할 수 있습니다.

 

 

interactive

--interactive 옵션은 도커 컨테이너에 standard input stream(stdin)을 부착(attach)하는 것을 의미합니다. 즉, terminal의 input이 도커 컨테이너 안으로 "지속적으로" 전달될 수 있도록 이어주는 역할을 한다는 것입니다. 실제로 해당 옵션을 추가하지 않고 --tty 옵션만 추가한 상태로 해당 컨테이너를 시작하면, 터미널의 입력값이 제대로 전달되지 않는 것을 확인할 수 있습니다. (실제로 Echo의 역할만 수행합니다)

 

 

--interactive flag를 전달한 컨테이너의 경우 터미널의 입력값이 제대로 전달되는 것을 확인할 수 있습니다.

 

tty

tty란 teletypewriter를 의미하며, 리눅스 디바이스 드라이브 중 콘솔이나 터미널을 의미합니다. 도커에서 이 -tty 옵션을 주는 것은 해당 컨테이너의 메인 프로세스에게 "인풋이 terminal device"라는 것을 알려주는 옵션입니다. 실제로 "docker run -t --help" 커맨드를 통해 공식적인 -t 옵션에 대한 설명을 확인해보면 "Allocate a pseudo-TTY"라고 되어 있는데, pseudo-TTY는 Pseudo Terminal로써, 유저의 터미널과 STDIN, STDOUT을 연결해주는 역할을 합니다. 

 

 

The -t (or --tty) flag tells Docker to allocate a virtual terminal session within the container. This is commonly used with the -i (or --interactive) option, which keeps STDIN open even if running in detached mode (more about that later).

 

따라서 위 두 옵션을 묶어서 실제로는 "docker run -it ~"의 형태로 주로 사용하게 됩니다. 이렇게 -it 옵션을 사용하게 되면 유저의 Terminal Input이 해당 컨테이너의 STDIN, STDOUT과 연결되게 되며, 지속적으로 유저가 Input Stream을 해당 컨테이너에 제공할 수 있게 되는 것입니다. 

 

Interactive & TTY 옵션에 대한 자세한 설명은 아래 stackoverflow글을 참고해주세요.

 

Confused about Docker -t option to Allocate a pseudo-TTY

What exactly does this option do? I've been reading a lot on TTY and I'm still confused. I played around without having the -t and just -i and it seems like programs that expect user input throw an...

stackoverflow.com

 

 

따라서 아래의 옵션은 다음과 같이 해석할 수 있습니다.

  • busybox image로부터 interactive 한 CLI 애플리케이션을 실행합니다. (유저가 터미널로 지속적인 상호작용 가능)
  • web 컨테이너와 연결합니다. (--link <container_name>:<alias>)
  • 해당 컨테이너의 이름은 'web_test'로 합니다. (--name web_test)

 

해당 컨테이너를 실행하면 -it옵션을 주었으므로 Terminal에서 해당 애플리케이션에 Input Stream을 전달할 수 있으며,  다음과 같이 wget 리눅스 기본 명령어를 사용해서 web 컨테이너에 요청을 보낼 수 있습니다.

 

 

 

 

Listing, stopping restarting and viewing output of containers

"docker ps" 명령어를 사용하면 "현재 가동 중인 컨테이너"의 리스트를 확인할 수 있습니다. 여기서 ps는 Process Status를 의미합니다. 해당 명령어에서는 Container ID, Image name, Command, Create Time, Status, Ports, Name 등의 정보를 확인할 수 있습니다. 

 

 

이외에 도커 컨테이너를 조작하는 몇 가지 주요한 명령어가 있습니다.

 

docker stop

말 그대로 해당 컨테이너를 종료하는 커맨드입니다. 실행하면 해당 컨테이너의 PID = 1인 프로세스를 "halt" 상태로 만들게 됩니다. PID = 1인 프로세스는 모든 프로세스의 Root 이므로 해당 프로세스가 halt 상태로 변경되면 컨테이너가 종료됩니다.

 

docker restart

컨테이너를 재시작하는 명령어입니다.

 

docker logs

해당 컨테이너의 로그를 조회할 수 있는 명령어로, "-f" flag를 추가하면 (--follow) 지속적으로 해당 컨테이너에서 생성되는 로그들을 모니터링할 수 있습니다. (Ctrl + C)를 통해 종료할 수 있습니다.

 

 

 

 

 

Solved Problems and the PID namespace

도커의 "환경 격리"는 리눅스의 PID 네임스페이스 격리를 사용해서 구현 됩니다.(정확히는 리눅스의 네임스페이스 격리"도" 사용하는 것입니다. 그외에 다른 여러 가지 리눅스 기능들을 사용하여 격리를 하지만 여기서는 PID 네임스페이스에 집중하겠습니다.) 리눅스 머신에서 동작하는 모든 프로그램, 즉 프로세스들은 Process ID(PID)라는 Unique Number를 갖게 됩니다. 따라서(Unique Number이므로) 리눅스 시스템 하에서 PID=1인 프로세스는 init 프로세스 단 하나뿐입니다. 하지만 리눅스에서 제공하는 PID 네임스페이스를 사용하면 이들을 조금 더 유연하게 다룰 수 있게 되는데, 가령 전체 리눅스 머신 상에서는 1보다 큰 다른 어떤 숫자를 PID로 갖는 프로세스가 특정 PID 네임스페이스 안의 PID=1인 프로세스일 수 있다는 것입니다. 바로 이 부분을 도커가 사용하여 컨테이너를 "격리"시키는 것입니다.

 

 

다음과 같이 2개의 도커 컨테이너를 생성하고 각각의 컨테이너에서 "ps" 명령어를 실행시켜 보겠습니다. (ps 명령어를 실행시키기 위해 또 다른 프로세스가 동작하게 됩니다.)

 

 

실제로 같은 머신에서 동작하는 두 개의 컨테이너이지만 PID 네임스페이스로 분리되어 있기 때문에 두 개의 컨테이너 모두 PID=1인 프로세스를 가지고 있는 것을 확인할 수 있습니다. 도커는 이렇게 각 컨테이너마다 고유한 PID 네임스페이스를 할당하여 프로세스를 마치 격리된 머신에서 동작되고 있는 것처럼 만들 수 있습니다. (실제로는 Linux namespace, resource limits, filesystem roots, virtualized network components 등을 사용하여 격리를 구현하지만 프로세스의 관점에서는 이렇다는 의미입니다. 그 밖의 다른 내용들은 이후 포스팅에서 차례로 다룰 예정입니다.)

 

 

 

반응형