Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigate docker image size reduction #1428

Closed
om26er opened this issue Oct 29, 2018 · 19 comments
Closed

Investigate docker image size reduction #1428

om26er opened this issue Oct 29, 2018 · 19 comments
Assignees
Labels
CI-CD Test, build and packaging infra enhancement

Comments

@om26er
Copy link
Contributor

om26er commented Oct 29, 2018

The last time we tried that, we had to revert it because it broke crossbarfx images. This time lets be better prepared and make sure to test everything and do the required changes.

@om26er om26er self-assigned this Oct 29, 2018
@haizaar
Copy link
Contributor

haizaar commented Jan 8, 2019

my 2c:

@oberstet
Copy link
Contributor

oberstet commented Jan 8, 2019

as @om26er mentions, we need to make sure it works not only for crossbar, but also for crossbarfx, and for all supported architectures. I have spent countless hours in fiddling around with this to make it work (see )

unless and until s.o. comes up with a PR that provably works for all of above, we won't merge, and I won't spend more time on it. sorry

also, I never actually understood why reducing the size is important. disk is cheap, who cares.

fwiw, we ourself are using snaps and single-file executables https://download.crossbario.com/?prefix=crossbarfx/linux-amd64/ a lot

with the latter, crossbar with all dependencies boils down to 40MB. actually, crossbarfx has a lot more than crossbar (eg the 40MB include a complete embedded numpy etc). and works on anything Linux 2.6+, any distro, no docker needed.

@haizaar
Copy link
Contributor

haizaar commented Jan 9, 2019

Interesting input about snaps. In my fishbowl everything goes docker now :)

I can provide my reasons for reducing image sizes for our app:

  • Disk space is cheap, but network bandwidth is not always. Sometimes we deploy our k8s app in locations with limited network connectivity. Pulling 20x40MB compared to 20x700MB makes a notable difference.
  • Another reason is faster fail-over - if you have a certain container running as single instance (no scale-out support) and your k8s node crashes, the container is rescheduled to another healthy host where the image is being pulled to. Pull/uncompress time of larger images can add 10-30 seconds to your recovery time, depending on the network and CPU load of the target node. It's not always critical, but sometimes it is. You can pre-pull everything to every node, but custom tooling is required for that.

@oberstet
Copy link
Contributor

oberstet commented Jan 9, 2019

Thanks for laying out the reasons why image size matters to you!

In absolute terms, and discounting other aspects, the smallest possible image size we'll be able to bake is likely using the EXE (40MB) and a scratch container (FROM scratch: https://www.mgasch.com/post/scratch/)

The only downside: the EXE is baked on top of CPython, not PyPy. We haven't managed to bake the EXE on top of PyPy, and this is likely a non-trivial research like endeavor


rgd snaps "vs" docker: yeah, docker rules for anything cloud, but for devices (IoT, edge, desktop, etc), snaps bring a couple of very substantial advantages compared to docker. in my view. anyways, surely we want to support both systems in the best possible way.


rgd our single-file EXE approach. check this out (this should work on any linux 2.6+ .. regardless of distro or whatever):

oberstet@intel-nuci7:~$ wget https://download.crossbario.com/crossbarfx/linux-amd64/crossbarfx-linux-amd64-20190108-9553865
--2019-01-09 08:23:29--  https://download.crossbario.com/crossbarfx/linux-amd64/crossbarfx-linux-amd64-20190108-9553865
Auflösen des Hostnamens download.crossbario.com (download.crossbario.com) … 54.230.93.8, 54.230.93.162, 54.230.93.219, ...
Verbindungsaufbau zu download.crossbario.com (download.crossbario.com)|54.230.93.8|:443 … verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet … 200 OK
Länge: 41838920 (40M) [binary/octet-stream]
Wird in »crossbarfx-linux-amd64-20190108-9553865« gespeichert.

crossbarfx-linux-amd64-20190108-9553865                              100%[=====================================================================================================================================================================>]  39,90M  11,1MB/s    in 3,6s    

2019-01-09 08:23:33 (10,9 MB/s) - »crossbarfx-linux-amd64-20190108-9553865« gespeichert [41838920/41838920]

oberstet@intel-nuci7:~$ chmod +x crossbarfx-linux-amd64-20190108-9553865
oberstet@intel-nuci7:~$ ./crossbarfx-linux-amd64-20190108-9553865 edge version

    :::::::::::::::::
          :::::          _____                 __              _____  __
    :::::   :   :::::   / ___/______  ___ ___ / /  ___ _____  / __/ |/_/
    :::::::   :::::::  / /__/ __/ _ \(_-<(_-</ _ \/ _ `/ __/ / _/_>  <
    :::::   :   :::::  \___/_/  \___/___/___/_.__/\_,_/_/   /_/ /_/|_|
          :::::
    :::::::::::::::::   Crossbar Fabric XBR v18.12.1 [20190108-9553865]

    Copyright (c) 2013-2019 Crossbar.io Technologies GmbH. All rights reserved.

 Crossbar.io        : 18.12.1
   txaio            : 18.8.1
   Autobahn         : 18.12.1
   Twisted          : 18.9.0-EPollReactor
   LMDB             : 0.94/lmdb-0.9.22
   Python           : 3.7.1/CPython
 Crossbar.io FX     : 18.12.1
   XBR              : 18.12.2
   zLMDB            : 18.12.1
 Frozen executable  : yes
 Operating system   : Linux-4.15.0-43-generic-x86_64-with-debian-buster-sid
 Host machine       : x86_64
 Release key        : RWRL2URPgi6MK0CkpI9a4HzKYMTH/wZtSX2tISkJIXE1ctt7+Bf+fWEn

oberstet@intel-nuci7:~$ ll ./crossbarfx-linux-amd64-20190108-9553865 
-rwxr-xr-x 1 oberstet oberstet 41838920 Jan  8 10:41 ./crossbarfx-linux-amd64-20190108-9553865*
oberstet@intel-nuci7:~$ file ./crossbarfx-linux-amd64-20190108-9553865 
./crossbarfx-linux-amd64-20190108-9553865: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=28ba79c778f7402713aec6af319ee0fbaf3a8014, stripped
oberstet@intel-nuci7:~$ ldd ./crossbarfx-linux-amd64-20190108-9553865 
	linux-vdso.so.1 (0x00007fff719fc000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f2b54ddc000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f2b54bbf000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2b547ce000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f2b54fe0000)
oberstet@intel-nuci7:~$ 
oberstet@intel-nuci7:~$ mkdir foo2
oberstet@intel-nuci7:~$ cd foo2
oberstet@intel-nuci7:~/foo2$ ../crossbarfx-linux-amd64-20190108-9553865 edge init
Initializing node in directory '/home/oberstet/foo2'
Using template from '/tmp/_MEIq2Fe2p/crossbar/node/templates/default'
Creating directory /home/oberstet/foo2/web
Creating directory /home/oberstet/foo2/.crossbar
Creating file /home/oberstet/foo2/README.md
Creating file /home/oberstet/foo2/web/README.md
Creating file /home/oberstet/foo2/.crossbar/config.json
Application template initialized

To start your node, run 'crossbar start --cbdir /home/oberstet/foo2/.crossbar'

oberstet@intel-nuci7:~/foo2$ ../crossbarfx-linux-amd64-20190108-9553865 edge start
2019-01-09T08:28:16+0100 [Controller  14458] 
2019-01-09T08:28:16+0100 [Controller  14458]     :::::::::::::::::
2019-01-09T08:28:16+0100 [Controller  14458]           :::::          _____                 __              _____  __
2019-01-09T08:28:16+0100 [Controller  14458]     :::::   :   :::::   / ___/______  ___ ___ / /  ___ _____  / __/ |/_/
2019-01-09T08:28:16+0100 [Controller  14458]     :::::::   :::::::  / /__/ __/ _ \(_-<(_-</ _ \/ _ `/ __/ / _/_>  <
2019-01-09T08:28:16+0100 [Controller  14458]     :::::   :   :::::  \___/_/  \___/___/___/_.__/\_,_/_/   /_/ /_/|_|
2019-01-09T08:28:16+0100 [Controller  14458]           :::::
2019-01-09T08:28:16+0100 [Controller  14458]     :::::::::::::::::   Crossbar Fabric XBR v18.12.1 [20190108-9553865]
2019-01-09T08:28:16+0100 [Controller  14458] 
2019-01-09T08:28:16+0100 [Controller  14458]     Copyright (c) 2013-2019 Crossbar.io Technologies GmbH. All rights reserved.
2019-01-09T08:28:16+0100 [Controller  14458] 
2019-01-09T08:28:16+0100 [Controller  14458] Initializing <class 'crossbarfx.edge.personality.Personality'> node from node directory /home/oberstet/foo2/.crossbar 
2019-01-09T08:28:16+0100 [Controller  14458] New node key pair generated! Public key is 0x195f43223955c625b37e99e5e5ff58577ee2f4e4a9ac20f6a2fe735a40d06e93
2019-01-09T08:28:16+0100 [Controller  14458] File permissions on node private key fixed
2019-01-09T08:28:16+0100 [Controller  14458] Node key loaded from /home/oberstet/foo2/.crossbar/key.priv
2019-01-09T08:28:16+0100 [Controller  14458] Node configuration loaded from /home/oberstet/foo2/.crossbar/config.json
2019-01-09T08:28:16+0100 [Controller  14458] Configuration source 2
2019-01-09T08:28:16+0100 [Controller  14458] Entering event reactor ...
2019-01-09T08:28:16+0100 [Controller  14458] Starting edge node 
2019-01-09T08:28:16+0100 [Controller  14458] Node ID intel-nuci7 set from hostname
2019-01-09T08:28:16+0100 [Controller  14458] RouterServiceAgent ready (realm_name="crossbar", on_ready=None)
2019-01-09T08:28:16+0100 [Controller  14458] Docker daemon integration disabled
2019-01-09T08:28:16+0100 [Controller  14458] Registered 46 procedures
2019-01-09T08:28:16+0100 [Controller  14458] Signal handler installed on process 14458 thread 140717636888384
2019-01-09T08:28:16+0100 [Controller  14458] Using default node shutdown triggers ['shutdown_on_shutdown_requested']
2019-01-09T08:28:16+0100 [Controller  14458] Booting node 
2019-01-09T08:28:16+0100 [Controller  14458] No fabric uplink configured (running unmanaged/single-node)
2019-01-09T08:28:16+0100 [Controller  14458] Booting node from local configuration .. 
2019-01-09T08:28:16+0100 [Controller  14458] Will start 1 worker ..
2019-01-09T08:28:16+0100 [Controller  14458] Order node to start Router worker001
2019-01-09T08:28:16+0100 [Controller  14458] Starting router worker worker001 
2019-01-09T08:28:17+0100 [Router      14470] Starting worker "worker001" for node "intel-nuci7" with personality "edge" 
2019-01-09T08:28:17+0100 [Router      14470] Running as PID 14471 on CPython-EPollReactor
2019-01-09T08:28:17+0100 [Router      14470] Entering event reactor ...
2019-01-09T08:28:17+0100 [Router      14470] Router worker session for "worker001" joined realm "crossbar" on node router 
2019-01-09T08:28:17+0100 [Router      14470] Registered 50 procedures
2019-01-09T08:28:17+0100 [Router      14470] Router worker session for "worker001" ready
2019-01-09T08:28:17+0100 [Controller  14458] Ok, node has started Router worker001
2019-01-09T08:28:17+0100 [Controller  14458] Configuring Router worker001 ..
2019-01-09T08:28:17+0100 [Controller  14458] Order Router worker001 to start Realm realm001
2019-01-09T08:28:17+0100 [Router      14470] Starting router realm "realm001" 
2019-01-09T08:28:17+0100 [Router      14470] Starting router realm realm001 
2019-01-09T08:28:17+0100 [Router      14470] RouterServiceAgent ready (realm_name="realm1", on_ready=<Deferred at 0x7fb02eb587f0>)
2019-01-09T08:28:17+0100 [Router      14470] Realm "realm001" (name="realm1") started
2019-01-09T08:28:17+0100 [Controller  14458] Ok, Router worker001 has started Realm realm001
2019-01-09T08:28:17+0100 [Controller  14458] Order Realm realm001 to start Role role001
2019-01-09T08:28:17+0100 [Router      14470] Starting role "role001" on realm "realm001" 
2019-01-09T08:28:17+0100 [Router      14470] role role001 on realm realm001 started
2019-01-09T08:28:17+0100 [Controller  14458] Ok, Realm realm001 has started Role role001
2019-01-09T08:28:17+0100 [Controller  14458] Order Router worker001 to start Transport transport001
2019-01-09T08:28:17+0100 [Router      14470] Starting router transport "transport001" 
2019-01-09T08:28:17+0100 [Router      14470] Creating router transport for "transport001" 
2019-01-09T08:28:17+0100 [Router      14470] Router transport created for "transport001" 
2019-01-09T08:28:17+0100 [Router      14470] Created "static" Web service on root path "/" of Web transport "transport001"
2019-01-09T08:28:17+0100 [Router      14470] UniSocketServerFactory starting on 8080
2019-01-09T08:28:17+0100 [Controller  14458] Ok, Router worker001 has started Transport transport001
2019-01-09T08:28:17+0100 [Controller  14458] Order Transport transport001 to start Web Service webservice001
2019-01-09T08:28:17+0100 [Router      14470] Starting "nodeinfo" Web service on path "info" of transport "transport001" 
2019-01-09T08:28:17+0100 [Controller  14458] Ok, Transport transport001 has started Web Service webservice001
2019-01-09T08:28:17+0100 [Controller  14458] Ok, Router worker001 configured
2019-01-09T08:28:17+0100 [Controller  14458] Ok, local node configuration booted successfully!
^C2019-01-09T08:28:19+0100 [Controller  14458] Controller received SIGINT [signal=2]: shutting down node [shutdown_was_clean=True] ..
2019-01-09T08:28:19+0100 [Controller  14458] Node shutdown requested (restart=False, mode=None, reactor.running=True) ..
2019-01-09T08:28:19+0100 [Controller  14458] sending TERM to subprocess 14470
2019-01-09T08:28:19+0100 [Controller  14458] waiting for 14470 to exit...
2019-01-09T08:28:19+0100 [Router      14470] INVALID JSON: Native worker received SIGTERM - shutting down ..
{"log_time": 1547018899.546754, "level": "warn", "namespace": "crossbarfx.edge.worker.router.ExtRouterController", "text": "Native worker received SIGTERM - shutting down .."}
2019-01-09T08:28:19+0100 [Router      14470] Shutdown of worker requested!
2019-01-09T08:28:19+0100 [Router      14470] Connection to node controller closed cleanly
2019-01-09T08:28:19+0100 [Router      14470] (TCP Port 8080 Closed)
2019-01-09T08:28:19+0100 [Controller  14458] Native worker connection closed cleanly.
2019-01-09T08:28:19+0100 [Controller  14458] Node worker worker001 ended successfully
2019-01-09T08:28:19+0100 [Controller  14458] Checking for node shutdown: worker_exit_success=True, shutdown_requested=True, node_shutdown_triggers=['shutdown_on_shutdown_requested']
oberstet@intel-nuci7:~/foo2$ 

@haizaar
Copy link
Contributor

haizaar commented Jan 9, 2019

Great. Managed to run it on my Ubuntu 16.04. Generally, 40MB + 5MB for alpine is much better than ~430Mb for today's crossbar. I agree that it's too bad to loose PyPy.

What tech do you use for baking the executable?

@oberstet
Copy link
Contributor

oberstet commented Jan 9, 2019

For the EXE, we are using pyinstaller. A Docker image from that can be done it exactly 40MB, no need for Alpine. The base image is scratch.

@haizaar
Copy link
Contributor

haizaar commented Jan 10, 2019

Sorry, I initially confused scratch for debian:stretch :)

Well, your binary still needs libz and libc - it's not statically linked. Using just

FROM scratch
COPY crossbar /
CMD ["/crossbar"]

Results in:

$ docker build -t cb .
$ docker run -ti cb
standard_init_linux.go:190: exec user process caused "no such file or directory"

Even gcr.io/distroless/base (which is 3 times bigger than alpine) is not enough because of libz. Since this particular binary was compiled against glibc it does not work on alpine as well. If rebuilt on alpine the binary itself would've been smaller.

Or, if bundled with libc/z, FROM scratch would work I suppose.

@oberstet
Copy link
Contributor

try this:

oberstet@intel-nuci7:~/test3$ make build 
docker build -t cfx-exe -f Dockerfile .
Sending build context to Docker daemon  41.84MB
Step 1/3 : FROM scratch
 ---> 
Step 2/3 : COPY crossbarfx-linux-amd64-20190108-9553865 /
 ---> Using cache
 ---> 7b7075defa03
Step 3/3 : CMD ["/crossbarfx-linux-amd64-20190108-9553865", "edge", "version"]
 ---> Running in 0024b9314d2b
Removing intermediate container 0024b9314d2b
 ---> b56297313556
Successfully built b56297313556
Successfully tagged cfx-exe:latest
oberstet@intel-nuci7:~/test3$ make run 
docker run -it --rm \
	--volume=/tmp:/tmp \
	--volume=/lib:/lib \
	--volume=/lib64:/lib64 \
	cfx-exe
WARNING: The XBR smart contracts are not yet depoyed to public networks. Please set XBR_DEBUG_TOKEN_ADDR manually.
WARNING: The XBR smart contracts are not yet depoyed to public networks. Please set XBR_DEBUG_NETWORK_ADDR manually.

    :::::::::::::::::
          :::::          _____                 __              _____  __
    :::::   :   :::::   / ___/______  ___ ___ / /  ___ _____  / __/ |/_/
    :::::::   :::::::  / /__/ __/ _ \(_-<(_-</ _ \/ _ `/ __/ / _/_>  <
    :::::   :   :::::  \___/_/  \___/___/___/_.__/\_,_/_/   /_/ /_/|_|
          :::::
    :::::::::::::::::   Crossbar Fabric XBR v18.12.1 [20190108-9553865]

    Copyright (c) 2013-2019 Crossbar.io Technologies GmbH. All rights reserved.

 Crossbar.io        : 18.12.1
   txaio            : 18.8.1
   Autobahn         : 18.12.1
   Twisted          : 18.9.0-EPollReactor
   LMDB             : 0.94/lmdb-0.9.22
   Python           : 3.7.1/CPython
 Crossbar.io FX     : 18.12.1
   XBR              : 18.12.2
   zLMDB            : 18.12.1
 Frozen executable  : yes
 Operating system   : Linux-4.15.0-43-generic-x86_64-with-glibc2.3.4
 Host machine       : x86_64
 Release key        : RWRL2URPgi6MK0CkpI9a4HzKYMTH/wZtSX2tISkJIXE1ctt7+Bf+fWEn

oberstet@intel-nuci7:~/test3$ cat Dockerfile 
FROM scratch

COPY crossbarfx-linux-amd64-20190108-9553865 /
CMD ["/crossbarfx-linux-amd64-20190108-9553865", "edge", "version"]
oberstet@intel-nuci7:~/test3$ cat Makefile 
build:
	docker build -t cfx-exe -f Dockerfile .

run:
	docker run -it --rm \
		--volume=/tmp:/tmp \
		--volume=/lib:/lib \
		--volume=/lib64:/lib64 \
		cfx-exe
oberstet@intel-nuci7:~/test3$ 

@oberstet
Copy link
Contributor

rgd "bundling" libc: not possible. the C runtime library (and the shared object loader) are the actual interface to the kernel, and cannot be statically linked (as far as I know / am aware)

anyways. it works:

oberstet@intel-nuci7:~/test3$ docker images cfx-exe
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
cfx-exe             latest              d17fc25ca331        54 seconds ago      41.8MB

starting the node:

oberstet@intel-nuci7:~/test3$ make run 
docker run -it --rm \
	--volume=/tmp:/tmp \
	--volume=/lib:/lib \
	--volume=/lib64:/lib64 \
	cfx-exe
WARNING: The XBR smart contracts are not yet depoyed to public networks. Please set XBR_DEBUG_TOKEN_ADDR manually.
WARNING: The XBR smart contracts are not yet depoyed to public networks. Please set XBR_DEBUG_NETWORK_ADDR manually.
2019-01-10T08:03:59+0000 [Controller      8] 
2019-01-10T08:03:59+0000 [Controller      8]     :::::::::::::::::
2019-01-10T08:03:59+0000 [Controller      8]           :::::          _____                 __              _____  __
2019-01-10T08:03:59+0000 [Controller      8]     :::::   :   :::::   / ___/______  ___ ___ / /  ___ _____  / __/ |/_/
2019-01-10T08:03:59+0000 [Controller      8]     :::::::   :::::::  / /__/ __/ _ \(_-<(_-</ _ \/ _ `/ __/ / _/_>  <
2019-01-10T08:03:59+0000 [Controller      8]     :::::   :   :::::  \___/_/  \___/___/___/_.__/\_,_/_/   /_/ /_/|_|
2019-01-10T08:03:59+0000 [Controller      8]           :::::
2019-01-10T08:03:59+0000 [Controller      8]     :::::::::::::::::   Crossbar Fabric XBR v18.12.1 [20190108-9553865]
2019-01-10T08:03:59+0000 [Controller      8] 
2019-01-10T08:03:59+0000 [Controller      8]     Copyright (c) 2013-2019 Crossbar.io Technologies GmbH. All rights reserved.
2019-01-10T08:03:59+0000 [Controller      8] 
2019-01-10T08:03:59+0000 [Controller      8] Initializing <class 'crossbarfx.edge.personality.Personality'> node from node directory / 
2019-01-10T08:03:59+0000 [Controller      8] New node key pair generated! Public key is 0x45cccf216ac2870cd8ef3657a83e7c71d444f0157473b1e4d7323d17f0312592
2019-01-10T08:03:59+0000 [Controller      8] File permissions on node private key fixed
2019-01-10T08:03:59+0000 [Controller      8] Node key loaded from /key.priv
2019-01-10T08:03:59+0000 [Controller      8] Node configuration loaded from built-in config.
2019-01-10T08:03:59+0000 [Controller      8] Configuration source 1
2019-01-10T08:03:59+0000 [Controller      8] Entering event reactor ...
2019-01-10T08:03:59+0000 [Controller      8] Starting edge node 
2019-01-10T08:03:59+0000 [Controller      8] Node ID 9ab34c4bbaa1 set from hostname
2019-01-10T08:03:59+0000 [Controller      8] RouterServiceAgent ready (realm_name="crossbar", on_ready=None)
2019-01-10T08:03:59+0000 [Controller      8] Docker daemon integration disabled
2019-01-10T08:03:59+0000 [Controller      8] Registered 46 procedures
2019-01-10T08:03:59+0000 [Controller      8] Signal handler installed on process 8 thread 139749817354048
2019-01-10T08:03:59+0000 [Controller      8] Using default node shutdown triggers ['shutdown_on_shutdown_requested']
2019-01-10T08:03:59+0000 [Controller      8] Booting node 
2019-01-10T08:03:59+0000 [Controller      8] No fabric uplink configured (running unmanaged/single-node)
2019-01-10T08:03:59+0000 [Controller      8] Booting node from local configuration .. 
2019-01-10T08:03:59+0000 [Controller      8] Will start 1 worker ..
2019-01-10T08:03:59+0000 [Controller      8] Order node to start Router worker001
2019-01-10T08:03:59+0000 [Controller      8] Starting router worker worker001 
2019-01-10T08:04:00+0000 [Router         21] Starting worker "worker001" for node "9ab34c4bbaa1" with personality "edge" 
2019-01-10T08:04:00+0000 [Router         21] Running as PID 22 on CPython-EPollReactor
2019-01-10T08:04:00+0000 [Router         21] Entering event reactor ...
2019-01-10T08:04:00+0000 [Router         21] Router worker session for "worker001" joined realm "crossbar" on node router 
2019-01-10T08:04:00+0000 [Router         21] Registered 50 procedures
2019-01-10T08:04:00+0000 [Router         21] Router worker session for "worker001" ready
2019-01-10T08:04:00+0000 [Controller      8] Ok, node has started Router worker001
2019-01-10T08:04:00+0000 [Controller      8] Configuring Router worker001 ..
2019-01-10T08:04:00+0000 [Controller      8] Order Router worker001 to start Transport transport001
2019-01-10T08:04:00+0000 [Router         21] Starting router transport "transport001" 
2019-01-10T08:04:00+0000 [Router         21] Creating router transport for "transport001" 
2019-01-10T08:04:00+0000 [Router         21] Router transport created for "transport001" 
2019-01-10T08:04:00+0000 [Router         21] Created "pairme" Web service on root path "/" of Web transport "transport001"
2019-01-10T08:04:00+0000 [Router         21] Site starting on 8080
2019-01-10T08:04:00+0000 [Controller      8] Ok, Router worker001 has started Transport transport001
2019-01-10T08:04:00+0000 [Controller      8] Order Transport transport001 to start Web Service webservice001
2019-01-10T08:04:00+0000 [Router         21] Starting "websocket" Web service on path "ws" of transport "transport001" 
2019-01-10T08:04:00+0000 [Controller      8] Ok, Transport transport001 has started Web Service webservice001
2019-01-10T08:04:00+0000 [Controller      8] Ok, Router worker001 configured
2019-01-10T08:04:00+0000 [Controller      8] Ok, local node configuration booted successfully!

@haizaar
Copy link
Contributor

haizaar commented Jan 10, 2019

Well, you are cheating :)
By mounting /lib/ into container you assume that host has a compatible version of libc. Your image may run on Google ChromeOS-based host in GKE, or on some CentOS 6 with upgraded kernel and latest docker, or on some alpine VM with docker daemon.

the C runtime library (and the shared object loader) are the actual interface to the kernel, and cannot be statically linked (as far as I know / am aware)

A glimpse of my past memories relates to what you say, but I can't remember details and looks like at least with "Hello World" it works fine (see below). Also have a look at https://github.com/GoogleContainerTools/distroless/blob/master/base/README.md - gcr.io/distroless/static image contains nothing but /etc and /usr/share files, i.e. no libraries.

$ cat hello.c 
#include <stdio.h>
int main() { printf("Hello world\n"); return 0; }
$ gcc -static -Wall -o hello hello.c
$ ldd ./hello
	not a dynamic executable
$ cat Dockerfile 
FROM scratch
COPY hello /
CMD ["/hello"]
$ docker build -t hello .
$ docker run hello
Hello world

@oberstet
Copy link
Contributor

host has a compatible version of libc

currently yes, but we could build on an old centos, and make any kernel since 2.6.23 work from a single EXE. the linux kernel api is supposed to allow that (https://liquidat.wordpress.com/2007/07/21/linux-kernel-2623-to-have-stable-userspace-driver-api/) - though I haven't tested that.

@haizaar
Copy link
Contributor

haizaar commented Jan 11, 2019

Yes, kernel API is stable, but you are relying on glibc API, namely requiring host to have glibc (not just libc, bug GNU libc).

Mounting /lib and /tmp into container will surely raise eyebrows. I'm not a security expert, but you need to be very careful with it - by default container processes run as root, hence by default crossbar will be able to modify system libraries and have a root access to /tmp directory.

Anyhow, going static looks easy - there is staticx project that does exactly that - stati'fies existing binaries. PyInstaller docs refer to it.

I've tried it on crossbar and after patching staticx as suggested it worked (the resulting library is only 600k bigger than the dynamic one):

$ cd /tmp/cb/
$ staticx /tmp/crossbarfx-linux-amd64-20190108-9553865 crossbarfx.static
$ ldd crossbarfx.static 
	not a dynamic executable
$ cat Dockerfile 
FROM alpine
COPY crossbarfx.static /crossbar
CMD ["/crossbar", "edge", "version"]
$ docker build -t cb .
$ docker run -ti cb
WARNING: The XBR smart contracts are not yet depoyed to public networks. Please set XBR_DEBUG_TOKEN_ADDR manually.
WARNING: The XBR smart contracts are not yet depoyed to public networks. Please set XBR_DEBUG_NETWORK_ADDR manually.

    :::::::::::::::::
          :::::          _____                 __              _____  __
    :::::   :   :::::   / ___/______  ___ ___ / /  ___ _____  / __/ |/_/
    :::::::   :::::::  / /__/ __/ _ \(_-<(_-</ _ \/ _ `/ __/ / _/_>  <
    :::::   :   :::::  \___/_/  \___/___/___/_.__/\_,_/_/   /_/ /_/|_|
          :::::
    :::::::::::::::::   Crossbar Fabric XBR v18.12.1 [20190108-9553865]

...

I've used alpine as a base image because crossbar seems to require some temp directories. Once you have them, I think FROM scratch will work as well.

@oberstet
Copy link
Contributor

@haizaar ok, yes, you are right rgd kernel API and tmp. I agree. (note: this thread now has gotten a bit OT, as we definitely do want great docker images building on upstream pypy image eg - so we will follow up on this as well).


what you found with staticx: awesome! thank you very much for pointing me there=)

I can confirm, this works. It increases the startup time (which doesn't matter much, because crossbar is a long running server thingy anyways), and the size a bit (expected). That's fine.

means: we will add that staticx libc/libdl bundling as a build step. (follow up issue on private repo https://github.com/crossbario/crossbarfx/issues/176)

thanks again, cool;)

-rwxr-xr-x  1 oberstet oberstet 42118480 Jan 11 08:29 crossbarfx*
-rwxr-xr-x  1 oberstet oberstet 41838920 Jan  8 10:41 crossbarfx-linux-amd64-20190108-9553865*

(cpy372_1) oberstet@intel-nuci7:~/test3$ time ./crossbarfx-linux-amd64-20190108-9553865 edge version | grep real

real	0m1,488s
user	0m1,539s
sys	0m0,637s
(cpy372_1) oberstet@intel-nuci7:~/test3$ time ./crossbarfx edge version | grep real

real	0m4,448s
user	0m4,428s
sys	0m0,740s
(cpy372_1) oberstet@intel-nuci7:~/test3$ ldd crossbarfx
	Das Programm ist nicht dynamisch gelinkt
(cpy372_1) oberstet@intel-nuci7:~/test3$ ldd crossbarfx-linux-amd64-20190108-9553865 
	linux-vdso.so.1 (0x00007fff3a795000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9ee141f000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f9ee1202000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9ee0e11000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f9ee1623000)

@oberstet
Copy link
Contributor

because crossbar seems to require some temp directories

fwiw, the need for /tmp comes from pyinstaller (it actually unpacks the EXE on the fly there)

@haizaar
Copy link
Contributor

haizaar commented Jan 11, 2019

You are welcome.
Yes, it's indeed OT, but it was a fruitful discussion.

P.S. Is there a chance someone can follow up on Autobahn issue? - https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/autobahnws/4vnuE8Q7yZ8/Xnb532ykBQAJ? (Sorry for abusing the discussion :)

@oberstet
Copy link
Contributor

sure! I replied on the list. let me know if that helps, I'll answer

@oberstet
Copy link
Contributor

ok, I spent a couple of hours fiddling around and testing our current EXE (everything static but libc/libdl) and fully static EXE (using staticx).

turns out as this:

OS                                   Linux              libc            EXE       100% STATIC

RHEL/CentOS 7                        3.10               glibc 2.17      ok         broken
SLES 12                              3.12               glibc 2.19      ?          ?
Ubuntu 14.04 LTS "Trusty Tahr"       3.13               eglibc 2.19     ok         broken
Debian 8 "Jessie"                    3.16               glibc 2.19      ok         broken

summary: we will keep our current build approach for the EXE, and not statically link libc/libdl


the issues with the 100% static EXE are all similar to this:

(cpy372_1) oberstet@intel-nuci7:~/scm/crossbario/crossbar$ ssh 192.168.1.31
oberstet@192.168.1.31's password: 

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Jan 12 13:28:35 2019
oberstet@debian8:~$ su
Passwort: 
root@debian8:/home/oberstet# cd /media/sf_oberstet/test3
root@debian8:/media/sf_oberstet/test3# ls -la
insgesamt 86528
drwxrwx--- 1 root vboxsf     4096 Jan 12 10:22 .
drwxrwx--- 1 root vboxsf     4096 Jan 12 09:26 ..
drwxrwx--- 1 root vboxsf     4096 Jan 11 11:06 .crossbar
-rwxrwx--- 1 root vboxsf 42094120 Jan 11 14:15 crossbarfx-linux-amd64-20190108-9553865
-rwxrwx--- 1 root vboxsf 41838920 Jan  8 10:41 _crossbarfx-linux-amd64-20190108-9553865
-rwxrwx--- 1 root vboxsf      127 Jan 10 09:03 Dockerfile
-rwxrwx--- 1 root vboxsf        9 Jan 12 09:44 foo
-rwxrwx--- 1 root vboxsf      331 Jan 11 08:30 key.priv
-rwxrwx--- 1 root vboxsf      226 Jan 11 08:30 key.pub
-rwxrwx--- 1 root vboxsf      548 Jan 12 10:22 Makefile
drwxrwx--- 1 root vboxsf     4096 Jan 12 10:19 node2
-rwxrwx--- 1 root vboxsf       50 Jan 11 10:40 README.md
-rwxrwx--- 1 root vboxsf  4610488 Jan 12 10:22 test
-rwxrwx--- 1 root vboxsf       69 Jan 10 13:49 test.c
-rwxrwx--- 1 root vboxsf      360 Jan 11 11:04 test.py
drwxrwx--- 1 root vboxsf     4096 Jan 11 10:40 web
root@debian8:/media/sf_oberstet/test3# ldd crossbarfx-linux-amd64-20190108-9553865 
	das Programm ist nicht dynamisch gelinkt
root@debian8:/media/sf_oberstet/test3# file crossbarfx-linux-amd64-20190108-9553865 
crossbarfx-linux-amd64-20190108-9553865: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
root@debian8:/media/sf_oberstet/test3# ./crossbarfx-linux-amd64-20190108-9553865 edge version
[841] Error loading Python lib '/tmp/_MEIPcBlhX/libpython3.7m.so.1.0': dlopen: /lib/x86_64-linux-gnu/libm.so.6: symbol __get_cpu_features version GLIBC_PRIVATE not defined in file libc.so.6 with link time reference
root@debian8:/media/sf_oberstet/test3# ldd _crossbarfx-linux-amd64-20190108-9553865 
	linux-vdso.so.1 (0x00007ffeecb11000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f90a49d3000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f90a47b8000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f90a440f000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f90a4bd7000)
root@debian8:/media/sf_oberstet/test3# ./_crossbarfx-linux-amd64-20190108-9553865 edge version
WARNING: The XBR smart contracts are not yet depoyed to public networks. Please set XBR_DEBUG_TOKEN_ADDR manually.
WARNING: The XBR smart contracts are not yet depoyed to public networks. Please set XBR_DEBUG_NETWORK_ADDR manually.

    :::::::::::::::::
          :::::          _____                 __              _____  __
    :::::   :   :::::   / ___/______  ___ ___ / /  ___ _____  / __/ |/_/
    :::::::   :::::::  / /__/ __/ _ \(_-<(_-</ _ \/ _ `/ __/ / _/_>  <
    :::::   :   :::::  \___/_/  \___/___/___/_.__/\_,_/_/   /_/ /_/|_|
          :::::
    :::::::::::::::::   Crossbar Fabric XBR v18.12.1 [20190108-9553865]

    Copyright (c) 2013-2019 Crossbar.io Technologies GmbH. All rights reserved.

 Crossbar.io        : 18.12.1
   txaio            : 18.8.1
   Autobahn         : 18.12.1
   Twisted          : 18.9.0-EPollReactor
   LMDB             : 0.94/lmdb-0.9.22
   Python           : 3.7.1/CPython
 Crossbar.io FX     : 18.12.1
   XBR              : 18.12.2
   zLMDB            : 18.12.1
 Frozen executable  : yes
 Operating system   : Linux-3.16.0-4-amd64-x86_64-with-debian-8.1
 Host machine       : x86_64
 Release key        : RWRL2URPgi6MK0CkpI9a4HzKYMTH/wZtSX2tISkJIXE1ctt7+Bf+fWEn

root@debian8:/media/sf_oberstet/test3# 

after compiling 3 versions of glibc from source and trying I gave up on CentOS 6 (kernel 2.6.32 and oldish glibc)

@haizaar
Copy link
Contributor

haizaar commented Jan 18, 2019

I guess that's some glitch between PyInstaller and staticx. I can only suggest opening a ticket in staticx - the maintainer seems to be interested to get these issues solved.

You can still use staticx just for building small docker images as I tried earlier - results in 47Mb Docker image

Out of curiosity I built alpine based docker image of crossbar (not fx obviously) using this Dockerfile - 104Mb

$ docker build -t cbmin .
$ docker images cbmin
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
cbmin               latest              90d0292bce63        24 seconds ago      104MB
$ docker run --rm -ti cbmin version

    :::::::::::::::::
          :::::          _____                      __
    :::::   :   :::::   / ___/____ ___   ___  ___  / /  ___ _ ____
    :::::::   :::::::  / /__ / __// _ \ (_-< (_-< / _ \/ _ `// __/
    :::::   :   :::::  \___//_/   \___//___//___//_.__/\_,_//_/
          :::::
    :::::::::::::::::   Crossbar v19.1.1

    Copyright (c) 2013-2019 Crossbar.io Technologies GmbH, licensed under AGPL 3.0.

 Crossbar.io        : 19.1.1
   txaio            : 18.8.1
   Autobahn         : 19.1.1
   Twisted          : 18.9.0-EPollReactor
   LMDB             : 0.94/lmdb-0.9.22
   Python           : 3.7.2/CPython
 Frozen executable  : no
 Operating system   : Linux-4.15.0-43-generic-x86_64-with
 Host machine       : x86_64
 Release key        : RWSYMhkKjlumkHKD93y/IDruNCLAbAgcBYW551mEk1qtv0KkTapYBfmc

@oberstet
Copy link
Contributor

crossbar docker is ~500MB https://hub.docker.com/r/crossbario/crossbar/tags
crossbar EXE is ~60MB
that's good for me, closing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI-CD Test, build and packaging infra enhancement
Projects
None yet
Development

No branches or pull requests

3 participants