2. Running software in containers

2024. 3. 20. 12:45Docker

Process isolation and environment-independent computing

격리는 많은 컴퓨팅 패턴, 리소스 관리 전략, 일반적인 계정 관행에 있어 핵심 개념으로, 그 목록을 시작하는 것조차 어려울 정도입니다. 리눅스 컨테이너가 실행 중인 프로그램에 대한 격리를 어떻게 제공하는지, 그리고 Docker를 사용하여 그 격리를 어떻게 제어하는지 배우는 사람은 재사용, 자원 효율성, 시스템 단순화라는 놀라운 업적을 달성할 수 있습니다.

컨테이너를 적용하는 방법을 배우는 가장 어려운 부분은 격리하려고 하는 소프트웨어의 필요성을 인식하는 것입니다. 다른 프로그램들은 다른 요구 사항을 가지고 있습니다. 웹 서비스는 텍스트 에디터, 패키지 관리자, 컴파일러 또는 데이터베이스와 다릅니다. 각각의 프로그램에 대한 컨테이너는 다른 구성이 필요할 것입니다.

이 부분은 컨테이너 구성과 운영의 기초를 다루며, 전체 기능 범위를 보여주기 위해 더 자세한 컨테이너 구성으로 확장됩니다. 그래서 앞으로 넘어가고 싶은 충동을 억누르려고 노력하는 것이 좋습니다. 여러분의 마음에 있는 특정 질문에 도달하기까지 다소 시간이 걸릴 수 있지만, 우리는 여러분이 길을 따라 몇 가지 이상의 통찰을 얻게 될 것이라고 확신합니다.

 

이 장에서 다루는 내용:

  • 컨테이너에서 대화형 및 데몬 터미널 프로그램 실행하기
  • 기본 Docker 작업 및 명령어
  • 프로그램을 서로 격리하고 구성을 주입하기
  • 컨테이너에서 여러 프로그램 실행하기
  • 내구성 있는 컨테이너와 컨테이너 생명 주기
  • Cleaning up

이 장의 끝에 다다르면, 여러분은 컨테이너를 사용하는 모든 기초를 이해하고 Docker를 통해 기본 프로세스 격리를 어떻게 제어하는지 알게 될 것입니다. 이 책의 대부분의 예제는 실제 소프트웨어를 사용합니다. 실용적인 예제들은 Docker 기능을 소개하고 여러분이 일상 활동에서 이를 어떻게 사용할지를 보여줄 것입니다. 시중에 나와 있는 이미지를 사용하는 것은 새로운 사용자의 학습 곡선을 줄여줍니다. 컨테이너화하고 싶은 소프트웨어가 있고, 서두르고 있다면 2부에서 여러분의 직접적인 질문에 더 많은 답을 제공할 가능성이 큽니다. 이 장에서는 NGINX라고 하는 웹 서버를 설치할 것입니다. 웹 서버는 네트워크를 통해 웹 브라우저가 웹사이트 파일과 프로그램에 접근할 수 있도록 하는 프로그램입니다. 여러분은 웹사이트를 구축하지 않을 것이지만, Docker를 사용하여 웹 서버를 설치하고 시작할 것입니다.

 

2.1 컨테이너 제어하기: 웹사이트 모니터링 구축하기

새로운 고객이 여러분의 사무실에 들어와 상당히 놀라운 요청을 합니다: 그들은 새 웹사이트를 만들어 달라고 하며, 이 웹사이트는 철저하게 모니터링되어야 한다고 합니다. 이 특별한 고객은 자체 운영을 실행하길 원하기 때문에, 서버가 다운됐을 때 그들의 팀에게 이메일을 보내는 솔루션을 원합니다. 또한, NGINX라고 불리는 인기 있는 웹 서버 소프트웨어에 대해 들었고, 구체적으로 그것을 사용해달라고 요청했습니다. Docker를 사용하는 것의 장점에 대해 읽은 후, 이 프로젝트에 그것을 사용하기로 결정했습니다. 그림 2.1은 이 프로젝트를 위한 계획된 아키텍처를 보여줍니다.

그림 2.1 이 예제에서 구축할 세 개의 컨테이너


이 예제는 세 개의 컨테이너를 사용합니다. 첫 번째는 NGINX를 실행할 것이고, 두 번째는 mailer라고 불리는 프로그램을 실행할 것입니다. 이 둘은 모두 분리된 컨테이너로 실행될 것입니다. 분리된다는 것은 컨테이너가 백그라운드에서 실행되며, 어떤 입력이나 출력 스트림에도 연결되지 않는다는 의미입니다. 세 번째 프로그램인 watcher는 상호작용하는 컨테이너에서 모니터링 에이전트로 실행될 것입니다. mailerwatcher 에이전트는 이 예제를 위해 만들어진 작은 스크립트입니다. 이 섹션에서는 다음을 수행하는 방법을 배울 것입니다:

  • 분리된 및 상호작용하는 컨테이너 생성하기
  • 시스템의 컨테이너 목록 보기
  • 컨테이너 로그 보기
  • 컨테이너 정지 및 재시작하기
  • 컨테이너에 터미널 다시 연결하기
  • 연결된 컨테이너에서 분리하기

더 이상 지체하지 말고, 고객의 주문을 처리하기 시작합시다.

 

2.1.1 새 컨테이너 만들기 및 시작하기

Docker는 소프트웨어 프로그램을 실행하는 데 필요한 파일과 지침의 집합을 이미지라고 부릅니다. Docker를 사용하여 소프트웨어를 설치할 때, 우리는 실제로 Docker를 사용하여 이미지를 다운로드하거나 생성하는 것입니다. 이미지를 설치하는 방법은 여러 가지가 있으며 이미지의 소스도 여러 가지입니다. 이미지에 대해서는 3장에서 더 자세히 다루겠지만, 지금은 전 세계적으로 물리적 상품을 운송하는 데 사용되는 화물 컨테이너로 생각할 수 있습니다. Docker 이미지는 소프트웨어를 실행하는 데 컴퓨터가 필요한 모든 것을 담고 있습니다.

이 예제에서는 Docker Hub에서 NGINX의 이미지를 다운로드하고 설치할 것입니다. Docker Hub는 Docker Inc.가 제공하는 공개 레지스트리입니다. NGINX 이미지는 Docker Inc.가 신뢰하는 저장소라고 부릅니다. 일반적으로, 소프트웨어를 게시하는 사람이나 재단이 해당 소프트웨어의 신뢰할 수 있는 저장소를 제어합니다. 다음 명령을 실행하면 NGINX를 실행하는 컨테이너를 다운로드, 설치, 시작합니다:

 

docker run --detach \ 
 --name web nginx:latest

 

이 명령을 실행하면, Docker는 Docker Hub에 호스팅된 NGINX 저장소에서 nginx:latest를 설치하고 소프트웨어를 실행할 것입니다(3장에서 다룹니다). Docker가 NGINX를 설치하고 실행을 시작한 후, 터미널에는 seemingly random characters처럼 보이는 한 줄이 작성됩니다. 이것은 대략 다음과 같아 보일 것입니다:

7cb5d2b9a7eab87f07182b5bf58936c9947890995b1b94f412912fa822a9ecb5


그 문자들의 덩어리는 방금 NGINX를 실행하기 위해 생성된 컨테이너의 고유 식별자입니다. docker run을 실행하여 새 컨테이너를 생성할 때마다, 그 새 컨테이너는 고유 식별자를 받게 됩니다. 사용자가 다른 명령과 함께 사용하기 위해 이 출력을 변수로 캡처하는 것이 일반적입니다. 이 예제를 위해서는 그럴 필요가 없습니다.

식별자가 표시된 후에는 아무 일도 일어나지 않은 것처럼 보일 수 있습니다. 그 이유는 --detach 옵션을 사용하고 프로그램을 백그라운드에서 시작했기 때문입니다. 이는 프로그램이 시작되었지만 터미널에 연결되지 않았음을 의미합니다. 몇 개의 프로그램을 실행할 예정이기 때문에 NGINX를 이런 식으로 시작하는 것이 타당합니다. 서버 소프트웨어는 일반적으로 연결된 터미널에 의존하지 않는 경우가 드물기 때문에 분리된 컨테이너에서 실행됩니다.

Docker run --detach 명령은 컨테이너를 백그라운드에서 실행하도록 지시하는 옵션입니다. 이 옵션을 사용하면 컨테이너가 백그라운드에서 실행되며, 터미널이 컨테이너의 출력을 차지하지 않고 새 명령을 입력할 수 있습니다. 일반적으로 이 옵션은 `-d` 또는 `--detach`로 사용됩니다. 따라서 "docker run --detach" 명령은 컨테이너를 백그라운드에서 실행하도록 하고, 새로운 터미널 창을 여는 것 없이 다른 명령을 입력할 수 있도록 합니다.


백그라운드에서 조용히 실행되는 프로그램에 대해서는 분리된 컨테이너를 실행하는 것이 완벽한 선택입니다. 그러한 유형의 프로그램을 데몬 또는 서비스라고 합니다. 데몬은 일반적으로 네트워크나 다른 통신 채널을 통해 다른 프로그램이나 사람과 상호작용합니다. 백그라운드에서 실행하고자 하는 데몬이나 다른 프로그램을 컨테이너에서 시작할 때는 --detach 플래그 또는 그 짧은 형태인 -d를 사용하는 것을 기억하세요.

 

이 예제에서 고객이 필요로 하는 또 다른 데몬은 mailer입니다. mailer는 발신자로부터의 연결을 기다렸다가 이메일을 보냅니다. 다음 명령은 이 예제에 적합한 메일러를 설치하고 실행합니다:

docker run -d \ 
 --name mailer \
 dockerinaction/ch2_mailer

 

이 명령은 --detach 플래그의 짧은 형태를 사용하여 mailer라는 이름의 새 컨테이너를 백그라운드에서 시작합니다. 이 시점에서, 당신은 두 개의 명령을 실행하고 고객이 원하는 시스템의 2/3을 제공했습니다. 마지막 구성 요소인 에이전트는 상호작용하는 컨테이너에 잘 어울립니다.

 

 

2.1.2 대화형 컨테이너 실행

터미널 기반 텍스트 편집기는 연결된 터미널이 필요한 프로그램의 좋은 예입니다. 키보드(또는 마우스)를 통해 사용자로부터 입력을 받아 터미널에 출력을 표시합니다. 입력 및 출력 스트림에 대해 대화형입니다. Docker에서 대화형 프로그램을 실행하려면 터미널의 일부를 실행 중인 컨테이너의 입력 또는 출력에 바인딩해야 합니다.
  대화형 컨테이너 작업을 시작하려면 다음 명령을 실행하세요.

docker run --interactive --tty \ 
 --link web:web \
 --name web_test \
 busybox:1.29 /bin/sh

※ 아쉽게도 gitbash에서는 위 커맨드를 정상적으로 실행시킬 수 없습니다. PowerShell에서 이 커맨드를 실행시켜야 합니다. 그런데 PowerShell에서는 위와 같이 \가 인식되지 않습니다. 그러므로 \ 대신, (`)를 사용해 커맨드를 작성해야 합니다.

위 커맨드 중, busybox:1.29 /bin/sh에서 busybox:1.29는 사용할 BusyBox의 버전 1.29 이미지를 지정합니다. /bin/sh는 이 BusyBox 이미지 내에서 실행될 셸의 경로를 나타내며, 컨테이너가 시작되면 /bin/sh를 통해 셸이 실행됩니다. 이를 통해 사용자는 컨테이너 내부에서 셸을 사용할 수 있게 되어, BusyBox가 제공하는 다양한 명령어를 실행할 수 있습니다.

busybox:
BusyBox는 다양한 UNIX 유틸리티를 단일 작은 실행 파일로 결합한 소프트웨어로, 주로 임베디드 시스템, 모바일 장치 및 가상 컨테이너 환경에서 사용됩니다. 이를 통해 리소스가 제한된 환경에서도 효율적으로 작업을 수행할 수 있습니다. BusyBox는 Linux의 스위스 군용 칼로 불리며, 파일 관리, 셸 스크립팅, 네트워킹 등 다양한 명령어를 제공합니다.
BusyBox는 이를 통해 개별 명령어들을 각각 별도의 프로그램으로 관리하는 대신, 하나의 경량화된 실행 파일로 다양한 기능을 사용할 수 있게 됩니다. 이러한 특성 때문에 BusyBox는 리소스가 제한된 환경에서 매우 유용하게 사용됩니다.

 

--link 옵션
이 옵션은 Docker에서 컨테이너 간의 통신을 설정할 때 사용되는 옵션입니다. 이 옵션은 한 컨테이너가 다른 컨테이너의 정보에 접근할 수 있도록 하며, 초기 Docker 버전에서 컨테이너 간의 네트워킹을 구성하는 주된 방법 중 하나였습니다. 그러나 현재는 Docker 네트워크 기능이 발전하면서 --link 옵션은 더 이상 권장되지 않으며, 대신 Docker 네트워크를 사용하는 것이 권장됩니다.
--link <컨테이너 이름>:<별칭> 형식을 사용하며, 여기서 <컨테이너 이름>은 연결하려는 대상 컨테이너의 이름이고, <별칭>은 해당 컨테이너를 참조하기 위해 사용할 이름입니다. 이렇게 설정하면, 소스 컨테이너(옵션을 사용하는 컨테이너)는 대상 컨테이너를 별칭으로 참조할 수 있게 됩니다.

 

위 명령은 run 명령에 두 가지 플래그를 사용합니다: --interactive (또는 -i)와 --tty (또는 -t). 먼저, --interactive 옵션은 터미널이 연결되어 있지 않더라도 컨테이너에 대해 표준 입력 스트림(stdin)을 열어 두라고 Docker에게 지시합니다. 두 번째로, --tty 옵션은 컨테이너에 가상 터미널을 할당하도록 Docker에게 지시하는데, 이를 통해 컨테이너에 신호를 전달할 수 있습니다. 이것은 보통 여러분이 상호작용적(Interactive)인 커맨드 라인 프로그램에서 원하는 바입니다. 상호작용적인 프로그램, 예를 들어 상호작용적인 컨테이너에서 셸을 실행할 때 보통 이 두 옵션을 함께 사용할 것입니다.

상호작용 플래그만큼 중요한 것은, 이 컨테이너를 시작할 때 컨테이너 내부에서 실행할 프로그램을 지정했다는 것입니다. 이 경우, sh라고 불리는 셸 프로그램을 실행했습니다. 컨테이너 내부에서 사용할 수 있는 어떤 프로그램이든 실행할 수 있습니다.

상호작용하는 컨테이너 예제의 명령은 컨테이너를 생성하고, UNIX 셸을 시작하며, NGINX를 실행하는 컨테이너와 연결됩니다. 이 셸에서, 여러분의 웹 서버가 올바르게 실행되고 있는지 확인하기 위한 명령을 실행할 수 있습니다:

 

wget -O - http://web:80/

위 커맨드 옵션 설정 설명 참조

 

이것은 wget이라는 프로그램을 사용하여 웹 서버(앞서 컨테이너에서 시작한 NGINX 서버)에 HTTP 요청을 만들고, 그 다음 터미널에 웹 페이지의 내용을 표시하는 데 사용됩니다. 다른 줄들 사이에, "Welcome to NGINX!"와 같은 메시지가 있어야 합니다. 그 메시지를 본다면, 모든 것이 올바르게 작동하고 있는 것이므로, exit을 입력하여 이 상호작용 컨테이너를 종료할 수 있습니다. 이것은 셸 프로그램을 종료하고 컨테이너를 중지시킬 것입니다.

상호작용하는 컨테이너를 생성하고, 그 컨테이너 안에서 수동으로 프로세스를 시작한 다음, 터미널을 분리할 수 있습니다. Ctrl(또는 Control) 키를 누른 상태에서 P를 누르고 그 다음 Q를 누르면 됩니다. 이것은 --tty 옵션을 사용했을 때만 작동합니다.

고객을 위한 작업을 마무리하기 위해, 모니터링 에이전트를 시작해야 합니다. 이것은 앞선 예제에서 한 것처럼 웹 서버를 테스트하고 웹 서버가 멈추면 메일러를 통해 메시지를 보내는 모니터링 에이전트입니다. 이 명령은 짧은 형태의 플래그를 사용하여 상호작용하는 컨테이너에서 에이전트를 시작할 것입니다:

 

Creates a virtual terminal and binds stdin

docker run -it \ 
 --name agent \
 --link web:insideweb \
 --link mailer:insidemailer \
 dockerinaction/ch2_agent

 

실행 중인 컨테이너는 매초마다 웹 컨테이너를 테스트하고 다음과 같은 메시지를 출력할 것입니다:

System up.

 

이제 그것이 무엇을 하는지 보았으니, 컨테이너에서 터미널을 분리하십시오. 구체적으로, 컨테이너를 시작하고 "System up"을 쓰기 시작하면, Ctrl(또는 Control) 키를 누르고 P를 누른 다음 Q를 누릅니다. 이렇게 하면 호스트 컴퓨터의 셸로 돌아갑니다. 프로그램을 중지하지 마십시오; 그렇지 않으면 모니터가 웹 서버를 확인하는 것을 중지할 것입니다.

네트워크의 서버에 배포하는 소프트웨어에 대해 주로 분리된 또는 데몬 컨테이너를 사용할 것이지만, 데스크탑에서 소프트웨어를 실행하거나 서버에서 수동 작업을 하는 데 상호작용하는 컨테이너가 유용합니다. 이 시점에서, 고객이 필요로 하는 세 가지 애플리케이션을 모두 컨테이너에서 시작했습니다. 시스템을 테스트하기 전까지는 자신있게 완성을 주장할 수 없습니다.

 

2.1.3 컨테이너의 목록 보기, 정지하기, 재시작하기, 출력 보기

현재 설정을 테스트하기 위해 해야 할 첫 번째 일은 docker ps 명령을 사용하여 현재 실행 중인 컨테이너를 확인하는 것입니다:

docker ps


명령을 실행하면 각 실행 중인 컨테이너에 대한 다음 정보가 표시됩니다:

  • 컨테이너 ID
  • 사용된 도커 이미지
  • 컨테이너에서 실행된 커맨드
  • 컨테이너 생성 이후 경과 시간
  • 컨테이너가 실행된 기간
  • 컨테이너가 공개한 네트워크 포트
  • 컨테이너의 이름

이 시점에서, web, mailer, agent라는 이름의 세 개의 실행 중인 컨테이너가 있어야 합니다. 만약 어떤 것이 누락되었지만 지금까지 예제를 따라왔다면, 실수로 정지되었을 수 있습니다. 이는 Docker에게 컨테이너를 재시작하는 명령이 있기 때문에 문제가 되지 않습니다. 다음 세 명령은 컨테이너 이름을 사용하여 각 컨테이너를 재시작할 것입니다. 실행 중인 컨테이너 목록에서 누락된 컨테이너를 재시작하기 위해 적절한 명령을 선택하세요:

docker restart web
docker restart mailer
docker restart agent


이제 세 개의 컨테이너가 모두 실행 중이므로, 시스템이 올바르게 작동하는지 테스트해야 합니다. 그렇게 하는 가장 좋은 방법은 각 컨테이너의 로그를 검사하는 것입니다. web 컨테이너부터 시작하세요:

docker logs web


이것은 "GET / HTTP/1.0" 200이 포함된 여러 줄의 긴 로그를 표시해야 합니다.
이는 웹 서버가 실행 중이며 에이전트가 사이트를 테스트하고 있다는 것을 의미합니다. 에이전트가 사이트를 테스트할 때마다, 이러한 줄 중 하나가 로그에 기록될 것입니다. docker logs 명령은 이러한 경우에 도움이 될 수 있지만, 의존하기에는 위험할 수 있습니다. 프로그램이 stdout 또는 stderr 출력 스트림에 기록하는 모든 것이 이 로그에 기록됩니다. 이 패턴의 문제는 기본적으로 로그가 절대로 로테이트되거나 축소되지 않기 때문에, 컨테이너에 대해 로그에 기록된 데이터는 컨테이너가 존재하는 한 계속 남아 있고 증가할 것입니다. 장기 실행 프로세스에 대해 이러한 장기 지속성은 문제가 될 수 있습니다. 로그 데이터를 사용하는 더 좋은 방법은 볼륨을 사용하는 것이며, 4장에서 논의됩니다.

에이전트가 웹 서버를 모니터링하고 있는지 확인하기 위해 web의 로그만 검사할 수 있습니다. 완전성을 위해 mailer와 agent의 로그 출력도 검사해야 합니다:

docker logs mailer
docker logs agent

 

mailer의 로그는 다음과 같은 모습일 것입니다:

CH2 Example Mailer has started.


agent의 로그는 컨테이너를 시작했을 때 보았던 것처럼 다음과 같은 여러 줄을 포함해야 합니다:

System up.

Tip: docker logs 명령에는 로그를 표시한 다음 발생하는 변경 사항과 함께 표시를 계속 업데이트하는 --follow 또는 -f 플래그가 있습니다. 완료했을 때, Ctrl-C(또는 Command-C)를 눌러 logs 명령을 중단하세요.

컨테이너가 실행 중이고 에이전트가 웹 서버에 도달할 수 있음을 확인했으니, 이제 웹 컨테이너가 정지될 때 에이전트가 그것을 감지하고 메일러에 호출을 트리거할 수 있는지 테스트해야 합니다. 그런 일이 발생하면, 에이전트와 메일러의 로그에 이벤트가 기록됩니다. docker stop 명령은 컨테이너 내 PID 1인 프로그램에 중단하라고 지시합니다. 시스템을 테스트하기 위해 다음 명령을 사용하세요:

docker stop web
docker logs mailer


mailer 로그의 끝 부분에서 다음과 같은 줄을 찾으세요:

Sending email: To: admin@work Message: The service is down!


이 줄은 에이전트가 web이라는 이름의 컨테이너 내 NGINX 서버가 정지되었음을 성공적으로 감지했음을 의미합니다. 축하합니다! 고객이 만족할 것이며, 당신은 컨테이너와 Docker를 사용하여 첫 번째 실제 시스템을 구축했습니다.

기본 Docker 기능을 배우는 것은 하나의 일이지만, 그것들이 유용한 이유와 격리를 사용자 정의하는 방법을 이해하는 것은 완전히 다른 과제입니다.

docker stop web 
docker logs mailer

 

mailer 로그의 끝 부분에서 다음과 같은 줄을 찾으세요:

Sending email: To: admin@work Message: The service is down!


이 라인은 에이전트가 web이라는 이름의 컨테이너 내 NGINX 서버가 정지되었음을 성공적으로 감지했음을 의미합니다. 축하합니다! 고객이 만족할 것이며, 당신은 컨테이너와 Docker를 사용하여 첫 번째 실제 시스템을 구축했습니다.

기본 Docker 기능을 배우는 것은 한 가지 일이지만, 그것들이 왜 유용하고 어떻게 격리를 사용자 정의하는 데 사용할 수 있는지를 이해하는 것은 전혀 다른 과제입니다.

 

2.2 해결된 문제들과 PID 네임스페이스

리눅스 기계에서 실행 중인 모든 프로그램 또는 프로세스는 프로세스 식별자(PID)라고 불리는 고유한 번호를 가집니다. PID 네임스페이스는 프로세스를 식별하는 고유한 번호의 집합입니다. 리눅스는 여러 PID 네임스페이스를 생성하는 도구를 제공합니다. 각 네임스페이스는 가능한 PID의 완전한 집합을 가집니다. 이는 각 PID 네임스페이스가 자체 PID 1, 2, 3 등을 포함할 것임을 의미합니다.

대부분의 프로그램은 다른 실행 중인 프로세스에 접근하거나 시스템상의 다른 실행 중인 프로세스를 나열할 필요가 없습니다. 그래서 Docker는 기본적으로 각 컨테이너에 대해 새로운 PID 네임스페이스를 생성합니다. 컨테이너의 PID 네임스페이스는 해당 컨테이너의 프로세스를 다른 컨테이너의 프로세스로부터 격리시킵니다.

자체 네임스페이스를 가진 한 컨테이너의 프로세스 관점에서, PID 1은 runit 또는 supervisord와 같은 초기 시스템 프로세스를 가리킬 수 있습니다. 다른 컨테이너에서는, PID 1이 bash와 같은 명령 셸을 가리킬 수 있습니다. 이를 실제로 확인하기 위해 다음을 실행해보세요:

docker run -d --name namespaceA \
 busybox:1.29 /bin/sh -c "sleep 30000"
 
docker run -d --name namespaceB \
 busybox:1.29 /bin/sh -c "nc -l 0.0.0.0 -p 80"

 

docker exec namespaceA ps     ----> (1)
docker exec namespaceB ps     ----> (2)

공통점: 두 명령어 모두 실행 중인 컨테이너 내에서 ps 명령을 사용해 현재 실행 중인 프로세스 목록을 조회합니다.

차이점: namespaceA 컨테이너는 sleep 30000 커맨드로 인해 주로 sleep 프로세스가 실행 중임을 보여줄 것이며, namespaceB 컨테이너는 nc -l 0.0.0.0 -p 80 커맨드로 인해 네트워크 리스닝 관련 프로세스(여기서는 nc)가 실행 중임을 보여줄 것입니다.


docker exec 커맨드는 이미 실행 중인 컨테이너에서 새로운 커맨드를 실행할 수 있게 해줍니다. 이 커맨드는 컨테이너의 주 프로세스가 활성 상태일 때만 지정된 커맨드를 실행하며, 컨테이너가 재시작될 때 커맨드는 재시작되지 않습니다. 실행은 컨테이너의 기본 작업 디렉토리에서 이루어지며, 실행할 커맨드는 실행 파일이어야 합니다. 이 커맨드는 컨테이너 내에서 대화형 쉘 세션을 시작하거나 새 컨테이너를 시작하지 않고 단일 커맨드를 실행하는 등 다양한 작업에 유용합니다

 

 

이 설정은 특정 기간 동안 활동적인 작업을 수행하지 않으면서도 계속 실행되는 컨테이너를 생성하는 데 유용합니다. 이는 종종 테스트나 기타 관리 목적으로 사용됩니다.

 

위 커맨드 (1)는 다음과 같은 프로세스 목록을 생성해야 합니다:

PID USER TIME COMMAND
 1 root 0:00 sleep 30000
 8 root 0:00 ps

 

커맨드 (2)는 약간 다른 프로세스 목록을 생성해야 합니다:

PID USER TIME COMMAND
 1 root 0:00 nc -l 0.0.0.0 -p 80
 9 root 0:00 ps

 

이 예제에서는 실행 중인 컨테이너에서 추가 프로세스를 실행하기 위해 docker exec 명령을 사용합니다. 이 경우 사용하는 명령은 ps로, 실행 중인 모든 프로세스와 그들의 PID를 보여줍니다. 출력에서 볼 수 있듯이, 각 컨테이너는 PID 1을 가진 프로세스를 가지고 있습니다.

PID 네임스페이스 없이는 컨테이너 내부에서 실행 중인 프로세스가 다른 컨테이너나 호스트의 같은 ID 공간을 공유할 것입니다. 컨테이너 내의 프로세스는 호스트 기계에서 실행 중인 다른 프로세스를 확인할 수 있을 것입니다. 더 나쁜 것은, 한 컨테이너의 프로세스가 다른 컨테이너의 프로세스를 제어할 수도 있습니다. 자신의 네임스페이스 밖의 어떤 프로세스도 참조할 수 없는 프로세스는 특정 공격을 수행하는 능력이 제한됩니다.

프로세스 네임스페이스는 운영 체제에서 프로세스가 다른 프로세스와 격리되어 실행될 수 있도록 하는 기술입니다. 각 네임스페이스는 자체적인 프로세스 ID, 시스템 리소스, 파일 시스템 등을 가지며, 이를 통해 프로세스가 시스템의 다른 부분과 독립적으로 작동할 수 있습니다. 이는 보안과 리소스 관리 측면에서 유용하며, 특히 컨테이너화된 환경에서 각 컨테이너가 호스트 시스템과 독립적으로 작동할 수 있도록 지원합니다.

 

대부분의 Docker 격리 기능과 마찬가지로, 선택적으로 자체 PID 네임스페이스 없이 컨테이너를 생성할 수 있습니다. 이것은 컨테이너 내부에서 프로세스 열거를 요구하는 시스템 관리 작업을 수행하는 프로그램을 사용하는 경우 중요합니다. docker create 또는 docker run에 --pid 플래그를 설정하고 값으로 host를 설정함으로써 이를 직접 시도해볼 수 있습니다. BusyBox Linux를 실행하는 컨테이너와 ps Linux 커맨드를 사용하여 직접 시도해보세요:

docker run --pid host busybox:1.29 ps

 

컨테이너가 각자의 PID 네임스페이스를 가지고 있기 때문에, 그것을 검토함으로써 의미 있는 통찰을 얻거나 더 고정적인 의존성을 가질 수 없습니다. 컨테이너가 서버와 로컬 프로세스 모니터 두 개의 프로세스를 실행한다고 가정해봅시다. 그 모니터는 서버의 예상 PID에 대한 확실한 의존성을 가질 수 있고, 그것을 사용하여 서버를 모니터링하고 제어할 수 있습니다. 이것은 환경 독립성의 한 예입니다.

명령어 docker run --pid host busybox:1.29 ps는 버전 1.29의 BusyBox 컨테이너를 실행하고 내부에서 ps 명령을 실행합니다. --pid host 옵션은 컨테이너의 프로세스 네임스페이스를 호스트의 것으로 설정하여 컨테이너가 컨테이너 내부가 아닌 호스트 시스템에서 실행 중인 모든 프로세스를 볼 수 있게 합니다. 이는 컨테이너 내부에서 시스템 프로세스를 모니터링하거나 관리하는 데 특히 유용합니다.


이전의 웹 모니터링 예를 고려해보세요. Docker를 사용하지 않고 컴퓨터에서 직접 NGINX를 실행하고 있다고 가정해 봅시다. 다른 프로젝트를 위해 이미 NGINX를 시작한 것을 잊었다고 가정해보세요. NGINX를 다시 시작할 때, 두 번째 프로세스는 첫 번째 프로세스가 이미 그 자원을 가지고 있기 때문에 필요한 자원에 접근할 수 없을 것입니다. 이것은 기본적인 소프트웨어 충돌 예입니다. 동일한 컨테이너에서 NGINX의 두 복사본을 실행하려고 시도함으로써 이를 실제로 볼 수 있습니다:

docker run -d --name webConflict nginx:latest
docker logs webConflict 
docker exec webConflict nginx -g 'daemon off;'

 

마지막 명령은 다음과 같은 출력을 보여줘야 합니다:

2015/03/29 22:04:35 [emerg] 10#0: bind() to 0.0.0.0:80 failed (98: 
Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
...

 

두 번째 프로세스는 제대로 시작하지 못하고 필요한 주소가 이미 사용 중임을 보고합니다. 포트 충돌이라고 불리는 이 문제는 같은 컴퓨터에서 여러 프로세스가 실행되거나 여러 사람이 같은 환경에 기여하는 실제 시스템에서 흔히 발생하는 문제입니다. Docker가 단순화하고 충돌 문제 해결의 좋은 예입니다. 다음과 같이 각각을 다른 컨테이너에서 실행하세요:

docker run -d --name webA nginx:latest 
docker logs webA 
docker run -d --name webB nginx:latest 
docker logs webB

 

Environment 독립성은 다른 동일한 위치의 소프트웨어와 충돌하는 요구 사항을 가진 희귀한 시스템 리소스에 의존하는 소프트웨어를 구성할 수 있는 자유를 제공합니다.

다음은 일반적인 충돌 문제들입니다:

  • 두 프로그램이 같은 네트워크 포트에 바인드하려고 합니다.
  • 두 프로그램이 같은 임시 파일 이름을 사용하고, 파일 잠금이 이를 방지합니다.
  • 두 프로그램이 글로벌하게 설치된 라이브러리의 다른 버전을 사용하려고 합니다.
  • 두 프로세스가 같은 PID 파일을 사용하려고 합니다.
  • 설치한 두 번째 프로그램이 다른 프로그램이 사용하는 환경 변수를 수정했습니다. 이제 첫 번째 프로그램이 작동을 멈춥니다.
  • 여러 프로세스가 메모리나 CPU 시간을 경쟁합니다.


이러한 충돌은 하나 이상의 프로그램이 공통의 의존성을 가지고 있지만 공유하거나 다른 요구 사항을 가진 것에 동의하지 못할 때 발생합니다. 이전의 포트 충돌 예제에서처럼, Docker는 리눅스 네임스페이스, 리소스 제한, 파일시스템 루트, 가상화된 네트워크 구성 요소와 같은 도구로 소프트웨어 충돌을 해결합니다. 이 모든 도구는 Docker 컨테이너 내부에서 소프트웨어를 격리하는 데 사용됩니다.

 

2.3 메타충돌[metaconflicts] 제거: 웹사이트 Farm 구축

앞선 섹션에서는 프로세스 격리를 통해 Docker가 소프트웨어 충돌을 피하는 데 어떻게 도움을 주는지 보았습니다. 하지만 주의하지 않으면, Docker 레이어에서 컨테이너 간의 충돌, 즉 메타충돌을 생성하는 시스템을 구축하게 될 수 있습니다.

또 다른 예를 생각해 보십시오. 고객이 고객을 위해 다양한 수의 웹사이트를 호스팅할 수 있는 시스템을 구축해 달라고 요청했습니다. 또한, 이 장에서 앞서 구축한 같은 모니터링 기술을 사용하기를 원합니다. 이전에 구축한 시스템을 확장하는 것이 NGINX에 대한 설정을 커스터마이징하지 않고도 이 작업을 수행하는 가장 간단한 방법일 것입니다. 이 예제에서는 여러 컨테이너가 웹 서버를 실행하고 각 웹 서버에 대한 모니터링 감시자를 가진 시스템을 구축할 것입니다. 시스템은 그림 2.2에서 설명한 아키텍처와 같아 보일 것입니다.

그림 2.2 일련의 웹 서버 컨테이너 및 관련 모니터링 에이전트

 

처음에는 단순히 더 많은 웹 컨테이너를 시작하는 것이 좋을 수 있습니다. 하지만 그것은 보이는 것처럼 단순하지 않습니다. 컨테이너의 수가 증가함에 따라 컨테이너를 식별하는 것이 복잡해집니다.

 

 

2.3.1 유연한 컨테이너 식별

이전 예제에서 사용한 NGINX 컨테이너의 복사본을 단순히 더 많이 생성하는 것이 나쁜 생각인 이유를 알아보는 가장 좋은 방법은 직접 시도해보는 것입니다:

docker run -d --name webid nginx 
docker run -d --name webid nginx


여기서 두 번째 커맨드는 충돌 오류로 실패할 것입니다:

FATA[0000] Error response from daemon: Conflict. The name "webid" is 
already in use by container 2b5958ba6a00. You have to delete (or rename) 
that container to be able to reuse that name.

 

웹과 같은 고정된 컨테이너 이름을 사용하는 것은 실험과 문서화에 유용하지만 여러 컨테이너가 있는 시스템에서 이와 같은 고정된 이름을 사용하면 충돌이 발생할 수 있습니다. 기본적으로 Docker는 생성되는 각 컨테이너에 고유한(사람에게 친숙한) 이름을 할당합니다. --name 플래그는 알려진 값으로 해당 프로세스의 이름을 재정의합니다. 컨테이너 이름을 변경해야 하는 상황이 발생하면 언제든지 docker rename 명령을 사용하여 컨테이너 이름을 바꿀 수 있습니다:

docker rename webid webid-old   // 현재 web 컨테이너 이름을 webid-old로 변경합니다
docker run -d --name webid nginx  // webid라는 이름의 또 다른 컨테이너를 생성합니다

 

컨테이너의 이름을 바꾸는 것은 일회성으로 이름 충돌을 완화할 수 있지만, 처음부터 문제를 방지하는 데는 큰 도움이 되지 않습니다. 이름 외에도 Docker는 고유 식별자를 할당합니다. 이 ID는 16진수로 인코딩된 1024비트 숫자로 구성되어 있으며, 특정 형식의 긴 문자열로 나타납니다.

7cb5d2b9a7eab87f07182b5bf58936c9947890995b1b94f412912fa822a9ecb5

 

컨테이너가 Detach 모드(백그라운드로 실행)로 시작될 때, 그 ID는 터미널에 출력됩니다. 이 ID들을 특정 컨테이너를 식별해야 하는 모든 명령어에서 컨테이너 이름 대신 사용할 수 있습니다. 예를 들어, 이전에 언급된 ID를 stop 또는 exec 명령어와 함께 사용할 수 있습니다.

docker exec \
 7cb5d2b9a7eab87f07182b5bf58936c9947890995b1b94f412912fa822a9ecb5 \
echo hello
docker stop \
 7cb5d2b9a7eab87f07182b5bf58936c9947890995b1b94f412912fa822a9ecb5

 

생성된 ID의 고유성 확률이 높기 때문에 이 ID와 충돌할 확률은 매우 낮습니다. 더 나아가 같은 컴퓨터에서 이 ID의 처음 12자가 충돌할 확률도 낮습니다. 그래서 대부분의 Docker 인터페이스에서는 컨테이너 ID를 처음 12자로 축약하여 표시합니다. 이렇게 함으로써 생성된 ID를 사용자 친화적으로 만듭니다. 컨테이너 식별자가 필요한 어느 곳에서나 이를 사용할 수 있습니다. 이전 두 명령어는 이렇게 작성될 수 있습니다:

docker exec 7cb5d2b9a7ea ps
docker stop 7cb5d2b9a7ea

 

이러한 ID들은 인간이 사용하기에 특별히 적합하지 않지만, 스크립트와 자동화 기법에는 잘 작동합니다. Docker는 컨테이너의 ID를 획득하여 자동화를 가능하게 하는 몇 가지 방법을 제공합니다. 이 경우, 전체 또는 축약된 숫자 ID가 사용됩니다.

 

컨테이너의 숫자 ID를 얻는 첫 번째 방법은 새로운 컨테이너를 시작하거나 생성하고 커맨드의 결과를 셸 변수에 할당하는 것입니다. 앞서 보셨듯이, 새 컨테이너가 Detach 모드에서 시작될 때, 컨테이너 ID가 터미널에 출력됩니다. 만약 이것이 생성 시점에 컨테이너 ID를 얻는 유일한 방법이라면, 대화형 컨테이너와 함께 사용할 수 없습니다. 다행히도, 컨테이너를 시작하지 않고 생성하는 또 다른 커맨드를 사용할 수 있습니다. docker create 커맨드는 docker run과 유사하지만, 주요 차이점은 컨테이너가 정지 상태로 생성된다는 것입니다.

docker create nginx

 

The result should be a line like this:

b26a631e536d3caae348e9fd36e7661254a11511eb2274fb55f9f7c788721b0d

 

Linux 커맨드 쉘, 예를 들어 sh나 bash를 사용하고 있다면, 그 결과를 셸 변수에 할당하고 나중에 다시 사용할 수 있습니다.

CID=$(docker create nginx:latest) // This will work on POSIX-compliant shells.
echo $CID

 

PowerShell에서는 다음과 같이 처리해야 합니다.

PS C:\Users\xxx> $CID=docker create nginx:latest

 

 

쉘 변수는 충돌의 새로운 가능성을 만들지만, 그 충돌의 범위는 스크립트(.bashrc)가 시작된 터미널 세션 또는 현재 처리 환경에 한정됩니다. 한 프로그램이 그 Enviornment을 관리하기 때문에 이러한 충돌은 피하기 쉽습니다. 하지만 이 방법은 여러 사용자나 자동화된 프로세스가 해당 정보를 공유해야 하는 경우 도움이 되지 않습니다. 그런 경우에는 컨테이너 ID(CID) 파일을 사용할 수 있습니다. docker run과 docker create 명령어는 새 컨테이너의 ID를 지정된 파일에 쓰는 또 다른 플래그를 제공합니다.

docker create --cidfile /tmp/web.cid nginx // 정지된 상태의 새로운 컨테이너를 생성한다
cat /tmp/web.cid   // web.cid 파일을 검사한다.

 

※만약, windows os에서 이 작업을 수행하고 있고, 이전에 다른 컨테이너가 web.cid 파일을 사용한 적이 있다면,

PowerShell에서 다음과 같은 커맨드를 수행하여 web.cid 파일을 삭제해야 합니다.

PS C:\Users\xxx> Remove-Item /tmp/web.cid

 

쉘 변수를 사용하는 것과 마찬가지로 이 기능은 충돌 가능성을 높입니다. --cidfile 다음에 제공되는 CID 파일의 이름은 알려져 있거나 알려진 구조를 가지고 있어야 합니다. 수동 컨테이너 이름 지정과 마찬가지로 이 접근 방식은 전역(Docker 전체) 네임스페이스에서 알려진 이름을 사용합니다. 좋은 소식은 Docker가 제공된 CID 파일이 이미 있는 경우 해당 파일을 사용하여 새 컨테이너를 생성하지 않는다는 것입니다. 동일한 이름을 가진 두 개의 컨테이너를 생성할 때와 마찬가지로 명령이 실패합니다.

도커의 전역 네임스페이스는 도커가 관리하는 모든 컨테이너, 이미지, 볼륨, 네트워크 등의 리소스가 속하는 네임스페이스를 의미합니다. 이 전역 네임스페이스 안에서 모든 리소스는 고유한 식별자 또는 이름을 가집니다. 따라서 전역 네임스페이스에서의 이름 충돌을 피하기 위해 도커는 리소스 생성 시 고유한 이름이나 식별자를 요구하거나 생성합니다.


  이름 대신 CID 파일을 사용하는 한 가지 이유는 CID 파일을 컨테이너와 쉽게 공유하고 해당 컨테이너의 이름을 바꿀 수 있기 때문입니다. 이는 4장에서 다루는 Volume 이라는 Docker 기능을 사용합니다.

 

참고 CID 파일 이름 충돌을 다루는 한 가지 전략은 알려진 또는 예측 가능한 경로 규칙을 사용하여 네임스페이스를 분할하는 것입니다. 예를 들어, 이 시나리오에서는 모든 웹 컨테이너를 알려진 디렉토리 아래에 위치시키고, 그 디렉토리를 고객 ID별로 더 세분화할 수 있습니다. 이러한 접근 방식은 /containers/web/customer1/web.cid 또는 /containers/web/customer8/web.cid와 같은 경로 결과를 초래할 것입니다.

 

다른 경우에는, docker ps와 같은 다른 명령어를 사용하여 컨테이너의 ID를 얻을 수 있습니다. 예를 들어, 가장 최근에 생성된 컨테이너의 축약된 ID를 얻고 싶다면, 이 명령어를 사용할 수 있습니다.

CID=$(docker ps --latest --quiet) 
echo $CID
CID=$(docker ps -l -q) 
echo $CID

 

전체 컨테이너 ID를 얻고 싶다면, docker ps 명령에 --no-trunc 옵션을 사용할 수 있습니다.

 

도커는 각 컨테이너에 대해 사람이 읽을 수 있는 이름도 생성합니다. 이 이름은 개인적인 형용사, 밑줄, 그리고 영향력 있는 과학자, 엔지니어, 발명가 또는 사상가의 성을 조합한 명명 규칙을 따릅니다. 예를 들어, compassionate_swartz, hungry_goodall, distracted_turing과 같은 이름이 생성됩니다. 이러한 이름은 가독성과 기억에 있어 좋은 균형을 제공합니다. docker ps 명령을 사용하여 이러한 사용자 친화적인 이름을 찾을 수 있습니다. Docker의 ID 및 이름 생성 기능을 사용하면 컨테이너 식별 문제를 관리할 수 있습니다.

 

2.3.2 Container state and dependencies

이 새로운 지식을 바탕으로, 새 시스템은 이렇게 보일 수 있습니다:

MAILER_CID=$(docker run -d dockerinaction/ch2_mailer) 
WEB_CID=$(docker create nginx)
AGENT_CID=$(docker create --link $WEB_CID:insideweb \
 --link $MAILER_CID:insidemailer \
 dockerinaction/ch2_agent)

 

 

이 스니펫은 각 고객을 위해 새로운 NGINX와 agent 인스턴스를 실행하는 새 스크립트를 시작하는 데 사용될 수 있습니다. 생성된 것을 확인하려면 docker ps를 사용할 수 있습니다.

docker ps
CONTAINER ID   IMAGE                       COMMAND               CREATED         STATUS         PORTS       NAMES
910f7ec71be9   dockerinaction/ch2_mailer   "/mailer/mailer.sh"   2 minutes ago   Up 2 minutes   33333/tcp   wonderful_aryabhata





$ docker ps -a
CONTAINER ID   IMAGE                       COMMAND                  CREATED              STATUS         PORTS       NAMES
242a6d7a5c42   dockerinaction/ch2_agent    "/watcher/watcher.sh"    16 seconds ago       Created                    epic_rhodes

98185dab9716   nginx                       "/docker-entrypoint.…"   About a minute ago   Created                    hardcore_jackson

910f7ec71be9   dockerinaction/ch2_mailer   "/mailer/mailer.sh"      2 minutes ago        Up 2 minutes   33333/tcp   wonderful_aryabhata

 

docker ps 출력에 NGINX도 agent도 포함되지 않은 이유는 컨테이너 상태와 관련이 있습니다. Docker 컨테이너는 그림 2.3에 표시된 상태 중 하나에 있을 것입니다. 상태 간을 이동하는 Docker 컨테이너 관리 명령은 각 전환을 주석으로 답니다.

그림 2.3 Docker 컨테이너의 상태 전환 다이어그램

 

docker kill
목적: docker kill 명령은 하나 또는 여러 Docker 컨테이너를 즉시 중지(종료)시키는 데 사용됩니다. 이 명령은 SIGKILL 신호나 다른 종료 신호를 컨테이너에 보내어 강제 종료합니다.

사용 시나리오: docker kill은 컨테이너가 정상적으로 반응하지 않거나, 일반적인 docker stop 명령으로 중지되지 않는 경우에 사용됩니다. 예를 들어, 컨테이너가 데드락 상태에 빠졌거나 응답하지 않을 때 유용합니다.

예제 명령:
docker kill [CONTAINER_ID 또는 NAME]

 

docker rm
목적: docker rm 명령은 하나 또는 여러 Docker 컨테이너를 시스템에서 완전히 삭제하는 데 사용됩니다. 이 명령은 컨테이너의 실행을 중지시키지 않고, 컨테이너의 메타데이터와 파일 시스템을 삭제합니다.

사용 시나리오: 컨테이너의 사용이 끝났고, 더 이상 필요하지 않을 때 컨테이너를 시스템에서 제거하려고 할 때 사용됩니다. docker rm을 사용하기 전에, 해당 컨테이너가 실행 중이면 먼저 docker stop 또는 docker kill로 중지해야 합니다.

예제 명령:
docker rm [OPTIONS] [CONTAINER_ID 또는 NAME]
-f 또는 --force 옵션을 사용하면 실행 중인 컨테이너도 강제로 삭제할 수 있습니다. 이 경우 내부적으로 컨테이너를 중지한 후 삭제합니다.

 

시작한 새 컨테이너 중 어느 것도 컨테이너 목록에 나타나지 않는 이유는 docker ps가 기본적으로 실행 중인 컨테이너만 보여주기 때문입니다. 해당 컨테이너들은 특별히 docker create로 생성되었고 절대 시작되지 않았습니다(생성된 상태). 모든 컨테이너를 보려면(생성된 상태의 컨테이너 포함) -a 옵션을 사용하세요.

$ docker ps -a
CONTAINER ID   IMAGE                       COMMAND                  CREATED              STATUS         PORTS       NAMES
242a6d7a5c42   dockerinaction/ch2_agent    "/watcher/watcher.sh"    16 seconds ago       Created                    epic_rhodes

98185dab9716   nginx                       "/docker-entrypoint.…"   About a minute ago   Created                    hardcore_jackson

910f7ec71be9   dockerinaction/ch2_mailer   "/mailer/mailer.sh"      2 minutes ago        Up 2 minutes   33333/tcp   wonderful_aryabhata

 

 docker ps 명령은 그림 2.3에서 회색으로 표시된 "친근한" 이름을 사용하여 컨테이너 상태를 표시합니다. docker inspect 명령은 각 상태의 하단에 표시된 이름을 사용합니다(예: created). restarting, removing, 그리고 dead(도시되지 않음) 상태는 Docker 내부적으로 사용되며, docker ps에서 볼 수 있는 상태 간의 전환을 추적하는 데 사용됩니다.

PS C:\Users\inthe> docker inspect agent
[
    {
        "Id": "7879986ea5947f2fe5c4f2319040bd5c3785c2036f3b3dbf153597c44f529d75",
        "Created": "2024-03-25T01:04:23.751911845Z",
        "Path": "/watcher/watcher.sh",
        "Args": [],
        "State": {
            "Status": "created",
            "Running": false,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 0,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "0001-01-01T00:00:00Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:e7230aac0356b24c80b7840ce05e94790fb1e8891b9314ffd6010c9a33b8bb35",
        "ResolvConfPath": "",
        "HostnamePath": "",
        "HostsPath": "",
        "LogPath": "",
        "Name": "/agent",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "ConsoleSize": [
                0,
                0
            ],
            "CapAdd": null,
            "CapDrop": null,
            "CgroupnsMode": "host",
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": [
                "/affectionate_dhawan:/agent/insideweb",
                "/wizardly_heyrovsky:/agent/insidemailer"
            ],
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": [],
            "BlkioDeviceWriteBps": [],
            "BlkioDeviceReadIOps": [],
            "BlkioDeviceWriteIOps": [],
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": [],
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware",
                "/sys/devices/virtual/powercap"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/f60652c56d0c97576a923a2aa6fda08ee876ff0979d1900a56c658c6b1d9d18c-init/diff:/var/lib/docker/overlay2/fb450c28bee7cb57a9f9958e1b2311425b21ce13b0e148e8bd071520974f7d2b/diff:/var/lib/docker/overlay2/a8caaf364c9ab37157590255fce0a1ae57183c4850f8a3dafe38a9a2d084f296/diff:/var/lib/docker/overlay2/b7db9cee6b475211032028f3e72e6d636772b054a81cc13a5ee06f6415a1c6e3/diff:/var/lib/docker/overlay2/b678aef7e062369e516e04097cf7c85a88ee6727c18f488fec55444fbb427f41/diff:/var/lib/docker/overlay2/b0f00e7ddaf1074349b40fdacf6e056d705ee6c4bcb8d641e6a84c4a3a095b6c/diff:/var/lib/docker/overlay2/e79fe13a5dbeb1f777f61fa2e6500c7c8a8bcd982a1210219f327deffe082044/diff:/var/lib/docker/overlay2/cbd3373e676445d4de8cd8ed660d0ad1ff0c7c8de88411bf152c91fcd8258978/diff:/var/lib/docker/overlay2/ebbf7c8a3b246fac1a1174b4ebe804b2ae8e1fb60c9f8e14b151ec3348269af9/diff:/var/lib/docker/overlay2/9add727126b1563591f0923b1245afe2307d8a8f1d9cdc36821987c8260fbdd2/diff:/var/lib/docker/overlay2/f0c4e74c2e24a9e25d1dd7b25793a5fbf66048cbfe09964be5711bddcd78fb0d/diff",
                "MergedDir": "/var/lib/docker/overlay2/f60652c56d0c97576a923a2aa6fda08ee876ff0979d1900a56c658c6b1d9d18c/merged",
                "UpperDir": "/var/lib/docker/overlay2/f60652c56d0c97576a923a2aa6fda08ee876ff0979d1900a56c658c6b1d9d18c/diff",
                "WorkDir": "/var/lib/docker/overlay2/f60652c56d0c97576a923a2aa6fda08ee876ff0979d1900a56c658c6b1d9d18c/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "7879986ea594",
            "Domainname": "",
            "User": "example",
            "AttachStdin": false,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/watcher/watcher.sh"
            ],
            "Image": "dockerinaction/ch2_agent",
            "Volumes": null,
            "WorkingDir": "/watcher",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "",
            "SandboxKey": "",
            "Ports": {},
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "MacAddress": "",
                    "NetworkID": "",
                    "EndpointID": "",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "DriverOpts": null,
                    "DNSNames": null
                }
            }
        }
    }
]

 

컨테이너가 모두 생성되었음을 확인한 지금, 그것들을 시작해야 합니다. 이를 위해 docker start 명령을 사용할 수 있습니다:

docker start $AGENT_CID
docker start $WEB_CID


이 명령들을 실행하면 오류가 발생할 것입니다. 컨테이너는 의존성 체인의 역순으로 시작되어야 합니다. 웹 컨테이너를 시작하기 전에 에이전트 컨테이너를 시작하려고 했기 때문에, Docker는 다음과 같은 메시지를 보고했습니다:

PS C:\Users\inthe> docker start $AGENT_CID
Error response from daemon: Cannot link to a non running container: /affectionate_dhawan AS /agent/insideweb
Error: failed to start containers: 7879986ea5947f2fe5c4f2319040bd5c3785c2036f3b3dbf153597c44f529d75

 

이 예시에서, 에이전트 컨테이너는 웹 컨테이너에 대한 의존성을 가집니다. 먼저 웹 컨테이너를 시작해야 합니다:

docker start $WEB_CID
docker start $AGENT_CID


이것은 작동하는 메커니즘을 고려했을 때 이치에 맞습니다. 링크 메커니즘은 의존하는 컨테이너에 IP 주소를 주입하며, 실행 중이지 않은 컨테이너는 IP 주소가 없습니다. 실행 중이지 않은 컨테이너에 의존하는 컨테이너를 시작하려고 하면, Docker는 주입할 IP 주소가 없을 것입니다. 5장에서는 이 특정 의존성 문제를 피하기 위해 사용자 정의 브리지 네트워크로 컨테이너를 연결하는 방법을 배울 것입니다. 여기서 핵심 포인트는 Docker가 애플리케이션 런타임 실패를 피하기 위해 컨테이너를 생성하거나 시작하기 전에 컨테이너의 의존성을 해결하려고 시도한다는 것입니다.

컨테이너 네트워크 연결의 유산[Legacy]

Docker 문서에서 네트워크 링크를 레거시 기능으로 설명한 것을 주목할 수 있습니다. 네트워크 링크는 초기에 컨테이너를 연결하는 인기 있는 방법이었습니다. 링크는 같은 호스트 상의 한 컨테이너로부터 다른 컨테이너로의 단방향 네트워크 연결을 생성합니다. 컨테이너 생태계의 상당 부분은 컨테이너 간의 완전한 피어링, 양방향 연결을 요구했습니다. Docker는 5장에서 설명된 사용자 정의 네트워크로 이를 제공합니다. 이러한 네트워크는 13장에서 설명된 바와 같이 호스트 클러스터에 걸쳐 확장될 수도 있습니다. 네트워크 링크와 사용자 정의 네트워크는 동등하지 않지만, Docker는 사용자 정의 네트워크로의 마이그레이션을 권장합니다.

컨테이너 네트워크 연결 기능이 언제 제거될지는 불확실합니다. 많은 유용한 도구들과 단방향 통신 패턴이 연결에 의존하고 있으며, 이는 이 섹션에서 웹 및 메일러 구성 요소를 조사하고 관찰하는 데 사용된 컨테이너들에 의해 입증됩니다.

 

docker run이나 docker create를 사용하든, 결과적으로 생성된 컨테이너는 의존성 체인의 역순으로 시작되어야 합니다. 이는 Docker 컨테이너 관계를 사용하여 순환 의존성을 구축하는 것이 불가능함을 의미합니다. 이 시점에서, 다음과 같이 모든 것을 하나의 간결한 스크립트로 정리할 수 있습니다:

MAILER_CID=$(docker run -d dockerinaction/ch2_mailer)
WEB_CID=$(docker run -d nginx)
AGENT_CID=$(docker run -d \
 --link $WEB_CID:insideweb \
 --link $MAILER_CID:insidemailer \
 dockerinaction/ch2_agent)


이제 이 스크립트를 실행하여 클라이언트가 새 사이트를 구축할 때마다 예외 없이 실행될 수 있음을 확신합니다. 클라이언트가 돌아와 지금까지 완료한 웹 및 모니터링 작업에 대해 감사를 표했지만, 상황이 변했습니다.

 

클라이언트가 워드프레스(인기 있는 오픈 소스 콘텐츠 관리 및 블로깅 프로그램)를 사용하여 웹사이트를 구축하는 데 집중하기로 결정했습니다. 다행히도, 워드프레스는 Docker Hub에서 'wordpress'라는 이름의 저장소를 통해 공개되어 있습니다. 제공해야 할 것은 이미 제공한 모니터링 및 알림 기능이 있는 새로운 워드프레스 웹사이트를 준비하기 위한 명령 세트뿐입니다.

콘텐츠 관리 시스템과 기타 상태를 가진 시스템에 대한 흥미로운 점은 그들이 작업하는 데이터로 인해 각각의 실행 프로그램이 특수화된다는 것입니다. 아담의 워드프레스 블로그는 베티의 워드프레스 블로그와 다르며, 심지어 같은 소프트웨어를 실행하고 있어도 마찬가지입니다. 오직 콘텐츠만 다릅니다. 콘텐츠가 같더라도 다른 사이트에서 실행되고 있기 때문에 서로 다릅니다.

환경에 대해 너무 많이 알고 있는 시스템이나 소프트웨어를 구축하는 경우—예를 들어, 의존 서비스의 주소나 고정된 위치—그 환경을 변경하거나 소프트웨어를 재사용하기 어렵습니다. 계약이 완료되기 전에 환경 의존도를 최소화하는 시스템을 제공해야 합니다.

 

2.4 Building environment-agnostic[특정 플랫폼에 구애받지 않는] systems

소프트웨어 설치 또는 컴퓨터 유지 관리와 관련된 작업의 대부분은 컴퓨팅 환경의 전문화를 다루는 데 있습니다. 이러한 특수화는 전역 범위 종속성(예: 알려진 호스트 파일 시스템 위치), 하드코딩된 배포 아키텍처(코드 또는 구성의 환경 검사) 또는 데이터 지역성(배포 아키텍처 외부의 특정 컴퓨터에 저장된 데이터)으로 제공됩니다. 이를 알고 유지 관리가 적은 시스템을 구축하는 것이 목표라면 이러한 사항을 최소화하도록 노력해야 합니다.
  Docker에는 환경에 구애받지 않는 시스템을 구축하는 데 도움이 되는 세 가지 특정 기능이 있습니다.

  • 읽기 전용 파일 시스템
  • 환경변수 주입
  • 볼륨

볼륨 작업은 큰 주제이자 4장의 주제입니다. 처음 두 기능을 배우려면 이 장의 나머지 부분에서 사용되는 예제 상황에 대한 요구 사항 변경을 고려하십시오. WordPress는 MySQL이라는 데이터베이스 프로그램을 사용하여 대부분의 데이터를 저장합니다. 따라서 WordPress를 실행하는 컨테이너에 읽기 전용 파일 시스템을 제공하여 데이터가 데이터베이스에만 기록되도록 하는 것이 좋습니다.

 

Docker의 볼륨 기능은 컨테이너와 호스트 시스템 간에 파일 시스템을 연결하고 데이터를 공유하기 위해 사용됩니다. 볼륨은 데이터를 영구적으로 저장하고 관리하는 데 중요한 역할을 하며, 컨테이너가 삭제되어도 볼륨에 저장된 데이터는 유지됩니다.
이를 통해 다음과 같은 이점을 얻을 수 있습니다:
1. 데이터의 영속성: 컨테이너 내부에서 생성되거나 변경된 데이터를 볼륨에 저장함으로써, 컨테이너가 삭제되거나 재생성되어도 데이터가 보존됩니다. 이는 설정 파일, 사용자 데이터, 데이터베이스 파일 등 중요한 데이터를 안전하게 관리하는 데 필수적입니다.
2. 데이터 공유와 재사용: 볼륨은 여러 컨테이너 간에 데이터를 공유할 수 있는 방법을 제공합니다. 예를 들어, 한 컨테이너에서 생성된 데이터를 다른 컨테이너가 읽고 사용할 수 있습니다. 이를 통해 컨테이너 간 데이터의 재사용성과 효율성을 높일 수 있습니다.
3. 성능 향상: 볼륨을 사용하면 컨테이너의 파일 시스템 성능을 향상시킬 수 있습니다. 볼륨은 호스트의 파일 시스템을 직접 사용하기 때문에, 컨테이너의 오버레이 파일 시스템을 사용할 때보다 더 높은 입출력 성능을 제공합니다.
4. 호스트 시스템과의 상호 작용: 볼륨을 사용하면 호스트 시스템의 특정 디렉터리와 컨테이너 내의 디렉터리를 직접 매핑할 수 있습니다. 이를 통해 컨테이너가 호스트 시스템의 파일에 접근하고 수정할 수 있으며, 개발 과정에서 코드나 리소스를 쉽게 공유하고 업데이트할 수 있습니다.
Docker 볼륨을 생성하고 관리하는 기능은 Docker의 데이터 관리 전략에서 중요한 부분을 차지하며, 애플리케이션의 배포와 운영을 보다 유연하고 효율적으로 만듭니다.

 

2.4.1 Read-only filesystems

읽기 전용 파일 시스템을 사용하면 두 가지 긍정적인 효과가 있습니다. 첫째, 컨테이너가 포함하는 파일의 변경으로부터 특수화되지 않을 것이라는 확신을 가질 수 있습니다. 둘째, 공격자가 컨테이너 내의 파일을 타협할 수 없다는 확신이 높아집니다.

클라이언트의 시스템 작업을 시작하기 위해, --read-only 플래그를 사용하여 WordPress 이미지로부터 컨테이너를 생성하고 시작하세요:

docker run -d --name wp --read-only wordpress:6.4.3-php8.1-apache


이 작업이 완료되면, 컨테이너가 실행 중인지 확인하세요. 이전에 소개된 방법을 사용하거나, 컨테이너 메타데이터를 직접 조사할 수 있습니다. 다음 명령은 wp라는 이름의 컨테이너가 실행 중이면 true를, 그렇지 않으면 false를 출력할 것입니다:

docker inspect --format "{{.State.Running}}" wp


docker inspect 명령은 Docker가 컨테이너에 대해 유지하는 모든 메타데이터(하나의 JSON 문서)를 표시합니다. format 옵션은 그 메타데이터를 변환하고, 이 경우, 컨테이너의 실행 상태를 나타내는 필드만 필터링합니다. 이 명령은 단순히 false를 출력해야 합니다.

이 경우, 컨테이너는 실행되지 않습니다. 왜 그런지 알아보기 위해, 컨테이너의 로그를 검사하세요:

docker logs wp


그 명령은 다음과 같은 것을 출력해야 합니다:

docker logs wp
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
Mon Mar 25 01:51:20 2024 (1): Fatal Error Unable to create lock file: Bad file descriptor (9)
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
Mon Mar 25 01:51:35 2024 (1): Fatal Error Unable to create lock file: Bad file descriptor (9)

 

읽기 전용 파일 시스템으로 WordPress를 실행할 때, Apache 웹 서버 프로세스는 잠금 파일을 생성할 수 없다고 보고합니다. 불행히도, 생성하려고 시도하는 파일의 위치를 보고하지 않습니다. 위치를 알고 있다면, 그것들에 대해 예외를 생성할 수 있습니다. Apache가 원하는 곳에 자유롭게 쓸 수 있도록, 쓰기 가능한 파일 시스템을 가진 WordPress 컨테이너를 실행해 봅시다.

 docker run -d --name wp_writable wordpress:6.4.3-php8.1-apache

 

이제 docker diff 명령을 사용하여 Apache가 컨테이너의 파일 시스템을 어떻게 변경했는지 확인해 봅시다.

docker container diff wp_writable
C /run
C /run/apache2
A /run/apache2/apache2.pid

 

3장에서 파일 시스템에서 Docker가 변경 사항을 어떻게 알아내는지와 diff 명령에 대해 더 자세히 설명할 예정입니다. 지금은 출력 결과가 Apache가 /run/apache2 디렉토리를 생성하고 그 안에 apache2.pid 파일을 추가했다는 것을 나타낸다는 것을 알면 충분합니다.

docker container diff 명령은 지정된 컨테이너의 파일 시스템에서 변경된 부분을 나열합니다. 이 명령의 출력에서 각 줄은 컨테이너 내의 특정 파일이나 디렉터리에 대한 변경을 나타냅니다. 출력에서 사용되는 앞에 붙는 문자는 다음과 같은 변경 유형을 나타냅니다:
1. A: 파일이나 디렉터리가 추가됨(Added)
2. C: 파일이나 디렉터리가 변경됨(Changed)
3. D: 파일이나 디렉터리가 삭제됨(Deleted) 

 

이는 정상적인 애플리케이션 운영의 예상되는 부분이므로, 읽기 전용 파일 시스템에 대한 예외를 만들겠습니다. 호스트에 마운트된 쓰기 가능한 볼륨을 사용하여 컨테이너가 /run/apache2에 쓸 수 있도록 허용할 것입니다. Apache가 쓰기 가능한 임시 디렉토리를 요구하기 때문에, 호스트 루트 파일 시스템의 /tmp에 임시 메모리 파일 시스템을 컨테이너에 제공할 것입니다.

docker run -d --name wp2 \
 --read-only \    <-- Makes container’s root filesystem read-only
 -v /run/apache2/ \  <-- Mounts a writable directory from the host
 --tmpfs /tmp \  <-- Provides container an inmemory temp filesystem
 wordpress:6.4.3-php8.1-apache

※GitBash는 터미널을 minwtty를 사용하기 때문에 위 도커 커맨드가 적용되지 않습니다. Windows OS에서는 위 작업을 PowerShell에서 수행해 주세요(PowerShell에서 \는 `로 대체해야 합니다)

 

그 명령은 다음과 같은 성공 메시지를 기록해야 합니다.

PS C:\Users\inthe> docker logs wp2
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
[Mon Mar 25 02:13:03.195780 2024] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.57 (Debian) PHP/8.1.27 configured -- resuming normal operations
[Mon Mar 25 02:13:03.195817 2024] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'

 

워드프레스는 MySQL 데이터베이스에도 의존성을 가지고 있습니다. 데이터베이스는 데이터를 저장하고 나중에 검색 가능하게 만드는 프로그램입니다. 좋은 소식은 워드프레스처럼 Docker를 사용하여 MySQL을 설치할 수 있다는 것입니다.

 docker run -d --name wpdb -e MYSQL_ROOT_PASSWORD=ch2demo mysql:latest

 

그 다음, 새로운 데이터베이스 컨테이너에 연결된 다른 워드프레스 컨테이너를 생성합니다.

docker run -d --name wp3 \      < -- Uses a unique name
 --link wpdb:mysql \            < -- Creates a link to the database
 -p 8000:80 \                   < -- Directs traffic from host port 8000 to container port 80
 --read-only \
 -v /run/apache2/ \
 --tmpfs /tmp \
 wordpress:5.0.0-php7.2-apache

※ 호스트 시스템에서 이미 8000을 사용하고 있다면, 다른 포트 넘버를 할당해야 합니다.

 

워드프레스가 제대로 실행되고 있는지 다시 한 번 확인하세요.

docker inspect --format "{{.State.Running}}" wp3

 

이제 출력 결과가 true여야 합니다. 새로 설치한 워드프레스를 사용하고 싶다면, 웹 브라우저에서 http://127.0.0.1:8000으로 이동하세요.

작업 중이던 스크립트의 업데이트된 버전은 다음과 같아야 합니다:

#!/bin/sh
DB_CID=$(docker create -e MYSQL_ROOT_PASSWORD=ch2demo mysql:5.7)
docker start $DB_CID
MAILER_CID=$(docker create dockerinaction/ch2_mailer)
docker start $MAILER_CID
WP_CID=$(docker create --link $DB_CID:mysql -p 80 \
 --read-only -v /run/apache2/ --tmpfs /tmp \
 wordpress:5.0.0-php7.2-apache)
docker start $WP_CID
AGENT_CID=$(docker create --link $WP_CID:insideweb \
 --link $MAILER_CID:insidemailer \
 dockerinaction/ch2_agent)
docker start $AGENT_CID

 

이 시점에서는 워드프레스 컨테이너가 실행 중이어야 합니다! 읽기 전용 파일 시스템을 사용하고 워드프레스를 데이터베이스를 실행하는 다른 컨테이너에 연결함으로써, 워드프레스 이미지를 실행하는 컨테이너가 결코 변경되지 않을 것임을 확신할 수 있습니다. 이는 클라이언트의 워드프레스 블로그를 실행하는 컴퓨터에 문제가 발생해도, 다른 곳에서 그 컨테이너의 복사본을 문제 없이 시작할 수 있음을 의미합니다. 하지만 이 디자인에는 두 가지 문제가 있습니다. 첫째, 데이터베이스가 워드프레스 컨테이너와 같은 컴퓨터에서 컨테이너로 실행되고 있습니다. 둘째, 워드프레스는 데이터베이스 이름, 관리자 사용자, 관리자 비밀번호, 데이터베이스 솔트 등 중요한 설정에 대해 여러 기본값을 사용하고 있습니다. 이 문제를 해결하기 위해, 환경 변수를 통한 구성 주입 방법이 더 나은 방법일 수 있습니다.

 

2.4.2 Environment variable injection

환경 변수는 프로그램이 실행되는 컨텍스트를 통해 프로그램에 제공되는 키/값 쌍입니다. 이들은 프로그램의 구성을 수정하지 않고도 프로그램을 구성할 수 있게 해줍니다. Docker는 의존하는 컨테이너에 관한 정보, 컨테이너의 호스트 이름 및 컨테이너에서 실행 중인 프로그램에 대한 편리한 정보를 전달하기 위해 환경 변수를 사용합니다. Docker는 또한 사용자가 새 컨테이너에 환경 변수를 주입할 수 있는 메커니즘을 제공합니다. 중요한 정보를 환경 변수를 통해 얻고자하는 프로그램은 컨테이너 생성 시에 구성될 수 있습니다. 다행히도, 워드프레스는 이와 같은 프로그램 중 하나입니다.

워드프레스 특정 사항에 대해 자세히 들어가기 전에, 환경 변수를 주입하고 확인해 보세요. UNIX 명령어 env는 현재 실행 컨텍스트(당신의 터미널)에서 모든 환경 변수를 표시합니다. 환경 변수 주입이 실제로 어떻게 작동하는지 확인하려면 다음 명령어를 사용하십시오:

docker run --env MY_ENVIRONMENT_VAR="this is a test" \ <--Injects an environment variable
	busybox:1.29 \
 	env <--Executes the env command inside the container

 

--env 플래그 또는 -e를 사용하여 어떤 환경 변수든 주입할 수 있습니다. 이미지나 Docker에서 이미 설정된 변수인 경우 해당 값이 재지정됩니다. 이렇게 함으로써 컨테이너 내에서 실행되는 프로그램은 항상 변수가 설정되어 있다고 가정할 수 있습니다. 워드프레스는 다음과 같은 환경 변수를 관찰합니다.

  • WORDPRESS_DB_HOST
  • WORDPRESS_DB_USER
  • WORDPRESS_DB_PASSWORD
  • WORDPRESS_DB_NAME
  • WORDPRESS_AUTH_KEY
  • WORDPRESS_SECURE_AUTH_KEY
  • WORDPRESS_LOGGED_IN_KEY
  • WORDPRESS_NONCE_KEY
  • WORDPRESS_AUTH_SALT
  • WORDPRESS_SECURE_AUTH_SALT
  • WORDPRESS_LOGGED_IN_SALT
  • WORDPRESS_NONCE_SALT

TIP: 이 예제는 KEY와 SALT 변수를 무시하지만, 어떤 실제 프로덕션 시스템에서도 반드시 이러한 값을 설정해야 합니다.

 

시작하기 전에, 데이터베이스가 워드프레스 컨테이너와 동일한 컴퓨터의 컨테이너에서 실행되고 있는 문제를 해결해야 합니다. 워드프레스의 데이터베이스 의존성을 충족시키기 위해 링킹 대신 WORDPRESS_DB_HOST 변수에 값을 주입하세요.

docker create --env WORDPRESS_DB_HOST=<my database hostname> \
 wordpress: 5.0.0-php7.2-apache

 

이 예시는 워드프레스를 위한 컨테이너를 생성(시작하지는 않음)하여 <내 데이터베이스 호스트명>에 지정된 곳에서 MySQL 데이터베이스에 연결하려고 시도할 것입니다. 원격 데이터베이스는 아마도 기본 사용자 이름이나 비밀번호를 사용하지 않을 것이므로 해당 설정에 대한 값을 주입해야 합니다. 

docker create \
 --env WORDPRESS_DB_HOST=<my database hostname> \
 --env WORDPRESS_DB_USER=site_admin \
  --env WORDPRESS_DB_PASSWORD=MeowMix42 \
 wordpress:5.0.0-php7.2-apache

 

이렇게 환경 변수 주입을 사용하면 워드프레스 컨테이너와 MySQL 컨테이너 간의 물리적인 연결을 분리하는 데 도움이 됩니다. 데이터베이스를 호스팅하고 고객의 워드프레스 사이트를 모두 동일한 컴퓨터에 호스팅하려는 경우에도 앞서 언급한 두 번째 문제를 해결해야 합니다. 모든 사이트가 동일한 기본 데이터베이스 이름을 사용하고 있으므로 다른 클라이언트가 단일 데이터베이스를 공유하게 됩니다. 각 독립적인 사이트를 위해 데이터베이스 이름을 설정하려면 WORDPRESS_DB_NAME 변수를 지정하여 환경 변수 주입을 사용해야 합니다.

// For client A
docker create --link wpdb:mysql \
 -e WORDPRESS_DB_NAME=client_a_wp \ 
 wordpress:5.0.0-php7.2-apache
 
// For client B
docker create --link wpdb:mysql \
 -e WORDPRESS_DB_NAME=client_b_wp \ 
 wordpress:5.0.0-php7.2-apache

 

이제 WordPress 애플리케이션에 구성을 주입하고 협업 프로세스에 연결하는 방법을 이해했으므로 프로비저닝 스크립트를 수정해 봅시다. 먼저 클라이언트가 공유할 데이터베이스와 메일러 컨테이너를 시작하고 그 ID를 환경 변수에 저장합시다.

export DB_CID=$(docker run -d -e MYSQL_ROOT_PASSWORD=ch2demo mysql:5.7)
export MAILER_CID=$(docker run -d dockerinaction/ch2_mailer)

 

이제 클라이언트 사이트 프로비저닝 스크립트를 업데이트하여 데이터베이스 컨테이너 ID, 메일러 컨테이너 ID 및 새로운 CLIENT_ID를 환경 변수에서 읽도록 합시다.

#!/bin/sh
// Assumes $CLIENT_ID variable is set as input to script
if [ ! -n "$CLIENT_ID" ]; then 
 echo "Client ID not set"
 exit 1 
fi
WP_CID=$(docker create \
 --link $DB_CID:mysql \   <-- Creates link using DB_CID
 --name wp_$CLIENT_ID \
 -p 80 \
 --read-only -v /run/apache2/ --tmpfs /tmp \
 -e WORDPRESS_DB_NAME=$CLIENT_ID \
 --read-only wordpress:5.0.0-php7.2-apache)
docker start $WP_CID
AGENT_CID=$(docker create \
 --name agent_$CLIENT_ID \
 --link $WP_CID:insideweb \
 --link $MAILER_CID:insidemailer \
 dockerinaction/ch2_agent)
docker start $AGENT_CID

 

이 스크립트를 start-wp-for-client.sh라는 파일로 저장하면 다음과 같은 명령어를 사용하여 dockerinaction 클라이언트를 위해 WordPress를 프로비저닝할 수 있습니다:

CLIENT_ID=dockerinaction ./start-wp-multiple-clients.sh

 

이 새로운 스크립트는 각 고객마다 WordPress 및 모니터링 에이전트의 인스턴스를 시작하고 이러한 컨테이너를 서로 연결하며 단일 메일러 프로그램과 MySQL 데이터베이스에 연결합니다. WordPress 컨테이너는 데이터 손실에 대해 걱정할 필요 없이 삭제, 재시작 및 업그레이드할 수 있습니다. Figure 2.4에서는 이 아키텍처를 보여줍니다.

그림 2.4 각 WordPress 및 에이전트 컨테이너는 동일한 데이터베이스와 메일러를 사용합니다.

 

고객은 제공되고 있는 것에 만족할 것입니다. 그러나 한 가지가 당신을 괴롭힐 수 있습니다. 이전 테스트에서 모니터링 에이전트가 사이트가 이용 불가능할 때 메일러에게 올바르게 알렸지만, 사이트와 에이전트를 다시 시작하는 것에 수동적인 작업이 필요했습니다. 실패가 감지되었을 때 시스템이 자동으로 복구를 시도하는 것이 더 나을 것입니다. Docker는 이러한 상황을 다루기 위한 재시작 정책을 제공하지만, 더 견고한 해결책이 필요할 수 있습니다.

 

 

2.5 Building durable containers

소프트웨어는 일시적인 본질을 가진 드문 조건에서 실패할 수 있습니다. 이러한 조건이 발생했을 때 알림을 받는 것은 중요하지만, 서비스를 최대한 빨리 복구하는 것이 적어도 그만큼 중요합니다. 이 장에서 구축한 모니터링 시스템은 시스템 소유자가 시스템의 문제를 인식하는 데에는 좋은 시작이지만, 서비스를 복구하는 데는 아무런 도움이 되지 않습니다.

 

모든 프로세스가 종료되면 해당 컨테이너는 종료된 상태로 전환됩니다. 기억해야 할 것은 Docker 컨테이너가 여섯 가지 상태 중 하나에 있을 수 있다는 것입니다.

  • Created
  • Running
  • Restarting
  • Paused
  • Removing
  • Exited (also used if the container has never been started)

일시적인 장애로부터 회복하기 위한 기본적인 전략은 프로세스가 종료되거나 실패할 때 자동으로 다시 시작하는 것입니다. Docker는 컨테이너를 모니터링하고 다시 시작하는 몇 가지 옵션을 제공합니다.

 

2.5.1 Automatically restarting containers

도커는 재시작 정책을 통해 이러한 기능을 제공합니다. 컨테이너 생성 시 --restart 플래그를 사용하여 도커에 다음 중 하나를 지시할 수 있습니다:

  • 절대 재시작하지 않음 (디폴트값)
  • 실패 감지 시 재시도
  • 실패 감지 시 일정 시간 동안 재시도
  • 상태와 관계없이 컨테이너를 항상 재시작

도커는 항상 컨테이너를 즉시 다시 시작하지 않습니다. 그렇게 한다면 문제가 더 많이 발생할 것입니다. 시간을 출력하고 종료하는 것만 하는 컨테이너를 상상해보세요. 그 컨테이너가 항상 다시 시작하도록 구성되어 있고, 도커가 항상 즉시 다시 시작한다면 시스템은 그 컨테이너만 계속 재시작할 것입니다. 대신, 도커는 재시도 시간을 결정하기 위해 지수 백오프 전략을 사용합니다.

백오프 전략은 연속된 재시도 사이에 경과해야 하는 시간을 결정합니다. 지수 백오프 전략은 각 연속된 재시도마다 이전 대기 시간을 두 배로 증가시키는 것과 같은 작업을 수행합니다. 예를 들어, 컨테이너를 처음 재시작해야 하는 경우 도커가 1초를 기다린다면, 두 번째 시도에서는 2초를 기다리고, 세 번째 시도에서는 4초를 기다리며, 네 번째에서는 8초를 기다리게 됩니다. 초기 대기 시간이 낮은 지수 백오프 전략은 흔한 서비스 복원 기술입니다. 도커가 이러한 전략을 사용하는 방법을 직접 확인할 수 있습니다.

docker run -d --name backoff-detector --restart always busybox:1.29 date

 

몇 초 후에는, 뒤이어 로그의 추적 기능을 사용하여 백오프 및 재시작을 관찰합니다.

docker logs -f backoff-detector

 

로그는 이미 다시 시작된 횟수를 모두 표시하고, 다음에 다시 시작될 때까지 기다린 다음 현재 시간을 인쇄하고 종료합니다. 이 단일 플래그를 모니터링 시스템 및 작업 중인 WordPress 컨테이너에 추가하면 복구 문제를 해결할 수 있습니다.
 직접 채택하지 않을 수 있는 유일한 이유는 백오프 기간 동안 컨테이너가 실행되지 않는다는 것입니다. 다시 시작되기를 기다리는 컨테이너는 다시 시작 상태에 있습니다. 이를 증명하기 위해 backoff-detector 컨테이너에서 다른 프로세스를 실행해 보세요.

docker exec backoff-detector echo Just a Test

 

그 명령을 실행하면 오류 메시지가 표시됩니다.

Container <ID> is restarting, wait until the container is running

 

그 의미는 컨테이너가 실행 중인 상태여야 하는 작업을 수행할 수 없다는 것입니다. 즉, 컨테이너에서 추가 명령을 실행할 수 없습니다. 이는 고장난 컨테이너에서 진단 프로그램을 실행해야 할 경우 문제가 될 수 있습니다. 더 완전한 전략은 가벼운 초기화 시스템을 시작하는 컨테이너를 사용하는 것입니다.

 

2.5.2 Using PID 1 and init systems

init 시스템은 다른 프로그램을 시작하고 유지하는 데 사용되는 프로그램입니다. 리눅스 커널에서 PID 1을 가진 프로세스는 기술적으로 init 시스템이 아니더라도 초기화 프로세스처럼 처리됩니다. init 시스템은 다른 중요한 기능들과 함께 다른 프로세스를 시작하고, 그들이 실패할 경우 다시 시작하며, 운영 체제에서 보낸 시그널을 변환하고 전달하며, 리소스 누수를 방지합니다. 여러 프로세스를 실행하거나 실행 중인 프로그램이 자식 프로세스를 사용하는 경우 컨테이너 내에서 실제 init 시스템을 사용하는 것이 일반적인 관행입니다.
컨테이너 내에서 여러 초기화 시스템을 사용할 수 있습니다. 가장 인기 있는 것은 runit, Yelp/dumb-init, tini, supervisord, tianon/gosu 등이 있습니다. 이러한 프로그램을 사용하는 소프트웨어를 배포하는 것은 8장에서 다루게 됩니다. 지금은 supervisord를 사용하는 컨테이너를 살펴보겠습니다.
도커는 단일 컨테이너 내에서 완전한 LAMP(Linux, Apache, MySQL, PHP) 스택을 포함하는 이미지를 제공합니다. 이런 식으로 생성된 컨테이너는 모든 관련 프로세스가 실행되도록 supervisord를 사용합니다. 예제 컨테이너를 시작해보겠습니다.

docker run -d -p 80:80 --name lamp-test tutum/lamp

 

이 컨테이너 내에서 실행 중인 프로세스를 확인하려면 docker top 명령을 사용할 수 있습니다.

docker top lamp-test

 

top 하위 명령어는 컨테이너 내의 각 프로세스의 호스트 PID를 보여줍니다. 실행 중인 프로그램 목록에는 supervisord, mysql 및 apache가 포함됩니다. 이제 컨테이너가 실행 중이므로 컨테이너 내에서 프로세스 중 하나를 수동으로 중지하여 supervisord의 다시 시작 기능을 테스트할 수 있습니다.

문제는 컨테이너 내에서 컨테이너 내의 프로세스를 종료하려면 컨테이너의 PID 네임스페이스에서 PID를 알아야 합니다. 그 목록을 얻으려면 다음 exec 하위 명령을 실행하세요.

docker exec lamp-test ps

 

생성된 프로세스 목록에서 CMD 열에는 apache2가 나타납니다.

    PID TTY          TIME CMD
      1 ?        00:00:00 supervisord
    434 ?        00:00:00 mysqld_safe
    435 ?        00:00:00 apache2
    816 ?        00:00:00 ps

 

PID 열의 값은 명령을 실행할 때마다 다를 것입니다. apache2에 해당하는 행에서 PID를 찾은 다음, 아래 명령에서 <PID> 자리에 그 값을 넣으십시오:

docker exec lamp-test kill <PID>

 

이 명령을 실행하면 lamp-test 컨테이너 내에서 Linux kill 프로그램이 실행되어 apache2 프로세스를 종료하도록 지시합니다. apache2가 중지되면 supervisord 프로세스가 해당 이벤트를 로그에 기록하고 프로세스를 다시 시작합니다. 컨테이너 로그에는 이러한 이벤트가 명확하게 표시됩니다:

docker logs lamp-test
=> An empty or uninitialized MySQL volume is detected in /var/lib/mysql
=> Installing MySQL ...
=> Done!
=> Waiting for confirmation of MySQL service startup
=> Creating MySQL admin user with random password
=> Done!
========================================================================
You can now connect to this MySQL Server using:

    mysql -uadmin -py2fILgS19S9l -h<host> -P<port>

Please remember to change the above password as soon as possible!
MySQL user 'root' has no password but only allows local connections
========================================================================
/usr/lib/python2.7/dist-packages/supervisor/options.py:295: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directory); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.
  'Supervisord is running as root and it is searching '
2024-03-25 06:49:50,545 CRIT Supervisor running as root (no user in config file)
2024-03-25 06:49:50,545 WARN Included extra file "/etc/supervisor/conf.d/supervisord-mysqld.conf" during parsing
2024-03-25 06:49:50,545 WARN Included extra file "/etc/supervisor/conf.d/supervisord-apache2.conf" during parsing
2024-03-25 06:49:50,562 INFO RPC interface 'supervisor' initialized
2024-03-25 06:49:50,562 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2024-03-25 06:49:50,562 INFO supervisord started with pid 1
2024-03-25 06:49:51,565 INFO spawned: 'mysqld' with pid 434
2024-03-25 06:49:51,566 INFO spawned: 'apache2' with pid 435
2024-03-25 06:49:52,661 INFO success: mysqld entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-03-25 06:49:52,661 INFO success: apache2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-03-25 06:54:42,960 INFO exited: apache2 (exit status 0; expected)
2024-03-25 06:54:43,964 INFO spawned: 'apache2' with pid 828
2024-03-25 06:54:44,983 INFO success: apache2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-03-25 06:55:35,944 INFO exited: apache2 (exit status 0; expected)
2024-03-25 06:55:36,947 INFO spawned: 'apache2' with pid 848
2024-03-25 06:55:37,968 INFO success: apache2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

 

init 시스템 사용 대안 중 하나는 포함된 소프트웨어를 성공적으로 시작하기 위한 전제 조건을 확인하는 시작 스크립트를 사용하는 것입니다. 이러한 스크립트는 때로는 컨테이너의 기본 커맨드로 사용됩니다. 예를 들어, 여러분들이 만든 워드프레스 컨테이너는 워드프레스 프로세스를 시작하기 전에 환경 변수를 확인하고 기본값을 설정하기 위해 스크립트를 실행합니다. 기본 커맨드 재정의하여 시작 스크립트의 내용을 보려면 다음과 같이 하십시오:

docker run wordpress:5.0.0-php7.2-apache \
 cat /usr/local/bin/docker-entrypoint.sh

 

도커 컨테이너는 커맨드를 실행하기 전에 entrypoint라는 것을 실행합니다. Entrypoint는 컨테이너의 전제 조건을 확인하는 코드를 놓기에 이상적인 장소입니다. 이 내용은 이 책의 제2부에서 자세히 다루지만, 커맨드라인에서 컨테이너의 entrypoint를 재정의하거나 명시적으로 설정하는 방법을 알아야 합니다. 마지막 커맨드를 다시 실행해 보세요. 이번에는 --entrypoint 플래그를 사용하여 실행할 프로그램을 지정하고 커맨드 부분을 사용하여 아규먼트를 전달하십시오:

docker run --entrypoint="cat" \   <--Uses “cat” as the entrypoint
 wordpress:5.0.0-php7.2-apache \
 /usr/local/bin/docker-entrypoint.sh <--Passes the full path of the default
						entrypoint script as an argument to cat

 

스크립트를 따라 실행하면 환경 변수를 소프트웨어의 종속성에 대한 것으로 유효성을 검사하고 기본 값을 설정하는 방법을 볼 수 있습니다. 스크립트가 워드프레스를 실행할 수 있는지 확인한 후 요청된 또는 기본 커맨드를 시작합니다. 시작 스크립트는 견고한 컨테이너를 구축하는 중요한 부분이며 Docker 재시작 정책과 결합하여 각각의 장점을 활용할 수 있습니다. MySQL 및 워드프레스 컨테이너가 이미 시작 스크립트를 사용하고 있기 때문에 예제 스크립트의 업데이트된 버전에서 각각에 대한 재시작 정책을 설정하는 것이 적절합니다. 시작 스크립트를 PID 1로 실행하는 것은 스크립트가 init 시스템에 대한 Linux의 기대를 충족하지 못할 때 문제가 될 수 있습니다. 사용 사례에 따라 가장 적합한 방법 또는 하이브리드 접근 방식이 가장 잘 작동할 수 있습니다. 최종 수정을 통해 완전한 워드프레스 사이트 프로비저닝 시스템을 구축하고 Docker를 사용한 컨테이너 관리의 기본을 배웠습니다. 이를 위해 상당한 실험이 필요했습니다. 컴퓨터에는 더 이상 필요하지 않은 여러 컨테이너가 남아 있을 수 있습니다. 이러한 컨테이너가 사용하는 리소스를 회수하려면 해당 컨테이너를 중지하고 시스템에서 제거해야 합니다.

 

2.6 Cleaning up

정리의 용이성은 컨테이너와 Docker를 사용하는 가장 강력한 이유 중 하나입니다. 컨테이너가 제공하는 격리는 프로세스를 중지하고 파일을 제거하는 단계를 단순화합니다. Docker를 사용하면 정리 프로세스 전체가 몇 가지 간단한 명령 중 하나로 축소됩니다. 정리 작업에서는 먼저 중지하거나 제거하려는 컨테이너를 식별해야 합니다. 컴퓨터에 있는 모든 컨테이너를 나열하려면 docker ps 명령을 사용하십시오.

docker ps -a

 

이 장에서 예제로 생성한 컨테이너는 더 이상 사용되지 않으므로 안전하게 모든 나열된 컨테이너를 중지하고 제거할 수 있어야 합니다. 본인 활동용으로 생성한 컨테이너가 있는 경우 정리 중인 컨테이너를 주의해서 확인하십시오.
 모든 컨테이너는 로그, 컨테이너 메타데이터 및 컨테이너 파일 시스템에 작성된 파일을 저장하기 위해 하드 드라이브 공간을 사용합니다. 또한 모든 컨테이너는 컨테이너 이름 및 호스트 포트 매핑과 같은 글로벌 네임스페이스의 리소스를 사용합니다. 대부분의 경우 더 이상 사용되지 않을 컨테이너는 제거해야 합니다.
 컴퓨터에서 컨테이너를 제거하려면 docker rm 명령을 사용하십시오. 예를 들어, 중지된 이름이 wp인 컨테이너를 삭제하려면 다음을 실행합니다:

docker rm wp

 

docker ps -a 명령을 실행하여 생성된 목록의 모든 컨테이너를 확인하고 종료된 상태인 모든 컨테이너를 제거해야 합니다. 실행 중인, 일시 중지된 또는 다시 시작 중인 컨테이너를 제거하려고 하면 Docker가 다음과 같은 메시지를 표시합니다:

Error response from daemon: Conflict, You cannot remove a running 
container. Stop the container before attempting removal or use -f
FATA[0000] Error: failed to remove one or more containers

 

컨테이너 내에서 실행 중인 프로세스는 컨테이너의 파일이 제거되기 전에 중지되어야 합니다. 이를 위해 docker stop 명령을 사용하거나 docker rm에 -f 플래그를 사용할 수 있습니다. 주요 차이점은 -f 플래그를 사용하여 프로세스를 중지할 때 Docker가 SIG_KILL 신호를 보내어 이 신호를 수신하는 프로세스를 즉시 종료한다는 것입니다. 반면에 docker stop을 사용하면 SIG_HUP 신호가 전송됩니다. SIG_HUP의 수신자는 마무리 및 정리 작업을 수행할 시간을 가집니다. SIG_KILL 신호는 그러한 허용을 하지 않으며 파일 손상이나 네트워크 경험의 저하를 초래할 수 있습니다. 필요한 경우 docker kill 명령을 사용하여 컨테이너에 직접 SIG_KILL을 보낼 수 있지만, 컨테이너를 표준 30초 최대 중지 시간보다 짧게 중지해야하는 경우에만 docker kill 또는 docker rm -f를 사용해야 합니다.

앞으로 짧은 수명을 가지는 컨테이너를 실험할 때는 명령에 --rm을 지정하여 정리 부담을 피할 수 있습니다. 이렇게 하면 컨테이너가 종료 상태로 들어가자마자 자동으로 제거됩니다. 예를 들어, 다음 명령은 새로운 BusyBox 컨테이너에서 화면에 메시지를 작성하고, 컨테이너가 종료되자마자 제거됩니다.

docker run --rm --name auto-exit-test busybox:1.29 echo Hello World
docker ps -a

 

이 경우에는 docker stop 또는 docker rm을 사용하여 정리할 수 있으며, 한 단계로 docker rm -f 명령을 사용하는 것이 적절할 것입니다. 또한 나중에 챕터 4에서 다룰 이유로 -v 플래그를 사용해야 합니다. docker CLI를 사용하면 빠른 정리 명령을 손쉽게 작성할 수 있습니다.

docker rm -vf $(docker ps -a -q)

 

이로써 컨테이너에서 소프트웨어를 실행하는 기본 사항을 마칩니다. 부록 1의 나머지 각 장은 컨테이너 작업의 특정 측면에 중점을 둘 것입니다. 다음 장에서는 이미지를 설치하고 제거하는 방법, 이미지와 컨테이너 간의 관계 이해, 그리고 컨테이너 파일 시스템 작업에 중점을 둘 것입니다.

'Docker' 카테고리의 다른 글

Docker Redis  (0) 2024.04.07
4. Working with storage and volumes  (0) 2024.03.26
3. Software installation simplified  (0) 2024.03.25
1. Welcome to Docker  (0) 2024.03.20
Docker  (0) 2023.11.08