/dockman

Docker manager tool written in D (D Language) for making easier the use of toolchains and development tools from Docker containers.

Primary LanguageD

Docker Front-End Tool

dockman - Docker Front-End Tool

Introduction

This tool simplifies using of docker containers for testing and exploring programming languages toolchain; compilers and servers encapsulated in containers. Dockman is wrapper around ‘docker’ client with convenient command line switches and default converntions which makes easier to use development tools from docker image.

By using containers, the user can access a full-featured toolchain for a given programming language without any installation effort or running the risk of currently installed toolchain. For instance, it is far easier and faster to run a new GCC (GNU C) compiler version from a container than installing it on the current machine risking breaking the current GCC installation.

Building Instructions

Dockman is written in D programming language and has the following compilation options:

Building

  • Option 1:
    • Compilation with system-installed DMD (D-Language compiler).
$ make build1 

# Show help 
$ ./dockman.bin --help
  • Option 2: Compilation using the Docker image
    • The advantage of this option is that the D-language tooling doesn’t need to be installed in the system as the toolchain is provided by the docker image: dlangchina/docker-dlang.
$ make build2 

# Show help 
$ ./dockman.bin --help

Installing

The binary can become accessible from command line from any directory, if it is placed in any folder listed in $PATH variable, for instance /bin or /usr/bin.

Another way to install locally without root access is to use the following commands:

$ make build1 
$ mkdir -p ~/bin && cp dockman.bin ~/bin/dockman 
# Add ~/bin directory to ~/.bashrc 
$ echo "export PATH=$$PATH:~/bin" >> ~/.bashrc 

Examples and Use-cases

Show help

$ ./dockman 
 dockman - Docker Manager Tool

Usage: $ dockman <SUBCOMMAND> <DOCKER-IMAGE> [<OPTIONS>...]

 => Run docker-image's default entrypoint
  $ dockman run <DOCKER-IMAGE> [<OPTIONS>...] 
  $ dockman r <DOCKER-IMAGE> [<OPTIONS>...] 

 => Run docker-image's default unix shell (REPL) or any other entrypoint.
  $ dockman shell <DOCKER-IMAGE> [<OPTIONS>...] 
  $ dockman s <DOCKER-IMAGE> [<OPTIONS>...] 

 => Run docker-image's bash (REPL) or any other entrypoint.
  $ dockman bash <DOCKER-IMAGE> [<OPTIONS>...] 
  $ dockman b <DOCKER-IMAGE> [<OPTIONS>...] 

 => Run docker-image as service (aka daemon) 
  $ dockman service <DOCKER-IMAGE> [<OPTIONS>...] 
  $ dockman sr <DOCKER-IMAGE> [<OPTIONS>...] 

 => Build docker image from file 
  $ dockman build <DOCKER-IMAGE-NAME> <DOCKER-FILE>

 => Open shell (bash) in a docker named-volume.
  $ dockman volume-shell <DOCKER-VOLUME-NAME>
  $ dockman vs <DOCKER-VOLUME-NAME>

 Options:
         --verbose Log docker commands for debugging.
 -w      --workdir Working directory, default current directory of host.
 -n         --name Human-readable name for container.
 -c      --command Command to be executed by image entrypoint
 -e   --entrypoint Alternative entrypoint.
 -x          --x11 Enable X11 GUI graphical user interface
 -u         --user Alternative user.
 -m         --home Mount $HOME directory to /uhome dir. in container.
 -v       --volume Volume to be mounted.
 -p         --port Host port to be mapped to container.
 -q --expose-ports Expose all container ports to host (same as --network=host).
-pr   --privileged Enable privileged mode, useful for GDB
 -g          --gdb Enable GDB (GNU Debugger) and PTrace inside docker containers.
 -h         --help This help information.

Open a shell in a Docker named-volume

This command opens a shell (bash) REPL in a Docker volume. It mounts the volume to the default directory ‘/volume’ in the container and mounts the current working directory in the host to ‘/cwd’ in the container. It makes easier for manipulating the container data or transferring data from container to a host directory and vice versa.

Example: List volumes.

$ docker volume ls
DRIVER              VOLUME NAME
local               alpine
local               myvolume
local               myvolyme
local               nginx.conf
local               pdev-dlang

List volume contents:

 # Get current directory in container. 
 $ dockman volume-shell pdev-dlang -- pwd
/volume

 # List contents of pdev-dlang volume 
 $ dockman volume-shell pdev-dlang -- ls -a 
 .              .bash_logout   .config        .local         .vscode
 ..             .bashrc        .dub           .pki           dlang
 .bash_history  .cache         .gnupg         .profile       file.txt

 $ dockman volume-shell pdev-dlang -- ls dlang
 d-keyring.gpg  dmd-2.091.1    install.sh

List contents of /cwd (directory which the current working directory of local machine is mounted.)

# Within host machine 
$ ls
test_code/            dockman.bin*  dockman-linux-x86_64-elf.bin*  Makefile
test_codenginx.conf/  dockman.d     dockman.o                      README.org

# Within docker container 
$ dockman volume-shell pdev-dlang -- ls /cwd
Makefile                      dockman.d
README.org                    dockman.o
dockman-linux-x86_64-elf.bin  test_code
dockman.bin                   test_codenginx.conf

Open shell in a Docker volume.

$ dockman volume-shell pdev-dlang 
/volume # ls 
dlang     file.txt

/volume # cat file.txt
My text here

/volume # ls dlang
d-keyring.gpg  dmd-2.091.1    install.sh

Nginx web server docker image for serving static directories

Serve static content from a local directory with Nginx Web Server. After the following code is run, the server can be accessed through any web browser at URL http://localhost.

$ cd test_code 

 $ dockman service nginx -p=80:80 --name=nginx-http-server -v=~/projects:/srv -v=./nginx.conf:/etc/nginx/conf.d/default.confw
 f4947564b5520d358e5708462b59e5369b7d3750b578c0666dfbe3baeff21114

Or the server can also be launched exposing all container ports, defined in Dockerfile, to the host with the flag –expose-ports or (-q).

$ dockman service nginx --expose-ports --name=nginx-http-server -v=~/projects:/srv -v=./nginx.conf:/etc/nginx/conf.d/default.conf
$ dockman service nginx -q --name=nginx-http-server -v=~/projects:/srv -v=./nginx.conf:/etc/nginx/conf.d/default.conf

Check container status:

$ docker ps  | grep nginx-http
f4947564b552        nginx               "nginx -g 'daemon ..."   2 minutes ago       Up 2 minutes        0.0.0.0:80->80/tcp   nginx-http-server

Check container’s logs.

$ docker logs nginx-http-server
172.17.0.1 - - [07/May/2020:00:21:02 +0000] "GET / HTTP/1.1" 200 6128 "-" "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36" "-"
172.17.0.1 - - [07/May/2020:00:24:12 +0000] "GET /cppexperiments/ HTTP/1.1" 200 3122 "http://localhost/" "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36" "-"

Watch container logs:

$ docker logs -f nginx-http-server
172.17.0.1 - - [07/May/2020:00:21:02 +0000] "GET / HTTP/1.1" 200 6128 "-" "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36" "-"
172.17.0.1 - - [07/May/2020:00:24:12 +0000] "GET /cppexperiments/ HTTP/1.1" 200 3122 "http://localhost/" "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36" "-"

172.17.0.1 - - [07/May/2020:00:24:34 +0000] "GET /cppexperiments/CMakeLists.txt HTTP/1.1" 200 5601 "http://localhost/cppexperiments/" "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36" "-"

Stop container:

$ docker stop nginx-http-server

Start container:

$ docker start nginx-http-server

Remove container:

$ docker rm -f nginx-http-server

File: nginx.conf

server{
        listen 80;
        server_name _;

        location / {	
                root /srv;
                # Activate the next line if you want to list files
                autoindex on;
        }
}

SAMBA/SMB Windows-share server

Docker image for compiling and running TypeScript

This example uses the following alpine-based Docker image for compiling and running typescript sandrokeil/typescript (source code: typescript/Dockerfile). This Docker image contains pre-installed NodeJS, npm and TSC - typescript compiler.

  • File: test.ts - Typescript source to be compiled.
class MetaObject{
      constructor (public Name: string){ }
}

let obj1 = new MetaObject("Something");
let obj2 = new MetaObject("Else");

console.log(" =>> Hello world typescript ");
console.log(`\t Obj = ${obj1.Name} `);

for (let j of  [10, 9, 100, 52]){
  console.log(`j = ${j}`);
}

Compile typescript with docker image in interactive mode

  • Note: It is assumed that the file ‘test.ts’ is in the current directory.
  • ‘-e=sh’, equivalent to ‘–entrypoint=sh’ changes the current container entrypoint.
$ cd test_code/
$ dockman shell sandrokeil/typescript -e=sh
/work $ 
/work $ ls
typescript_example.ts

Compile and check generated files:

# Compile to Javascript =>> Generates test.js
/work $ tsc typescript_example.ts 

# Check generated files 
/work $ ls
typescript_example.js  typescript_example.ts

# Show content of compiled javascript "Object-code"
/work $ cat typescript_example.js

var MetaObject = /** @class */ (function () {
    function MetaObject(Name) {
        this.Name = Name;
    }
    return MetaObject;
}());
var obj1 = new MetaObject("Something");
var obj2 = new MetaObject("Else");
console.log(" =>> Hello world typescript ");
console.log("\t Obj = " + obj1.Name + " ");
for (var _i = 0, _a = [10, 9, 100, 52]; _i < _a.length; _i++) {
    var j = _a[_i];
    console.log("j = " + j);
}

Run compiled typescript:

/work $ node test.js

 =>> Hello world typescript 
         Obj = Something 
j = 10
j = 9
j = 100
j = 52

Compile typescript with docker image in batch mode

Compilation: generates test.js

$ dockman shell sandrokeil/typescript -- tsc typescript_example.ts

Running with nodeJS:

$ dockman shell sandrokeil/typescript -- node typescript_example.js
 =>> Hello world typescript 
         Obj = Something 
j = 10
j = 9
j = 100
j = 52

Docker image with Julia Language and chart plotting (X11 GUI Forwarding)

Example A:

The following command runs the docker image docker.io/terasakisatoshi/myjulia’ which contains a Julia language REPL with PyPlot and Plots plotting packages. The (-x) command line switch, equivlant to (–x11) enables X11 forwarding which allows running GUI graphical user interface applications such as chart pannels, IDEs and so on.

$ ./dockman.bin shell docker.io/terasakisatoshi/myjulia -x 
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.1.0 (2019-01-21)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

 julia> x = 0:2:10
 0:2:10

 julia> y = @. x^2 - 4x + 10
 6-element Array{Int64,1}:
  10
   6
  10
  22
  42
  70

 julia> import PyPlot; plt = PyPlot;

 julia> plt.plot(x, y)
 QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'
 libGL error: MESA-LOADER: failed to retrieve device information
 libGL error: unable to load driver: i915_dri.so
 libGL error: driver pointer missing
 libGL error: failed to load driver: i915
 libGL error: failed to open drm device: No such file or directory
 libGL error: failed to load driver: i965
 libGL error: unable to load driver: swrast_dri.so
 libGL error: failed to load driver: swrast
 1-element Array{PyCall.PyObject,1}:
  PyObject <matplotlib.lines.Line2D object at 0x7f6768fe5278>

julia> readdir()
8-element Array{String,1}:
 ".git"       
 ".gitignore" 
 "Makefile"   
 "README.org" 
 "dockman.bin"
 "dockman.d"  
 "dockman.elf"
 "dockman.o"

Example B:

Run previous command with flag (–verbose) for debugging purposes and working directory, which the default value is the current directory, set to ‘/etc’ directory of host machine.

$ ./dockman.bin shell docker.io/terasakisatoshi/myjulia -x -w=/etc --verbose 
 [TRACE] Mount /etc to /work 
 [TRACE] Enable X11 - graphical user interfaces 
 Docker command run: 
 ["docker", "run", "-it", "--rm", "-v", "/etc:/work", "-w", "/work", "-e", "DISPLAY", "-v", "/tmp/.X11-unix:/tmp/.X11-unix", "-v", "/home/archbox/.Xauthority:/root/.Xauthority", "docker.io/terasakisatoshi/myjulia"]
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.1.0 (2019-01-21)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> readdir()
357-element Array{String,1}:
 ".java"                  
 ".pwd.lock"              
 ".updated"               
 "DIR_COLORS"             
 "DIR_COLORS.256color"    
 "DIR_COLORS.lightbgcolor"
 "GREP_COLORS"            
 "ImageMagick-6"          
 "NetworkManager"         

 "yum"                    
 "yum.repos.d"            
 "zfs-fuse"               
 "zlogin"                 
 "zlogout"                
 "zprofile"               
 "zshenv"                 
 "zshrc"                  

Example C:

Run the same Julia docker image with a different entry-point, now ‘bash’:

$ dockman shell docker.io/terasakisatoshi/myjulia -x -e=bash
root@e4db8ba7098e:/work# 

$ dockman shell docker.io/terasakisatoshi/myjulia -x --entrypoint=bash
root@932dc38e9107:/work# 
root@932dc38e9107:/work# exit

Example D

Run Julia script in batch mode:

  • Note: this script plots a chart in a new window with PyPlot (Matplotlib Python library).
  • (-x) flag, equivalent to –x11, enables X11 forwarding or running GUI applications.
$ dockman shell docker.io/terasakisatoshi/myjulia -x -- julia -i julia_lang_script.jl

File: julia_lang_script.jl

import PyPlot; plt = PyPlot;

x = 0:2:10
y = @. x^2 - 4x + 10

println(" x = ", collect(x)')
println(" y = ", y')

plt.plot(x, y)

Docker image with Rust and GCC/MingGW compiler

Example: compile a sample rust source code using the Docker image rust official Docker image.

Source code: rust_example.rs

// Rust compiler testing ....                                                                      
fn main(){
   println!(" [TRACE] Hello world RUST programming language.");

   let x = 5 + /* 90 + */ 5;
   println!("Is `x` 10 or 100? x = {}", x);

   for n in 1..10 { println!(" n = {}", n); }
}
  1. Enter the directory:
$ cd test_code/
  1. Run:
root@60391179c521:/work# rustc rust_example.rs -o app.bin

root@60391179c521:/work# ./app.bin 
 [TRACE] Hello world RUST programming language.
Is `x` 10 or 100? x = 10
 n = 1
 n = 2
 n = 3
 n = 4
 n = 5
 n = 6
 n = 7
 n = 8
 n = 9
  1. Build windows 64 bits executable:
$ apt-get update && apt-get install mingw-w64
$ rustup target add x86_64-pc-windows-gnu

# Build 
$ rustc rust_example.rs -o main.exe --target x86_64-pc-windows-gnu

# Check file 
$ file main.exe 
main.exe: PE32+ executable (console) x86-64, for MS Windows

# Run Windows executable with wine. 
 root@32cd56482af1:/work# wine main.exe 
 it looks like wine32 is missing, you should install it.
 multiarch needs to be enabled first.  as root, please
 execute "dpkg --add-architecture i386 && apt-get update &&
 apt-get install wine32"
  [TRACE] Hello world RUST programming language.
 Is `x` 10 or 100? x = 10
  n = 1
  n = 2
  n = 3
  n = 4
  n = 5
  n = 6
  n = 7
  n = 8
  n = 9
 root@32cd56482af1:/work# 
  1. Build in batch mode (without entering bash shell script REPL):
$ dockman shell rust -- rustc rust_example.rs --verbose -o app2.bin

$ dockman shell rust --verbose -- rustc rust_example.rs --verbose -o app2.bin
 [TRACE] Mount /home/archbox/Documents/projects/dockman.dlang/test_code to /work 
 Docker command run: 
 ["docker", "run", "-it", "--rm", "-v", "/home/user/test_code:/work", "-w", "/work", "rust", "rustc", "rust_example.rs", "--verbose", "-o", "app2.bin"]

$ file app2.bin 
app.bin: ELF 64-bit LSB shared object, x86-64, ... ... ... .. 

$ ./app2.bin 
 [TRACE] Hello world RUST programming language.
Is `x` 10 or 100? x = 10
 n = 1
 n = 2
 n = 3
 n = 4
 n = 5
 n = 6
 n = 7
 n = 8
 n = 9

Docker image with C++ CERN’s Root CLing REPL

The Root REPL developed by CERN allows evaluating and playing with most of C++ (mostly C++11 supported) and supported ‘C’ subset of C++ interactively without any compilation.

  • Example 1: Interactive CLing REPL
$ dockman shell dhavenith/jupyter-cling -e=cling

****************** CLING ******************
* Type C++ code and press enter to run it *
*             Type .q to exit             *
*******************************************
[cling]$ 
[cling]$ 

[cling]$ #include <iostream>
[cling]$ #include <vector>
[cling]$ #include <algorithm>
[cling]$ #include <numeric>

[cling]$ auto xs = std::vector<double>{2.5, 10.523, 9.25, -10.356, 9.726, 10.53}

[cling]$ std::accumulate(xs.begin(), xs.end(), 0.0)
(double) 32.173

// ---=>> Create a lambda function <<=== ----------------//
[cling]$ auto print_value = [](double x){ std::cout << " x = " << x << "\n"; }
((lambda) &) @0x7fb6af18e030

[cling]$ std::for_each(xs.begin(), xs.end(), print_value);
 x = 2.5
 x = 10.523
 x = 9.25
 x = -10.356
 x = 9.726
 x = 10.53
[cling]$ 
  • Example 2: Run Jupyter Notebook web server with C++ support (CLing REPL)
    • To access the Jupuyter Server, open the URL ( http://127.0.0.1:8888/?token=0ab60534327956dcc9012bb955ffa772f589839f6759eab6) in the web browser.
$ dockman shell dhavenith/jupyter-cling -p=8888:8888 --verbose
 [TRACE] Mount /home/archbox/Documents/projects/dockman.dlang to /work 
 Docker command run: 
 ["docker", "run", "-it", "--rm", "-v", "/home/user/server:/work", "-w", "/work", "-p", "8888:8888", "dhavenith/jupyter-cling"]

** using mounted /work directory
[I 15:51:56.741 NotebookApp] Writing notebook server cookie secret to /home/notebooker/.local/share/jupyter/runtime/notebook_cookie_secret
[W 15:51:56.963 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended.
[I 15:51:56.967 NotebookApp] Serving notebooks from local directory: /work
[I 15:51:56.967 NotebookApp] The Jupyter Notebook is running at:
[I 15:51:56.967 NotebookApp] http://91fcb638b834:8888/?token=0ab60534327956dcc9012bb955ffa772f589839f6759eab6
[I 15:51:56.967 NotebookApp]  or http://127.0.0.1:8888/?token=0ab60534327956dcc9012bb955ffa772f589839f6759eab6
[I 15:51:56.967 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 15:51:56.972 NotebookApp] 

    To access the notebook, open this file in a browser:
        file:///home/notebooker/.local/share/jupyter/runtime/nbserver-9-open.html
    Or copy and paste one of these URLs:
        http://91fcb638b834:8888/?token=0ab60534327956dcc9012bb955ffa772f589839f6759eab6
     or http://127.0.0.1:8888/?token=0ab60534327956dcc9012bb955ffa772f589839f6759eab6

  ...  ... ... ... ... ... ... ... ... ... ... 
  • Example 3: Run the previous example as daemon, without blocking the current REPL.

Launch container as daemon:

$ dockman daemon dhavenith/jupyter-cling -p=8888:8888 --name=jupyter-cpp-server
0f13910da196af6789a2ee0432518e2bec041b2fbde4414dc047d36d6d319d44

Show container logs:

$ docker logs -f jupyter-cpp-server
** using mounted /work directory
[I 16:00:38.061 NotebookApp] Writing notebook server cookie secret to /home/notebooker/.local/share/jupyter/runtime/notebook_cookie_secret
[W 16:00:38.274 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended.
[I 16:00:38.278 NotebookApp] Serving notebooks from local directory: /work
[I 16:00:38.278 NotebookApp] The Jupyter Notebook is running at:
[I 16:00:38.278 NotebookApp] http://0f13910da196:8888/?token=9be9c8aac1954430dc77815b60d3469b75b1e391d011d7cf
[I 16:00:38.278 NotebookApp]  or http://127.0.0.1:8888/?token=9be9c8aac1954430dc77815b60d3469b75b1e391d011d7cf
[I 16:00:38.278 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 16:00:38.283 NotebookApp] 

    To access the notebook, open this file in a browser:
        file:///home/notebooker/.local/share/jupyter/runtime/nbserver-9-open.html
    Or copy and paste one of these URLs:
        http://0f13910da196:8888/?token=9be9c8aac1954430dc77815b60d3469b75b1e391d011d7cf
     or http://127.0.0.1:8888/?token=9be9c8aac1954430dc77815b60d3469b75b1e391d011d7cf

  ... ...   ... ...   ... ...   ... ...   ... ...   ... ...   ... ... 
  ... ...   ... ...   ... ...   ... ...   ... ...   ... ...   ... ... 

Inspect container:

$ docker inspect jupyter-cpp-server
[
    {
        "Id": "0f13910da196af6789a2ee0432518e2bec041b2fbde4414dc047d36d6d319d44",
        "Created": "2020-05-04T16:00:36.84126218Z",
        "Path": "/bin/sh",
        "Args": [
            "-c",
            "start-notebook.sh"
        ],
... ... ... ... ... ... ... ... ... ... ... ... ... 
... ... ... ... ... ... ... ... ... ... ... 

Inspect container mapped TCP or UDP ports:

$ docker port jupyter-cpp-server
8888/tcp -> 0.0.0.0:8888

Stop container:

$ docker stop jupyter-cpp-server

Restart container:

$ docker start jupyter-cpp-server

Force stopping and deleting container

$ docker rm -f jupyter-cpp-server

Running shell (bash) in a docker-image with D-Language compiler

Get current directory and list it (before running docker image)

$ pwd
/home/archbox/Documents/projects/dockman.dlang

# List files of current directory 
$ ls
dockman.bin*  dockman.d  dockman.o  Makefile  README.org

Run shell (bash) in docker image ‘docker.io/dlangchina/dlang-dmd’ mounting current directory to the ‘/work’ directory in the container which is set as the current working directory.

$ ./dockman.bin shell docker.io/dlangchina/dlang-dmd --verbose

 [TRACE] Mount /home/archbox/Documents/projects/dockman.dlang to /work 
 [TRACE] Mount /home/archbox to /uhome 
 Docker command run: 
 ["docker", "run", "-it", "--rm", "-v", "/home/archbox/Documents/projects/dockman.dlang:/work", "-w", "/work", "docker.io/dlangchina/dlang-dmd"]
 root@c8dbe6974808:/work# 

 root@c8dbe6974808:/work# pwd

 root@c8dbe6974808:/work# ls
 Makefile  README.org  dockman.bin  dockman.d  dockman.o

Check DMD compiler.

root@c8dbe6974808:/work# dmd --version
DMD64 D Compiler v2.091.1
Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved written by Walter Bright

Build dockman.d with the Docker container shell.

# Compile 
#---------------------------------------------------------
root@c8dbe6974808:/work# dmd dockman.d -of=dockman.elf

# Test 
#---------------------------------------------------------
root@c8dbe6974808:/work# ./dockman.elf 
 dockman - Docker Manager Tool

Usage: $ dockman <SUBCOMMAND> <DOCKER-IMAGE> [<OPTIONS>...]

 => Run docker-image unix shell (REPL) or any other entrypoint.
 $ dockman shell <DOCKER-IMAGE> [<OPTIONS>...] 

... ... ... ... ... ... ... ... ... ... ... ... 
... ... ... ... ... ... ... ... ... ... ... ...