Docker: non é mal consello

Nos comentarios do meu artigo Docker: mal consello houbo moitas solicitudes para explicar por que o Dockerfile descrito nel era tan terrible.

Resumo do episodio anterior: Dous desenvolvedores compoñen un Dockerfile nun prazo axustado. No proceso, Ops Igor Ivanovich chega a eles. O Dockerfile resultante é tan malo que a IA está ao bordo dun ataque cardíaco.

Docker: non é mal consello

Agora imos descubrir o que hai de malo con este Dockerfile.

Así que pasou unha semana.

Dev Petya coñece ao Ops Igor Ivanovich no comedor tomando unha cunca de café.

P: Igor Ivanovich, estás moi ocupado? Gustaríame descubrir onde fomos.

AI: É bo, non adoitas atopar desenvolvedores que estean interesados ​​na explotación.
En primeiro lugar, imos acordar algunhas cousas:

  1. Ideoloxía Docker: un recipiente - un proceso.
  2. Canto máis pequeno sexa o recipiente, mellor.
  3. Canto máis leves do caché, mellor.

P: Por que debería haber un proceso nun recipiente?

AI: Docker, ao iniciar un contenedor, supervisa o estado do proceso co pid 1. Se o proceso morre, Docker tenta reiniciar o contenedor. Digamos que tes varias aplicacións en execución nun contedor ou que a aplicación principal non se está a executar con pid 1. Se o proceso morre, Docker non o saberá.

Se non tes máis preguntas, móstranos o teu Dockerfile.

E Petya mostrou:

FROM ubuntu:latest

# Копируем исходный код
COPY ./ /app
WORKDIR /app

# Обновляем список пакетов
RUN apt-get update 

# Обновляем пакеты
RUN apt-get upgrade

# Устанавливаем нужные пакеты
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor

# Устанавливаем bundler
RUN gem install bundler

# Устанавливаем nodejs используется для сборки статики
RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash -
RUN apt-get install -y nodejs

# Устанавливаем зависимости
RUN bundle install --without development test --path vendor/bundle

# Чистим за собой кэши
RUN rm -rf /usr/local/bundle/cache/*.gem 
RUN apt-get clean 
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 
RUN rake assets:precompile
# Запускаем скрипт, при старте контейнера, который запустит все остальное.
CMD ["/app/init.sh"]

AI: Oh, poñémolo en orde. Imos comezar coa primeira liña:

FROM ubuntu:latest

Levas a etiqueta latest. Usando unha etiqueta latest leva a consecuencias imprevisibles. Imaxina, o mantedor da imaxe constrúe unha nova versión da imaxe cunha lista diferente de software, esta imaxe recibe a etiqueta máis recente. E o teu recipiente, no mellor dos casos, deixa de construírse e, no peor, colles bichos que antes non existían.

Tomas unha imaxe cun sistema operativo completo con moito software innecesario, o que aumenta o volume do recipiente. E canto máis software, máis buracos e vulnerabilidades.

Ademais, canto maior sexa a imaxe, máis espazo ocupará no host e no rexistro (gardas imaxes nalgún lugar)?

P: Si, claro, temos un rexistro, configuralo ti.

AI: Entón, de que falo?... Ah, si, os volumes... A carga na rede tamén está crecendo. Para unha soa imaxe isto non se nota, pero cando hai unha compilación, probas e despregamento continuos, nótase. E se non tes o modo de Deus en AWS, tamén terás unha factura cósmica.

Polo tanto, cómpre escoller a imaxe máis adecuada, coa versión exacta e o software mínimo. Por exemplo, tome: FROM ruby:2.5.5-stretch

P: Ah, xa vexo. Como e onde podo ver as imaxes dispoñibles? Como sei cal necesito?

AI: Normalmente as imaxes son tomadas de dockerhub, non confundas con pornhub :). Normalmente hai varios conxuntos para unha imaxe:
alpino: as imaxes recóllense nunha imaxe minimalista de Linux, só 5 MB. A súa desvantaxe: está compilado coa súa propia implementación libc, os paquetes estándar non funcionan nel. Buscar e instalar o paquete necesario levará moito tempo.
Raspe: imaxe base, non usada para construír outras imaxes. Está pensado unicamente para executar datos binarios preparados. Ideal para executar aplicacións binarias que inclúen todo o que precisa, como aplicacións GO.
Baseado en calquera sistema operativo, como Ubuntu ou Debian. Ben, non creo que haxa que explicar.

AI: Agora necesitamos instalar todos os extras. paquetes e borrar os seus cachés. E podes botalo inmediatamente apt-get upgrade. En caso contrario, con cada compilación, a pesar da etiqueta fixa da imaxe base, obteranse imaxes diferentes. A actualización dos paquetes na imaxe é tarefa do mantedor e vai acompañada do cambio de etiqueta.

P: Si, intentei facelo, resultou así:

WORKDIR /app
COPY ./ /app

RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - 
    && apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor nodejs 
    && gem install bundler 
    && bundle install --without development test --path vendor/bundle

RUN rm -rf /usr/local/bundle/cache/*.gem 
    && apt-get clean  
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

AI: Non está mal, pero tamén hai algo no que traballar. Mira, aquí tes este comando:

RUN rm -rf /usr/local/bundle/cache/*.gem 
    && apt-get clean  
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*  

... non elimina datos da imaxe final, senón que só crea unha capa adicional sen estes datos. Correctamente así:

RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - 
    && apt-get -y install libpq-dev imagemagick gsfonts nodejs 
    && gem install bundler 
    && bundle install --without development test --path vendor/bundle   
    && rm -rf /usr/local/bundle/cache/*.gem 
    && apt-get clean  
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

Pero iso non é todo. Que tes alí, Ruby? Entón non tes que copiar todo o proxecto ao principio. É suficiente con copiar Gemfile e Gemfile.lock.

Con este enfoque, a instalación do paquete non se executará para cada cambio de orixe, pero só se cambiou o Gemfile ou Gemfile.lock.

Os mesmos métodos funcionan para outros idiomas cun xestor de dependencias, como npm, pip, composer e outros baseados nun ficheiro cunha lista de dependencias.

E, finalmente, lembra que ao principio falei sobre a ideoloxía Docker "un recipiente - un proceso"? Isto significa que non é necesario un supervisor. Tampouco deberías instalar systemd, polos mesmos motivos. Esencialmente, o propio Docker é un supervisor. E cando tentas executar varios procesos nel, é como executar varias aplicacións nun proceso supervisor.
Ao construír, creará unha única imaxe e, a continuación, lanzará o número necesario de contedores para que se execute un proceso en cada un.

Pero máis sobre iso máis tarde.

P: Creo que entendo. Mira que pasa:

FROM ruby:2.5.5-stretch

WORKDIR /app
COPY Gemfile* /app

RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - 
    && apt-get -y install libpq-dev imagemagick gsfonts nodejs 
    && gem install bundler 
    && bundle install --without development test --path vendor/bundle   
    && rm -rf /usr/local/bundle/cache/*.gem 
    && apt-get clean  
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

COPY . /app
RUN rake assets:precompile

CMD ["bundle”, “exec”, “passenger”, “start"]

Podemos anular o lanzamento de daemons ao iniciar o contedor?

AI: Si, é certo. Por certo, podes usar tanto CMD como ENTRYPOINT. E descubrir cal é a diferenza é a túa tarefa. Hai un bo sobre este tema en Habré artigo.

Entón, sigamos adiante. Descargas un ficheiro para instalar o nodo, pero non hai garantía de que conteña o que necesitas. Necesitamos engadir validación. Por exemplo, así:

RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x 
    && echo "958c9a95c4974c918dca773edf6d18b1d1a41434  setup_9.x" | sha1sum -c - 
    &&  bash  setup_9.x 
    && rm -rf setup_9.x 
    && apt-get -y install libpq-dev imagemagick gsfonts nodejs 
    && gem install bundler 
    && bundle install --without development test --path vendor/bundle   
    && rm -rf /usr/local/bundle/cache/*.gem 
    && apt-get clean  
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

Usando a suma de verificación pode verificar que descargou o ficheiro correcto.

P: Pero se o ficheiro cambia, a compilación fallará.

AI: Si, e curiosamente, isto tamén é un plus. Sabrás que o ficheiro cambiou e poderás ver o que se cambiou alí. Nunca se sabe, engadiron, por exemplo, un script que elimina todo o que pode chegar ou crea unha porta traseira.

P: Grazas. Resulta que o Dockerfile final terá o seguinte aspecto:

FROM ruby:2.5.5-stretch

WORKDIR /app
COPY Gemfile* /app

RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x 
    && echo "958c9a95c4974c918dca773edf6d18b1d1a41434  setup_9.x" | sha1sum -c - 
    &&  bash  setup_9.x 
    && rm -rf setup_9.x 
    && apt-get -y install libpq-dev imagemagick gsfonts nodejs 
    && gem install bundler 
    && bundle install --without development test --path vendor/bundle   
    && rm -rf /usr/local/bundle/cache/*.gem 
    && apt-get clean  
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

COPY . /app
RUN rake assets:precompile

CMD ["bundle”, “exec”, “passenger”, “start"]

P: Igor Ivanovich, grazas pola túa axuda. É hora de que corra, teño que facer 10 compromisos máis hoxe.

Igor Ivanovich, parando coa mirada ao seu colega apresurado, toma un grolo de café forte. Despois de pensar uns segundos sobre o SLA do 99.9% e o código sen erros, fai unha pregunta.

AI: Onde gardas os rexistros?

P: Por suposto, en production.log. Por certo, si, pero como podemos acceder a eles sen ssh?

AI: Se os deixas nos ficheiros, xa se inventou unha solución para ti. O comando docker exec permítelle executar calquera comando nun contedor. Por exemplo, podes facer gato para rexistros. E usando a chave e executar bash (se está instalado no contedor) darache acceso interactivo ao contedor.

Pero non deberías gardar rexistros en ficheiros. Como mínimo, isto leva a un crecemento incontrolado do recipiente e ninguén xira os rexistros. Todos os rexistros deben enviarse a stdout. Alí xa se poden ver mediante o comando rexistros docker.

P: Igor Ivanovich, quizais podo poñer os rexistros nun directorio montado, nun nodo físico, como datos de usuario?

AI: É bo que non se esqueza de eliminar os datos cargados no disco do nodo. Tamén podes facelo cos rexistros, pero non te esquezas de configurar a rotación.
Xa está, podes correr.

P: Igor Ivanovich, podes aconsellarme que ler?

AI: Primeiro, le recomendacións dos desenvolvedores de Docker, case ninguén coñece a Docker mellor ca eles.

E se queres facer prácticas, vai a intensivo. Despois de todo, a teoría sen práctica está morta.

Fonte: www.habr.com

Engadir un comentario