Tery K.
Tery K.

Reputation: 13

Super Slow to public application on Docker

I researched everything and I can't find a solution.

I'm running the docker-compose compilation that takes 10 to 15 minutes to go up in production, it's crazy. I've tried everything, tried to erase the volume, tried to clean the entire system, changed the network connection, uninstalled / reinstalled the Docker, ran a complete docker system prune -a - nothing.

I don't know if it is because of this delay, but it seems that even the dns gets lost and cannot find the application after all this time. I have to press ctrl + f5 to load the new update.

But this time is driving me crazy, 15 minutes to get everything up.

I appreciate any help.

.yml:

version: '3'

volumes:
  app-volume:

services:

  nginx:
    container_name: nginx
    restart: always
    build:  
      context: ./nginx/
      dockerfile: ./Dockerfile
    depends_on:
      - app
    volumes:
      - app-volume:/app
    env_file:
      - ./nginx/.envs/nginx.env
    ports:
      - "80:80"
      - "443:443"

  app:
    container_name: app
    build:
      context: ./app/
      dockerfile: ./compose/production/Dockerfile
    image: app
    volumes:
      # - ./app/:/app
      - app-volume:/home/app
    env_file:
      - ./app/.envs/.production/app.env

app Dockefile:

FROM node:12-alpine as builder

WORKDIR /home/app

RUN apk update && \
    apk add --no-cache python make g++ iputils

RUN npm install -g @angular/cli

COPY package*.json ./
COPY *.lock ./

RUN yarn install --unsafe-perm

COPY . .

ARG FORCE_BUILD_ENV=1
ARG ENV_BUILD=0

RUN export $(grep -v '^#' .envs/.production/app.env | xargs) && \
    npm run build:env && \
    ls -lah && \
    cat ./src/environments/environment.ts && \
    cat ./src/environments/environment.prod.ts

CMD ["ng", "build", "--prod", "--outputPath=dist"]

nginx/Dockerfile:

FROM alpine:3.10

LABEL maintainer="NGINX Docker Maintainers <[email protected]>"

ENV NGINX_VERSION 1.16.1
ENV NGX_BROTLI_COMMIT e505dce68acc190cc5a1e780a3b0275e39f160ca

RUN GPG_KEYS=B0F4253373F8F6F510D42178520A9993A1C052F8 \
    && CONFIG="\
        --prefix=/etc/nginx \
        --sbin-path=/usr/sbin/nginx \
        --modules-path=/usr/lib/nginx/modules \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --http-log-path=/var/log/nginx/access.log \
        --pid-path=/var/run/nginx.pid \
        --lock-path=/var/run/nginx.lock \
        --http-client-body-temp-path=/var/cache/nginx/client_temp \
        --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
        --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
        --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
        --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
        --user=nginx \
        --group=nginx \
        --with-http_ssl_module \
        --with-http_realip_module \
        --with-http_addition_module \
        --with-http_sub_module \
        --with-http_dav_module \
        --with-http_flv_module \
        --with-http_mp4_module \
        --with-http_gunzip_module \
        --with-http_gzip_static_module \
        --with-http_random_index_module \
        --with-http_secure_link_module \
        --with-http_stub_status_module \
        --with-http_auth_request_module \
        --with-http_xslt_module=dynamic \
        --with-http_image_filter_module=dynamic \
        --with-http_geoip_module=dynamic \
        --with-http_perl_module=dynamic \
        --with-threads \
        --with-stream \
        --with-stream_ssl_module \
        --with-stream_ssl_preread_module \
        --with-stream_realip_module \
        --with-stream_geoip_module=dynamic \
        --with-http_slice_module \
        --with-mail \
        --with-mail_ssl_module \
        --with-compat \
        --with-file-aio \
        --with-http_v2_module \
        --add-module=/usr/src/ngx_brotli \
    " \
    && addgroup -S nginx \
    && adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx \
    && apk add --no-cache --virtual .build-deps \
        gcc \
        libc-dev \
        make \
        openssl-dev \
        pcre-dev \
        zlib-dev \
        linux-headers \
        curl \
        gnupg1 \
        libxslt-dev \
        gd-dev \
        geoip-dev \
        perl-dev \
    && apk add --no-cache --virtual .brotli-build-deps \
        autoconf \
        libtool \
        automake \
        git \
        g++ \
        cmake \
    && mkdir -p /usr/src \
    && cd /usr/src \
    && git clone --recursive https://github.com/google/ngx_brotli.git \
    && cd ngx_brotli \
    && git checkout -b $NGX_BROTLI_COMMIT $NGX_BROTLI_COMMIT \
    && cd .. \
    && curl -fSL https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz -o nginx.tar.gz \
    && curl -fSL https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz.asc  -o nginx.tar.gz.asc \
        && sha512sum nginx.tar.gz nginx.tar.gz.asc \
    && export GNUPGHOME="$(mktemp -d)" \
    && gpg --keyserver ipv4.pool.sks-keyservers.net --recv-keys "$GPG_KEYS" \
    && gpg --batch --verify nginx.tar.gz.asc nginx.tar.gz \
    && rm -rf "$GNUPGHOME" nginx.tar.gz.asc \
    && mkdir -p /usr/src \
    && tar -zxC /usr/src -f nginx.tar.gz \
    && rm nginx.tar.gz \
    && cd /usr/src/nginx-$NGINX_VERSION \
    && ./configure $CONFIG --with-debug \
    && make -j$(getconf _NPROCESSORS_ONLN) \
    && mv objs/nginx objs/nginx-debug \
    && mv objs/ngx_http_xslt_filter_module.so objs/ngx_http_xslt_filter_module-debug.so \
    && mv objs/ngx_http_image_filter_module.so objs/ngx_http_image_filter_module-debug.so \
    && mv objs/ngx_http_geoip_module.so objs/ngx_http_geoip_module-debug.so \
    && mv objs/ngx_http_perl_module.so objs/ngx_http_perl_module-debug.so \
    && mv objs/ngx_stream_geoip_module.so objs/ngx_stream_geoip_module-debug.so \
    && ./configure $CONFIG \
    && make -j$(getconf _NPROCESSORS_ONLN) \
    && make install \
    && rm -rf /etc/nginx/html/ \
    && mkdir /etc/nginx/conf.d/ \
    && mkdir -p /usr/share/nginx/html/ \
    && install -m644 html/index.html /usr/share/nginx/html/ \
    && install -m644 html/50x.html /usr/share/nginx/html/ \
    && install -m755 objs/nginx-debug /usr/sbin/nginx-debug \
    && install -m755 objs/ngx_http_xslt_filter_module-debug.so /usr/lib/nginx/modules/ngx_http_xslt_filter_module-debug.so \
    && install -m755 objs/ngx_http_image_filter_module-debug.so /usr/lib/nginx/modules/ngx_http_image_filter_module-debug.so \
    && install -m755 objs/ngx_http_geoip_module-debug.so /usr/lib/nginx/modules/ngx_http_geoip_module-debug.so \
    && install -m755 objs/ngx_http_perl_module-debug.so /usr/lib/nginx/modules/ngx_http_perl_module-debug.so \
    && install -m755 objs/ngx_stream_geoip_module-debug.so /usr/lib/nginx/modules/ngx_stream_geoip_module-debug.so \
    && ln -s ../../usr/lib/nginx/modules /etc/nginx/modules \
    && strip /usr/sbin/nginx* \
    && strip /usr/lib/nginx/modules/*.so \
    && rm -rf /usr/src/nginx-$NGINX_VERSION \
    && rm -rf /usr/src/ngx_brotli \
    \
    # Bring in gettext so we can get `envsubst`, then throw
    # the rest away. To do this, we need to install `gettext`
    # then move `envsubst` out of the way so `gettext` can
    # be deleted completely, then move `envsubst` back.
    && apk add --no-cache --virtual .gettext gettext \
    && mv /usr/bin/envsubst /tmp/ \
    \
    && runDeps="$( \
        scanelf --needed --nobanner /usr/sbin/nginx /usr/lib/nginx/modules/*.so /tmp/envsubst \
            | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
            | sort -u \
            | xargs -r apk info --installed \
            | sort -u \
    )" \
    && apk add --no-cache --virtual .nginx-rundeps tzdata $runDeps \
    && apk del .build-deps \
    && apk del .brotli-build-deps \
    && apk del .gettext \
    && mv /tmp/envsubst /usr/local/bin/ \
    \
    # forward request and error logs to docker log collector
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
    apk update && \
    apk add --no-cache openssl brotli vim && \
    rm -rf /var/cache/apk/*


RUN mkdir -p /etc/ssl/certs/private/ 

COPY ./server.crt /etc/ssl/certs/private/siap.crt
COPY ./server.key /etc/ssl/certs/private/siap.key

RUN openssl dhparam -out /etc/ssl/certs/private/dhparam.pem 4096

RUN rm -rf /etc/nginx/conf.d/*

COPY ./scripts/nginx.conf /etc/nginx/nginx.conf
COPY ./scripts/conf.d/brotli.conf /etc/nginx/conf.d/brotli.conf
COPY ./scripts/conf.d/siap.http.conf /etc/nginx/conf.d/siap.http.conf
COPY ./scripts/conf.d/siap.https.conf /etc/nginx/conf.d/siap.https.conf

COPY .envs ./.envs

RUN export $(grep -v '^#' .envs/nginx.env | xargs) && \
    sed -i '2s/^.*$/    server '$SERVER_ENDERECO';/g' /etc/nginx/conf.d/siap.https.conf;

EXPOSE 80

STOPSIGNAL SIGTERM

RUN head /etc/nginx/conf.d/siap.https.conf
# RUN nginx -t

CMD ["nginx", "-g", "daemon off;"]

Upvotes: 0

Views: 1187

Answers (1)

David Maze
David Maze

Reputation: 158888

You're rebuilding your application every time you start the container stack. While I wouldn't expect this to usually take 10+ minutes, it's better to build the application just once and built it into a Docker image that can be reused.

You can use a multi-stage build to build the application into static files, and then package it into an Nginx server. The Dockerfile you show is actually most of the way there. You need to change the final CMD to a RUN so it runs during the build, and then add the additional stage to COPY the files into an nginx-based image:

FROM node:12-alpine AS builder
WORKDIR /home/app
... exactly what is in the question ...
RUN ["ng", "build", "--prod", "--outputPath=dist"] # not CMD

FROM alpine:3.10 AS server
... the existing nginx/Dockerfile ...

FROM server
COPY --from=builder /home/app/dist /usr/share/nginx/html

With the extended nginx/Dockerfile in the question, you'll need to do a little bit of additional work to combine these together. You need to COPY files from both the nginx and app subtrees. To make this work, you need to move the Dockerfile up to the top level of your directory tree, and when you COPY files into the image, you need to COPY app/package*.json or COPY nginx/scripts/nginx.conf from one of the subdirectories.

In your docker-compose.yml file, then, you can remove the service that's only used to build files, since this is included in the image-build sequence. You can also remove the volume, again since the compiled application is included in the image.

version: '3.8'
services:
  nginx:
    build: .  # Dockerfile needs to COPY app/... and COPY nginx/...
    restart: always
    env_file:
      - ./nginx/.envs/nginx.env
    ports:
      - "80:80"
      - "443:443"
    # no volumes:; do not need to override container_name:
  # no app: container
# no top-level volumes:

You can do even better than this in production. If you have access to a Docker registry -- Docker Hub, something your cloud provider offers, something you run yourself -- you can build this image separately, run whatever integration tests you need, and then push it to the registry. Instead of build:, set image: to point to the specific tagged image you need (it's good practice to use a different tag for each build).

services:
  nginx:
    image: my/nginx:20210524
    # no build:
    restart: always
    env_file: [...]
    ports: [...]

Now when you go to do a deploy, build, tag, and push the image, maybe using a CI system for automation. Once you've pushed the image, log into the production machine and change image: to point to the new build, and run docker-compose pull; docker-compose up -d. Compose will pull the updated image and then recreate the container using it. The image contains the prebuilt application, and you pulled it while the old container was already running, so there should be almost no downtime to do this update.

Upvotes: 1

Related Questions