如何在 Mathematica 容器中创建服务器?

How to create a server in Mathematica container?

我尝试使用 Wolfram 语言在 Docker 容器中创建一个服务器。 Docker参数和启动服务器的Wolfram脚本在附件中:

services:
  wm:
    build: .
    volumes:
      - .:/app:ro
    ports:
      - "61104:8000"
    restart: on-failure
    hostname: wm
    container_name: wm
    working_dir: /app
    entrypoint: wolframscript -file /app/tcp_server.wls
FROM ubuntu:20.04

ENV DEBIAN_FRONTEND noninteractive
ENV DEBCONF_NONINTERACTIVE_SEEN true

RUN apt update -yq \
    && apt install -yq xz-utils curl gcc tzdata musl-dev python3-dev python3-pip clang \
    && dpkg-reconfigure tzdata \
    && apt install -y avahi-daemon wget sshpass sudo locales \
    locales-all ssh nano expect libfontconfig1 libgl1-mesa-glx libasound2 \
    build-essential mosquitto mosquitto-clients libnss-mdns mdns-scan nodejs \
    && apt clean \
    && apt autoremove -y \
    && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen

COPY ./Mathematica_12.1.1_LINUX.sh /root/

WORKDIR /root

RUN ./Mathematica_12.1.1_LINUX.sh --target ./extract --nox11 -- -auto -nodesktop \
    && systemctl enable avahi-daemon \
    && echo '!10.55.229.55' > /usr/local/Wolfram/Mathematica/12.1/Configuration/Licensing/mathpass
#!/usr/bin/env wolframscript
(* ::Package:: *)

httpResponseTemplate[text_] := StringTemplate[
  "HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: `2`
Date: Wed, 26 Jun 2013 07:00:56 GMT

`1`"
][text, StringLength[text]];

listener = SocketListen[address,
    Module[{
      socket = #SourceSocket,
      message = #Data,
      func,
      query,
      result
    },
(*      Print[message];*)
      query = StringExtract[message, "\n" -> 1];
      func = StringCases[query, "GET /" ~~ f__ ~~ " HTTP/1.1" :> f, 1][[1]];
      result = ToExpression[func][];
      BinaryWrite[socket,
        httpResponseTemplate[
          "Hello, World!\nYou called the " <> func <> "\nResult is: " <>
              ToString[result] <> "\n"
        ]
      ]
    ]&
];


While[True, Pause[60]]

我发现所描述的 Wolfram 服务器有一个特点。 Wolfram 函数 SocketListen 的第一个参数不能包含来自 docker-compose 文件的主机名作为主机地址。但是,缺少 docker 主机名会导致无法从容器外部访问服务器。

主机名指示的需要可以通过在容器内创建的 netcat 服务器来证明。例如,下面的第一个变体创建了一个可访问的服务器,但其他变体创建了一个从外部无法访问的服务器。

echo -e "HTTP/1.1 200 OK\n\n $(date)" | nc -l wm 8000
echo -e "HTTP/1.1 200 OK\n\n $(date)" | nc -l localhost 8000
echo -e "HTTP/1.1 200 OK\n\n $(date)" | nc -l 127.0.0.1 8000

Wolfram 的变体,类似于第二个和第三个,给出相同的结果:无法从外部访问。 tcp_server.wls 文件包含符号 address。使用前必须将其替换为确切的字符串或整数。以下是我在下面尝试过的所有变体的 Wolfram 错误。

"http://wm:8000" -> SocketOpen::addrspec: The host specification http://wm:8000 is wrong because the port number conflicts with the scheme specification

"http://localhost:8000" -> SocketOpen::addrspec: The host specification http://localhost:8000 is wrong because the port number conflicts with the scheme specification

"http://127.0.0.1:8000" -> SocketOpen::addrspec: The host specification http://127.0.0.1:8000 is wrong because the port number conflicts with the scheme specification

"wm:8000" -> SocketOpen::zmqexception: A ZeroMQ exception was thrown - No such device (code 19)

"localhost:8000" -> SocketOpen::zmqexception: A ZeroMQ exception was thrown - No such device (code 19)

"127.0.0.1:8000" -> without errors, but inaccessible from the outside (accessible only from inside)

":8000" -> SocketOpen::addrspec: The host specification :8000 is wrong because it has an invalid domain specification

8000 -> without errors, but inaccessible from the outside (accessible only from inside)

其他端口也是如此。

我如何强制 Wolfram 收听 http://wm:8000 或如何在通过 127.0.0.1 替换实际主机名的意义上更改 docker 设置,同时通过docker网络?

在构建 Mathematica 图像之前,您需要将 Dockerfile 中的许可方法更改为您使用的方法(目前使用我的网络许可)并下载 linux 安装程序 Mathematica_12.1.1_LINUX.sh从Wolfram官网下载,并放入post.

开头三个附件的文件夹中

请注意,这是一个项目草稿。完整的将在 docker-compose.yml 中包含其他服务,例如Go服务器,也应该可以从外部访问。

朋友们帮我解决了问题

函数 SocketListen 的第一个参数应该是 "0.0.0.0:8000"。然后,从外部请求的 http://wm:61104/f 将 link 到容器。

解释了 0.0.0.0 和 127.0.0.1 之间的区别 here