Demystify GNU Artanis service deployment in product environment
GNU Artanis is not limited to building web applications and websites, it can be used for much more. Recently, I contributed to the system integration testing for a smart factory project, and I’d like to share the process in this post.
While a full system integration test in a production environment can be complex, this post simplifies the process, making it more accessible and easier to reproduce. In the final section, I address cybersecurity concerns and outline some of the solutions I implemented. Additionally, I provide a simple benchmark for the performance of the RESTful API after enabling the Linux Kernel Vaccine.
Due to confidentiality concerns from our partners, I am unable to disclose the complete benchmarking results. However, I believe the information presented here is sufficient to understand the general performance improvements and the impact of the security enhancements.
In this post, I will not mention any non-free product, or even I mentioned any libre-licensed product, it doesn't mean I advocate it. You can always find option, and I may list the existing alternatives.
The OS and hardware
In a production environment, my OS experience spans Debian, Ubuntu, Red Hat, and SUSE, allowing you the flexibility to choose the others you prefer. In this post, we use Ubuntu 24.04 live server ISO.
I tested it under Intel. If you conduct this test on a different hardware platform, I would greatly appreciate your feedback. Feel free to reach out via email at [email protected].
Prepare the environment
What do we need?
- Nginx serves as a reverse proxy and handles static file delivery.
- Guile-3.0 as the Scheme compiler for GNU Artanis.
- GNU Artanis-1.0.0
- byggsteg for the API gateway performance test.
So what is byggsteg?
Byggsteg is a CI/CD project written in Guile Scheme, which I recently ported to GNU Artanis 1.0.0. Choosing an existing libre-licensed project for this system integration test was a practical and logical decision.
Install dependencies
For Artanis-1.0.0, here're the critical dependencies and their versions:
- guile-3.0.9
- guile-dbi-2.1.8
- guile-dbd-mysql-2.1.8
- guile-curl-0.9
- guile-redis-2.2.0
- guile-json-4.7.3
sudo apt install texinfo guile-3.0 guile-3.0-dev build-essential automake git sudo apt install autoconf libtool libmariadb-dev-compat libmariadb-dev libnss3 sudo apt install libnss3-dev gettext redis redis-server libcurl4-openssl-dev sudo apt install nginx
guile-dbi-2.1.8 and guile-dbd-mysql-2.1.8
Since these two dependencies are in the same repo, we build them in a row.
git clone https://github.com/opencog/guile-dbi.git cd guile-dbi git checkout guile-dbi-2.1.8 cd guile-dbi ./autogen.sh ./configure --prefix=/usr make -j5 sudo make install ldconfig cd .. git checkout guile-dbd-mysql-2.1.8 cd guile-dbd-mysql ./autogen.sh ./configure --prefix=/usr make -j5 sudo make install sudo ldconfig
guile-curl-0.9
git clone https://github.com/spk121/guile-curl.git
cd guile-curl
git checkout v0.9
./bootstrap && ./configure --prefix=/usr
make -j5
sudo make install
sudo ln -s /usr/lib/guile/3.0/extensions/libguile-curl.* /usr/lib/
sudo ldconfig
Please note that you need to make the soft link under Debian/Ubuntu. Otherwise the lib may not be recognized.
guile-redis-2.2.0
git clone https://github.com/aconchillo/guile-redis.git
cd guile-redis
git checkout -b 2.2.0
autoreconf -vif
./configure --prefix=/usr
make -j5
sudo make install
sudo ldconfig
guile-json-4.7.3
git clone https://github.com/aconchillo/guile-json.git
cd guile-json
git checkout -b 4.7.3
autoreconf -iv
./configure --prefix=/usr
make -j5
sudo make install
sudo ldconfig
Build GNU Artanis-1.0.0
wget -c https://ftp.gnu.org/gnu/artanis/artanis-1.0.0.tar.gz tar -zxvf artanis-1.0.0.tar.gz cd artanis-1.0.0 ./autogen.sh # don't miss this step, otherwise there's no configure script mkdir -p build cd build ../configure --prefix=/usr make -j5 sudo make install
Configuration
Nginx
For this post, we simplified the config for localhost.
server { listen 80; server_name localhost; root /var/www/byggsteg/pub; index /; server_name localhost: location / { proxy_pass http://127.0.0.1:3000; proxy_pass_header Server; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ~* \.(?:gif|jpg|jpeg|png|bmp|swf|ico|eof|woff|html|svg|bz2|ttf|TTF)$ { expires 30d; } location ~* \.(?:js|css)$ { expires 12h; } }
GNU Artanis
Edit conf/artanis.conf, find these items and change:
server.wqlen = 64 server.polltimeout = 10 server.bufsize = 12288 server.nginx = true
The meaning of these configs is out of the topic of this post.
CDN [optional]
If you deploy the service on the Internet, I recommend using a CDN. While I won’t endorse any specific provider, there are plenty of options available to choose from.
In my setup, I only cache static files such as PNG, CSS, and JS. You’ll need to configure rules to ensure that URLs requiring dynamic handling by GNU Artanis are excluded from caching.
Security enhancement
Cybersecurity is a complex and multifaceted topic. While the HardenedLinux community provides a robust security enhancement profile as a baseline, detailing it here would go against my goal of presenting a minimal and easily reproducible process.
In this post, we focus on the key aspect of the security enhancement: the Linux Kernel Vaccine. I will conduct and compare API stress tests before and after applying the Vaccine.
The Vaccine related performance tested RESTful API should remain unaffected by Nginx or the CDN.
Linux Kernel Vaccine
The Linux Kernel Vaccine represents a new frontier in cybersecurity technology, and its purpose is evident from the name. Currently, there are several implementations of the Linux Kernel Vaccine in the industry, but a detailed comparison among them is beyond the scope of this post.
In our production environment, we choose VED (Vault Exploit Defense) for the Linux Kernel Vaccine, which is licensed under GPLv2.0. If you’re interested, a community version is available through the HardenedLinux community.
In this system integration test, We are able to test with the full version of VED with commertial permission which is largely improved compared to the community version. And it's also GPLv2.0. However, we're not going to reveal the full version code according to this basic rule of GPL.
A notable libre-licensed alternative is GrSecurity, in case you're interested in.
So far, we have showed the best practice of deploying GNU Artanis in a production environment. The next chapter is not for
Performance benchmark [optional]
NOTE: This test is only for the GNU Artanis server core without Nginx or CDN.
Machine, OS and environment
We're not going to advocate the cloud provider, but we can tell you the machine configuration.
- Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz
- 2 CPU cores
- RAM 8GB
- Ubuntu 24.04 live server
- Linux 6.8.0-1021
- GNU Guile 3.0.9
- GNU Artanis 1.0.0
Enable multi.server feature
The benchmark test is based on the multi server feature of GNU Artanis for best throughput performance.
In this benchmark, we set the multi server instances to 2 since the cloud machine has only 2 CPU cores. We also compare the throughput with the unique instance case.
server.multi = true
The multi.server can let you start multiple GNU Artanis instances to handle the requests concurrently. This feature is essential for high-performance web applications. However, one may have to manage each instance by manually, it's a bit tricky since it's designed for distributed situation.
NOTE: If you're looking for an easy setup for higher performance in just one instance, there's experimental **server.workers feature in the coming v1.1.0.**
The benchmark purpose
First, let me define the purpose of this benchmark. According to our experiences, after enabled the Linux Kernel Vaccine, some of the performance will be affacted, for example I/O. So we have to make sure the performance of GNU Artanis will not be down too much under Linux Kernel Vaccine.
Do we need to tune the kernel prarameters?
I don't think so. Tuning kernel parameters is a complex task. Without careful research and adjustment, simple tweaks can potentially degrade performance. In this post, the benchmark is designed to compare the differences between the pre- and post-vaccine stages. The kernel parameters tuning is out of the topic, but could be further discussed in another post.
Specifically, I enabled the multi-server mode in GNU Artanis, which is a feature that allows multiple threads to handle coroutine workqueues concurrently. This feature is essential for high-performance web applications.
Clone byggsteg
In this post, we put byggsteg into /var/www, this will affect the Nginx config file later.
sudo apt install wrk # for the benchmark git clone https://codeberg.org/jjba23/byggsteg.git cd byggsteg make create # will ask for authenication, please input the password, feel free the check the Makefile for the details make prepare-dirs # run with tmux or screen tmux new -t byggsteg art work # ctrl+b d to detach # restore the session # tmux a -t byggsteg
The benchmark
We're going to test the API performance with 100 long connections, 10 threads, and 20 seconds duration. The brief meaning of the API is to check the building instance log and return a json response. Before the test, we need to start an instance with byggsteg.
curl 'localhost:3000/api/v1/jobs/manage/submit' -X POST -H 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'job-code=`((project . "free-alacarte")(branch-name . "trunk")(task . "stack-test")(clone-url . "https://codeberg.org/jjba23/free-alacarte"))' # ==> response a json # {"log-filename":"667265652d616c6163617274655f5f31323a34303a34345f5f32382d31322d323032342e62796767737465672e6c6f670a"}
According to the response, we can get the log file name, and we can use it to check the log. The API usage is trivial in this post, if you're interested, please visit the byggsteg project.
Now let's test the performance of the API.
wrk -c 100 -t 10 -d20s --latency http://localhost:3000/api/v1/logs/667265652d616c6163617274655f5f31323a34303a34345f5f32382d31322d323032342e62796767737465672e6c6f670a # the response will be like this json # {"success":"true","failure":"false","in-progress":"false","log-filename":"free-alacarte__12:40:44__28-12-2024.byggsteg.log","log-data":"0a7374617274696e67206e6577206a6f622e2e2e0a0a0a"}
Unique instance
- Without Linux Kernel Vaccine (VED)
Running 20s test @ http://localhost:3000/api/v1/logs/667265652d616c6163617274655f5f31323a34303a34345f5f32382d31322d323032342e62796767737465672e6c6f670a 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 462.23ms 182.24ms 794.22ms 59.34% Req/Sec 29.55 24.81 191.00 76.59% Latency Distribution 50% 460.47ms 75% 617.11ms 90% 713.63ms 99% 773.00ms 4274 requests in 20.05s, 1.44MB read Requests/sec: 213.16 Transfer/sec: 73.48KB
- With Linux Kernel Vaccine (VED)
Running 20s test @ http://localhost:3000/api/v1/logs/667265652d616c6163617274655f5f31323a34303a34345f5f32382d31322d323032342e62796767737465672e6c6f670a 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 497.41ms 199.72ms 887.80ms 60.05% Req/Sec 28.21 22.37 111.00 78.12% Latency Distribution 50% 499.25ms 75% 665.73ms 90% 768.93ms 99% 832.29ms 3970 requests in 20.05s, 1.34MB read Requests/sec: 198.03 Transfer/sec: 68.27KB
The performance was reduced around 7% with Linux Kernel Vaccine.
Multi instances = 2
- Without Linux Kernel Vaccine (VED)
Running 20s test @ http://localhost:3000/api/v1/logs/667265652d616c6163617274655f5f31323a34303a34345f5f32382d31322d323032342e62796767737465672e6c6f670a 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 378.10ms 398.30ms 1.93s 82.01% Req/Sec 59.04 55.22 303.00 78.40% Latency Distribution 50% 322.70ms 75% 649.01ms 90% 962.45ms 99% 1.45s 6946 requests in 20.05s, 2.34MB read Requests/sec: 346.49 Transfer/sec: 119.44KB
- With Linux Kernel Vaccine (VED)
Running 20s test @ http://localhost:3000/api/v1/logs/667265652d616c6163617274655f5f31323a34303a34345f5f32382d31322d323032342e62796767737465672e6c6f670a 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 406.81ms 444.74ms 1.98s 82.23% Req/Sec 58.04 54.69 320.00 77.38% Latency Distribution 50% 330.33ms 75% 707.31ms 90% 1.07s 99% 1.61s 6553 requests in 20.05s, 2.21MB read Socket errors: connect 0, read 0, write 0, timeout 15 Requests/sec: 326.82 Transfer/sec: 112.66KB
The performance was reduced around 5% with Linux Kernel Vaccine.
Conclusion
The tested byggsteg code is not a fast implementation, there're frequent disk I/O operations, and we don't set any memory cache intendedly. The purpose of this test is to show the performance (included I/O, string parsing and JIT) difference before and after enabling the Linux Kernel Vaccine.
The performance of GNU Artanis was reduced by around 5%~7% after enabling the VED Linux Kernel Vaccine. This result is within the expected range, and the performance is still acceptable for a production environment. Considering the security enhancements provided by the Vaccine, the trade-off is reasonable.
There's also ARM64 benchmark you may want to take a look.
Feedback please!
Comments are welcome, you may send mail to [email protected]. Happy hacking!