Como todos ya han
escuchado , a fines de mayo Telegram lanzó el servidor oficial MTProto Proxy (también conocido como MTProxy), escrito en el
siguiente idioma . En 2018, no hay mucho sin Docker, porque está acompañado por la misma forma "oficial" en formato de configuración cero. Todo estaría bien, pero tres "peros" estropearon un poco la impresión del lanzamiento: la imagen pesa> 130 Mb (hay un Debian bastante regordete, no el Alpine habitual), debido a la "configuración cero" no siempre está convenientemente configurado (solo por parámetros ambientales) y los chicos olvidaron la campaña, diseñaron el Dockerfile.
TL; DR Haremos una imagen de acoplador oficial prácticamente 1 en 1 con base alpina de 5,94 MB de tamaño y la pondremos
aquí (y el Dockerfile
aquí ); En el camino, descubriremos cómo a veces puedes hacer amigos con el software Alpine usando pinzas y un archivo, y jugaremos un poco de tamaño, exclusivamente por diversión.
Contenido de imagen
Una vez más, ¿por qué tanto alboroto? Veamos qué representa la imagen oficial con el comando de
historial :
$ docker history --no-trunc --format "{{.Size}}\t{{.CreatedBy}}" telegrammessenger/proxy
Las capas se leen de abajo hacia arriba, respectivamente:

La más gruesa es la Debian Jessie, de la cual se hereda la imagen original, primero tenemos que deshacernos de ella (alpine: 3.6, en comparación, pesa 3.97MB); seguido de las dimensiones son rizos y certificados frescos. Para comprender qué significan los otros dos archivos y el directorio, veremos el interior usando el comando de
ejecución , reemplazando CMD con bash (esto le permitirá caminar alrededor de la imagen lanzada, conocerse más de cerca, ejecutar ciertos fragmentos, copiar algo útil):
$ docker run -it --rm telegrammessenger/proxy /bin/bash
Ahora podemos restaurar fácilmente la imagen del incidente, algo así como el Dockerfile oficial perdido parecía:
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"]
Donde mtproto-proxy es un servidor compilado, la carpeta secreta contiene solo el archivo hola-exploradores-cómo-estás-haciendo con la clave de cifrado AES (ver los comandos del servidor, por cierto, hay una recomendación oficial para obtener la clave a través de la API, pero decirlo así) probablemente para evitar la situación cuando la API también está bloqueada), y run.sh hace todos los preparativos para iniciar el proxy.
El contenido del run.sh original Asamblea
Bajo CentOS 7 MTProxy en Habré ya
recopilado , intentaremos recopilar una imagen bajo Alpine y guardar megabytes, comerciales, 130 en la imagen de acoplador resultante.
Una característica distintiva de Alpine Linux es el uso de musl en lugar de glibc. Ambas son bibliotecas estándar de C. Musl es pequeño (no tiene una quinta parte del "estándar"), pero el volumen y el rendimiento (al menos prometido) deciden cuando se trata de Docker. Y poner glibc en Alpine no es racialmente correcto, el tío Jakub Jirutka
no lo entenderá , por ejemplo.
También construiremos en Docker para aislar dependencias y ganar libertad para la experimentación, así que cree un nuevo 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)
De las dependencias, git será útil (y no solo para clonar el repositorio oficial, el archivo make adjuntará el sha commit a la versión), make, gcc y archivos de encabezado (el conjunto mínimo se obtuvo empíricamente). Solo clonamos la rama maestra con una profundidad de 1 commit (definitivamente no necesitamos historial). Bueno, intentemos utilizar todos los recursos del host al compilar con el modificador -j. Lo partí deliberadamente en una mayor cantidad de capas para obtener un almacenamiento en caché conveniente durante la reconstrucción (generalmente hay muchas).
Ejecutaremos el comando de
compilación (estando en el directorio con el Dockerfile):
$ docker build -t mtproxy:test .
Y aquí está el primer problema:
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 realidad, todos los posteriores estarán conectados con él. Primero, para aquellos que no están familiarizados con ellos mismos, el compilador realmente jura por la falta de una declaración de la estructura drand48_data. En segundo lugar, los desarrolladores de musl puntuaron en funciones aleatorias seguras para subprocesos (con el postfix _r) y en todo lo relacionado con ellas (incluidas las estructuras). Y los desarrolladores de Telegram, a su vez, no se molestaron en compilar sistemas donde random_r y sus contrapartes no están implementados (en muchas bibliotecas del sistema operativo puede ver el indicador HAVE_RANDOM_R o su presencia o ausencia arbitraria de este grupo de funciones generalmente se tiene en cuenta en el autoconfigurador).
Bueno, ¿ahora definitivamente instalaremos glibc? No! Copiaremos lo que necesitamos de glibc al mínimo y haremos un parche para las fuentes MTProxy.
Además de los problemas con random_r, tenemos un problema con la función de retroceso (execinfo.h), que se usa para generar el retroceso de la pila en caso de una excepción: podría intentar reemplazarlo con la implementación de libunwind, pero no vale la pena, porque la llamada se enmarcó al verificar __GLIBC__.
Contenido del parche Random_compat.patch Póngalo en la carpeta ./patches y modifique un poco nuestro Dockerfile para aplicar el parche sobre la marcha:
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
Ahora el binario ensamblado mtproto-proxy es al menos lanzado, y podemos seguir adelante.
Liquidación
Es hora de convertir el run.sh original en docker-entrypoint.sh. En mi opinión, esto es lógico cuando el "enlace obligatorio" entra en ENTRYPOINT (siempre se puede sobrecargar desde el exterior), y los argumentos para iniciar la aplicación acoplada se ajustan al máximo en CMD (+ variables de entorno como suplente).
Podríamos instalar bash y un grep completo en nuestra imagen alpina (explicaré más adelante) para evitar dolores de cabeza y usar el código original tal como está, pero esto inflará nuestra imagen en miniatura para deshonrar, por lo que creceremos un verdadero, su madre, bonsai.
Comencemos con el shebang, reemplace
#!/bin/bash
con
#!/bin/sh
. El valor predeterminado para la ceniza alpina es capaz de digerir casi toda la sintaxis de "como está" de bash, pero aún encontramos un problema: por razones desconocidas, se negó a aceptar paréntesis en una de las condiciones, por lo tanto lo expandiremos invirtiendo la lógica de comparación:

Ahora estamos esperando un enfrentamiento con grep, que en la entrega de busybox es ligeramente diferente del habitual (y, por cierto, mucho más lento, tenga en cuenta en sus proyectos). En primer lugar, no entiende la expresión
{,15}
, tendrá que especificar explícitamente
{0,15}
. En segundo lugar, no admite el indicador
-P
(estilo perl), pero digiere silenciosamente la expresión cuando se habilita la extensión (-E).
En nuestras dependencias, solo queda curl (no tiene sentido reemplazarlo con wget desde busybox) y libcrypto (es suficiente, no se requiere directamente openssl en este ensamblaje).
Hace un par de años apareció en Docker una hermosa
construcción de varias etapas , es ideal, por ejemplo, para aplicaciones Go o para situaciones en las que el ensamblaje es complicado y es más fácil transferir artefactos de una imagen a otra que hacer la limpieza final. Lo usaremos para plantar nuestros bonsai, esto ahorrará un poco.
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 debería ser bonsai: elimine la instalación de libcrypto. Al compilar, necesitábamos los archivos de encabezado del paquete openssl-dev, que en las dependencias extraerá libcrypto y nuestro ejecutable estará orientado hacia el uso de libcrypto.so.1.0.0. Pero esta es la única dependencia, además, está preinstalado en Alpine (en la versión 3.6 es libcrypto.so.41, 3.7 - libcrypto.so.42, está en / lib /). Ahora me regañan, esta no es la forma más confiable, pero vale la pena y seguimos agregando un enlace simbólico a la versión existente (si tiene una mejor manera, con mucho gusto aceptaré relaciones públicas).
Toques finales y resultado:
Docker hubGithubAgradecería cualquier consejo y contribución.