-
Notifications
You must be signed in to change notification settings - Fork 8
Description
TL;DR
Every time an OpenSSL connection is made in a Crystal program built with the Alpine containers (tested on linux/arm64 images), there is a memory leak. I thought this was a Crystal bug at first, but changing the version of OpenSSL used in this repo's Alpine containers eliminated the leak.
Reproducing the bug
To reproduce this bug, you can use this Crystal file:
# src/http_get.cr
require "http/client"
loop { HTTP::Client.get "https://example.com" }And build a container defined by this Dockerfile:
FROM 84codes/crystal:1.17.0-alpine AS builder
WORKDIR /build
COPY src/http_get.cr .
RUN crystal build --release --static --progress --stats http_get.cr -o http_get
FROM scratch
COPY --from=builder /build/http_get /
CMD ["/http_get"]If I've copy/pasted this all from the right places, when you run that container, it will consume an ever-increasing amount of RAM.
Note that reusing TCP connections and/or using a connection pool mitigates this memory leak. It doesn't eliminate it, but there is a workaround as long as you're able to maintain long-lived connections to a low-cardinality set of remote hosts. For most of us, I assume that's true.
Solution (kinda)
Following this advice, I based another container image off of it using this Dockerfile:
ARG version=latest
FROM 84codes/crystal:$version-alpine AS builder
# The OpenSSL packages that come with Alpine have a memory leak, causing
RUN apk add curl perl linux-headers && \
curl -Ls "https://github.com/openssl/openssl/releases/download/openssl-3.3.3/openssl-3.3.3.tar.gz" | tar xz && \
cd openssl-3.3.3 && \
./Configure --openssldir=/etc/ssl && \
make -j
FROM 84codes/crystal:$version-alpine
COPY --from=builder /openssl-3.3.3/*.so.3 /openssl-3.3.3/*.a /usr/lib/
COPY --from=builder /openssl-3.3.3/*.pc /usr/lib/pkgconfig/I've published this container here. If you change the FROM ... AS builder line in the first Dockerfile above to the following, the memory leak disappears:
FROM jgaskins/crystal:1.17.0 AS builder
My container image isn't a drop-in replacement, though. It's just a proof of concept. Simply overwriting OpenSSL lib files, as you might imagine, breaks things like apk. I assume crystal can pull from a different OpenSSL installation than the one in /usr/lib, which would probably be a more useful solution.