|
| 1 | +# Nix expressions for emqttd |
| 2 | + |
| 3 | +This repo contains nix-expressions to build emqttd and a docker image for it in a deterministic manner. |
| 4 | + |
| 5 | +# Usage |
| 6 | + |
| 7 | +Ensure [nix][] is installed, and run |
| 8 | + |
| 9 | +`nix-build` to build emqttd. This version is expected to be run in a container. |
| 10 | + |
| 11 | +## Docker image |
| 12 | + |
| 13 | +`nix-build docker.nix` will build a docker image. Note that this does not require docker to be installed on the machine. |
| 14 | + |
| 15 | +A path that looks similar to |
| 16 | +`/nix/store/inxac5930nz66gsx64wvx8hh78bfaayv-docker-image-apinf-emqttd.tar.gz` |
| 17 | +will be printed at the end, if the build was succesful. This is an archive in |
| 18 | +the `docker save` format, and can be transferred to the production machines or |
| 19 | +distributed using container registries. |
| 20 | + |
| 21 | +To load the image into a local docker daemon, run `docker load -i <path-to-image>`. |
| 22 | + |
| 23 | +The image expects hostnames `elasticsearch` and `postgres` to resolve to the respective services. |
| 24 | + |
| 25 | +[nix]: https://nixos.org/nix/ |
| 26 | + |
| 27 | +## Docker compose |
| 28 | + |
| 29 | +The version of emqttd bundled here has elasticsearch logger and auth_pgsql modules enabled. To run everything together, |
| 30 | +a compose file is provided for convenience. With `docker-compose` installed, run |
| 31 | + |
| 32 | +``` |
| 33 | +cd docker-compose |
| 34 | +docker-compose up |
| 35 | +``` |
| 36 | + |
| 37 | +This will also run elasticsearch and postgresql populated with default values. |
| 38 | +Note that there may be start order issues at times, we are working on improving |
| 39 | +appropriate waiting and retry strategies. |
| 40 | + |
| 41 | +To prevent startup order issues, you can run |
| 42 | + |
| 43 | +``` |
| 44 | +docker-compose up postgres |
| 45 | +docker-compose up elasticsearch |
| 46 | +docker-compose up emqttd |
| 47 | +``` |
| 48 | + |
| 49 | +# Testing |
| 50 | + |
| 51 | +There is no automated end to end test suite (yet). To verify if everything |
| 52 | +works as expected, install an mqtt client and publish and/or subscribe to |
| 53 | +emqttd. |
| 54 | + |
| 55 | +Verify the following: |
| 56 | + |
| 57 | + - Access control rules set in postgres database must be honored for publish/subscribe. |
| 58 | + - Events corresponding to various phases in mqtt workflow should be logged to Elasticsearch. |
| 59 | + |
| 60 | +When running from docker compose, the testing must happen in containers |
| 61 | +connected to the same network, or with ports exposed to host to prevent |
| 62 | +connectivity issues. |
| 63 | + |
| 64 | +# FAQ |
| 65 | + |
| 66 | +## Why is this necessary? |
| 67 | + |
| 68 | +### Short answer |
| 69 | + |
| 70 | +We need a way to build emqttd in a deterministic manner, with strong assurance |
| 71 | +that simply repeating the build won't result in a wildly different result. |
| 72 | + |
| 73 | +### Long answer |
| 74 | + |
| 75 | +A simple make in `emq-relx` repository will build emqttd, and there is a |
| 76 | +helpful `emq-docker` repo to build a docker image. However, there is no |
| 77 | +guarantee that if the build is repeated, the results are functionally |
| 78 | +identical. |
| 79 | + |
| 80 | +Erlang ecosystem has a dependency problem. Semantic versioning was not around |
| 81 | +when a majority of projects were written. Most projects still use source |
| 82 | +dependencies, often referring to a git branch or tag instead of an sha. Lock |
| 83 | +files were unheard of until rebar3 or mix, but many projects still use |
| 84 | +erlang.mk or rebar. |
| 85 | + |
| 86 | +As a result, it is possible (and not very uncommon) for two builds made at |
| 87 | +different points of time to use different source versions of certain |
| 88 | +dependencies altogether. Most of the time the updates may be |
| 89 | +backwards-compatible, but it can be frustrating to have to hunt down the |
| 90 | +changes. |
| 91 | + |
| 92 | +Commonly packages are deployed in compiled form, exact versions of sources are |
| 93 | +not recorded. When an issue is encountered in production, there is no fool |
| 94 | +proof way to identify the corresponding source. |
| 95 | + |
| 96 | +In addition, erlang package namespace is global. i.e; there can only be one |
| 97 | +version of a package loaded into the erlang VM at any point of time. (Ignoring |
| 98 | +hot code upgrades). However global dependency resolution is a relatively new |
| 99 | +feature introduced with rebar3 and mix, both of which are not widely adopted. |
| 100 | +This can result in multiple copies of the same dep being pulled and compiled |
| 101 | +during a typical build. |
| 102 | + |
| 103 | +All of this results in a lot of headache when trying to debug issues in |
| 104 | +production. Not being able to reproduce a build is silly. It is an |
| 105 | +unforgivable sin to ship products that can not be reliably rebuilt from source. |
| 106 | + |
| 107 | +## How does this work? |
| 108 | + |
| 109 | +[Nix][nix] is a functional package manager. To quote <https://nixos.org/nix>: |
| 110 | + |
| 111 | +> Nix is a powerful package manager for Linux and other Unix systems that makes |
| 112 | +> package management reliable and reproducible. |
| 113 | +
|
| 114 | +The internals of nix are out of scope here, and are described in detail in the |
| 115 | +documentation. |
| 116 | + |
| 117 | +We just asked nix to build the dependencies and finally emqttd and the docker image. |
| 118 | + |
| 119 | +The builds are repeatable since expected git revisions and the hashes of sources |
| 120 | +are tracked in the repository. The expressions allow building everything |
| 121 | +(including erlang, bash, gcc etc) from source in a predictable manner. |
| 122 | + |
| 123 | +## Why is the docker image so large? |
| 124 | + |
| 125 | +The smallest image can probably be made in under 20MB, whereas the images built |
| 126 | +using this repo are a little over half a GB in size. This is due to a few reasons: |
| 127 | + |
| 128 | +1. Some nix expressions we use are very conservative and include all possible |
| 129 | + dependencies. This increases the closure size. |
| 130 | +2. The entire build closure is included in the image. This includes the exact |
| 131 | + erlang version, the exact bash version, sources of erlang deps, etc. Most of |
| 132 | + this is not strictly necessary, however, having this around does not |
| 133 | + particularly hurt. |
| 134 | +3. The lean image uses musl libc whereas we use glibc. This alone makes a huge |
| 135 | + difference, and replacing it takes some effort. |
| 136 | + |
| 137 | +We believe the increased size is worth the benefits of reproducible builds, |
| 138 | +when storage is relatively cheap. We will consider replacing glibc with musl |
| 139 | +libc to reduce the size if it becomes a pressing issue. |
| 140 | + |
| 141 | +## Won't updating the release be difficult? |
| 142 | + |
| 143 | +Updating the release requires each dependency to be carefully considered, and |
| 144 | +its derivation to be updated. This is a _good_ thing. |
| 145 | + |
| 146 | +Updating the revisions and hashes can be automated with some effort, and the |
| 147 | +resulting workflow can be made as simple as that of working with similar |
| 148 | +locking and verifying package managers and build systems, for example yarn. |
0 commit comments