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.
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
$ ./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.
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
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;
}
}
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
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)
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); }
}
- Enter the directory:
$ cd test_code/
- 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
- 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#
- 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
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.
- To access the Jupuyter Server, open the URL (
$ 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
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>...]
... ... ... ... ... ... ... ... ... ... ... ...
... ... ... ... ... ... ... ... ... ... ... ...