Skip to content

Commit ffedad5

Browse files
committed
add nix expressions, docker-compose config
1 parent 2828c76 commit ffedad5

12 files changed

+763
-0
lines changed

README.md

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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.

cuttlefish-remove-vsn-git.patch

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- cuttlefish.orig/src/cuttlefish.app.src 2017-11-09 14:46:55.813476414 +0000
2+
+++ cuttlefish/src/cuttlefish.app.src 2017-11-09 14:47:25.989264889 +0000
3+
@@ -1,7 +1,7 @@
4+
{application, cuttlefish,
5+
[
6+
{description, "cuttlefish configuration abstraction"},
7+
- {vsn, git},
8+
+ {vsn, "git-26989cf"},
9+
{registered, []},
10+
{applications, [
11+
kernel,

default.nix

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{ pkgs ? import <nixpkgs> {}}:
2+
3+
pkgs.callPackage ./emqttd.nix {
4+
erlang = pkgs.beam.interpreters.erlangR19_nox;
5+
beamPackages = pkgs.beam.packages.erlangR19.override {
6+
erlang = pkgs.beam.interpreters.erlangR19_nox;
7+
};
8+
relxExe = pkgs.callPackage ./relx.nix {};
9+
}

0 commit comments

Comments
 (0)