As everyone has already
heard , at the end of May Telegram was released by the official MTProto Proxy (aka MTProxy) server, written on this
page . In 2018, without Docker, there is little where, because it is accompanied in the same “official”
way in the zero-config format. Everything would be fine, but three “but” slightly spoiled the impression of the release: image weighs> 130 Mb (Debian is rather thick there, but not the usual Alpine), because of “zero-config” it is not always conveniently configured (only with environmental parameters) and the guys forgot to hike, put the Dockerfile.
TL; DR We will do almost 1-in-1 official alpine-based docker image of 5.94MB in size and put it
here (and Dockerfile
here ); Along the way, we will understand how sometimes you can make friends software with Alpine with the help of pliers and a file, and we’ll play with a bit of size, just a fan for.
Image content
Once again, why all the fuss? Let's see what is the official image of the
history team:
$ docker history --no-trunc --format "{{.Size}}\t{{.CreatedBy}}" telegrammessenger/proxy
The layers are read from the bottom above, respectively:

The thickest is Debian Jessie, from which the original image is inherited, and we have to get rid of it in the first place (alpine: 3.6, by comparison, it weighs 3.97MB); next in size are curl and fresh certificates. To figure out what the other two files and the directory mean, take a look inside using the
run command, replace CMD with bash (this way you can walk around the running image, get to know each other more closely, launch certain fragments, copy what is useful):
$ docker run -it --rm telegrammessenger/proxy /bin/bash
Now we can easily restore the picture of the incident - something like the lost official Dockerfile looked like:
FROM debian:jessie-20180312 RUN set -eux \ && apt-get update \ && apt-get install -y --no-install-recommends curl ca-certificates \ && rm -rf /var/lib/apt/lists/* COPY ./mtproto-proxy /usr/local/bin RUN mkdir /data COPY ./secret/ /etc/telegram/ COPY ./run.sh /run.sh CMD ["/bin/sh", "-c", "/bin/bash /run.sh"]
Where mtproto-proxy is a compiled server, the secret folder only contains the hello-explorers-how-are-you file with the AES encryption key (see the server commands, by the way, there is an official recommendation to get the key via the API, but put it in this way probably to avoid a situation where the API is also blocked), and run.sh makes all the preparations for starting the proxy.
Assembly
Under CentOS 7 MTProxy on Habré already
collected , we will try to assemble an image under Alpine and save megabytes, 130, in the resulting docker image.
A distinctive feature of Alpine Linux is the use of musl instead of glibc. Both are standard C libraries. Musl is miniaturized (it doesn’t have the fifth part of the “standard”), but the volume and performance (promised at least) decide when it comes to Docker. Yes, and put glibc on Alpine is not racially correct, uncle Jakub Jirutka
will not understand , for example.
We will also collect in docker, to isolate dependencies and get freedom for experiments, so create a new Dockerfile:
FROM alpine:3.6 RUN apk add
From dependencies, we can use git (and not only for cloning the official repository, the make file will embed the sha of the commit into the version), make, gcc and header files (the minimum set is obtained experimentally). We only clone the master branch with a depth of 1 commit (we definitely do not need a story). Well, let's try to utilize all host resources when compiling with the -j key. Deliberately divided into more layers in order to get convenient caching during reassembly (usually there are a lot of them).
We will run the
build command (being in a directory with Dockerfile):
$ docker build -t mtproxy:test .
Here is the first problem:
In file included from ./net/net-connections.h:34:0, from mtproto/mtproto-config.c:44: ./jobs/jobs.h:234:23: error: field 'rand_data' has incomplete type struct drand48_data rand_data; ^~~~~~~~~
Actually, all the following will be associated with it. First, for those who are not familiar with the syami, the compiler actually swears at the absence of the declaration of the drand48_data structure. Secondly, the musl developers scored on thread-safe random functions (with the _r postfix) and on everything connected with them (including structures). And the Telegram developers, in turn, did not bother with compiling for systems where random_r and its brethren are not implemented (in many OS libraries you can see the HAVE_RANDOM_R flag or its arbitrary + the presence or absence of this group of functions is usually taken into account in the auto-configurator).
Well, now we will definitely install glibc? Not! We will copy from glibc at a minimum what we need and make a patch for the MTProxy sources.
In addition to problems with random_r, we hapnem the problem with the backtrace function (execinfo.h), which is used to output stack backtrace in the case of an exception: you could try replacing it with implementation from libunwind, but it's not worth it, because the call was framed by checking for __GLIBC__.
The contents of the patch random_compat.patch Put it in the ./patches folder and slightly modify our Dockerfile to apply the patch on the fly:
FROM alpine:3.6 COPY ./patches /mtproxy/patches RUN apk add --no-cache --virtual .build-deps \ git make gcc musl-dev linux-headers openssl-dev \ && git clone --single-branch --depth 1 https://github.com/TelegramMessenger/MTProxy.git /mtproxy/sources \ && cd /mtproxy/sources \ && patch -p0 -i /mtproxy/patches/randr_compat.patch \ && make -j$(getconf _NPROCESSORS_ONLN) \ && cp /mtproxy/sources/objs/bin/mtproto-proxy /mtproxy/ \ && rm -rf /mtproxy/{sources,patches} \ && apk add --no-cache --virtual .rundeps libcrypto1.0 \ && apk del .build-deps
Now the assembled mtproto-proxy binary is at least launched, and we can move on.
Registration
It's time to turn the original run.sh into docker-entrypoint.sh. In my opinion, this is logical when the “binding binding” goes to ENTRYPOINT (it can always be overloaded from the outside), and the arguments for launching a docked application can be maximized in the CMD (+ environment variables as an understudy).
We could install bash and full-fledged grep in our alpine image (I will explain later) to avoid the headache and use the original code as it is, but it will disgrace our miniature image, so we will grow the real, his mother, bonsai.
Let's start with shebanga, replace
#!/bin/bash
with
#!/bin/sh
. The default for alpine ash is able to digest almost the entire bash "as is" syntax, but we will still face one problem - for unknown reasons, he refused to adopt a parenthesis in one of the conditions, so we will deploy it, inverting the comparison logic:

Now we are waiting for disassembly with grep'om, which in a busybox delivery is slightly different from the usual (and, by the way, much slower, keep in mind in your projects). First, he does not understand the expression
{,15}
, it will be necessary to explicitly specify
{0,15}
. Secondly, it does not support the
-P
(perl style) flag, but it calmly digests the expression when extended (-E) is enabled.
All we have in dependencies is curl (there is no point in replacing it with wget from busybox) and libcrypto (it’s enough, directly openssl is not required at all in this build).
A couple of years ago, Docker had a wonderful
multi-stage build , it is ideal, for example, for Go applications or for situations where the build is complex and it is easier to transfer artifacts from the image to the image than to do the final cleanup. For planting our bonsai we use it, it will save a little.
FROM alpine:3.6 # ( ) RUN apk add --no-cache --virtual .build-deps \ # ... , && make -j$(getconf _NPROCESSORS_ONLN) FROM alpine:3.6 # , , WORKDIR /mtproxy COPY --from=0 /mtproxy/sources/objs/bin/mtproto-proxy . # #
Bonsai should be bonsai - get rid of installing libcrypto. When building, we needed the header files from the openssl-dev package, which will pull up libcrypto in dependencies and our executable file will be oriented towards using libcrypto.so.1.0.0. But this is the only dependency, moreover, it is preinstalled in Alpine (in version 3.6 it is libcrypto.so.41, 3.7 - libcrypto.so.42, lies in / lib /). They will scold me now, this is not the most reliable way, but it is worth it and we will still add a symlink to the existing version (if you have a better way, I will accept PR with pleasure).
Final touches and results:
Docker hubGithubI would appreciate any advice and contribution.