Production Deployment

Занятие 13

Профессия
Node.js & React.js developer
продвинутый курс

  • Webpack, настройка для Production
  • Docker
  • Deployment & CI

План

1

Webpack

настройка для production

Пару слов про Webpack

Отличия

чем не является webpack

import { Button } from 'react-bootstrap';

import './style.css';

const data = require('raw!./file.txt')

function render(params) {
  const view = require("./buttons.jade");
  ...
    
  return data.split('\n').join('<br/>');
}

if (__DEV__) {
  console.log('LOG');
  connectToSSH('#password')
}
  
import Hello from '~/components/Hello';

How it works

Webpack.config.js

Кастомизация

  • Base config
  • Client / Server / Electron / Service-worker
  • Develop (verbose) / Production (minify)
  • PC / Stage / Stage2 / Production 7

Есть выход

  1. var config = require('./base');  config.taget = '';
  2. Object.assign, lodash.merge
  3. https://github.com/Fitbit/webpack-config

 

class WebpackConfig {
  getConfig() {}
}

class WebpackClientConfig extends WebpackConfig {
}

class WebpackServerConfig extends WebpackConfig {
}

class WebpackWidgetConfig extends WebpackClientConfig {
}

My way

class extends

getPreConfig() {
 return {
  context: this.resolvePath('src'),
  target: this.getTarget(),
  entry: this.getEntry(),
  resolve: this.getResolve(),
  output: this.getOutput(),
  module: this.getModule(),
  plugins: this.getPlugins(),
  cache: this.isDebug(),
  debug: this.isDebug(),
  stats: this.getStats(),
  postcss: (...args) => this.getPostcssModule(...args),
 }
}

WebpackConfig

getLoaders() {
  return [
    this.getJsxLoader(),
    ...this.getCssLoaders(),
    {
      test: /\.json$/,
      loader: 'json-loader',
    },
    {
      test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)(\?.+)?$/,
      loader: 'url-loader',
      query: {
        name: this.isDebug() ? '[path][name].[ext]?[hash]' : '[hash].[ext]',
        limit: 10000,
      },
    },
    {
      test: /\.(eot|ttf|wav|mp3)(\?.+)?$/,
      loader: 'file-loader',
      query: {
        name: this.isDebug() ? '[path][name].[ext]?[hash]' : '[hash].[ext]',
      },
    },
  ]
}

Loaders

{
  test: /\.global\.css$/,
  loaders: [
    'isomorphic-style-loader',
    `css-loader?${JSON.stringify({
      sourceMap: this.isDebug(),
      modules: false,
      minimize: !this.isDebug(),
    })}`,
  ],
},
{
  test: /\.css$/,
  exclude: /(global.css)/,
  loaders: [
    'isomorphic-style-loader',
    `css-loader?${JSON.stringify({
      sourceMap: this.isDebug(),
      modules: true,
      localIdentName: this.isDebug() ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]',
      minimize: !this.isDebug(),
    })}`,
    'postcss-loader?pack=default',
  ],
},

CSS Loaders

  getGlobals() {
    return {
      'process.env.NODE_ENV': JSON.stringify(this.getEnv()),
      __ENV__: JSON.stringify(this.getEnv()),
      __DEV__: this.getEnv() === 'development',
      __PROD__: this.getEnv() === 'production',
    }
  }

  getTarget() {
    return 'web'
  }
  getEntry() {
    return './client.js'
  }
  getGlobals() {
    return {
      ...super.getGlobals(),
      __SERVER__: false,
      __CLIENT__: true,
      __BROWSER__: true,
    }
  }

Globals

ProvidePlugin

lsk-build

тюнинг

компиляция webpack.config.js: ~/build/wepack.config.js

Проблемы

  1. React в бандле весит больше чем lib/react.js
  2. Несколько версий lodash, react-bootstrap
  3. Moment.js грузит 100+ локалей
  4. Непонятные полифилы

Как найти проблемы

stats-webpack-plugin && webpack-bundle-analyzer

Как найти проблемы

Попрактикуем чтение?

2

Docker

Why

Архитектура

Пару терминов

  1. Docker
  2. Docker Image
  3. Docker Container
  4. Docker AuFS, Layers
  5.  Docker Network
  6. Docker Volumes
  7. Dockerfile
  8. Docker Repository
  9. Docker Hub
  10. Docker Swarm

AuFS

docker ps

docker images

ncdu /

Docker-compose

Text

web:
  image: node:5.4.0
  restart: always
  command: /bin/bash -c "npm install && npm start"
  mem_limit: 100000000
  volumes:
      - ../../node_modules/:/app/node_modules/
      - ../../gizobi_frontend/:/app/src/
      - ../../gizobi_frontend/node_modules:/app/src/node_modules
  ports:
      - "8022:8080"
  environment:
    PORT: 8080
    ENV: production
    SITE_URL: http://gizobi.immuzov.ru
  working_dir: /app/src

docker-compose

web:
  image: node:5.4.0
  restart: always
  command: /bin/bash -c "npm install && npm start"
  mem_limit: 100000000
  volumes:
      - ../../node_modules/:/app/node_modules/
      - ../../gizobi_frontend/:/app/src/
      - ../../gizobi_frontend/node_modules:/app/src/node_modules
  ports:
      - "8022:8080"
  environment:
    PORT: 8080
    ENV: production
    NODE_ENV: production
    SITE_URL: http://gizobi.immuzov.ru
  working_dir: /app/src

api:
  image: node:5.4.0
  restart: always
  command: /bin/bash -c "npm install && npm start"
  mem_limit: 100000000
  volumes:
      - ../../node_modules/:/app/node_modules/
      - ../../gizobi_backend/:/app/src/
      - ../../gizobi_backend/node_modules:/app/src/node_modules
  ports:
      - "8023:8080"
  environment:
    PORT: 8080
    ENV: production
    NODE_ENV: production
  working_dir: /app/src

docker-compose

# Dockerfile
FROM node:6.9.2

WORKDIR /app

ADD ["./build", "/app"]
CMD ["npm", "start"]

Dockerfile

# Dockerfile.build
FROM node:6.9.2
RUN npm i -g yarn

# VOLUME ["/tmp/.cache", "/root/.cache"]

WORKDIR /app
CMD sh -c "echo \"Start\" && \
  NODE_ENV=development yarn install && \
  NODE_ENV=production yarn run build && \
  cd build && NODE_ENV=production yarn install"

#  cd .. && \
#  cp -R src/public/* build/public

Анализ

docker run -v /var/run/docker.sock:/run/docker.sock -ti -e TERM tomastomecek/sen

Немного команд

docker ps
docker ps -a
docker rm

docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)

docker images
docker images -a
docker images -q
docker images -f dangling=true
docker rmi

docker volume ls
docker volume ls -f dangling=true


docker rmi $(docker images -f dangling=true -q)
docker volume rm $(docker volume ls -f dangling=true -q)

3

Deployment & CI

Deployment Problems

  1. Интеграционные тесты
  2. Версионирование
  3. Конфигурирование
  4. Мониторинг 
  5. Service Discovery
  6. Orchestration
  7. Delivery to Environments

Как собирать?

  1. Руками
  2. SH
  3. NPM
  4. JS
  5. CI
  6. все сразу

Автоматизация

  1. Shell / NPM
  2. CRON
  3. Git hooks
  4. CI
  5. Telegram/Slack bots

Про CI

Concourse CI

Алгоритм

  1. Настроить CI
  2. Положить config в проект
#!groovy​
node('master') {

    currentBuild.result = "SUCCESS"

    try {

        stage('Checkout') {
            checkout scm
        }

        stage('Clean previous data') {
            sh 'rm -rf build node_modules'
        }

        stage('Build project') {
            sh 'docker build -f ./Dockerfile.build -t lsk-example-build .'
            sh 'docker run -v `pwd`:/app lsk-example-build'
            sh 'sudo chown -R jenkins:jenkins build node_modules'
        }

        stage('Creating Docker Image') {
            def image = docker.build("lsk-example:${env.BUILD_NUMBER}")
            docker.withRegistry('https://hq.mgbeta.ru:5000/', 'docker-registry') {
                image.push()
                image.push('latest')
            }
        }

        stage('Deploy') {
            sshagent(['s3']) {
                sh 'cd /projects/lsk && sh refresh.sh'
            }
        }

        stage('Test connection') {
            sh 'sleep 30'
            httpRequest('http://lsk.mgbeta.ru')
        }

        stage('Clean build') {
            sh 'sudo chown -R jenkins:jenkins build node_modules'
            sh 'rm -rf node_modules build'
            mail body: "lsk-example Build # ${env.BUILD_NUMBER} - SUCCESS:\nCheck console output at ${env.BUILD_URL} to view the results.",
                from: 'ci@mgbeta.ru',
                subject: "lsk-example - Build # ${env.BUILD_NUMBER} - SUCCESS!",
                to: 'obt195@gmail.com, errors@coder24.ru, shitric2@gmail.com'
        }

    } catch (err) {
        currentBuild.result = "FAILURE"
        sh 'sudo chown -R jenkins:jenkins build node_modules'
        sh 'rm -rf node_modules build'
        mail body: "lsk-example - Build # ${env.BUILD_NUMBER} - FAILURE:\nCheck console output at ${env.BUILD_URL} to view the results.",
            from: 'ci@mgbeta.ru',
            subject: "lsk-example - Build # ${env.BUILD_NUMBER} - FAILURE!",
            to: 'obt195@gmail.com, errors@coder24.ru, shitric2@gmail.com'

        throw err
    }

}

Посмотреть

  1. Микросервисная архитектура с непрерывной интеграцией — пример из практики — Виталий Аминев youtube.com/watch?v=skdmxAM2lVU
  2. Kubernetes. Дирижирование контейнерами Docker youtube.com/watch?v=cGqkTRbS8hw
  3. Микросервисная архитектура на базе CoreOS и Kubernetes youtube.com/watch?v=pq2swO_zRk8​

4

Пару слов

Игорь Суворов

Thanks!

any questions?

программист-предприниматель

Production Deployment

By Igor Suvorov

Production Deployment

* Webpack, настройка для Production * Docker * Deployment & CI

  • 1,126