1. 개요
이 예제에서는 Docker Engine API를 사용하여 컨테이너 내부에서 Docker 컨테이너 정보에 액세스하는 방법을 볼 것입니다.
2. 설정
여러 가지 방법으로 Docker 엔진에 연결할 수 있습니다. 우리는 Linux에서 가장 유용한 것들을 다룰 것이지만 다른 운영 체제에서도 작동합니다.
그러나, 우리는한다 매우 조심 , 원격 액세스를 가능하게하는 Security 위험을 나타 내기 때문이다 . 컨테이너가 엔진에 액세스할 수 있으면 호스트 운영 체제와의 격리가 해제됩니다 .
설정 부분의 경우 호스트를 완전히 제어할 수 있다고 간주합니다.
2.1. 기본 Unix 소켓 전달
기본적으로 Docker 엔진은 호스트 OS의 /var/run/docker.sock 아래 에 마운트된 Unix 소켓을 사용합니다 .
$ ss -xan | grep var
u_str LISTEN 0 4096 /var/run/docker/libnetwork/dd677ae5f81a.sock 56352 * 0
u_dgr UNCONN 0 0 /var/run/chrony/chronyd.sock 24398 * 0
u_str LISTEN 0 4096 /var/run/nscd/socket 23131 * 0
u_str LISTEN 0 4096 /var/run/docker/metrics.sock 42876 * 0
u_str LISTEN 0 4096 /var/run/docker.sock 53704 * 0
...
이 접근 방식을 사용하면 API에 액세스할 수 있는 컨테이너를 엄격하게 제어할 수 있습니다. 이것이 Docker CLI가 배후에서 작동하는 방식입니다.
알파인 Docker 컨테이너를 시작 하고 -v 플래그를 사용하여 이 경로를 마운트하겠습니다 .
$ docker run -it -v /var/run/docker.sock:/var/run/docker.sock alpine
(alpine) $
다음으로 컨테이너에 몇 가지 유틸리티를 설치해 보겠습니다.
(alpine) $ apk add curl && apk add jq
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/community/x86_64/APKINDEX.tar.gz
(1/4) Installing ca-certificates (20191127-r2)
(2/4) Installing nghttp2-libs (1.40.0-r1)
...
이제 curl 을 –unix-socket 플래그 및 Jq 와 함께 사용 하여 일부 컨테이너 데이터를 가져오고 필터링해 보겠습니다 .
(alpine) $ curl -s --unix-socket /var/run/docker.sock http://dummy/containers/json | jq '.'
[
{
"Id": "483c5d4aa0280ca35f0dbca59b5d2381ad1aa455ebe0cf0ca604900b47210490",
"Names": [
"/wizardly_chatelet"
],
"Image": "alpine",
"ImageID": "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
"Command": "/bin/sh",
"Created": 1595882408,
"Ports": [],
...
여기 에서 /containers/json 엔드포인트 에서 GET 을 실행하고 현재 실행 중인 컨테이너를 가져옵니다 . 그런 다음 jq 를 사용하여 출력을 예쁘게 만듭니다.
엔진 API에 대한 자세한 내용은 잠시 후에 다루겠습니다.
2.2. TCP 원격 액세스 활성화
TCP 소켓을 사용하여 원격 액세스를 활성화할 수도 있습니다.
systemd 와 함께 제공되는 Linux 배포의 경우 Docker 서비스 단위를 사용자 지정해야 합니다. 다른 Linux 배포판의 경우 일반적으로 /etc/docker 에 있는 daemon.json 을 사용자 지정해야 합니다 .
대부분의 단계가 유사하므로 첫 번째 유형의 설정만 다룰 것입니다.
기본 도커 설정은 다리 네트워크를 포함 . 이다 달리 명시하지 않는 한 모든 컨테이너가 연결되는 곳 .
컨테이너만 엔진 API에 액세스할 수 있도록 허용하고 싶기 때문에 먼저 컨테이너의 네트워크를 식별해 보겠습니다.
$ docker network ls
a3b64ea758e1 bridge bridge local
dfad5fbfc671 host host local
1ee855939a2a none null local
세부 정보를 살펴보겠습니다.
$ docker network inspect a3b64ea758e1
[
{
"Name": "bridge",
"Id": "a3b64ea758e1f02f4692fd5105d638c05c75d573301fd4c025f38d075ed2a158",
...
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
...
다음으로 Docker 서비스 단위가 있는 위치를 보겠습니다.
$ systemctl status docker.service
docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
...
CGroup: /system.slice/docker.service
├─6425 /usr/bin/dockerd --add-runtime oci=/usr/sbin/docker-runc
└─6452 docker-containerd --config /var/run/docker/containerd/containerd.toml --log-level warn
이제 서비스 단위 정의를 살펴보겠습니다.
$ cat /usr/lib/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.com
After=network.target lvm2-monitor.service SuSEfirewall2.service
[Service]
EnvironmentFile=/etc/sysconfig/docker
...
Type=notify
ExecStart=/usr/bin/dockerd --add-runtime oci=/usr/sbin/docker-runc $DOCKER_NETWORK_OPTIONS $DOCKER_OPTS
ExecReload=/bin/kill -s HUP $MAINPID
...
ExecStart의 어떤 명령 속성을 정의에 의해 실행되는 systemd 합니다 ( dockerd의 실행). -H 플래그를 전달하고 수신할 해당 네트워크와 포트를 지정합니다 .
이 서비스 단위를 직접 수정할 수 있지만(권장하지 않음) $DOCKER_OPTS 변수( EnvironmentFile=/etc/sysconfig/docker에 정의 됨)를 사용하겠습니다 .
$ cat /etc/sysconfig/docker
## Path : System/Management
## Description : Extra cli switches for docker daemon
## Type : string
## Default : ""
## ServiceRestart : docker
#
DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://172.17.0.1:2375"
여기에서는 브리지 네트워크의 게이트웨이 주소를 바인드 주소로 사용 합니다. 이것은 호스트 의 docker0 인터페이스에 해당합니다 .
$ ip address show dev docker0
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:6c:7d:9c:8d brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:6cff:fe7d:9c8d/64 scope link
valid_lft forever preferred_lft forever
또한 Docker CLI가 호스트에서 계속 작동하도록 로컬 Unix 소켓을 활성화합니다.
한 단계 더 해야 합니다. 컨테이너 패킷이 호스트에 도달하도록 허용해 보겠습니다 .
$ iptables -I INPUT -i docker0 -j ACCEPT
여기에서는 docker0 인터페이스를 통해 들어오는 모든 패키지를 허용하도록 Linux 방화벽을 설정합니다 .
이제 Docker 서비스를 다시 시작하겠습니다.
$ systemctl restart docker.service
$ systemctl status docker.service
docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
...
CGroup: /system.slice/docker.service
├─8110 /usr/bin/dockerd --add-runtime oci=/usr/sbin/docker-runc -H unix:///var/run/docker.sock -H tcp://172.17.0.1:2375
└─8137 docker-containerd --config /var/run/docker/containerd/containerd.toml --log-level wa
알파인 컨테이너를 다시 실행해 보겠습니다 .
(alpine) $ curl -s http://172.17.0.1:2375/containers/json | jq '.'
[
{
"Id": "45f13902b710f7a5f324a7d4ec7f9b934057da4887650dc8fb4391c1d98f051c",
"Names": [
"/unruffled_cray"
],
"Image": "alpine",
"ImageID": "sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e",
"Command": "/bin/sh",
"Created": 1596046207,
"Ports": [],
...
우리는해야 브릿지 네트워크에 연결된 모든 컨테이너 데몬 API에 액세스 할 수 있는지 알고 .
또한 TCP 연결은 암호화되지 않습니다 .
3. 도커 엔진 API
이제 원격 액세스를 설정했으므로 API를 살펴보겠습니다.
몇 가지 흥미로운 옵션만 살펴보겠지만 자세한 내용 은 전체 문서 를 항상 확인할 수 있습니다 .
컨테이너에 대한 정보를 알아 보겠습니다 .
(alpine) $ curl -s http://172.17.0.1:2375/containers/"$(hostname)"/json | jq '.'
{
"Id": "45f13902b710f7a5f324a7d4ec7f9b934057da4887650dc8fb4391c1d98f051c",
"Created": "2020-07-29T18:10:07.261589135Z",
"Path": "/bin/sh",
"Args": [],
"State": {
"Status": "running",
...
여기서 /containers/{container-id}/json URL을 사용하여 컨테이너 에 대한 세부 정보를 얻습니다.
이 경우 hostname 명령을 실행 하여 container-id 를 가져옵니다 .
다음으로 Docker 데몬에서 이벤트를 수신합니다 .
(alpine) $ curl -s http://172.17.0.1:2375/events | jq '.'
이제 다른 터미널에서 hello-world 컨테이너를 시작하겠습니다 .
$ docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
알파인 컨테이너로 돌아 가면 많은 이벤트가 발생합니다.
{
"status": "create",
"id": "abf881cbecfc0b022a3c1a6908559bb27406d0338a917fc91a77200d52a2553c",
"from": "hello-world",
"Type": "container",
"Action": "create",
...
}
{
"status": "attach",
"id": "abf881cbecfc0b022a3c1a6908559bb27406d0338a917fc91a77200d52a2553c",
"from": "hello-world",
"Type": "container",
"Action": "attach",
...
지금까지 우리는 방해가 되지 않는 일을 해왔습니다. 조금 흔들릴 시간입니다.
컨테이너를 만들고 시작합시다. 먼저 매니페스트를 정의합니다.
(alpine) $ cat > create.json << EOF
{
"Image": "hello-world",
"Cmd": ["/hello"]
}
EOF
이제 매니페스트를 사용하여 /containers/create 엔드포인트를 호출해 보겠습니다 .
(alpine) $ curl -X POST -H "Content-Type: application/json" -d @create.json http://172.17.0.1:2375/containers/create
{"Id":"f96a6360ad8e36271cc75a3cff05348761569cf2f089bbb30d826bd1e2d52f59","Warnings":[]}
그런 다음 id를 사용하여 컨테이너를 시작합니다.
(alpine) $ curl -X POST http://172.17.0.1:2375/containers/f96a6360ad8e36271cc75a3cff05348761569cf2f089bbb30d826bd1e2d52f59/start
마지막으로 로그를 탐색할 수 있습니다.
(alpine) $ curl http://172.17.0.1:2375/containers/f96a6360ad8e36271cc75a3cff05348761569cf2f089bbb30d826bd1e2d52f59/logs?stdout=true --output -
Hello from Docker!
KThis message shows that your installation appears to be working correctly.
;To generate this message, Docker took the following steps:
3 1. The Docker client contacted the Docker daemon.
...
각 줄의 시작 부분에 이상한 문자가 나타납니다. 이것은 로그가 전송되는 스트림이 stderr 과 stdout 을 구별하기 위해 다중화 되기 때문에 발생합니다 .
결과적으로 출력은 추가 처리가 필요합니다.
컨테이너를 생성할 때 TTY 옵션을 활성화하면 이를 방지할 수 있습니다 .
(alpine) $ cat create.json
{
"Tty":true,
"Image": "hello-world",
"Cmd": ["/hello"]
}
4. 결론
이 예제에서는 Docker Engine Remote API를 사용하는 방법을 배웠습니다.
UNIX 소켓 또는 TCP에서 원격 액세스를 설정하는 것으로 시작하여 원격 API를 사용하는 방법을 더 자세히 보여주었습니다.