libgdiplus
libgdiplus copied to clipboard
the functionality broken after install the latest version in alpine
dockerfile
FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine3.7 AS base
WORKDIR /docker
EXPOSE 5000
RUN echo 'http://mirrors.aliyun.com/alpine/v3.7/main' > /etc/apk/repositories && \
echo 'http://mirrors.aliyun.com/alpine/v3.7/community' >> /etc/apk/repositories && \
echo 'http://mirrors.aliyun.com/alpine/edge/testing' >> /etc/apk/repositories && \
sed -i "s@\(.*http://\)[^/]*\(.*\)@\1mirror.sy\2@" /etc/apk/repositories && apk update && apk add tzdata terminus-font libgdiplus && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone
there is a captcha functionality that depend the System.Drawing
,the functionality works well before but it was broken recently, the error message
System.TypeInitializationException: 'The type initializer for 'Gdip' threw an exception.'
I found there is a new release of libgdiplus(libgdiplus-6.0.5-r1)
at 2021-Jan-24 04:13:39.
And the early container that build from same dockerfile and install the libgdiplus-6.0.5-r0
works well
Is this package have any incompatible in alpine?
Regards.
public Bitmap CreateBitmapByImgVerifyCode(string verifyCode, int width, int height)
{
Random random = new Random();
Bitmap bitmap = new Bitmap(width, height);
Graphics g = Graphics.FromImage(bitmap);
using (var font = new Font("Arial", 36, (FontStyle.Bold | FontStyle.Italic)))
{
SizeF totalSizeF = g.MeasureString(verifyCode, font);
SizeF curCharSizeF;
PointF startPointF = new PointF(((width - totalSizeF.Width) / verifyCode.Length) / 2, (height - totalSizeF.Height) / 2);
g.Clear(Color.White);
for (int i = 0; i < verifyCode.Length; i++)
{
using (var brush = new LinearGradientBrush(new Point(0, 0), new Point(1, 1), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255))))
{
g.DrawString(verifyCode[i].ToString(), font, brush, startPointF);
curCharSizeF = g.MeasureString(verifyCode[i].ToString(), font);
startPointF.X += width / verifyCode.Length;
}
}
}
using (var pen = new Pen(Color.FromArgb(random.Next()), 5))
{
for (int i = 0; i < 20; i++)
{
int x1 = random.Next(bitmap.Width);
int x2 = random.Next(bitmap.Width);
int y1 = random.Next(bitmap.Height);
int y2 = random.Next(bitmap.Height);
g.DrawLine(pen, x1, y1, x2, y2);
}
}
for (int i = 0; i < 100; i++)
{
int x = random.Next(bitmap.Width);
int y = random.Next(bitmap.Height);
bitmap.SetPixel(x, y, Color.FromArgb(random.Next()));
}
using (var pen = new Pen(Color.Silver))
{
g.DrawRectangle(pen, 0, 0, bitmap.Width - 1, bitmap.Height - 1);
g.Dispose();
return bitmap;
}
}
public byte[] CreateByteByImgVerifyCode(string verifyCode, int width, int height)
{
using (var bitmap = CreateBitmapByImgVerifyCode(verifyCode, width, height))
{
using (var stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Jpeg);
return stream.ToArray();
}
}
}
That's odd. What output do you get if you export LD_DEBUG=libs
(or ENV LD_DEBUG=libs
in your Dockerfile) and then launch your .NET app?
After added ENV LD_DEBUG=libs
in dockerfile
The pod log doesn't output the library paths that trying file both in the application just started and the captcha API was invoked. It's weird, am I not use it correctly? FYI, Below is the error screenshot
Are you using a multi-stage build by any chance? Make sure you set ENV LD_DEBUG=libs
in your final container, and not just a build container (which is later thrown away).
You should probably also be able to set the LD_DEBUG environment variable via the --env
command line argument if you launch the container with docker run
, or in the env:
field for your container if you use Kubernetes.
yeap. the base container is also the final container.
and run kubectl exec
enter the current kubernetes pod, run echo $LD_DEBUG
, output lib
correctly
Ah yes, my bad. LD_DEBUG is a glbc-thing, and Alpine runs on musl. I'd recommend you use another mechanism, like strace, to see what's going on.
It's not my area of expertise so I can't help much, but this article seems like it might be useful: https://jvns.ca/blog/2014/03/10/debugging-shared-library-problems-with-strace/
After comparing the two container which one is r1
and another r0
. Found some clues
run ldd /usr/lib/libgdiplus.so.0
to trace the denpendency in r1
container, I found there is some error
same command in r0
container
And I copy the libgdiplus.so.0.0.0
file which the gdi package real link to from the r0
container to r1
container.
then the captcha functionality recovered
Hi, I've also started having the same issue recently, it started happening around a month ago.
Using .NET Core 3.1 Alpine (FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine) and libgdiplus-dev apk package from dl-3.alpinelinux.org/alpine/edge/testing. The containers are hosted by AWS Fargate (1.4.0), the error seems to be consistent and spinning up new containers doesn't help. At the same time I was unable to reproduce the issue in plain docker on my MacOS. :(
ldd gives pretty much the similar output as above:
/lib/ld-musl-x86_64.so.1 (0x7f862cc18000) libpangocairo-1.0.so.0 => /usr/lib/libpangocairo-1.0.so.0 (0x7f862cb95000) libpango-1.0.so.0 => /usr/lib/libpango-1.0.so.0 (0x7f862cb57000) libgobject-2.0.so.0 => /usr/lib/libgobject-2.0.so.0 (0x7f862cb0d000) libglib-2.0.so.0 => /usr/lib/libglib-2.0.so.0 (0x7f862ca13000) libharfbuzz.so.0 => /usr/lib/libharfbuzz.so.0 (0x7f862c97e000) libcairo.so.2 => /usr/lib/libcairo.so.2 (0x7f862c891000) libjpeg.so.8 => /usr/lib/libjpeg.so.8 (0x7f862c7fb000) libtiff.so.5 => /usr/lib/libtiff.so.5 (0x7f862c791000) libgif.so.7 => /usr/lib/libgif.so.7 (0x7f862c786000) libpng16.so.16 => /usr/lib/libpng16.so.16 (0x7f862c756000) libX11.so.6 => /usr/lib/libX11.so.6 (0x7f862c634000) libexif.so.12 => /usr/lib/libexif.so.12 (0x7f862c5f1000) libfontconfig.so.1 => /usr/lib/libfontconfig.so.1 (0x7f862c5b5000) libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f862cc18000) libpangoft2-1.0.so.0 => /usr/lib/libpangoft2-1.0.so.0 (0x7f862c59e000) libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0x7f862c4ec000) libfribidi.so.0 => /usr/lib/libfribidi.so.0 (0x7f862c4cf000) libffi.so.6 => /usr/lib/libffi.so.6 (0x7f862c4c6000) libpcre.so.1 => /usr/lib/libpcre.so.1 (0x7f862c469000) libintl.so.8 => /usr/lib/libintl.so.8 (0x7f862c459000) libgraphite2.so.3 => /usr/lib/libgraphite2.so.3 (0x7f862c432000) libpixman-1.so.0 => /usr/lib/libpixman-1.so.0 (0x7f862c3a1000) libxcb-shm.so.0 => /usr/lib/libxcb-shm.so.0 (0x7f862c39c000) libxcb.so.1 => /usr/lib/libxcb.so.1 (0x7f862c375000) libxcb-render.so.0 => /usr/lib/libxcb-render.so.0 (0x7f862c366000) libXrender.so.1 => /usr/lib/libXrender.so.1 (0x7f862c35a000) libXext.so.6 => /usr/lib/libXext.so.6 (0x7f862c347000) libz.so.1 => /lib/libz.so.1 (0x7f862c32d000) libexpat.so.1 => /usr/lib/libexpat.so.1 (0x7f862c30b000) libuuid.so.1 => /lib/libuuid.so.1 (0x7f862c302000) libbz2.so.1 => /usr/lib/libbz2.so.1 (0x7f862c2f3000) libXau.so.6 => /usr/lib/libXau.so.6 (0x7f862c2ee000) libXdmcp.so.6 => /usr/lib/libXdmcp.so.6 (0x7f862c2e6000) libbsd.so.0 => /usr/lib/libbsd.so.0 (0x7f862c2d0000) Error relocating /usr/lib/libgdiplus.so.0: hb_ot_metrics_get_position: symbol not found Error relocating /usr/lib/libgdiplus.so.0: pango_font_get_hb_font: symbol not found
The workaround of copying the older libgdiplus.so.0.0.0
file (from libgdiplus-dev, probably 6.0.5 or 6.0.4, whichever was previously on alpine apk feed in late 2020) does help (not sure if it's a legit thing to do and if that could cause any hard-to-trace issues in runtime).
Happy to provide more info if needed.
P.S. Would it be possible to restore previous version of libgdiplus-dev package back to alpine apk feed? If not, is there any way to build & install on (or for) alpine from github sources? (I've tried building on ubuntu, but the binaries it creates are significantly bigger than the ones in alpine feed packages)
I ended up building 6.0.5 (r0) using abuild to avoid any accidental binaries incompatibility in future, in case if anyone else needs it, here's how:
FROM alpine:latest AS build_libgdiplus
RUN apk add alpine-sdk
RUN adduser -D abuild_user \
&& addgroup abuild_user abuild
RUN mkdir /build \
&& chmod -R a+rwx /build
USER abuild_user
WORKDIR /build
COPY APKBUILD .
RUN abuild-keygen -a
RUN abuild -r -P /build
USER root
RUN chown -R root:root /build
FROM alpine:latest
COPY --from=build_libgdiplus /build/x86_64/ /libgdiplus-apk
RUN apk add --allow-untrusted /libgdiplus-apk/libgdiplus-6.0.5-r0.apk /libgdiplus-apk/libgdiplus-dev-6.0.5-r0.apk
Note: you'd need to have a file named APKBUILD in the working directory of where you build that dockerfile, the contents can be downloaded from alpine repo: https://gitlab.alpinelinux.org/alpine/aports/-/blob/44b1843ba5ebd29dc4c66b4269cb679bd14fad4b/testing/libgdiplus/APKBUILD (this one is 6.0.5-r0).