From e1501c69d266d378047d3048ba5e898f0f9e667b Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Sun, 10 May 2026 23:43:01 +0800 Subject: [PATCH] fix ports --- docs/container_command_docker_json.md | 55 ++++++++++++++++++++++++- src/docker/docker_container_builder.erl | 40 ++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/docs/container_command_docker_json.md b/docs/container_command_docker_json.md index 6dc08cb..ae97cd6 100644 --- a/docs/container_command_docker_json.md +++ b/docs/container_command_docker_json.md @@ -415,6 +415,7 @@ Body = iolist_to_binary(json:encode(Options)) | `memory_reservation` | `HostConfig.MemoryReservation` | 0 时省略。 | | `nano_cpus` | `HostConfig.NanoCpus` | 0 时省略。 | | `cpu_shares` | `HostConfig.CpuShares` | 0 时省略。 | +| `port_bindings` | `HostConfig.PortBindings` | 转成 Docker 端口绑定 object;空列表时省略。 | | `ulimits` | `HostConfig.Ulimits` | 空列表时省略。 | | `tmpfs` | `HostConfig.Tmpfs` | 空 map 时省略。 | | `sysctls` | `HostConfig.Sysctls` | 空 map 时省略。 | @@ -516,6 +517,40 @@ efka 自动补丁后输出: `cgroup_permissions` 为空时默认输出 `rwm`。 +### PortBindings + +输入: + +```erlang +[ + #{<<"host_ip">> => <<>>, <<"host_port">> => 8080, <<"container_port">> => 80, <<"protocol">> => <<"tcp">>}, + #{<<"host_ip">> => <<>>, <<"host_port">> => 443, <<"container_port">> => 443, <<"protocol">> => <<"tcp">>} +] +``` + +输出: + +```json +{ + "HostConfig": { + "PortBindings": { + "80/tcp": [ + {"HostIp": "", "HostPort": "8080"} + ], + "443/tcp": [ + {"HostIp": "", "HostPort": "443"} + ] + } + } +} +``` + +规则: + +- key 使用容器端口和协议组成:`container_port/protocol`。 +- `protocol` 为空或 `tcp` 时输出 `tcp`。 +- `HostPort` 按 Docker API 要求输出为字符串。 + ### 资源限制 输入: @@ -687,7 +722,15 @@ efka 自动补丁后输出: <<"host_config">> => #{ <<"binds">> => [<<"/host/data:/data">>], <<"restart_policy">> => #{<<"name">> => <<"always">>, <<"maximum_retry_count">> => 0}, - <<"memory">> => 536870912 + <<"memory">> => 536870912, + <<"port_bindings">> => [ + #{ + <<"host_ip">> => <<>>, + <<"host_port">> => 8080, + <<"container_port">> => 80, + <<"protocol">> => <<"tcp">> + } + ] }, <<"networking_config">> => #{ <<"endpoints">> => [#{<<"name">> => <<"bridge">>}] @@ -730,7 +773,15 @@ efka 自动补丁后输出: "RestartPolicy": { "Name": "always" }, - "Memory": 536870912 + "Memory": 536870912, + "PortBindings": { + "80/tcp": [ + { + "HostIp": "", + "HostPort": "8080" + } + ] + } } } ``` diff --git a/src/docker/docker_container_builder.erl b/src/docker/docker_container_builder.erl index 9b31455..f14e089 100644 --- a/src/docker/docker_container_builder.erl +++ b/src/docker/docker_container_builder.erl @@ -91,6 +91,7 @@ build_create_options(Create0) when is_map(Create0) -> field(HostConfig, <<"nano_cpus">>, 0), field(HostConfig, <<"cpu_shares">>, 0) ), + build_port_bindings(field(HostConfig, <<"port_bindings">>, [])), build_ulimits(field(HostConfig, <<"ulimits">>, [])), build_tmpfs(field(HostConfig, <<"tmpfs">>, #{})), build_sysctls(field(HostConfig, <<"sysctls">>, #{})), @@ -245,6 +246,45 @@ build_resources(MemoryBytes, ReservationBytes, NanoCpus, CpuShares) -> HostConfig3#{<<"CpuShares">> => CpuShares} end. +-spec build_port_bindings([map()]) -> map(). +build_port_bindings(PortBindings) when is_list(PortBindings) -> + case PortBindings of + [] -> + #{}; + _ -> + #{<<"PortBindings">> => fold_port_bindings(PortBindings)} + end. + +-spec fold_port_bindings([map()]) -> map(). +fold_port_bindings(PortBindings) -> + lists:foldl( + fun(PortBinding, Acc) -> + PortKey = port_binding_key(PortBinding), + Binding = port_binding_value(PortBinding), + Existing = maps:get(PortKey, Acc, []), + Acc#{PortKey => Existing ++ [Binding]} + end, + #{}, + PortBindings). + +-spec port_binding_key(map()) -> binary(). +port_binding_key(#{<<"container_port">> := ContainerPort, <<"protocol">> := Protocol}) -> + PortBin = integer_to_binary(ContainerPort), + ProtocolBin = to_binary(Protocol), + case ProtocolBin of + <<>> -> + <>; + <<"tcp">> -> + <>; + _ -> + <> + end. + +-spec port_binding_value(map()) -> map(). +port_binding_value(#{<<"host_port">> := HostPort} = PortBinding) -> + HostIp = to_binary(field(PortBinding, <<"host_ip">>, <<>>)), + #{<<"HostIp">> => HostIp, <<"HostPort">> => integer_to_binary(HostPort)}. + -spec build_ulimits([map()]) -> map(). build_ulimits(Ulimits) when is_list(Ulimits) -> case Ulimits of