Running Mattermost as a Unikernel

Mattermost is an open source, private cloud slack alternative written in Go and React. It has many integrations and can can connect to mysql or postgres for persistence. While there are many "hello world" tutorials out there for running the Nanos unikernel we thought this would be a nice little example to showcase two things:

1) How to run a multi-node setup. Most web applications are more than just one actual application - the canonical example would be a webapp server and its corresponding database.

2) How to interact with taps and bridges for a faster networking experience or if you wanted to build your own private cloud. (Creating taps, today, will only work on Linux as the tuntaposx package does not work anymore but we're looking at alternatives.)

Ready? Let's get started.

Running mysql as a Nanos unikernel

We start by running the mysql server as a unikernel. We've created a convenient package to use and the only thing you need to do is set your base volume size. You could create an external volume but we'll leave that to a different article.

  "BaseVolumeSz": "400m"

Now let's run mysql by creating a tap device and assigning it a static ip:

➜  mysql ops pkg load mysql_5.7.29 -c config.json -p 3306 -b -t tap0 --ip-address
[mysqld --user=root --explicit_defaults_for_timestamp]
booting /home/eyberg/.ops/images/mysqld.img ...
2021-01-11T17:45:54.942930Z 0 [Warning] The syntax '--log_warnings/-W' is deprecated and will be removed in a future release. Please use '--log_error_verbosity' instead.
2021-01-11T17:45:54.945335Z 0 [Warning] Insecure configuration for --secure-file-priv: Location is accessible to all OS users. Consider choosing a different directory.
2021-01-11T17:45:54.945564Z 0 [Note] mysqld (mysqld 5.7.29-0ubuntu0.18.04.1-log) starting as process 2 ...
2021-01-11T17:45:54.957853Z 0 [Note] InnoDB: PUNCH HOLE support available
2021-01-11T17:45:54.959551Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2021-01-11T17:45:54.961557Z 0 [Note] InnoDB: Uses event mutexes
2021-01-11T17:45:54.962979Z 0 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
2021-01-11T17:45:54.965371Z 0 [Note] InnoDB: Compressed tables use zlib 1.2.11
2021-01-11T17:45:54.967107Z 0 [Note] InnoDB: Using Linux native AIO
2021-01-11T17:45:54.968929Z 0 [Note] InnoDB: Number of pools: 1
2021-01-11T17:45:54.970498Z 0 [Note] InnoDB: Using CPU crc32 instructions
2021-01-11T17:45:54.974749Z 0 [Note] InnoDB: Initializing buffer pool, total size = 128M, instances = 1, chunk size = 128M
2021-01-11T17:45:54.985275Z 0 [Note] InnoDB: Completed initialization of buffer pool
2021-01-11T17:45:54.989751Z 0 [Note] InnoDB: If the mysqld execution user is authorized, page cleaner thread priority can be changed. See the man page of setpriority().
2021-01-11T17:45:55.008773Z 0 [Note] InnoDB: Highest supported file format is Barracuda.
2021-01-11T17:45:55.024639Z 0 [Note] InnoDB: Creating shared tablespace for temporary tables
2021-01-11T17:45:55.026961Z 0 [Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
2021-01-11T17:45:55.062911Z 0 [Note] InnoDB: File './ibtmp1' size is now 12 MB.
2021-01-11T17:45:55.065453Z 0 [Note] InnoDB: 96 redo rollback segment(s) found. 96 redo rollback segment(s) are active.
2021-01-11T17:45:55.068301Z 0 [Note] InnoDB: 32 non-redo rollback segment(s) are active.
2021-01-11T17:45:55.070845Z 0 [Note] InnoDB: 5.7.29 started; log sequence number 2631215
2021-01-11T17:45:55.073134Z 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
2021-01-11T17:45:55.075770Z 0 [Note] Plugin 'FEDERATED' is disabled.
2021-01-11T17:45:55.086078Z 0 [Note] InnoDB: Buffer pool(s) load completed at 210111 17:45:55
2021-01-11T17:45:55.097471Z 0 [Note] Found ca.pem, server-cert.pem and server-key.pem in data directory. Trying to enable SSL support using them.
2021-01-11T17:45:55.100836Z 0 [Note] Skipping generation of SSL certificates as certificate files are present in data directory.
2021-01-11T17:45:55.105239Z 0 [Warning] CA certificate ca.pem is self signed.
2021-01-11T17:45:55.107312Z 0 [Note] Skipping generation of RSA key pair as key files are present in data directory.
2021-01-11T17:45:55.110568Z 0 [Note] Server hostname (bind-address): ''; port: 3306
2021-01-11T17:45:55.112817Z 0 [Note]   - '' resolves to '';
2021-01-11T17:45:55.114596Z 0 [Note] Server socket created on IP: ''.
2021-01-11T17:45:55.116577Z 0 [Warning] Insecure configuration for --pid-file: Location '/var/lib/mysql' in the path is accessible to all OS users. Consider choosing a different directory.
2021-01-11T17:45:55.136775Z 0 [Note] Event Scheduler: Loaded 0 events
2021-01-11T17:45:55.138651Z 0 [Note] mysqld: ready for connections.
Version: '5.7.29-0ubuntu0.18.04.1-log'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  (Ubuntu)

We should be able to connect to the server and create a mattermost database:

mysql -u root -h
create datbase mattermost;

ops pkg load by default will re-build the image each time so if you want to skip that you can pass the '-s' flag or use 'ops instance create' instead.

Running MatterMost as a Unikernel

For mattermost we'll want to edit the mattermost config.json (not to be confused with the config.json we use for ops). Create a 'config' directory to store it in. Copy the config.json in. If you want, you can untar the tarball found in ~/.ops/packages/mattermost_5.12.0.tar.gz and grab a copy there. Then you'll want to edit the mysql "DataSource" line in config.json to modify the ip address and l/p:

"DataSource": "root:root@tcp(,utf8\u0026readTimeout=30s\u0026writeTimeout=30s",

Obviously this is just for demo purposes but as a side note - if you do accidently deploy this to a public ip with default credentials the bots that scour the internet looking for these things will be able to connect and write all the SQL their little bot hearts desire, but their db encryption operations fail, the cryptojacking fails, and all the C&C stuff fails. It just shows you how interesting this style of architecture truly is.

Now let's load up mattermost and give it it's own tap and assign it a static ip address:

➜ ops pkg load mattermost_5.12.0 -c config.json -p 8065 -b -t tap1 --ip-address
warning: overwriting existing file /config/config.json hostpath old: /home/eyberg/.ops/.staging/mattermost_5.12.0/sysroot/config/config.json new: config/config.json
[mattermost -c config/config.json --disableconfigwatch]
booting /home/eyberg/.ops/images/mattermost.img ...
{"level":"info","ts":1610387180.047103,"caller":"utils/i18n.go:83","msg":"Loaded system translations for 'en' from '/i18n/en.json'"}
{"level":"info","ts":1610387180.0498981,"caller":"app/server_app_adapters.go:58","msg":"Server is initializing..."}
{"level":"error","ts":1610387180.0560362,"caller":"app/server_app_adapters.go:69","msg":"Failed to parse server templates function not implemented"}
{"level":"info","ts":1610387180.0593297,"caller":"sqlstore/supplier.go:224","msg":"Pinging SQL master database"}
{"level":"info","ts":1610387180.231293,"caller":"sqlstore/upgrade.go:104","msg":"The database schema has been set to version 5.12.0"}
{"level":"error","ts":1610387204.4853191,"caller":"app/server_app_adapters.go:125","msg":"SiteURL must be set. Some features will operate incorrectly if the SiteURL is not set. See documentation for details:"}
{"level":"info","ts":1610387204.5032592,"caller":"filesstore/localstore.go:33","msg":"Able to write files to local storage."}
{"level":"info","ts":1610387204.7344124,"caller":"app/license.go:41","msg":"License key from required to unlock enterprise features."}
{"level":"info","ts":1610387204.982482,"caller":"app/migrations.go:26","msg":"Migrating roles to database."}
{"level":"info","ts":1610387208.7359424,"caller":"sqlstore/post_store.go:1277","msg":"Post.Message supports at most 16383 characters (65535 bytes)"}
{"level":"info","ts":1610387209.2314367,"caller":"app/migrations.go:102","msg":"Migrating emojis config to database."}
{"level":"info","ts":1610387246.2326972,"caller":"mlog/log.go:164","msg":"Starting up plugins"}
{"level":"info","ts":1610387246.241575,"caller":"app/server.go:213","msg":"Current version is 5.12.0 (5.12.0/Sat Jun 15 09:02:50 UTC 2019/bfd66aa445a2df8c6ed6b a9f2567021ecf6c9f3b/403add5df2a572f676fd9da85ad80623cb00b88a)"}
{"level":"info","ts":1610387246.2597792,"caller":"app/server.go:214","msg":"Enterprise Enabled: true"}
{"level":"info","ts":1610387246.267642,"caller":"app/server.go:216","msg":"Current working directory is /"}
{"level":"info","ts":1610387246.276085,"caller":"app/server.go:217","msg":"Loaded config","source":"file:///config/config.json"}
{"level":"info","ts":1610387246.9817462,"caller":"jobs/workers.go:68","msg":"Starting workers"}
{"level":"info","ts":1610387246.989871,"caller":"app/server.go:413","msg":"Starting Server..."}
{"level":"info","ts":1610387246.9986944,"caller":"app/server.go:479","msg":"Server is listening on [::]:8065"}
{"level":"info","ts":1610387247.0144029,"caller":"jobs/schedulers.go:72","msg":"Starting schedulers."}
{"level":"info","ts":1610387247.0446193,"caller":"app/web_hub.go:75","msg":"Starting 2 websocket hubs"}

If everything works you'll see the login/setup page for mattermost!

Deploy Your First Open Source Unikernel In Seconds

Get Started Now.