4

我想使用 /var/lib/docker.sock unix 域套接字访问 docker API。我已经看到可以使用(现代版本的)curl 调用 API 的示例,如下所示:

curl --unix-socket /var/run/docker.sock http:/containers/json

其中 REST 命令在 /containers/json 路径中表示。我很高兴看到 Alpakka Unix 域套接字适配器,但您似乎只能发送和接收原始字节。有什么优雅的方法可以做到这一点吗?还是我必须手动构建 HTTP 标头并手动管理所有困难的东西?

4

3 回答 3

4

这是一个工作片段(另请参阅akka/akka-http#2139上的其余讨论):

构建.sbt:

val scalaV = "2.12.6"
val akkaV = "2.5.14"
val akkaHttpV = "10.1.3"

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-http" % akkaHttpV,
  "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpV,
  "com.typesafe.akka" %% "akka-stream" % akkaV,
  "com.lightbend.akka" %% "akka-stream-alpakka-unix-domain-socket" % "0.20",
)

DockerSockMain.scala:

import java.io.File
import java.net.InetSocketAddress

import akka.actor.ActorSystem
import akka.http.scaladsl.ClientTransport
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.model.HttpRequest
import akka.http.scaladsl.model.HttpResponse
import akka.http.scaladsl.settings.ClientConnectionSettings
import akka.http.scaladsl.settings.ConnectionPoolSettings
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.ActorMaterializer
import akka.stream.alpakka.unixdomainsocket.scaladsl.UnixDomainSocket
import akka.stream.scaladsl.Flow
import akka.util.ByteString
import spray.json.JsValue

import scala.concurrent.Future

object DockerSockMain extends App {
  object DockerSockTransport extends ClientTransport {
    override def connectTo(host: String, port: Int, settings: ClientConnectionSettings)(implicit system: ActorSystem): Flow[ByteString, ByteString, Future[Http.OutgoingConnection]] = {
      // ignore everything for now

      UnixDomainSocket().outgoingConnection(new File("/var/run/docker.sock"))
        .mapMaterializedValue { _ =>
          // Seems that the UnixDomainSocket.OutgoingConnection is never completed? It works anyway if we just assume it is completed
          // instantly
          Future.successful(Http.OutgoingConnection(InetSocketAddress.createUnresolved(host, port), InetSocketAddress.createUnresolved(host, port)))
        }
    }
  }

  implicit val system = ActorSystem()
  implicit val mat = ActorMaterializer()
  import system.dispatcher

  val settings = ConnectionPoolSettings(system).withTransport(DockerSockTransport)

  import SprayJsonSupport._
  def handleResponse(response: HttpResponse): Future[String] =
    // TODO: create docker json model classes and directly marshal to them
    Unmarshal(response).to[JsValue].map(_.prettyPrint)

  Http().singleRequest(HttpRequest(uri = "http://localhost/images/json"), settings = settings)
    .flatMap(handleResponse)
    .onComplete { res =>
      println(s"Got result: [$res]")
      system.terminate()
    }
}
于 2018-08-16T13:03:32.840 回答
1

有趣的用例。您应该能够使用 Alpakka Unix Domain 套接字流并将 Akka Http ClientLayer放在它上面。

于 2018-07-28T16:24:03.203 回答
1

对这个问题的简短回答是“它无法完成”——至少对于 Akka HTTP 和 Alkappa Unix 域套接字的现有构建块来说不是。您必须通过手动发送标头来处理编写 HTTP GET 请求,即(以 Docker API 为例)

GET /v1.24/containers/json HTTP/1.1\n
Host: localhost\n
\n\n

...然后手动读取 TCP 响应。此外,Unix Domain Socket 逻辑不能使用 Alpakka 代码,因为它目前只提供一个 ServerBinding,因此旨在创建一个处理对 Unix 套接字的请求的服务器,而不是向 Unix 套接字发送数据并处理响应.

所以一切都必须手动完成。这里还有另一个StackOverflow 问题,它指出了如何使用 AFUNIXSocket github 源代码来帮助处理一些低级 Unix Domain Socket 逻辑,这些逻辑可能对其他想要解决同样问题的人有所帮助。

最优雅的解决方案还包括(如 dvim 的评论所建议的那样)编写一个 HTTP.ClientTransport 以插入 Unix 域套接字通信层并允许 HTTP 库公开编写请求/响应标头等的低级功能。(一个有趣的注意事项是 API 假定了一个主机/端口参数对,它与 TCP 范例紧密绑定。)

于 2018-08-07T17:11:44.613 回答