주요글: 도커 시작하기

컨테이너를 구동할 때 -p 옵션을 이용해서 컨테이너의 포트와 연결된 호스트 포트를 설정했다.

docker run --name mysqldb \
-e MYSQL_ROOT_PASSWORD=rootpw \
-p 33060:3306 -d mysql:5.7

실행한 MySQL에 접속하려면 호스트IP:33060으로 연결하면 된다.

도커 컨테이너 간에 연결할 경우에는 어떻게 할까? 각 컨테이너마다 환경 변수를 이용해서 연결할 호스트의 IP와 포트를 설정할 수 있을 것이다. 이 방법이 안 되는 것은 아니지만 이보다 좋은 방법은 도커 네트워크를 이용하는 것이다.

도커 네트워크

docker network ls 명령어를 사용하면 도커가 제공하는 네트워크 목록을 확인할 수 있다.

vagrant@ubuntu-bionic:~$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
e83fd9260276        bridge              bridge              local
f6a428e9d599        host                host                local
2e3d5809c8ac        none                null                local

도커는 기본으로 세 개의 네트워크를 제공한다. 각 네트워크는 다음과 같다.

  • bridge 네트워크 : bridge 드라이버로 제공하는 네트워크로 컨테이너간 연결을 제공하며 컨테이너를 구동하는 호스트 네트워크가 구분된다.
  • host 네트워크 : host 드라이버로 제공하는 네트워크로 컨테이너를 위한 별도 네트워크없이 호스트와 동일한 네트워크를 사용한다.
  • none 네트워크 : null 드라이버를 사용하며 네트워크 연결을 갖지 않는다.

네트워크 SCOPE는 네트워크의 범위를 의미하며 다음의 세 가지 범위가 존재한다.

  • local : 컨테이너간 연결은 네트워크가 존재하는 호스트로 제한된다.
  • global : 클러스터의 모든 노드에 네트워크가 존재하지만 호스트 간 라우팅은 지원하지 않는다.
  • swarm : 도커 스웜에 참여하는 모든 호스트로 연결을 확장한다.

이 글에서는 단일 호스트에서 네트워크를 연결하는 방법을 살펴본다. 여러 호스트에서 실행 중인 컨테이너 간의 연결은 도커 스웜을 설명할 때 살펴볼 것이다.

컨테이너 간 연결 : 기본 bridge 네트워크 이용

컨테이너 간에 연결하는 가장 쉬운 방법은 기본으로 제공하는 bridge 네트워크를 이용하는 것이다. 먼저 다음 명령을 이용해서 mysqldb 컨테이너를 생성하자. -p 옵션을 사용하지 않았으므로 호스트에서 컨테이너의 MySQL DB에 연결할 수 없다.

$ docker run --name mysqldb --rm \
-e MYSQL_ROOT_PASSWORD=rootpw \
-d mysql:5.7

이제 웹 기반 DB 관리도구 중 하나인 adminer를 다음 명령어를 이용해서 구동하자. Ctrl+C를 누르면 컨테이너가 종료되니 주의한다.

$ docker run --rm --name dbadmin --link mysqldb:db -p 8080:8080 adminer
PHP 7.3.10 Development Server started at Sun Sep 29 08:50:22 2019

여기서 --link 옵션이 중요하다. --link 옵션의 값으로 "mysqldb:db"를 주었는데 여기서 앞의 mysqldb는 컨테이너의 이름이며 뒤의 db는 컨테이너 내부에서 사용할 식별자이다. 즉 dbadmin 컨테이너 내부에서 db라는 이름으로 mysqldb 컨테이너에 접근할 수 있다는 것을 의미한다.

실제로 그런지 http://호스트IP:8080으로 연결해서 확인해보자. adminer 첫 화면에 연결하면 DB 로그인 폼이 표시되는데 여기서 서버에 "db:3306"이라고 입력한다. 사용자이름과 비밀번호는 각각 "root", "rootpw"를 입력한다(앞서 mysqldb 컨테이너를 구동할 때 MYSQL_ROOT_PASSWORD 환경 변수의 값으로 rootpw를 주었다).

로그인 버튼을 눌러보자. 다음과 같이 DB에 연결한 결과를 볼 수 있다.

dbadmin 컨테이너에서 db:3306으로 연결한 DB가 실제 mysqldb 컨테이너가 제공하는 DB인지 확인해보자. 먼저 mysqldb 컨테이너의 mysql db에 연결해서 이름이 test인 DB를 생성한다.

vagrant@ubuntu-bionic:~$ docker exec -it mysqldb mysql -u root -p
Enter password: (암호입력)
Welcome to the MySQL monitor.  Commands end with ; or \g.
...생략

mysql> create database test;
Query OK, 1 row affected (0.00 sec)

mysql> exit
Bye
vagrant@ubuntu-bionic:~$

그런 뒤 adminer 웹 화면에서 데이터베이스를 새로 고침해보자. 그러면 생성한 test DB가 목록에 표시될 것이다.

 

컨테이너 간 연결 : bridge 네트워크를 생성해서 연결

bridge 네트워크를 직접 생성할 수도 있다. 

$ docker network create \
--driver bridge \
--attachable \
--scope local \
--subnet 10.0.7.0/24 \
--ip-range 10.0.7.0/24 \
mynet

mynet 네트워크는 bridge 네트워크로 --attachable 옵션은 컨테이너가 언제든지 네트워크에 연결하거나 떨어질 수 있게 설정한다. --subnet과 --ip-range는 서브넷과 할당 가능한 IP 범위를 지정한다.

네트워크를 생성했다면 컨테이너를 네트워크에 붙일 수 있다. 컨테이너를 실행할 때 --network 옵션을 사용하면 된다.

docker run --name mysqldb \
--network mynet \
-e MYSQL_ROOT_PASSWORD=rootpw \
-d mysql:5.7

같은 네트워크에 참여하는 컨테이너는 이름을 사용해서 다른 컨테이너에 연결할 수 있다. 아래 코드를 보자.

vagrant@ubuntu-bionic:~$ docker run --name cent7 -it --network mynet centos:7 bash
[root@fc0936cc3177 /]# ping mysqldb
PING mysqldb (10.0.7.2) 56(84) bytes of data.
64 bytes from mysqldb.mynet (10.0.7.2): icmp_seq=1 ttl=64 time=0.183 ms
64 bytes from mysqldb.mynet (10.0.7.2): icmp_seq=2 ttl=64 time=0.136 ms
^C
--- mysqldb ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.136/0.159/0.183/0.026 ms

이 코드는 mynet 네트워크에 참여하는 cent7 컨테이너를 생성하고 bash 명령어를 실행한다. mynet 네트워크에 mysqldb 컨테이너도 참여했으므로 cent7 컨테이너는 컨테이너 이름을 사용해서 mysqldb에 연결할 수 있다. ping 명령어를 실행할 때 표시된 IP 주소는 10.0.7.2인데 이 IP 주소는 mynet 네트워크를 생성할 때 지정한 IP 범위에 속한다.

이미 생성한 컨테이너에 네트워크를 연결하고 싶거나 연결된 네트워크를 끊고 싶을 때에는 docker network connect 명령어나 docker network disconnet 명령어를 사용한다.

관련 글

호스트 포트 연결

컨테이너와 호스트 포트를 연결하는 방법은 이미 앞서 nginx 이미지로 컨테이너를 생성할 때 사용했다. -p 옵션을 사용해서 포트를 지정한다(또는 --publish 옵션을 사용).

docker run -d -p 8080:80 --name web nginx:latest

연결 포트는 "호스트포트:컨테이너포트" 형식으로 지정한다.

환경 변수 설정

-e 옵션(--env 옵션)을 사용하면 컨테이너를 실행할 때 환경 변수를 전달할 수 있다. 예를 들어 mysql 이미지의 실행 프로그램은 MYSQL_ROOT_PASSWORD 환경 변수를 이용해서 DB의 root 암호를 설정한다. 따라서 root 암호를 원하는 문자열로 지정하고 싶다면 다음과 같이 컨테이너를 구동할 때 -e 옵션을 사용해서 환경 변수를 전달하면 된다.

docker run --name mysqldb \
           -e MYSQL_ROOT_PASSWORD=rootpw \
           -p 33060:3306 -d mysql:5.7

도커 허브(hub.docker.com)에서 이미지가 어떤 환경 변수를 사용하는지 확인할 수 있다.

로컬 스토리지 연결

아래와 같이 nginx 이미지를 이용해서 생성한 컨테이너에 bash로 연결해서 /usr/share/nginx/html 디렉토리에 echo.txt 파일을 생성해보자.

vagrant@ubuntu-bionic:~$ docker run -d -p 8080:80 --name web nginx:latest

vagrant@ubuntu-bionic:~$ docker exec -it web bash

root@fe306ef365a7:~# cd /usr/share/nginx/html/

root@fe306ef365a7:/usr/share/nginx/html# echo "echo file" > echo.txt

root@fe306ef365a7:/usr/share/nginx/html# exit

웹 브라우저에서 http://호스트:8080/echo.txt를 실행하면 방금 생성한 파일이 출력될 것이다. 컨테이너를 중지하고 다시 시작해도 컨테이너에 생성한 파일은 유지되는 것을 확인할 수 있다.

컨테이너에 파일을 생성하고 수정하고 삭제하는 것이 가능은 하지만 컨테이너의 파일 시스템을 직접 변경하는 것은 추천하지는 않는다. 컨테이너를 삭제하면 변경 내역도 함께 사라지기 때문이다. 컨테이너의 삭제 여부에 상관없이 파일을 유지해야 한다면 로컬 스토리지나 볼륨을 연결해야 한다.

로컬 스토리지와 컨테이너를 연결할 때는 --mount 옵션을 사용한다. 테스트를 위해 앞서 생성한 web 컨테이너를 삭제하고 홈 디렉토리에 html 디렉토리를 생성하고 이 폴더에 index.html 파일과 echo.txt 파일을 생성하자. 그리고 다음 명령어를 사용해서 컨테이너를 생성한다.

vagrant@ubuntu-bionic:~/html$ echo '<html><body>index</body></html>' > index.html

vagrant@ubuntu-bionic:~/html$ echo 'echo file in local' > echo.txt

vagrant@ubuntu-bionic:~/html$ docker run -d --name web --rm \
>   --mount type=bind,src=/home/vagrant/html,dst=/usr/share/nginx/html \
>   -p 8080:80 \
>   nginx:latest
d1530bacb7176c9fe36d0f1097661deaf6f471edd3ddd3d849c51eeeb43b16c0

vagrant@ubuntu-bionic:~/html$

웹 브라우저를 열고 http://호스트:8080/index.html 이나 http://호스트:8080/echo.txt에 연결해보자. 로컬에 생성한 파일이 브라우저에 표시되는 것을 알 수 있다. ~/html 디렉토리에 새로운 파일을 추가하거나 삭제한 뒤에 브라우저에 확인해보자. 바로 반영될 것이다.

--mount 옵션에서 type을 bind로 지정하면 컨테이너의 파일 시스템을 호스트의 파일 시스템으로 대체한다. src는 호스트 경로를 값으로 갖고 dst는 대체할 컨테이너 경로를 값으로 갖는다. 위 설정은 생성한 컨테이너의 /usr/share/nginx/html 경로를 로컬 호스트의 /home/vagrant/html로 연결한다고 설정한다.

컨테이너의 경로를 호스트로 연결하면 컨테이너를 삭제해도 파일 변경 내역이 유지되므로 다음 작업에 이점이 생긴다.

  • 이미지를 변경해서 컨테이너를 새로 만들어도 데이터가 유지된다.
  • 이미지에 이미 존재하는 파일을 다른 파일로 쉽게 교체할 수 있다.

로컬 스토리지 연결뿐만 아니라 메모리 파일 시스템 연결과 볼륨이 있는데 이에 대한 내용은 다음 편에 이어서 살펴본다.

관련 글

윈도우8 PC에 Virtualbox를 설치하고 게스트OS로 윈도우7을 생성했다. 그런데, 이상하게 윈도우7 머신에서 네트워크가 안 되는 문제가 발생했다. 그래서 Vagrant로 Centos 머신을 생성했다. 역시나 Centos 머신도 네트워크가 안 됐다. 포트포워딩 설정을 해도 Centos에 붙질 않았다.


Virtualbox와 Vagrant를 재설치해보고 보안 관련 프로그램도 살펴봤는데 영 문제가 해결되지 않앗다. 이래 저래 삽질을 하면서 증상은 다음과 같음을 알 수 있다.

  • 게스트OS에서 ping은 됨
  • 게스트OS에서 TCP 연결이 안 됨 (예, 윈도우 게스트OS에서 브라우저로 연결이 안 됨. telnet으로 외부 서비스에 특정 포트로 연결이 안 됨)

증상이 이상해서 범위를 좁혀서 구글에서 검색을 하다가 아래 명령어를 통해서 해결할 수 있음을 알아냈다.


netsh winsock reset


그런데, 이 명령어를 실행하고 나니 DB 보안 프로그램인 샤크라맥스가 동작하지 않았다. 실제 원인은 샤크라와 버추얼박스 간에 문제였던 것있다. 샤크라맥스를 언인스톨하고 다시 설치했더니 이제 샤크라맥스도 정상 실행되고 버추얼박스에 생성한 게스트OS의 네트워크도 정상 동작한다.


정확히는 모르지만 버추얼박스를 설치한 뒤에 샤크라를 설치해야 버추얼박스의 네트워크 기능이 정상 동작하는 듯 하다.

  1. 2017.06.21 09:25

    비밀댓글입니다

+ Recent posts