obsidiansystems / obelisk

Functional reactive web and mobile applications, with batteries included.
https://reflex-frp.org
BSD 3-Clause "New" or "Revised" License
964 stars 107 forks source link

Some thoughts on cutting down on Docker image sizes #193

Open dalaing opened 6 years ago

dalaing commented 6 years ago

Hi again :)

I've been playing around with Obelisk and docker, initially via a Dockerfile and more recently via this bit of nix in the same directory as a fresh ob init:

# to use this:
# > nix-build docker.nix -o result-docker
# > docker load --input ./result-docker
# > docker run -p 8000:8000 ob-test

{ nixpkgs ? import <nixpkgs> {} }:
let
  ob-test = (import ./. {}).exe;
in
 nixpkgs.pkgs.dockerTools.buildImage {
    name = "ob-test";
    contents = ob-test;
    config = {
      Cmd = [ "${ob-test}/backend" ];
      ExposedPorts = {
        "8000/tcp" = {};
      };
    };
 }

I noticed that the image size of the default Obelisk project (and hence the closure size of the exe derivation) is pretty big - it weighs in at 603Mb.

I had a poke around and it looked like the bulk of that was coming from the backend, which uses the static renderer to create the head section of the page. This brings in the GHC build of reflex-dom, and via that Webkit, and via that a big pile of X libraries.

So I played around a little here and made some alterations so that the head section could be pregenerated. It seems to work with both ob run (and the associated ghcid reloading) and with the exe derivation, which is nice.

Initially it only saved a 1Mb, until I removed obelisk-executable-config-inject from skeleton/backend/backend.cabal, which also uses the GHC build of reflex-dom. If there is someway to pregenerate that stuff, then we can get the size of the output down. At that point we're at 85.5MB.

I poked around some more, and saw that nodejs and a few friends was in there, so I made this ultra dodgy change, and the output is now at 46.2Mb.

This has all been done in horrendous hack mode in a short amount of time, but I thought I'd post it in here in case it seems useful / gives people ideas / is an interesting discussion point.

ryantrinkle commented 6 years ago

Wow, nice sleuthing! I think that indicates we should definitely split different reflex-dom backends into their own packages, so this stuff can simply import the right one.

On Thu, Aug 9, 2018, 01:14 Dave Laing notifications@github.com wrote:

Hi again :)

I've been playing around with Obelisk and docker, initially via a Dockerfile and more recently via this bit of nix in the same directory as a fresh ob init:

to use this:

> nix-build docker.nix -o result-docker

> docker load --input ./result-docker

> docker run -p 8000:8000 ob-test

{ nixpkgs ? import {} }: let ob-test = (import ./. {}).exe; in nixpkgs.pkgs.dockerTools.buildImage { name = "ob-test"; contents = ob-test; config = { Cmd = [ "${ob-test}/backend" ]; ExposedPorts = { "8000/tcp" = {}; }; }; }

I noticed that the image size of the default Obelisk project (and hence the closure size of the exe derivation) is pretty big - it weighs in at 603Mb.

I had a poke around and it looked like the bulk of that was coming from the backend, which uses the static renderer to create the head section of the page. This brings in the GHC build of reflex-dom, and via that Webkit, and via that a big pile of X libraries.

So I played around a little here https://github.com/dalaing/obelisk/commit/55dae3dea4c3dea922ba5e36f014ba9dfefedb4f and made some alterations so that the head section could be pregenerated. It seems to work with both ob run (and the associated ghcid reloading) and with the exe derivation, which is nice.

Initially it only saved a 1Mb, until I removed obelisk-executable-config-inject from skeleton/backend/backend.cabal, which also uses the GHC build of reflex-dom. If there is someway to pregenerate that stuff, then we can get the size of the output down. At that point we're at 85.5MB.

I poked around some more, and saw that nodejs and a few friends was in there, so I made this https://github.com/dalaing/obelisk/commit/55dae3dea4c3dea922ba5e36f014ba9dfefedb4f#diff-5712e736e0de6ba170577f8472c398e9L192 ultra dodgy change, and the output is now at 46.2Mb.

This has all been done in horrendous hack mode in a short amount of time, but I thought I'd post it in here in case it seems useful / gives people ideas / is an interesting discussion point.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/obsidiansystems/obelisk/issues/193, or mute the thread https://github.com/notifications/unsubscribe-auth/ABGlYBvLSY9HB0O58yhRRh-zFl-QOyn9ks5uO8UogaJpZM4V1Dkb .

ElvishJerricco commented 6 years ago

Couldn't the backend code just import reflex-dom-core to avoid importing stuff like webkit?

ryantrinkle commented 6 years ago

reflex-dom-core actually still brings that stuff in: https://github.com/reflex-frp/reflex-dom/blob/4a65735dab7cc23962621a536fef4aaf983b3881/reflex-dom-core/reflex-dom-core.cabal#L59-L60

We'd need to chop out ImmediateDomBuilderT to save this space, I think.

ElvishJerricco commented 6 years ago

@ryantrinkle My impression is that bringing in ghcjs-dom and jsaddle does not bring in JSaddle backends such as jsaddle-webkit2gtk, and thus would not bring in webkit.

3noch commented 6 years ago

From my tests, @ElvishJerricco is right that webkit is not brought into the closure merely by depending on jsaddle and ghcjs-dom. I'm not sure what does bring it in.