Jak spakować aplikację VueJS + NodeJS + MongoDB w Dockerze

Jak spakować aplikację VueJS + NodeJS + MongoDB w Dockerze
Jak można się domyślić z poprzedniego artykułu, pracowałam nad różnymi projektami. Pierwsze dni w nowym zespole zazwyczaj przebiegają podobnie: backendowiec siada ze mną i wykonuje magiczne czynności związane z instalacją i wdrożeniem aplikacji. Docker jest niezbędny dla frontendowców, ponieważ backend często napisany jest w szerokim zakresie technologii PHP/Java/Python/C#, a frontend nie musi za każdym razem odciągać backendu, aby wszystko zainstalować i uruchomić. Tylko w jednym miejscu widziałam zestawienie Docker-Jenkins z przejrzystym wdrożeniem, logami oraz zintegrowanymi testami automatycznymi.

Na temat Dockera napisano wiele szczegółowych artykułów. W tym artykule omówię wdrożenie Single Page Application z wykorzystaniem VueJS/Vue Router, a część serwerowa jako RESTful API z NodeJS, gdzie bazą danych jest MongoDB. Do opisu i uruchamiania kilku aplikacji-kontenerów używamy Docker Compose.

Po co potrzebny jest Docker

Docker pozwala zautomatyzować proces uruchamiania aplikacji. Programista nie musi samodzielnie instalować programów ani zmagać się z niekompatybilnością wersji na swoim komputerze. Wystarczy zainstalować Dockera i wpisać 1-2 polecenia w konsoli. Najwygodniej robić to na Linuxie.

Zaczynamy

Instalujemy Docker i Docker compose

Struktura folderów

Tworzymy 2 foldery dla aplikacji klienckiej i serwerowej. Plik z rozszerzeniem .yml to konfiguracja Docker Compose, w której definiuje się i łączy kontenery aplikacji.
Docker-compose.yml:

version: "3"
services:
  mongo:
    container_name: mongo
    hostname: mongo
    image: mongo
    ports:
      - "27017:27017"
  server:
    build: server/
    #command: node ./server.js #tutaj można nadpisać CMD z Dockerfile w /server
    ports:
      - "3000:3000"
    links:
      - mongo
  client:
    build: client/
    #command: http-server ./dist #tutaj można nadpisać CMD z Dockerfile w /client
    network_mode: host
    ports:
      - "8089:8089"
    depends_on:
      - server

Tworzymy 3 serwisy w Dockerze: dla NodeJS, MongoDB oraz dla statyk na Vue. Aby połączyć klienta z serwerem, dodano depends on server. Do powiązania MongoDB z serwerowym API używa się links mongo. Serwer, klient, mongo – to nazwy serwisów.

Klient na VueJS

W folderze /client zainstalowane jest aplikacja na VueJS. Aplikacja została stworzona za pomocą Vue Cli. Podczas budowania obrazu aplikacja kliencka jest kompilowana do zestawu plików statycznych w folderze /dist. W Dockerfile opisano zestaw poleceń do budowy obrazu:

FROM node:10
WORKDIR /client
COPY ./package*.json ./
RUN npm install
RUN npm install -g http-server
COPY . .
RUN npm run build
EXPOSE 8081
CMD ["npm", "test:dev"]

Zwróć uwagę, że package.json jest kopiowany i instalowany osobno od pozostałych plików projektu. Zostało to zrobione dla wydajności, aby zawartość folderu /node_modules była cachowana przy ponownym budowaniu. Każda linia polecenia jest cachowana oddzielnie.

Na końcu, przy uruchamianiu kontenera wykonywane jest polecenie npm run dev. To polecenie opisane jest w package.json:


"scripts": {
	 "test:dev": "http-server dist -p 8081 -c 1 --push-state"
}

Aby uruchomić pliki z folderu /dist, globalnie jest instalowany http-server, a w dev-dependencies pakiet spa-http-server, aby Vue Router działał prawidłowo. Flaga —push-state powoduje przekierowanie na index.html. Flaga -c z wartością 1 sekunda została dodana, aby http-server nie cache'ować skryptów. To jest przykład testowy, w rzeczywistym projekcie lepiej używać nginx.

W sklepie Vuex tworzymy pole apiHost: 'http://localhost:3000', gdzie zapisany jest port NodeJS Api. Część kliencka jest gotowa. Teraz wszystkie zapytania z klienta do backendu kierowane są na ten adres URL.

NodeJS server API

W folderze /server tworzymy server.js i Dockerfile:


FROM node:10
WORKDIR /server
COPY ./package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

W server.js jest określany url dla bazy danych const url = 'mongodb://mongo:27017/';. Zezwalamy na zapytania międzydomenowe z klienta:


const clientUrl = 'http://localhost:8081';
const corsOptions = {
  origin: clientUrl,
  optionsSuccessStatus: 200, // niektóre starsze przeglądarki (IE11, różne SmartTV) mają problemy z 204
};
app.use(cors());
app.all('/*', (req, res, next) => {
  res.header('Access-Control-Allow-Origin', clientUrl);
  res.header('Access-Control-Allow-Headers', 'X-Requested-With');
  next();
});
  app.get('/getProducts', cors(corsOptions), (req, res) => {
    products.getContent
      .then(data => res.json(data), err => res.json(err));
  });
  app.get('/getUsers', cors(corsOptions), (req, res) => {
    db.getUsers()
      .then(data => res.json(data), err => res.json(err));
  });

Podsumowanie

Teraz przechodzimy do katalogu projektu i uruchamiamy docker-compose build do budowy obrazów i docker-compose up do uruchamiania kontenerów. Komenda uruchomi 3 kontenery: server, client, mongo. Dla serwera na NodeJS można skonfigurować hot-reload, łącząc go z folderem user. A klienta w fazie rozwoju uruchomić lokalnie z hot reload, uruchamiając osobno server i mongo. Aby uruchomić osobną usługę, wystarczy podać jej nazwę docker-compose up client. Nie zapominaj czasami wykonać prune i usunięcie kontenerów (containers), sieci (networks) i obrazów (images), aby zwolnić zasoby.

Pełny kod można zobaczyć tutaj. Projekt nadal jest w fazie rozwoju.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster