Node.jsでUnix Domain Socketでdocker.sockを叩く

CommonLispでDocker Client Libraryを作ろうとしていたのだが、Unix Domain Socketについての理解が足りないことに気がついた。

とりあえず慣れてるNodeJSで動かせるように取り組んだのでメモしていく。


curlは高機能で --unix-socket optionでsocket通信をすることができる。

そして、dockerは /var/run/docker.sock からhttpで話すことが出来るようだ。

~ (*´ω`*) < curl --unix-socket /var/run/docker.sock -H 'Content-Type: application/json' http://localhost:80/info
{"ID":"KOQ4:UHAI:KYS7:FGPH:LURU:5LGT:NMUM:BPD2:6UMV:NUXF:XNIG:BPII","Containers":23,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":23,"Images":15,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Native Overlay Diff","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","ipvlan","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","local","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":false,"KernelMemory":true,"KernelMemoryTCP":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":false,"NFd":21,"OomKillDisable":true,"NGoroutines":34,"SystemTime":"2019-09-30T12:43:34.2740042+09:00","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"5.0.0-29-generic","OperatingSystem":"Ubuntu 18.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":[],"AllowNondistributableArtifactsHostnames":[],"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":[],"Secure":true,"Official":true}},"Mirrors":[]},"NCPU":8,"MemTotal":8210616320,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"take-UX461UN","Labels":[],"ExperimentalBuild":false,"ServerVersion":"19.03.2","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"894b81a4b802e4eb2a91d1ce216b8817763c29fb","Expected":"894b81a4b802e4eb2a91d1ce216b8817763c29fb"},"RuncCommit":{"ID":"425e105d5a03fabd737a126ad93d62a9eeede87f","Expected":"425e105d5a03fabd737a126ad93d62a9eeede87f"},"InitCommit":{"ID":"fec3683","Expected":"fec3683"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":["WARNING: No swap limit support"]}

内部挙動は以下のとおりだ。

  1. socket connectで docker.sock と接続をする
  2. socketに対してhttp text messageをwriteする
  3. socket readでdocker側がwriteしたものをreadする

これをnodejsで表現するとこんな感じだ。

const net = require("net");

const client = net.createConnection('/var/run/docker.sock');

client.write("GET /info HTTP/1.1\r\nContent-Type: application/json\r\nHost: localhost\r\n\r\n");

client.on('connect', () => {
    console.log("connected");
});

client.on('data', (data) => {
    console.log(data.toString());
});

client.on('error', function(e){
  console.log('Connection Failed');
  console.error(e.message);
});

ネットワーク周りよくわからなくて苦労したが、想像以上に単純なのかも知れないと思えた良い週末だった。