[Docker] 호스트와 컨테이너간의 상호작용 및 통신
이번 글에서는 호스트 - 컨테이너 간 상호작용 및 통신에 대해 살펴보도록 하겠다. 다음의 내용들을 다룰 것이다.
- 외부에서도 컨테이너에 접근 가능하게 하는 방법.
- 예) 웹 브라우저를 통해 컨테이너로 띄운 웹 서버에 웹 페이지 요청하기.
- 호스트 - 컨테이너 간 파일 복사
- 터미널로 컨테이너 내부에 접속하는 방법.
포트 포워딩(Port forwarding)
이전 글인 “[Docker] Docker image, container의 life cycle”에서는 주로 Docker에서의 컨테이너 생명주기와 그에 따른 명령어 숙지를 위해 간단한 예시로 Apache 웹 서버인 httpd를 대상으로 다뤄보았었다. 그런데 사실 해당 글에서는 컨테이너 사용법을 주축으로 다뤘어서 사실 이 상태로는 웹 브라우저에서 해당 웹 서버에 접근할 수 없는 상황이다. 이러한 문제를 해결하기 위해선 포트 관련 설정을 해야한다.
포트(Port)는 원래 일반적인 의미에서는 “항구”라는 의미를 뜻하고 있다. 조금만 생각해보면, 배로 바다를 항해하면서 목적지로 향할 때, 해당 지역의 정확히 어느 위치에 항구가 있는지를 파악해야 그 배에 정착하여 무역 물품이나 보급품을 그 지역에 전달할 수 있을 것이다. 소프트웨어 분야 쪽에서의 포트도 이와 비슷한 의미로 사용된다.
소프트웨어에서 포트는, 하나의 IP 주소 내에서, 또는 하나의 기기 내에서 실행되는 여러 프로세스들을 구분하기 위한 논리적 번호 식별자이다. 예를 들어, 내가 컴퓨터로 멀리 떨어진 누군가와 메시지로 대화를 나눈다고 해보자. 외부로부터의 데이터가 내 컴퓨터의 IP 주소를 통해 들어왔다면, 여기서 문제가 하나 발생하는데, 그것은 “정확히 어떤 프로그램에 해당 데이터를 전달하느냐”이다. 컴퓨터에서 디스코드, 카톡 등 여러 프로그램들이 동시에 실행되고 있을 때 어떤 프로그램에 데이터를 전달할지 결정해야한다. 그래서 각 프로그램들을 서로 구분하고 외부로부터의 데이터 통신을 위해 “포트”라는 개념이 필요한 것이다. 각 프로세스들에 고유한 포트 번호가 부여되면 이제 외부 데이터를 어느 프로세스로 전달해야할지가 뚜렷해진다. IP 주소가 일종의 “이 지역에서 어떤 아파트 단지?”를 알려준다면, 포트 번호는 “그 아파트 단지에서 정확히 몇 동 몇 호?”를 알려주는 역할이라 보면 되겠다.
이는 호스트 컴퓨터에서 동작하고 있는 도커 컨테이너들도 마찬가지이다. 각 컨테이너들에도 포트 번호가 부여되어 있다1. 도커 컨테이너는 서로 똑같은 프로그램이든 서로 다른 프로그램이든 여러 개의 컨테이너들을 동시에 띄우는 경우가 많기에 포트를 통해 각 컨테이너들을 구분하여 데이터 송수신하는 것이 중요할 것이다.
한 편, 외부에서 호스트를 매개체로 하여 그 안의 특정 컨테이너와 통신을 하려면 “포트 포워딩(port forwarding, port mapping이라고도 함)”이 필요하다. 예를 들어 서버 관리자가 외부에서 호스트 서버에 “8080” 포트 번호로 요청을 하면 “웹 서버에서 웹 페이지를 보여주도록 설정”하도록 원한다고 해보자. 그러면 호스트의 8080 포트 번호와 웹 서버 컨테이너 포트 번호 80과 매핑하여 해당 컨테이너로 요청을 전달하는 것이다. 그러면 그 결과로 해당 컨테이너에서 요청에 맞는 웹 페이지를 다시 요청자에게 전달함으로써 웹 브라우저에 해당 웹 페이지가 보이게 되는 것이다.
예를 들어 하나의 호스트 서버 내에서 2개의 웹 서버를 도커 컨테이너를 이용하여 띄우고 있다고 해보자. 그리고 하나는 “음식에 관한 정보들을 제공하는 웹 서버”라 해보고, 또 다른 하나는 “소프트웨어에 관한 정보들을 제공하는 목적의 웹 서버”라고 가정해보자. 그리고 이 호스트 서버의 주소를 “http://myserver.com”이라고 해보자. 그러면 다음과 같이 설정할 수 있겠다.
http://myserver.com:8080로 들어오는 요청은 “음식에 관한 정보들을 제공하는 웹 서버”로 전달하자.http://myserver.com:8081로 들어오는 요청은 “소프트웨어에 관한 정보들을 제공하는 웹 서버”로 전달하자.
이렇게 설정 가능하다. 물론 실무에서는 굳이 포트로 구별하기보다는 http://myserver.com/foods 와 http://myserver.com/it 처럼 URL로 구분할 수도 있겠지만 여기서는 포트에 대한 이해를 위해 일부러 위와 같이 예시를 들어보았다.
그림 1-1. 포트 포워딩을 하지 않은 상태의 호스트 컴퓨터 내부를 포트 관점에서 그려보았다. 외부에서 요청이 오거나 데이터 송수신을 원할 경우, 포트 포워딩이 되어있지 않기에 해당 데이터 및 요청을 정확히 어떤 프로세스에 전달할지 몰라서 제대로된 요청 처리 및 데이터 송수신이 되지 않는다.
그림 1-2. 포트 포워딩이 된 상태에서의 호스트 컴퓨터 내부를 포트 관점에서 그려보았다. 어느 포트로 요청이 들어올 때 어떤 프로세스로 전달할지 이미 매핑이 되어있기에, 외부에서 특정 요청 또는 데이터 송수신이 들어올 때 매핑 정보에 따라 특정 프로세스로 전달할 수 있게 된다. 따라서 결과적으로 외부의 요청 처리, 데이터 송수신 등의 작업이 가능해진다.
도커, CLI 환경에서는 이러한 포트 포워딩을 위해 명령어 옵션으로 -p <host port>:<container port> 형식으로 줄 수 있다. -p 옵션 뒤에는 콜론(:) 기호 왼쪽에는 호스트 포트 번호를, 오른쪽에는 컨테이너 포트 번호를 주는 것이다. 예를 들어 “외부로부터 호스트의 8080 포트 번호로 요청이 오면 웹 서버 컨테이너로 전달하자”를 구현하고자 한다면 -p 8080:80 으로 설정하면 되는 것이다. 참고로 컨테이너 포트 번호를 굳이 80으로 언급하는 이유는 httpd 에서 기본으로 그렇게 설정되어 있기 때문이다.
한 편, 동일한 물리적 기기, 즉 동일한 호스트 내에서 똑같은 프로그램을 돌리고자 한다면 호스트 포트 번호는 다르게 해야한다. 예를 들어, 2개의 웹 서버를 동일한 호스트 위에서 구동시키고자 할 때 둘 다 httpd를 사용한다면, 해당 컨테이너는 기본 포트로 80으로 정해져 있기에 두 컨테이너가 같은 80 번호를 사용해도 되지만, 호스트 포트 번호도 동일해서는 안된다. 그래서 각각 -p 8080:80 , -p 8081:80 과 같이 호스트 포트 번호를 달리해야 한다. 그래야 어느 요청을 어느 컨테이너에 분배할지가 명확해지기 때문이다.
한 편, 방금 글에서 의문이 들 것이다. “둘 이상의 컨테이너들을 동일한 물리적 서버 위에서 동작시킬거라면, 왜 호스트 번호만 달리해야하는걸까? 컨테이너 번호들은 왜 서로 겹쳐도 되는걸까?” 왜냐하면 두 컨테이너는 서로 격리된 네트워크 네임스페이스(Network namespace)를 사용하기 때문이며, 반대로 두 컨테이너는 하나의 동일한 호스트 PC 내에서 동일한 네트워크 네임스페이스 영역에 있기 때문에 호스트 포트 번호는 서로 겹치면 안되는 것이다.
- 네임스페이스(namespace): 물리적 영역 및 리소스를 논리적으로 나눠 공간을 격리하는 기능. 리눅스 커널 기능 중 하나. 격리 대상에는 네트워크, 프로세스, User 등 여러 가지가 있어 각각의 단어에 “네트워크 네임스페이스”, “프로세스 네임스페이스”와 같이 뒤에 “네임스페이스”가 붙을 수 있다.
- 네트워크 네임스페이스: 네트워크와 관련된 모든 리소스들(network interface, IP address 등등)이 하나의 독립된 공간으로 격리된 것.
하나의 네트워크 네임스페이스에서는 포트 번호들이 고유하게 존재한다. 다만 호스트와 달리 각 컨테이너들은 각각의 격리된 네트워크 네임스페이스를 소유하고 있기에 두 컨테이너의 포트 번호가 겹쳐도 상관없는 것이다. 이것은 마치 “A 아파트 101동”과 “B 아파트 101동”과도 같은 것이다. 같은 “101동”이라도 아파트 자체가 다른 것과 같은 이치이다.
그림 1-3. 호스트 및 컨테이너 각각의 네트워크 네임스페이스를 도식화한 모습. 하나의 독립적인 네트워크 네임스페이스 내에서는 포트 번호들이 고유하게 존재해야한다. 즉, 하나의 네트워크 네임스페이스 내에서 8080 번호를 가지는 둘 이상의 포트 번호가 존재할 수는 없다.. 마치 동일한 하나의 아파트 한 동 안에서 둘 이상의 “101호”가 존재할 수 없는 것처럼 말이다. 그래서 위 그림에서처럼 호스트에서는 하나의 포트 번호를 단 하나의 컨테이너하고만 매핑할 수밖에 없다. 다만 컨테이너의 경우, 호스트의 네트워크 네임스페이스와도, 다른 컨테이너의 것과도 구분, 독립적으로 존재하기에, 둘 이상의 컨테이너들과 포트 포워딩 시 컨테이너들끼리의 포트 번호는 서로 겹쳐도 되는 것이다. 마치 똑같은 “101호”라 하더라도 “A동 101호”와 “B동 101호”는 서로 엄연히 다른 공간인 것과 마찬가지이다.
Docker에서 이러한 포트 포워딩을 적용하여 웹 브라우저에서 웹 서버에 접속하는 실습을 해보겠다. 여기서는 단 하나의 httpd 웹 서버를 띄우고 웹 브라우저에서 해당 웹 서버에 접속하는 실습이다.
터미널 창에서 다음과 같은 명령어를 입력한다.
docker run --name my-web-server -d -p 8080:80 httpd
코드 1-1.
그러면 터미널 창에서는 다음과 같은 결과를 볼 수 있다.
$ docker run --name my-web-server -d -p 8080:80 httpd
4d0752e5866403d8af4d42fa10abb40bff52bda201c21b4c87de3255245b2d99
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4d0752e58664 httpd "httpd-foreground" 5 seconds ago Up 4 seconds 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp my-web-server
코드 1-2.
docker ps 를 통해 해당 컨테이너가 잘 작동하는 것을 확인할 수 있다.
이제 웹 브라우저를 키고, URL 입력 창에 http://localhost:8080 을 입력한다. 그러면 다음과 같은 화면을 볼 수 있다.
사진 1-4. 웹 브라우저에서 8080 포트로 입력한 결과. 아파치 웹 서버에서 기본으로 제공하는 화면이 보인다.
지금까지는 웹 서버에 별도로 무언가를 하지는 않았기에 아파치 웹 서버에서 기본으로 제공하는 화면이 위와 같이 보이게 될 것이다. 어찌되었건 위와 같은 화면이 보이면 포트 포워딩이 잘 되었다는 뜻이며, 그에 따라 외부에서 해당 웹 서버에 접속이 가능한 상태임을 확인할 수 있다.
호스트 - 컨테이너 간 파일 복사
한 편, 보통은 컨테이너 그대로 사용하기보다는 원하는 목적을 달성하기 위해 일종의 커스텀을 할 것이다. 간단한 예로, 앞서 실습으로 보았던 웹 서버 실습에서, 브라우저에서 보이는 웹 페이지 화면을 다르게 보이도록 하고 싶을 수도 있을 것이다. 이에 대한 방법으로 호스트와 컨테이너 간에 파일 복사를 통해 파일을 주고 받는 방식이 있다. 이 챕터에서는 호스트와 컨테이너 간 파일을 주고 받는 방법에 대해 다뤄보고자 한다.
파일 복사 방식으로 파일을 주고 받는 방식이라 해당 도커 커맨드는 docker cp 로 시작한다. 호스트에서 컨테이너로, 또는 반대로 컨테이너에서 호스트로 파일 복사할 수도 있으며, 각각의 경우에 대한 커맨드 구조는 다음과 같다.
# (호스트 -> 컨테이너)
docker cp <호스트_경로> <컨테이너_이름:컨테이너_경로>
# (컨테이너 -> 호스트)
docker cp <컨테이너_이름:컨테이너_경로> <호스트 경로>
# 쉽게 보면, `docker cp <소스> <대상>` 순서라고 보면 되겠다.
코드 2-1.
이에 대한 실습을 해보자. 앞서 다뤘던 아파치 웹 서버를 대상으로 할 것이다. 기본 웹 페이지 화면을 내가 원하는 페이지로 바꾸는 것을 목표로 한다.
먼저 index.html 파일을 어딘가에 만들어둬야 한다. 필자의 경우, Windows 11 운영체제 위에서 WSL 2를 이용하여 Ubuntu 및 Docker Desktop을 작동시키고 있다. Ubuntu 내에 원하는 경로에 해당 파일을 만들어도 되지만, 필자의 경우, Windows 11 쪽에서 파일을 만들어 컨테이너로 복사하도록 하겠다.
필자의 경우, C:\docker\study 경로를 만들어 그 안에 다음과 같은 index.html 파일을 만들었다. 참고로 VSCode 에디터로 편집하였다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First Page In Docker</title>
</head>
<body>
<div>
<h1>Docker에서의 첫 웹페이지</h1>
<p>반갑습니다!</p>
<div>
<p>요즘 먹고 싶은 음식들</p>
<ul>
<li>피자</li>
<li>햄버거 (버거킹!)</li>
<li>국밥</li>
<li>소고기</li>
</ul>
</div>
</div>
</body>
</html>
코드 2-2. index.html
이제 이 파일을 컨테이너에 복사할 것이다. 먼저 httpd 컨테이너는 이미 실행 중에 있어야 한다. 필자는 앞선 챕터에서처럼 docker run --name my-web-server -d -p 8080:80 httpd 명령어로 이미 해당 컨테이너를 하나 실행 중에 있다.
우분투 터미널 창에서 다음과 같은 명령어를 입력한다.
docker cp /mnt/c/docker/study/index.html my-web-server:/usr/local/apache2/htdocs/
코드 2-3.
이 때 주의할 점이 있다. Windows에서 작성한 파일을 도커 컨테이너로 옮기고자 하는 경우, Windows에서의 폴더 및 파일 경로는 C:\docker\study 와 같이 명시하면 해당 명령어가 제대로 수행되지 않을 것이다. 대신, /mnt/c 로 시작하도록 하면 해당 문제는 해결될 것이다. WSL 2를 실행시킬 때 Windows에서의 C, D 드라이브 등은 리눅스에서 /mnt/c , /mnt/d 등 /mnt 폴더를 통해 자동으로 마운트 되기에 이 경로를 대신 사용하는 것이다.
한 편, /usr/local/apache2/htdocs/ 경로는 httpd 컨테이너에서 이미 정한 경로로, 웹 서버에 자신이 원하는 정적 파일들을 해당 폴더에 위치시키면 웹 브라우저에서 해당 컨텐츠들을 볼 수 있다. 해당 httpd 에 대한 자세한 사항은 [https://hub.docker.com//httpd](https://hub.docker.com//httpd) 을 참고.
위 명령어를 입력하여 엔터키를 친 후 다음과 같은 결과 메시지가 나오면 성공이다.
Successfully copied 522B (transferred 2.56kB) to my-web-server:/usr/local/apache2/htdocs/
코드 2-4.
이제 웹 브라우저에서 http://localhost:8080 으로 접속해보자. 이전에는 기본으로 웹 화면에 it works! 만 떴다면 이젠 필자가 커스텀한 웹 페이지가 보일 것이다.
사진 2-1. 원하는 내용으로 html 파일을 httpd 컨테이너에 주입 후의 모습.
한 편, 이번에는 반대로 컨테이너 속 파일을 호스트로 복사, 전달하는 것에 대해 실습해보도록 하겠다. 앞서 만들어 컨테이너에 주입한 index.html 파일을 그대로 호스트로 복사해볼 것이다. 다만 호스트에 같은 경로에 이미 index.html 파일이 존재하기에 이번에는 my-web-server-main.html 로 이름을 바꿔 복사하도록 해보겠다.
여전히 해당 컨테이너가 실행된 상태에서, 리눅스 터미널 창에서 다음과 같은 명령어를 입력한다.
docker cp my-web-server:/usr/local/apache2/htdocs/index.html /mnt/c/docker/study/my-web-server-main.html
코드 2-5.
그러면 Windows에서 해당 경로로 폴더 탐색기를 열어보면 my-web-server-main.html 파일이 그대로 생성될 것이다. 해당 파일 내용을 열어봐도 똑같은 html 코드가 들어 있을 것이다.
이렇듯, 호스트 - 컨테이너 간 파일 복사 방법을 알아두면 다음과 같은 상황에서 유용할 것이다.
- 컨테이너 내용이나 환경 설정을 파일을 통해 내가 원하는대로 커스텀하고자 할 경우.
- 컨테이너 삭제 전 컨테이너 내에 있는 중요한 파일들을 호스트로 백업하고자 할 경우.
포트 포워딩과 파일 복사를 이용한 종합 실습
이제 포트 포워딩을 통해 웹 브라우저에서 웹 서버에 접근하는 방법과, 호스트 - 컨테이너 간 파일 복사 방법에 대해 살펴봤으니, 이 챕터에서는 이 두 기능을 이용하여 똑같은 여러 웹 서버 컨테이너들을 띄워보는 실습을 해볼 것이다. 사실, “호스트 - 컨테이너 간 파일 복사 방법”은 모르더라도 똑같은 여러 웹 서버들을 호스트 포트 번호만 다르게 하여 띄우는 건 가능하다. 다만, 이렇게 할 경우 웹 브라우저 상에서는 어떤 포트로 요청해도 모두 똑같은 웹 화면만 보일 것이기에 제대로 된 구분을 위해 각 웹 서버마다 서로 다른 웹 페이지를 제공하도록 커스텀하여 구분해보고자 한다.
먼저, 3개의 웹 서버를 동시에 띄울 것인데, 구분을 위해 먼저 각자의 index.html 파일을 만들고자 한다. 필자의 경우 Windows에서 다음과 같은 각각의 폴더 경로들을 만들고 그 안에 각각의 index.html 파일들을 만들었다.
C/
docker/
apache-1/
index.html
apache-2/
index.html
코드 3-1. Windows에서의 폴더 구조.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>음식 정보 사이트</title>
</head>
<body>
<h2>음식 정보 사이트에 오신 것을 환영합니다.</h2>
</body>
</html>
코드 3-2. C:\docker\apache-1\index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>IT/소프트웨어 정보 사이트</title>
</head>
<body>
<h2>IT/소프트웨어 정보 사이트에 오신 것을 환영합니다.</h2>
</body>
</html>
코드 3-3. C:\docker\apache-2\index.html
이번에는 리눅스 터미널 창에서 작업한다. 먼저, httpd 이미지로부터 3개의 컨테이너를 각각 생성한다. 필자의 경우 다음과 같이 생성하였다.
docker run --name web-server-default -d -p 8080:80 httpd
docker run --name web-server-food -d -p 8081:80 httpd
docker run --name web-server-it -d -p 8082:80 httpd
코드 3-4.
docker ps 명령어를 통해 세 컨테이너가 모두 문제 없이 동작 중인지 확인한다.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
699da46c7cbc httpd "httpd-foreground" 2 seconds ago Up 1 second 0.0.0.0:8082->80/tcp, [::]:8082->80/tcp web-server-it
d80f1287c892 httpd "httpd-foreground" 12 seconds ago Up 10 seconds 0.0.0.0:8081->80/tcp, [::]:8081->80/tcp web-server-food
657af4a750d4 httpd "httpd-foreground" 23 seconds ago Up 22 seconds 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp web-server-default
코드 3-5.
모두 정상적으로 작동한다면, 앞서 준비한 html 파일들을 각각의 웹 서버에 주입한다. C:\docker\apache-1\index.html 파일은 web-server-food 컨테이너에, C:\docker\apache-2\index.html 는 web-server-it 서버에 주입한다. 다음 명령어들을 한 줄씩 차례대로 입력한다.
docker cp /mnt/c/docker/apache-1/index.html web-server-food:/usr/local/apache2/htdocs/
docker cp /mnt/c/docker/apache-2/index.html web-server-it:/usr/local/apache2/htdocs/
코드 3-6.
그 후, 웹 브라우저를 키고 각각 다음의 주소를 입력하여 방문해본다.
http://localhost:8080http://localhost:8081http://localhost:8082
그러면 각각 다음과 같은 페이지가 보일 것이다. 8080 포트 번호로 요청한 경우, 앞선 사진 1-4와 같이 아파치 웹 서버 기본 웹 페이지가 보일 것이다.

사진 3-1. 8081 포트로 접근한 결과

사진 3-2. 8082 포트로 접근한 결과
이 결과로 다음을 확인하였다.
- 같은 이미지로부터 여러 대의 컨테이너를 띄우고, 각자 포트 포워딩을 통해 웹 브라우저로 접속 가능하게 할 수 있다.
- 각 컨테이너마다 서로 다른 파일들을 호스트로부터 주입할 수 있어 각자 다른 웹 페이지를 보여주도록 할 수 있다.
이전에 컨테이너의 생명주기에 관한 글에서도 다뤘지만, 복습 차원에서 위에서 실행 중인 컨테이너들을 중단, 삭제하는 방법을 다시 상기해보고자 한다.
다음과 같이 한 줄씩 차례대로 명령어를 입력하여 여러 컨테이너들을 차례대로 중단시킨다. 여러 컨테이너들을 중단시키는 순서는 상관없으니 명령어 순서는 상관없이 입력해도 된다.
docker stop web-server-default
docker stop web-server-food
docker stop web-server-it
코드 3-7.
중간에 어떤 컨테이너가 동작 중인지, 또는 컨테이너 이름을 까먹었다면 docker ps 명령어를 통해 중간에 확인하면서 진행한다.
그 후, 비슷한 과정으로 각 컨테이너마다 rm 명령어를 이용하여 삭제한다. 역시나 중간에 어떤 컨테이너가 아직 삭제되지 않았는지는 docker ps -a 명령어로 확인해가면서 삭제하면 된다.
docker rm web-server-default
docker rm web-server-food
docker rm web-server-it
코드 3-8.
터미널로 컨테이너 내부에 접속하기
보통 docker container에 들어있는 소프트웨어들도 운영체제 위에서 동작해야하기에 container마다 운영체제의 user space(사용자 공간) 부분이 이미 설치되어 있을 것이다. 그리고 대부분 리눅스 배포판일 것이다.
지금까지는 docker ~ 로 시작하는 도커 관련 명령어들은 호스트 OS에서의 쉘에서 입력하는 것이었고, 입력된 명령어들은 실행 중인 도커 엔진으로 전달되어 결과적으로 docker container들에게 전달되는 형식이었다. 이번에는 호스트의 터미널 창에서 특정 컨테이너 내부의 쉘을 통해 해당 OS 사용자 공간에 접근하는 방법에 대해 살펴보도록 하겠다. 이를 이용하면, 이전 챕터에서는 호스트에서 컨테이너로 파일 복사를 통해 파일을 전달하는 방법에 대해서만 보았다면, 이 방법을 통해 아예 컨테이너 내부에서 직접 파일을 만들어볼 수도 있을 것이다.
대부분의 컨테이너들은 리눅스 배포판이 설치되어 있을 것이다. 그리고 그 안에는 운영체제에서 명령어를 입력하여 실행되도록 하는 쉘(Shell)이 설치되어 있을 것이다. 쉘에도 여러 종류가 있지만 일반적으로 사용되는 bash라는 쉘이 대부분 설치되어 있을 것이다. 이 bash를 이용하여 컨테이너 내부에 접속해볼 것이다.
컨테이너 내부에 접근하는 도커 명령어는 크게 두 가지가 있다.
docker exec -it <컨테이너_이름> /bin/bashdocker run <옵션> -it <이미지_이름> /bin/bash
공통점은, 두 명령어 모두 맨 뒤에 /bin/bash 를 입력함으로써 컨테이너 내부의 bash 쉘을 실행하도록 한다는 것이다.
다만 첫 번째 exec 명령어의 경우, 먼저 해당 컨테이너가 실행 중인 상태에 있어야 한다. 두 번째 명령어는 처음 컨테이너를 실행시키자마자 바로 bash를 실행시키고자 할 때 사용한다. 다만 해당 명령어의 경우, 컨테이너 내부에 들어있는 프로그램과 bash 프로그램이 동시에 실행되지 않고 bash만 실행되므로, 이 경우 bash를 종료시킨 후에 docker start 명령어를 통해 해당 컨테이너를 재시작해야 한다.
exec 명령어의 경우에는 그러한 제약이 없어서 실행 중인 컨테이너 내부를 조작하면서 실시간으로 그 결과를 확인할 수 있다는 장점이 있다. 따라서 이번 실습도 exec 를 통해 해보겠다.
앞선 챕터들에서 사용했던 httpd 이미지를 토대로 실습하겠다. 먼저 다음과 같이 해당 이미지로부터 새 컨테이너를 생성, 실행시킨다.
docker run --name my-web-server -d -p 8080:80 httpd
코드 4-1.
원한다면 docker ps 명령어 또는 웹 브라우저를 통해 해당 컨테이너가 잘 작동 중인지 확인해봐도 된다.
그 후, 다음과 같은 명령어를 입력한다.
docker exec -it my-web-server /bin/bash
코드 4-2.
그러면 다음과 같이 쉘에서의 프롬프트의 색이 변할 것이다. 현재 호스트 이름도 바뀔 것이다.

사진 4-1. 컨테이너 내부의 bash 쉘로 접속한 모습.
이는 기존에는 호스트 리눅스에서 작업하다가 컨테이너 내부의 리눅스로 들어왔다는 뜻이다.
여기서부터는 해당 컨테이너에 기본으로 존재하는 index.html 파일을 읽고, 파일 내용을 수정해보는 시간을 갖겠다.
다음의 명령어들을 한 줄씩 차례대로 입력하여 해당 경로에 어떤 파일이 있는지 확인부터 한다.
# cd htdocs
# ls
index.html
코드 4-3.
cd htdocs 를 통해 최종적으로 /usr/local/apache2/htdocs/ 경로에 접근하였고, 해당 경로에 어떤 파일이 있는지 ls 로 확인한 결과, index.html 파일이 있는 것을 확인할 수 있었다.
이번에는 해당 파일에 대해 cat index.html 명령어를 실행해보자. 그러면 다음과 같은 결과를 얻는다.
# cat index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>It works! Apache httpd</title>
</head>
<body>
<p>It works!</p>
</body>
</html>
코드 4-4.
해당 html 파일 내용을 위와 같이 확인할 수 있다.
이번에는 해당 파일 내용을 조금 수정해보도록 한다.
# nano index.html
bash: nano: command not found
코드 4-5.
nano 라는 텍스트 편집기를 이용하여 nano index.html 명령어를 통해 해당 파일을 텍스트 에디터로 열려고 했으나 위와 같은 에러 메시지가 떴다. 해당 컨테이너 내부에 nano 라는 에디터 자체가 없어서 발생한 메시지이다. 이를 위해선 컨테이너 내부 리눅스에서의 패키지 매니저를 이용하여 해당 에디터를 먼저 다운로드 받도록 한다.
다음의 명령어들을 한 줄씩 순서대로 입력한다.
apt update
apt install -y nano
코드 4-6.
필자의 경우 설치 과정에서 다음과 같은 메시지들을 볼 수 있었다.

사진 4-2. 컨테이너 내부에서 apt 패키지 매니저를 이용하여 nano 에디터를 다운로드 받는 모습.
설치를 모두 받았다면 다시 nano index.html 명령어를 입력해보자. 그러면 다음과 같은 화면으로 전환되는 것을 볼 수 있다.

사진 4-3. 컨테이너 내부의 index.html 파일 내용을 nano 에디터를 통해 접근한 모습.
여기서는 키보드 화살표를 통해 수정하고픈 위치의 내용을 수정하면 되겠다. 필자의 경우 다음과 같이 수정해봤다.

사진 4-4.
한글이 써지지 않아서 영어로 작성해보았다.
작성을 완료했으면 키보드에서 Ctrl + o (영문자)를 눌러 저장한다.

사진 4-5.
그러면 위 사진과 같이 아래 부분에 흰색 배경의 줄로 Write to File: index.html 부분이 나타날 것이다. 여기서는 그냥 엔터키를 입력한다.
그 후, Ctrl + x 를 눌러 nano 에디터로부터 빠져나온다. 그러면 이전의 컨테이너 내부 bash 화면으로 돌아올 것이다.
이제 웹 브라우저에서 http://localhost:8080 으로 접속해보자. 그러면 아파치 웹 서버에서 기본으로 제공하는 웹 페이지와는 다른 화면이 다음과 같이 뜰 것이다.

사진 4-6. 웹 브라우저에서 해당 컨테이너에 접속한 모습. 앞서 수정한 내용대로 출력되는 것을 확인할 수 있다.
사실 컨테이너 작동 중에 미리 웹 브라우저를 켜놓고 bash 쉘에서 파일 내용을 수정해가도 된다. 그러면 실시간으로 웹 브라우저에도 반영되어 수정 후 모습을 빠르게 확인할 수 있다.
작업을 완료하였다면 exit 명령어를 입력하여 컨테이너 내부를 빠져나온다. 그러면 다음과 같이 다시 호스트 리눅스로 돌아온다.

사진 4-7.
글을 마치며
이번 글에서는 주로 호스트와 컨테이너 간의 상호작용 및 통신에 관해 살펴보았다. 외부와의 연결을 위한 포트 포워딩, 호스트 - 컨테이너 간 파일 복사, 터미널로 컨테이너 내부에 접속해보는 방법들에 대해 살펴보았다.
다음 글에서도 역시 컨테이너의 상호작용 및 통신에 대해 살펴보도록 하겠다. 다만 이번에는 동일 호스트 내에서 여러 컨테이너들을 띄울 시 각 컨테이너 간 통신을 가능하게 하기 위해 연동하는 방법에 대해 다뤄보도록 하겠다. 예를 들어 하나의 호스트 서버 위에서 웹 서버, WAS, DB 서버를 각각의 컨테이너로 띄운다고 할 때 이 컨테이너들끼리 통신이 가능해야 할 것이다. 이러한 상황에 대비하기 위해 컨테이너 간 연동 방법에 대해 살펴볼 것이다.
그리고 사실 “컨테이너와의 상호작용”이란 측면에서는 디스크와 컨테이너 간 상호작용을 통해 데이터를 주고 받는 것도 포함될 것이다. 도커에서는 마운트, 즉 바인딩 마운트와 볼륨 마운트가 이에 해당된다. 다만 해당 주제도 이 글에서 다루기엔 내용이 너무 길어져서 다음에 별도의 글에서 다뤄보고자 한다.
References
[1] 지은이: 오가사와라 시게타카, 옮긴이: 심효섭, “그림과 실습으로 배우는 도커 & 쿠버네티스“, 위키북스
[2] [Docker] 도커 포트 포워딩하기 (웹서비스 사용하기)
[3] 도커(Docker) : 포트 포워딩 설정(포트 맵핑)하기
[4] Docker docs - CLI references
[5] 포트 포워딩
[7] [PORT] 네트워크 포트(PORT)의 개념과 포트포워딩, 기본 포트 번호 정리
[8] 나무위키 - 네트워크 포트
[9] 컨테이너 기술에서의 네트워크 네임스페이스 개념 관련.
-
사실 엄연하게 말하자면 컨테이너 내부에 있는 프로그램에 포트 번호가 부여된다, 라고 말하는게 더 정확할지 모르겠다. 기술적으로는 하나의 컨테이너 내부에 여러 개의 프로그램을 넣을 수도 있지만, 관리 측면에서는 문제점이 있어 대부분 하나의 컨테이너에는 하나의 프로그램만 넣고, 차라리 여러 필요한 컨테이너들을 띄워서 서로 상호작용하게끔 운용하는게 일반적이다. 즉, 일반적으로는 “1 컨테이너 = 1 프로그램” 이기에 편의상 “컨테이너에 포트 번호가 부여되어 있다”라고 표현한 것이다. ↩
This content is licensed under
CC BY-NC 4.0
댓글남기기