우리는 도커를 사용할 때 docker라는 명령어를 맨 앞에 붙여서 사용합니다. 그리고 실제 docker는 /usr/bin/docker에 위치하고 있습니다. 이는 'which docker' 를 입력하면 확인할 수 있습니다.
이처럼 도커 명령어는 /usr/bin/docker에 위치한 파일을 통해 사용되고 있습니다. 하지만 실제 도커 엔진의 프로세스를 확인해보면 /usr/bin/dockerd 파일로 실행되는 것을 알 수 있습니다.
컨테이너나 이미지를 다루는 명령어는 /usr/bin/docker에서 실행되지만 도커 엔진의 프로세스는 /usr/bin/dockerd 파일로 실행되고 있습니다. 이는 docker의 명령어가 실제 도커 엔진이 아닌 클라이언트로서의 도구이기 때문입니다.
도커의 구조는 크게 두 가지로 나누어집니다. 하나는 클라이언트로서의 도커이고, 다른 하나는 서버로서의 도커입니다.
실제로 컨테이너를 생성하고 실행하며 이미지를 관리하는 주체는 서버도커이고, 이는 dockerd 프로세스로서 동작합니다. 도커 엔진은 외부에서 API 입력을 받아 도커 엔진의 기능을 수행하는데, 도커 프로세스가 실행되어 서버로서 입력을 받을 준비가 된 상태를 도커 데몬이라고 이야기합니다.
다른 하나는 도커 클라이언트입니다. 도커 데몬은 API 입력을 받아 도커 엔진의 기능을 수행하는데, 이 API를 사용할 수 있도록 CLI(Command Line Interface)를 제공하는 것이 도커 클라이언트입니다.
사용자가 docker로 시작하는 명령어를 입력하면 도커 클라이언트를 사용하는 것이며, 도커 클라이언트는 입력된 명령어를 로컬에 존재하는 도커 데몬에게 API로서 전달합니다. 이때 도커 클라이언트는 /var/run/docker.sock에 위치한 유닉스 소켓을 통해 도커 데몬의 API를 호출합니다. 도커 클라이언트가 사용하는 유닉스 소켓은 같은 호스트 내에 있는 도커 데몬에게 명령을 전달할 때 사용되며, tcp로 원격으로 도커 데몬을 제어하는 방법도 있습니다.
즉, 터미널이나 PuTTY 등으로 도커가 설치된 호스트에 접속해 docker 명령어를 입력하면 아래와 같은 과정으로 도커가 제어됩니다.
위 과정은 아무런 설정을 하지 않았을 때 일반적으로 도커 데몬을 제어하는 순서이며, 각종 옵션을 추가해 실행한다면 위 순서에 별도의 과정이 포함될 수 있습니다.
도커 데몬은 일반적으로 아래의 명령어로 시작 및 정지를 할 수 있습니다. 우분투에서는 도커가 설치되면 자동으로 서비스로 등록되므로 호스트가 재시작되더라도 자동으로 실행됩니다.
1. service docker start
2. service docker stop
도커 서비스는 dockerd로 도커 데몬을 실행합니다. 하지만, 서비스를 사용하지 않고 직접 도커 데몬을 실행할 수도 있습니다.
아래 명령어로 도커 서비스를 정지한 다음에 dockerd로 도커를 직접 실행해보도록 하겠습니다.
1. sudo service docker stop
2. sudo dockerd
실행하게 되면 도커 데몬에 대한 각종 정보가 출력되는데, 마지막 줄은 /var/run/docker.sock에서 입력을 받을 수 있는 상태라는 메세지입니다. 여기서 터미널을 하나 더 열어서 도커 명령어를 입력하면 이전처럼 도커를 사용할 수 있습니다.
직접 도커 데몬을 실행하면 하나의 터미널을 차지하는 포그라운드(foreground) 상태로 실행되므로 운영 및 >관리 측면에서 바람직하지 않고, 실제 운영 환경에서는 직접 실행하기보다는 service, systemctl 명령어>를 통해 리눅스 서비스로 관리하는 것이 좋습니다.
도커 데몬으로 설정할 수 있는 옵션은 dockerd --help 명령어로 확인이 가능합니다.
이전에 Private Registry에 대한 글에서 https를 사용하지 않아도 되도록 하는 설정을 도커 데몬에 직접 적용하여 실행하면 다음의 커맨드로 실행할 수 있습니다.(참고 : 09. Private Registry)
sudo dockerd --insecure-registry=${HOST_IP}:PORT
하지만 보통 dockerd 명령어로 도커 데몬을 직접 실행하는 것보다 더커 설정 파일을 수정한 뒤에 도커 데몬이 설정 파일을 읽어 서비스로 실행되게 하는 것이 일반적입니다.
이번 글에서는 dockerd 명령어로 실행하는 것으로 예제를 따라해볼것이지만, 실제 사용할 때에는 설정 파일의 DOCKER_OPTS에 입력하면 됩니다.
아래 두 가지 방법은 모두 동일하게 도커 데몬을 설정하며, 직접 도커 데몬을 실행하느냐 또는 서비스로 실행하느냐의 차이만 있습니다.
dockerd -H tcp://0.0.0.:2375 -insecure-registry=192.168.100.99:5000 -tls=false
1. # vi /etc/default/docker
2. ...
3. DOCKER_OPTS="-H tcp://0.0.0.0:2375 --insecure-registry=192.168.100.99:5000 --tls=false"
저의 경우에는 2번 방법을 사용해도 정상적으로 옵션이 적용되지 않고 있습니다. 파일이 전부 주석처리되어 있고, 주석 내용을 보아 적용이 되지 않는 느낌입니다.. https://docs.docker.com/engine/admin/systemd/ 에서도 아래의 configuration 파일을 사용하라고 나옵니다.
→공식 문서에서는 configuration 파일로 /etc/docker/daemon.json을 사용하라고 되어 있습니다.
https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file
+) /etc/default/docker가 아닌 /lib/systemd/system/docker.service 파일을 수정하면 정상적으로 적용이 되는 것을 확인했습니다. ExecStart를 원하는 옵션을 넣어서 실행하면 잘 적용되어서 실행이 됩니다.
위와 같이 변경 후, 아래 명령어로 도커 서비스 파일을 다시 로드한 뒤 재시작하면 됩니다. (도커 클라이언트에서도 사용할 수 있도록 unix:///var/run/docker.sock 도 추가해주었습니다.)
1. systemctl daemon-reload
2. systemctl restart docker
-H 옵션은 도커 데몬의 API를 사용할 수 있는 방법을 추가합니다. 아무런 옵션을 설정하지 않고 도커 데몬을 실행하면 도커 클라이언트인 /usr/bin/docker를 위한 유닉스 소켓 /var/run/docker.sock을 사용합니다.
그러므로 단순히 dockerd를 입력해 도커 데몬을 실행해도 도커 클라이언트의 CLI를 실행할 수 있습니다. 따라서, 아래의 두 명령어는 동일합니다.
1. dockerd
2. dockerd -H unix:///var/run/docker.sock
-H에 IP주소와 포트 번호를 입력하면 원격 API인 Docker Remote API로 도커를 제어할 수 있습니다. Remote API는 도커 클라이언트와는 다르게 로컬에 있는 도커 데몬이 아니더라도 제어할 수 있으며, RESTful API 형식을 띠고 있으므로 HTTP 요청으로 도커를 제어할 수 있습니다.
다음 명령어로 도커 데몬을 실행하면 호스트에 존재하는 모든 네트워크 인터페이스의 IP 주소와 2375번 포트를 바인딩해 입력을 받습니다.
dockerd -H tcp://0.0.0.0:2375
-H에 unix:///var/run/docker.sock을 지정하지 않고, 위와 같이 Remote API만을 위한 바인딩 주소를 입력했다면, 유닉스 소켓은 비활성화되므로 도커 클라이언트를 사용할 수 없게 되며, 호스트에서는 docker로 시작하는 명령어를 사용할 수 없게 됩니다.
따라서 일반적으로 도커 클라이언트를 위한 유닉스 소켓과 Remote API를 위한 바인딩 주소를 동시에 설정합니다.
dockerd -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375
도커 클라이언트가 도커 데몬에게 명령어를 수행하도록 요청할 때도 내부적으로는 같은 API를 사용하므로 Remote API 또한 도커 클라이언트에서 사용 가능한 모든 명령어를 사용할 수 있습니다. -H로 Remote API를 사용하려면 cURL과 같은 HTTP 요청 도구를 사용합니다.
예를 들어, IP주소가 192.168.99.100인 도커 호스트에서 -H로 Remote API를 허용했다면, 다른 호스트에서는 다음과 같이 Remote API를 사용할 수 있습니다.
curl 192.168.99.100:2375/version --silent | python -m json.tool
(PC 1대로 PC의 IP 주소-localhost를 이용하여 테스트해볼 수 있습니다.)
Remote API의 종류는 도커 명령어의 개수만큼 있으며, API에 따라서는 사용하는 방법이 도커 명령어와 조금씩 다른 부분이 있으므로 HTTP 도구로 직접 API 요청을 전송하기보다는 특정 언어로 바인딩된 라이브러리를 사용하는 것이 일반적입니다.
HTTP 요청에 대한 자세한 내용은 링크(공식문서)를 참조하시기 바랍니다.
shell의 환경변수를 설정해 원격에 있는 도커를 제어할 수도 있습니다. 도커 클라이언트는 shell의 DOCKER_HOST 변수가 설정되어 있다면, 해당 도커 데몬에 API 요청을 전달합니다.
1. export DOCKER_HOST="tcp://localhost:2375"
2. docker verion
또는 -H 옵션을 설정하여 제어할 원격 도커 데몬을 설정할 수도 있습니다.
docker -H tcp://localhost:2375 version
도커를 설치하면 기본적으로 보안 연결이 설정되어 있지 않습니다.
이는 도커 클라이언트, Remote API를 사용할 때 별도의 보안이 적용되지 않음을 의미합니다. 그러나 실제 운영 환경에서 도커를 사용해야 한다면 보안을 적용하지 않는 것은 바람직하지 않으며, 보안이 적용되어 있지 않다면 Remote API를 위해 바인딩된 IP주소와 포트 번호만 알면 도커를 제어할 수 있기 때문입니다.
이번에는 도커 데몬에 TLS 보안을 적용하고, 도커 클라이언트와 Remote API 클라이언트가 인증되지 않으면 도커 데몬을 제어할 수 없도록 설정하는 방법을 설명하도록 하겠습니다.
보안을 적용할 때 사용되는 파일은 총 5개로서, ca.pem, server-cert.pem, server-key.pem, cert.pem, key.pem 입니다. 클라이언트 측에서는 그림과 같이 ca.pem, cert.pem, key.pem 이 필요합니다.
먼저 서버 측에서 필요한 파일을 생성하겠습니다.
1. mkdir keys && cd keys
2. openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
echo subjectAltName = IP:$HOST,IP:127.0.0.1 > extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
이어서 클라이언트 측 파일을 생성합니다.
1. openssl genrsa -out key.pem 4096
2. openssl req -subj '/CN=client' -new -key key.pem -out client.csr
3. echo extendedKeyUsage = clientAuth > extfile.cnf
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf
위 파일에서 ca.pem / cert.pem / key.pem / server-cert.pem / server-key.pem 만 사용됩니다.
1. chmod -v 0400 key.pem server-key.pem
2. chmod -v 0444 ca.pem server-cert.pem cert.pem
여기까지 따라왔으면 보안 적용을 위한 파일은 모두 생성되었습니다. 이제 암호화가 적용된 도커 데몬을 실행하도록 하겠습니다. TLS 보안 적용을 활성화하기 위해서 --tlsverify 옵션을 추가하고, --tlscacert, --tlscert, --tlskey에는 각각 보안을 적용하는데 필요한 파일의 위치를 입력합니다.
1. sudo dockerd --tlsverify \
2. --tlscacert=ca.pem \
3. --tlscert=server-cert.pem \
4. --tlskey=server-key.pem \
5. -H 0.0.0.0:2376 \
6. -H unix:///var/run/docker.sock
이제 클라이언트에서 사용해보아야 하는데, PC가 한대이기 때문에 그냥 키를 만든 디렉토리에서 아래 명령어를 테스트해보겠습니다.
우선 TLS 연결 설정을 하지 않고 테스트해보았습니다.
TLS 연결 설정을 하지 않았다는 에러가 출력되어야 하는데, HTTP로 요청했다고 에러가 발생했습니다... 도커 데몬을 실행할 때 --insecure-registry 옵션을 주었지만, 에러는 동일했습니다...
(컴퓨터 1대로 테스트를 해서 그런진 모르겠지만... 원인을 알 수가 없네요 ㅠㅠ)
아래는 TLS 연결 설정을 하고 테스트해보았습니다.
1. docker -H ${HOST_IP}:2376 \
2. --tlscacert=ca.pem \
3. --tlscert=cert.pem \
4. --tlskey=key.pem \
5. --tlsverify version
이 경우에는 인증이 정상적으로 이루어졌고, 도커 클라이언트로 원격 제어가 정상적으로 수행된 것을 알 수 있습니다.
그러나 이렇게 매번 도커 명령어를 입력할 때마다 위와 같이 인증 관련 옵션을 입력하는 것은 귀찮습니다. 이 또한 shell의 DOCKER_HOST 환경 변수와 마찬가지로 인증 관련 환경변수를 설정해 매번 파일 위치를 입력하지 않도록 설정할 수 있습니다.
DOCKER_CERT_PATH는 도커 데몬 인증에 필요한 파일의 위치를, DOCKER_TLS_VERIFY는 TLS 인증을 사용할지를 설정합니다.
curl로 보안이 적용된 도커 데몬의 Remote API를 사용하려면 다음과 같은 플래그를 추가하여 사용할 수 있습니다.
1. curl https://{host ip:port}/version \
2. --cert cert.pem \
3. --key key.pem \
4. --cacert ca.pem
도커는 특정 스토리지 벡엔드 기술을 사용하여 도커 컨테이너와 이미지를 저장하고 관리합니다.
일부 OS는 도커를 설치할 때 기본적으로 사용되도록 설정된 스토리지 드라이버가 있는데, 우분투와 같은 데비안 계열의 OS는 overlay2를, 구버전의 CentOS와 같은 OS는 devicemapper를 사용합니다.
이는 docker info 명령어로 확인할 수 있습니다.
도커를 사용하는 환경에 따라서 스토리지 드라이버는 자동으로 정해지지만, 도커 데몬 실행 옵션에서 스토리지 드라이버를 변경할 수도 있습니다. 이는 도커 데몬 옵션 중 --storage-driver 를 사용하여 변경할 수 있으며, 지원하는 드라이버는 OverlayFS, AUFS, Btrfs, Devicemapper, VFS, ZFS 등이 있습니다.
이 가운데 하나만 선택해 도커 데몬에 적용할 수 있으며, 적용된 스토리지 드라이버에 따라 컨테이너와 이미지가 별도로 생성됩니다.
예를 들면, 도커가 AUFS를 기본적으로 사용하도록 설정된 우분투 OS에서 Devicemapper를 사용하도록 변경하게 되면, 기존에 AUFS에서 사용했던 이미지와 컨테이너는 더 이상 사용할 수 없게 됩니다.
별도로 생성된 Devicemapper 파일은 /var/lib/docker/devicemapper 디렉토리에 저장되며, AUFS 파일은 /var/lib/docker/aufs 디렉토리에 저장되어 따로 관리됩니다.
어떤 스토리지 드라이버를 사용할지는 개발하는 컨테이너 어플리케이션 및 개발 환경에 따라서 다릅니다. RedHat 계열의 OS를 사용하고 있다면 OverlayFS를 선택하는 것이 좋을 수 있고, 안정성을 우선시하는 컨테이너 어플리케이션을 개발하고 있다면 Btrfs가 좋은 선택이 될 수도 있습니다. 무조건 좋은 스토리지 드라이버라는 것은 없기 때문에 상황에 따라 각 드라이버의 장단점을 감안하여 선택하는 것이 바람직합니다.
스토리지 선택 가이드는 공식 문서에도 잘 나와있으니 관심있으시면 참조바랍니다.
도커 데몬이 사용하는 컨테이너와 이미지가 저장되는 디렉토리를 별도로 지정하지 않았다면, 드라이버별로 사용>되는 컨테이너와 이미지는 /var/lib/docker/{드라이버 이름}에 저장됩니다. 컨테이너와 이미지 파일들이 >저장될 디렉토리를 임의로 지정하려면 도커 데몬 옵션에 --data-root 옵션을 사용하면 됩니다. 빈 디>렉토리를 설정하게 되면 도커 엔진이 초기화된 상태로 도커 데몬이 실행됩니다.
단, 여러 개의 디바이스 드라이버의 디렉토리가 하나의 디렉토리 안에 존재할 경우, --storage- driver로 사용할 드라이버를 명시하지 않는다면 도커 데몬이 어느 드라이버를 사용할지 찾지 못하는 상황이 발생하여 도커 데몬이 시작하지 않습니다. 따라서 테스트 용도로 여러 개의 스토리지 드라이버를 번갈아 사용하고 있다면 사용할 스토리지 드라이버를 명시하는 것이 좋습니다.
컨테이너 내부에서 사용되는 파일시스템의 크기는 도커가 사용하고 있는 스토리지 드라이버에 따라 조금씩 다릅니다.
예를 들어, 도커 엔진이 AUFS나 overlay2 등의 스토리지 드라이버를 사용하도록 설정되어 있다면 컨테이너는 호스트의 저장 공간의 크기를 공유합니다. 따라서 overlay2를 기본적으로 사용하는 우분투의 도커에서는 컨테이너 내부에서의 저장 공간이 호스트의 저장 공간 크기와 같습니다.
이번에 예시로 overlay2에서 컨테이너의 저장 공간을 설정하는 방법에 대해서 알아보겠습니다.
컨테이너의 저장 공간을 설정하기 위해서는 도커 데이터가 저장되어 있는 디스크가 xfs 파일시스템인 경우에 가능하며 project quota라는 기능을 이용해서 컨테이너의 저장 공간을 제한할 수 있습니다.
이 기능을 사용하기 위해 대략적인 준비과정은 다음과 같습니다.
호스트 서버에 새로운 디스크를 추가
해당 디스크를 xfs 파일시스템으로 포맷하고, 호스트 서버에 마운트
도커 엔진이 데이터를 저장하는 경로를 xfs 파일시스템의 디렉토리로 변경
사용할 디스크의 이름은 fdisk -l 명령어로 확인할 수 있습니다.
디스크가 준비(여기서는 /dev/xvdf를 사용)되었으면, 사용할 디스크를 xfs 파일 시스템으로 포맷합니다.
mkfs.xfs /dev/xvdf
해당 디스크를 마운트하기 위한 디렉토리를 생성하고 디스크를 마운트합니다.
1. mkdir /mnt/xfs
2. mount /dev/xvdf /mnt/xfs -o rm,pquota
마운트가 완료되면 '--storage-driver=overlay2 --data-root=/mnt/xfs' 옵션을 추가해서 도커 데몬의 설정을 적용합니다.
설정이 완료되면 컨테이너를 생성할 때, '--storage-opt' 옵션을 통해서 저장 공간을 제한할 수 있습니다.
docker run -it --storage-opt size=1G centos:7