diff --git a/apps/efka/src/docker/docker_uds_commands.erl b/apps/efka/src/docker/docker_uds_commands.erl index 4631801..a45f33a 100644 --- a/apps/efka/src/docker/docker_uds_commands.erl +++ b/apps/efka/src/docker/docker_uds_commands.erl @@ -12,12 +12,92 @@ %% API -export([pull_image/2, check_image_exist/1]). -export([create_container/3, check_container_exist/1, is_container_running/1, start_container/1, stop_container/1]). --export([test/0]). +-export([test/0, test_create_container/0]). test() -> Image = <<"docker.1ms.run/library/nginx:latest">>, pull_image(Image, fun(Msg) -> lager:debug("msg is: ~p", [Msg]) end). +test_create_container() -> + M = #{ + <<"image">> => <<"docker.1ms.run/library/nginx:latest">>, + <<"container_name">> => <<"my_nginx_new">>, + <<"command">> => [ + <<"nginx">>, + <<"-g">>, + <<"daemon off;">> + ], + <<"entrypoint">> => [ + <<"/docker-entrypoint.sh">> + ], + <<"envs">> => [ + <<"ENV1=val1">>, + <<"ENV2=val2">> + ], + <<"env_file">> => [ + <<"./env.list">> + ], + <<"ports">> => [ + <<"8080:80">>, + <<"443:443">> + ], + <<"expose">> => [ + <<"80">>, + <<"443">> + ], + <<"volumes">> => [ + <<"/host/data:/data">>, + <<"/host/log:/var/log">> + ], + <<"networks">> => [ + <<"mynet">> + ], + <<"labels">> => #{ + <<"role">> => <<"web">>, + <<"env">> => <<"prod">> + }, + <<"restart">> => <<"always">>, + <<"user">> => <<"www-data">>, + <<"working_dir">> => <<"/app">>, + <<"hostname">> => <<"myhost">>, + <<"privileged">> => true, + <<"cap_add">> => [ + <<"NET_ADMIN">> + ], + <<"cap_drop">> => [ + <<"MKNOD">> + ], + <<"devices">> => [ + <<"/dev/snd:/dev/snd">> + ], + <<"mem_limit">> => <<"512m">>, + <<"mem_reservation">> => <<"256m">>, + <<"cpu_shares">> => 512, + <<"cpus">> => 1.5, + <<"ulimits">> => #{ + <<"nofile">> => <<"1024:2048">> + }, + <<"sysctls">> => #{ + <<"net.ipv4.ip_forward">> => <<"1">> + }, + <<"tmpfs">> => [ + <<"/tmp">> + ], + <<"extra_hosts">> => [ + <<"host1:192.168.0.1">> + ], + <<"healthcheck">> => #{ + <<"test">> => [ + <<"CMD-SHELL">>, + <<"curl -f http://localhost || exit 1">> + ], + <<"interval">> => <<"30s">>, + <<"timeout">> => <<"10s">>, + <<"retries">> => 3 + } + }, + create_container(<<"my_nginx_xx">>, "/usr/local/code/efka/", M). + -spec pull_image(Image :: binary(), Callback :: fun((Msg :: binary()) -> no_return())) -> ok | {error, ExitCode :: integer()}. pull_image(Image, Callback) when is_binary(Image), is_function(Callback, 1) -> Url = io_lib:format("/images/create?fromImage=~s", [binary_to_list(Image)]), @@ -36,35 +116,23 @@ check_image_exist(Image) when is_binary(Image) -> -spec create_container(ContainerName :: binary(), ContainerDir :: string(), Config :: map()) -> {ok, ContainerId :: binary()} | {error, Reason :: any()}. create_container(ContainerName, ContainerDir, Config) when is_binary(ContainerName), is_list(ContainerDir), is_map(Config) -> - Url = "/containers/create", + Url = io_lib:format("/containers/create?name=~s", [binary_to_list(ContainerName)]), Image = maps:get(<<"image">>, Config), - Cmd = maps:get(<<"command">>, Config, []), %% 挂载预留的目录,用来作为配置文件的存放 BinContainerDir = list_to_binary(docker_container_helper:make_etc_dir_name(ContainerDir)), BaseOptions = [<<"-v">>, <>], - Options = build_options(Config), - Args = lists:flatten([Image | BaseOptions ++ Options ++ Cmd]), - CreateArgs = iolist_to_binary(lists:join(<<" ">>, Args)), - PortSettings = [stream, exit_status, stderr_to_stdout, use_stdio, binary], - ExecCmd = "docker create --name " ++ binary_to_list(ContainerName) ++ " " ++ binary_to_list(CreateArgs), - lager:debug("create_container cmd : ~p", [ExecCmd]), + lists:foreach(fun({K, V}) -> + lager:debug("~p => ~p", [K, V]) + end, maps:to_list(Options)), - case catch erlang:open_port({spawn, ExecCmd}, PortSettings) of - Port when is_port(Port) -> - case gather_output(Port) of - {0, ContainerId} -> - {ok, string:trim(ContainerId)}; - {ExitCode, Error} -> - {error, {ExitCode, Error}} - end; - Error -> - lager:debug("error: ~p", [Error]), - {error, <<"exec command startup failed">>} - end. + + Body = iolist_to_binary(jiffy:encode(Options, [force_utf8])), + Res = docker_http:request("POST", Url, Body, []), + lager:debug("res is: ~p", [Res]). -spec is_container_running(ContainerId :: binary()) -> boolean(). is_container_running(ContainerId) when is_binary(ContainerId) -> @@ -154,26 +222,28 @@ build_options(Config) when is_map(Config) -> <<"Image">> => maps:get(<<"image">>, Config, <<>>), <<"Cmd">> => maps:get(<<"command">>, Config, []), <<"Entrypoint">> => maps:get(<<"entrypoint">>, Config, []), - <<"Env">> => maps:get(<<"envs">>, Config, []) + <<"Env">> => maps:get(<<"envs">>, Config, []), + <<"HostConfig">> => fold_merge([ + build_volumes(Config), + build_restart(Config), + build_privileged(Config), + build_cap_add_drop(Config), + build_devices(Config), + build_memory(Config), + build_cpu(Config), + build_ulimits(Config), + build_tmpfs(Config), + build_extra_hosts(Config) + ]) }, fold_merge([ build_expose(Config), build_volumes(Config), build_networks(Config), build_labels(Config), - build_restart(Config), build_user(Config), build_working_dir(Config), build_hostname(Config), - build_privileged(Config), - build_cap_add_drop(Config), - build_devices(Config), - build_memory(Config), - build_cpu(Config), - build_ulimits(Config), - build_sysctls(Config), - build_tmpfs(Config), - build_extra_hosts(Config), build_healthcheck(Config) ]) ). @@ -226,13 +296,6 @@ build_labels(Config) -> _ -> #{<<"Labels">> => Labels} end. -build_restart(Config) -> - case maps:get(<<"restart">>, Config, undefined) of - undefined -> - #{}; - Policy -> - #{<<"HostConfig">> => #{<<"RestartPolicy">> => #{<<"Name">> => Policy}}} - end. build_user(Config) -> case maps:get(<<"user">>, Config, undefined) of @@ -258,58 +321,6 @@ build_hostname(Config) -> #{<<"Hostname">> => H} end. -build_privileged(Config) -> - case maps:get(<<"privileged">>, Config, false) of - true -> - #{<<"HostConfig">> => #{<<"Privileged">> => true}}; - _ -> - #{} - end. - -build_cap_add_drop(Config) -> - Add = maps:get(<<"cap_add">>, Config, []), - Drop = maps:get(<<"cap_drop">>, Config, []), - case {Add, Drop} of - {[], []} -> - #{}; - _ -> - #{<<"HostConfig">> => #{<<"CapAdd">> => Add, <<"CapDrop">> => Drop}} - end. - -build_devices(Config) -> - Devs = maps:get(<<"devices">>, Config, []), - case Devs of - [] -> - #{}; - _ -> - DevObjs = [#{<<"PathOnHost">> => H, <<"PathInContainer">> => C, - <<"CgroupPermissions">> => <<"rwm">>} - || D <- Devs, - [H, C] <- [binary:split(D, <<":">>, [])]], - #{<<"HostConfig">> => #{<<"Devices">> => DevObjs}} - end. - -build_memory(Config) -> - Mem = maps:get(<<"mem_limit">>, Config, undefined), - MemRes = maps:get(<<"mem_reservation">>, Config, undefined), - HCfg = #{}, - HCfg1 = if - Mem /= undefined -> - maps:put(<<"Memory">>, parse_mem(Mem), HCfg); - true -> - HCfg - end, - HCfg2 = if - MemRes /= undefined -> - maps:put(<<"MemoryReservation">>, parse_mem(MemRes), HCfg1); - true -> - HCfg1 - end, - case maps:size(HCfg2) of - 0 -> #{}; - _ -> #{<<"HostConfig">> => HCfg2} - end. - parse_mem(Val) -> case binary:last(Val) of $m -> @@ -322,67 +333,7 @@ parse_mem(Val) -> list_to_integer(binary_to_list(Val)) end. -build_cpu(Config) -> - CPU = maps:get(<<"cpus">>, Config, undefined), - Shares = maps:get(<<"cpu_shares">>, Config, undefined), - HCfg = #{}, - HCfg1 = if - CPU /= undefined -> - maps:put(<<"NanoCpus">>, trunc(CPU * 1000000000), HCfg); - true -> - HCfg - end, - HCfg2 = if - Shares /= undefined -> - maps:put(<<"CpuShares">>, Shares, HCfg1); - true -> - HCfg1 - end, - case maps:size(HCfg2) of - 0 -> #{}; - _ -> #{<<"HostConfig">> => HCfg2} - end. -build_ulimits(Config) -> - UL = maps:get(<<"ulimits">>, Config, #{}), - case maps:size(UL) of - 0 -> #{}; - _ -> - ULList = [#{<<"Name">> => K, <<"Soft">> => S, <<"Hard">> => H} - || {K, V} <- maps:to_list(UL), - [S1, H1] <- [binary:split(V, <<":">>, [])], - S = list_to_integer(binary_to_list(S1)), - H = list_to_integer(binary_to_list(H1))], - - #{<<"HostConfig">> => #{<<"Ulimits">> => ULList}} - end. - -build_sysctls(Config) -> - SC = maps:get(<<"sysctls">>, Config, #{}), - case maps:size(SC) of - 0 -> - #{}; - _ -> - #{<<"HostConfig">> => #{<<"Sysctls">> => SC}} - end. - -build_tmpfs(Config) -> - Tmp = maps:get(<<"tmpfs">>, Config, []), - case Tmp of - [] -> - #{}; - _ -> - #{<<"HostConfig">> => #{<<"Tmpfs">> => maps:from_list([{T, <<>>} || T <- Tmp])}} - end. - -build_extra_hosts(Config) -> - Hosts = maps:get(<<"extra_hosts">>, Config, []), - case Hosts of - [] -> - #{}; - _ -> - #{<<"HostConfig">> => #{<<"ExtraHosts">> => Hosts}} - end. build_healthcheck(Config) -> HC = maps:get(<<"healthcheck">>, Config, #{}), @@ -410,4 +361,140 @@ parse_duration(Bin) -> N * 60000000000; _ -> N + end. + +%% --- 构建子字段 --- +build_volumes(Config) -> + Vols = maps:get(<<"volumes">>, Config, []), + case Vols of + [] -> #{}; + _ -> + HostBinds = Vols, + VolMap = maps:from_list(lists:map(fun(V) -> + [_Host, Cont] = binary:split(V, <<":">>, []), + {Cont, #{}} + end, Vols)), + #{ + <<"HostConfig">> => #{ + <<"Binds">> => HostBinds + }, + <<"Volumes">> => VolMap + } + end. + +build_restart(Config) -> + case maps:get(<<"restart">>, Config, undefined) of + undefined -> + #{}; + Policy -> + #{<<"RestartPolicy">> => #{<<"Name">> => Policy}} + end. + +build_privileged(Config) -> + case maps:get(<<"privileged">>, Config, false) of + true -> + #{<<"Privileged">> => true}; + _ -> + #{} + end. + +build_cap_add_drop(Config) -> + Add = maps:get(<<"cap_add">>, Config, []), + Drop = maps:get(<<"cap_drop">>, Config, []), + case {Add, Drop} of + {[], []} -> + #{}; + _ -> + #{<<"CapAdd">> => Add, <<"CapDrop">> => Drop} + end. + +build_devices(Config) -> + Devs = maps:get(<<"devices">>, Config, []), + case Devs of + [] -> + #{}; + _ -> + DevObjs = [#{<<"PathOnHost">> => H, <<"PathInContainer">> => C, + <<"CgroupPermissions">> => <<"rwm">>} + || D <- Devs, + [H, C] <- [binary:split(D, <<":">>, [])]], + #{<<"Devices">> => DevObjs} + end. + +build_memory(Config) -> + Mem = maps:get(<<"mem_limit">>, Config, undefined), + MemRes = maps:get(<<"mem_reservation">>, Config, undefined), + HCfg = #{}, + HCfg1 = if + Mem /= undefined -> + maps:put(<<"Memory">>, parse_mem(Mem), HCfg); + true -> + HCfg + end, + if + MemRes /= undefined -> + maps:put(<<"MemoryReservation">>, parse_mem(MemRes), HCfg1); + true -> + HCfg1 + end. + + +build_cpu(Config) -> + CPU = maps:get(<<"cpus">>, Config, undefined), + Shares = maps:get(<<"cpu_shares">>, Config, undefined), + HCfg = #{}, + HCfg1 = if + CPU /= undefined -> + maps:put(<<"NanoCpus">>, trunc(CPU * 1000000000), HCfg); + true -> + HCfg + end, + if + Shares /= undefined -> + maps:put(<<"CpuShares">>, Shares, HCfg1); + true -> + HCfg1 + end. + +build_ulimits(Config) -> + UL = maps:get(<<"ulimits">>, Config, #{}), + case maps:size(UL) of + 0 -> + #{}; + _ -> + ULList = lists:map(fun({K, V}) -> + [S1, H1] = binary:split(V, <<":">>, []), + S = list_to_integer(binary_to_list(S1)), + H = list_to_integer(binary_to_list(H1)), + #{<<"Name">> => K, <<"Soft">> => S, <<"Hard">> => H} + end, maps:to_list(UL)), + + #{<<"Ulimits">> => ULList} + end. + +build_sysctls(Config) -> + SC = maps:get(<<"sysctls">>, Config, #{}), + case maps:size(SC) of + 0 -> + #{}; + _ -> + #{<<"Sysctls">> => SC} + end. + +build_tmpfs(Config) -> + Tmp = maps:get(<<"tmpfs">>, Config, []), + case Tmp of + [] -> + #{}; + _ -> + #{<<"Tmpfs">> => maps:from_list([{T, <<>>} || T <- Tmp])} + end. + +build_extra_hosts(Config) -> + Hosts = maps:get(<<"extra_hosts">>, Config, []), + case Hosts of + [] -> + #{}; + _ -> + #{<<"ExtraHosts">> => Hosts} end. \ No newline at end of file