📜 ⬆️ ⬇️

Deploim isomorphic web application on the example of Nuxt.js


In medium and large projects, the site is not limited to one service - for example, only a site, as a rule, there is a database, API, server that routes requests to all these services. Rolling out and updating all this without any standardization is not easy, and scaling to multiple servers is even more difficult.

Docker will help us solve this problem - it has become the de facto standard in the world of packaging, delivery and publication of applications.

Docker allows us to wrap an application or service with all dependencies and settings in an isolated container, ensuring the consistency of the content on any platform.

As an isomorphic application, we will use the Nuxt.js framework, which consists of Vue.js and Node.js, allowing us to write universal web applications with server-side rendering (SSR).

This choice is due to personal preference, but in the same way you can take any other framework, for example, Next.js.

We collect and publish the first image.


First of all, you need to configure the port and host within the application. There are several ways to do this, we will use the settings in package.json, adding a new section:

"config": { "nuxt": { "host": "0.0.0.0", "port": "3000" } } 

For further action, we need docker, docker-compose installed on the system and an editor with an open project.

Create a Dockerfile that we put in the root and describe the instructions for building the image.

We need to build an image based on the Node.js version 10 image, in this case the light version of alpine is used:

 FROM node:10-alpine 

Then set the environment variable with the directory name:

 ENV APP_ROOT /web 

Install as a working directory and add the source:

 WORKDIR ${APP_ROOT} ADD . ${APP_ROOT} 

Install the dependencies and build the application:

 RUN npm ci RUN npm run build 

And we write the command to launch the application inside the image:

 CMD ["npm", "run", "start"] 

Dockerfile
 FROM node:10-alpine ENV APP_ROOT /web ENV NODE_ENV production WORKDIR ${APP_ROOT} ADD . ${APP_ROOT} RUN npm ci RUN npm run build CMD ["npm", "run", "start"] 


After that, open the current folder in the terminal and collect the image:

 docker build -t registry.gitlab.com/vik_kod/nuxtjs_docker_example . 

Run the image locally to check that everything works correctly:

 docker run -p 3000:3000 registry.gitlab.com/vik_kod/nuxtjs_docker_example 

Going to localhost: 3000 we should see the following:



Fine! We have successfully launched production assembly of the application on the local machine.

Now we need to publish the image in the docker repository in order to use the ready-made image on the target server. You can use either the self-hosted repository or any other repository, for example, the official hub.docker.com .

I will use the repository in gitlab, the tab with the docker repositories there is called registry. Previously, I have already created a repository for the project, so now I run the command:

 docker push registry.gitlab.com/vik_kod/nuxtjs_docker_example 

Once the image has successfully loaded, you can proceed to the configuration of the VPS server,
my she has the following:


I also took the opportunity to put docker right away when creating the server, so if it is not installed on your VPS, you can read the instructions on the official website.

After creating the server, go to it and log in to the docker repository, in my case it is gitlab:

 docker login registry.gitlab.com 

After authorization, we can start applications with the command previously seen:

 docker run -p 3000:3000 registry.gitlab.com/vik_kod/nuxtjs_docker_example 



The image downloaded and started, let's check:



We see a familiar picture, we launched the container with the application, but already on a remote server.

The final touch remains, now when the terminal is closed, the image will be stopped, so we will add the -d attribute to start the container in the background.
Stop and restart:

 docker run -d -p 3000:3000 registry.gitlab.com/vik_kod/nuxtjs_docker_example 

Now we can close the terminal and make sure that our application is functioning successfully.

We have achieved the necessary - we launched the application in docker and now it is suitable for deployment, both as a standalone image and as part of a larger infrastructure.

Add reverse proxy


At the current stage, we can publish simple projects, but what if we need to put the application and API on the same domain, and in addition to this, give the static not through Node.js?

Thus, there is a need for the so-called reverse proxy server, which will receive all requests and be redirected, depending on the request to related services.

We will use nginx as such a server.

Managing containers if there are more than one individually is not very convenient. Therefore, we will use docker-compose as a way to organize and manage containers.

Let's create a new empty project, in the root of which we will add the file docker-compose.yml and the nginx folder.

In docker-compose.yml we write the following:

 version: "3.3" # Указываем раздел со связанными сервисами services: # Первый сервис, nginx nginx: image: nginx:latest # Пробрасываем порты 80 для http и 443 для https ports: - "80:80" - "443:443" # Опциональный параметр с именем контейнера container_name: proxy_nginx volumes: # Используем свой nginx конфиг, он заменит дефолтный в контейнере - ./nginx:/etc/nginx/conf.d # Монтируем папку с логами на хост машину для более удобного доступа - ./logs:/var/log/nginx/ # Второй сервис Nuxt.js приложение nuxt: # Используем ранее собранный образ image: registry.gitlab.com/vik_kod/nuxtjs_docker_example container_name: nuxt_app # Также пробрасываем порт на котором висит приложение ports: - "3000:3000" 

Add the config to the nginx folder, which is recommended by the official site Nuxt.js , with minor changes.

nginx.conf
 map $sent_http_content_type $expires { "text/html" epoch; "text/html; charset=utf-8" epoch; default off; } server { root /var/www; listen 80; # Порт который слушает nginx server_name localhost; # домен или ip сервера gzip on; gzip_types text/plain application/xml text/css application/javascript; gzip_min_length 1000; location / { expires $expires; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 1m; proxy_connect_timeout 1m; # Адрес нашего приложения, так как контейнеры связаны при помощи # docker-compose мы можем обращаться к ним по имени контейнера, в данном случае nuxt_app proxy_pass http://nuxt_app:3000; } } 


Run the command to run:

 docker-compose up 



Everything started correctly, now if we go to the address that listens to nginx, localhost, then we will see our application, there will be no visual differences, but now all requests first go to nginx where they are redirected depending on the specified rules.

Now we have no additional services or statics, let's add a static folder in which we place some image.

Mount it in the nginx container by adding a line to docker-compose:

 ... container_name: proxy_nginx volumes: # Монтируем папку со статикой - ./static:/var/www/static ... 

Updated docker-compose.yml
 version: "3.3" # Указываем раздел со связанными сервисами services: # Первый сервис, nginx nginx: image: nginx:latest # Пробрасываем порты 80 для http и 443 для https ports: - "80:80" - "443:443" # Опциональный параметр с именем контейнера container_name: proxy_nginx volumes: # Используем свой nginx конфиг, он заменит дефолтный в контейнере - ./nginx:/etc/nginx/conf.d # Монтируем папку с логами на хост машину для более удобного доступа - ./logs:/var/log/nginx/ # Монтируем папку со статикой - ./static:/var/www/static # Второй сервис Nuxt.js приложение nuxt: # Используем ранее собранный образ image: registry.gitlab.com/vik_kod/nuxtjs_docker_example container_name: nuxt_app # Так же пробрасываем порт на котором висит приложение ports: - "3000:3000" 


Then add a new location to nginx.conf:

 location /static/ { try_files $uri /var/www/static; } 

Updated nginx.conf
 map $sent_http_content_type $expires { "text/html" epoch; "text/html; charset=utf-8" epoch; default off; } server { root /var/www; listen 80; # Порт который слушает nginx server_name localhost; # домен или ip сервера gzip on; gzip_types text/plain application/xml text/css application/javascript; gzip_min_length 1000; location / { expires $expires; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 1m; proxy_connect_timeout 1m; # Адрес нашего приложения, так как контейнеры связаны при помощи # docker-compose мы можем обращаться к ним по имени контейнера, в данном случае nuxt_app proxy_pass http://nuxt_app:3000; } location /static/ { try_files $uri /var/www/static; } } 


Restart docker-compose:

 docker-compose up --build 

Go to localhost / static / demo.jpg




Now the static is given through Nginx, removing the load from Node.js in the main application.

Making sure that everything works, you can publish our assembly on the server. To do this, I will create a repository in the current directory. Pre-adding the logs and static folder to .gitignore.

After that we go to the server, stop the previously launched docker image and clone the repository.



Before you start the build, you need to move the folder with statics to the server, go to the terminal on the local machine and through the scp command utility move the folder to the server:

 scp -r /Users/vik_kod/PhpstormProjects/nuxtjs_docker_proxy_example/static root@5.101.48.172:/root/example_app/ 

If the amount of static is large, it is better to first compress the folder and send it to the archive, then unpack it on the server. Otherwise, the download may take a long time.

We return to the terminal on the server and go to the cloned folder and run the command:

 docker-compose up -d 

We close the terminal and go to the site:




Fine! Using reverse proxy, we separated the static from the application.

Further steps


All that we have done above is a fairly simple option, in large projects it is necessary to take into account more things, below is a short list of what can be done next.


Total



Sources


application
Configs

Thank you for your attention and I hope this material has helped you!

Source: https://habr.com/ru/post/438862/