Comme tout le monde l'a déjà
entendu , fin mai, Telegram a lancé le serveur officiel MTProto Proxy (alias MTProxy), écrit dans la
langue suivante . En 2018, il n'y a pas grand-chose où sans Docker, car il est accompagné de la même manière «officielle» au format zero-config. Tout irait bien, mais trois "mais" ont gâché un peu l'impression de la sortie: l'image pèse> 130 Mo (il y a une Debian plutôt dodue, pas l'Alpine habituelle), en raison de la "zero-config", elle n'est pas toujours configurée de manière pratique (uniquement par les paramètres d'environnement) et les gars ont oublié la campagne, disposer le Dockerfile.
TL; DR Nous allons créer une image Docker officielle alpin pratiquement 1 en 1 de 5,94 Mo et la mettre
ici (et le Dockerfile
ici ); en cours de route, nous découvrirons comment vous pouvez parfois vous lier d'amitié avec le logiciel Alpine à l'aide de pinces et d'un fichier, et nous jouerons un peu en taille, exclusivement pour le plaisir.
Contenu de l'image
Encore une fois, à cause de tout ce bruit? Voyons ce que l'image officielle représente avec la commande
history :
$ docker history --no-trunc --format "{{.Size}}\t{{.CreatedBy}}" telegrammessenger/proxy
Les couches sont lues de bas en haut, respectivement:

La plus épaisse est la Debian Jessie, dont l'image d'origine est héritée, il faut d'abord s'en débarrasser (alpine: 3,6, à titre de comparaison, elle pèse 3,97 Mo); suivis des dimensions sont les certificats curl et frais. Pour comprendre ce que signifient les deux autres fichiers et le répertoire, nous allons regarder à l'intérieur en utilisant la commande
run , en remplaçant CMD par bash (cela vous permettra de vous déplacer dans l'image lancée, de vous connaître de plus près, d'exécuter certains fragments, de copier quelque chose d'utile):
$ docker run -it --rm telegrammessenger/proxy /bin/bash
Maintenant, nous pouvons facilement restaurer l'image de l'incident - quelque chose comme le Dockerfile officiel perdu ressemblait à:
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"]
Lorsque mtproto-proxy est un serveur compilé, le dossier secret contient uniquement le fichier hello-explorers-how-are-you-do avec la clé de chiffrement AES (voir les commandes du serveur, là, soit dit en passant, il y a une recommandation officielle pour obtenir la clé via l'API, mais le mettre comme ça) probablement pour éviter la situation où l'API est également bloquée), et run.sh fait toutes les préparations pour démarrer le proxy.
Le contenu du run.sh d'origine Assemblage
Sous CentOS 7 MTProxy sur Habré déjà
collecté , nous allons essayer de collecter une image sous Alpine et d'économiser mégaoctets, publicités, 130 dans l'image docker résultante.
Une caractéristique distinctive d'Alpine Linux est l'utilisation de musl au lieu de glibc. Les deux sont des bibliothèques C standard. Musl est minuscule (il n'a pas un cinquième de la «norme»), mais le volume et les performances (promis au moins) décident quand il s'agit de Docker. Et mettre de la glibc sur Alpine n'est pas racialement correct, l'oncle Jakub Jirutka
ne comprendra pas , par exemple.
Nous allons également intégrer Docker pour isoler les dépendances et gagner en liberté pour l'expérimentation, alors créez un nouveau Dockerfile:
FROM alpine:3.6 RUN apk add --no-cache git make gcc musl-dev linux-headers openssl-dev RUN git clone --single-branch --depth 1 https://github.com/TelegramMessenger/MTProxy.git /mtproxy/sources RUN cd /mtproxy/sources \ && make -j$(getconf _NPROCESSORS_ONLN)
Parmi les dépendances, git sera utile (et pas seulement pour le clonage du référentiel officiel, le fichier make attachera le commit sha à la version), les fichiers make, gcc et header (l'ensemble minimum a été obtenu empiriquement). Nous clonons uniquement la branche principale avec une profondeur de 1 commit (nous n'avons certainement pas besoin de l'historique). Eh bien, essayons d'utiliser toutes les ressources de l'hôte lors de la compilation avec le commutateur -j. Je l'ai délibérément divisé en un plus grand nombre de couches afin d'obtenir une mise en cache pratique lors de la reconstruction (généralement il y en a beaucoup).
Nous exécuterons la commande
build (se trouvant dans le répertoire avec le Dockerfile):
$ docker build -t mtproxy:test .
Et voici le premier problème:
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; ^~~~~~~~~
En fait, tous les suivants seront connectés avec lui. Premièrement, pour ceux qui ne se connaissent pas, le compilateur jure en fait de l'absence de déclaration de la structure drand48_data. Deuxièmement, les développeurs de musl ont obtenu des scores sur les fonctions aléatoires thread-safe (avec le suffixe _r) et sur tout ce qui y est lié (y compris les structures). Et les développeurs de Telegram, à leur tour, n'ont pas pris la peine de compiler pour des systèmes où random_r et ses homologues ne sont pas implémentés (dans de nombreuses bibliothèques de système d'exploitation, vous pouvez voir le drapeau HAVE_RANDOM_R ou son arbitraire + la présence ou l'absence de ce groupe de fonctions est généralement prise en compte dans l'auto-configurateur).
Eh bien, maintenant, nous allons définitivement installer la glibc? Non! Nous allons copier ce dont nous avons besoin de la glibc au minimum et créer un patch pour les sources MTProxy.
En plus des problèmes avec random_r, nous saisissons un problème avec la fonction backtrace (execinfo.h), qui est utilisée pour sortir la trace de la pile en cas d'exception: vous pouvez essayer de la remplacer par l'implémentation de libunwind, mais cela n'en vaut pas la peine, car l'appel a été cadré en vérifiant __GLIBC__.
Contenu du correctif Random_compat.patch Mettez-le dans le dossier ./patches et modifiez un peu notre Dockerfile pour appliquer le patch à la volée:
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
Maintenant, le binaire mtproto-proxy assemblé est au moins lancé, et nous pouvons continuer.
Liquidation
Il est temps de transformer le run.sh d'origine en docker-entrypoint.sh. À mon avis, cela est logique lorsque la «liaison obligatoire» entre dans ENTRYPOINT (elle peut toujours être surchargée de l'extérieur), et les arguments pour lancer l'application dockerized s'adaptent au maximum en CMD (+ variables d'environnement comme sous-étude).
Nous pourrions installer bash et full grep dans notre image alpine (je l'expliquerai plus tard) pour éviter les maux de tête et utiliser le code d'origine tel quel, mais cela gonflera notre image miniature pour faire honte, donc nous ferons grandir un vrai, sa mère, bonsaï.
Commençons par le shebang, remplacez
#!/bin/bash
par
#!/bin/sh
. La valeur par défaut pour les cendres alpines est capable de digérer presque toute la syntaxe bash «telle quelle», mais nous rencontrons toujours un problème - pour des raisons inconnues, il a refusé d'accepter les parenthèses dans l'une des conditions, nous allons donc l'étendre en inversant la logique de comparaison:

Nous attendons maintenant une confrontation avec grep, qui dans la livraison busybox est légèrement différente de celle habituelle (et, en passant, beaucoup plus lente, gardez à l'esprit dans vos projets). Premièrement, il ne comprend pas l'expression
{,15}
, il devra spécifier explicitement
{0,15}
. Deuxièmement, il ne prend pas en charge l'indicateur
-P
(style perl), mais digère discrètement l'expression lorsque étendu (-E) est activé.
Dans nos dépendances, il ne reste que curl (il ne sert à rien de le remplacer par wget de busybox) et libcrypto (cela suffit, directement openssl n'est pas du tout nécessaire dans cet assemblage).
Une belle construction à
plusieurs étapes est apparue dans Docker il y a quelques années, elle est idéale, par exemple, pour les applications Go ou pour les situations où l'assemblage est compliqué et plus facile de transférer des artefacts d'image en image que de faire le nettoyage final. Nous allons l'utiliser pour planter notre bonsaï, cela économisera un peu.
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 devrait être bonsai - débarrassez-vous de l'installation de libcrypto. Lors de la construction, nous avions besoin des fichiers d'en-tête du paquet openssl-dev, qui dans les dépendances afficheront libcrypto et notre exécutable sera orienté vers l'utilisation de libcrypto.so.1.0.0. Mais c'est la seule dépendance, en plus, elle est préinstallée dans Alpine (dans la version 3.6 c'est libcrypto.so.41, 3.7 - libcrypto.so.42, c'est dans / lib /). Ils me grondent maintenant, ce n'est pas le moyen le plus fiable, mais ça vaut le coup et nous ajoutons toujours un lien symbolique vers la version existante (si vous avez une meilleure façon, j'accepterai volontiers PR).
Touche finale et résultat:
Docker hubGithubJe serais reconnaissant pour tout conseil et contribution.