Razzle: Раззл и бессерверный

Созданный на 30 мая 2018  ·  30Комментарии  ·  Источник: jaredpalmer/razzle

Привет !
Я пытаюсь запустить проект razzle (materialUI, Typescript, Apollo & After) без сервера.
Как лучше всего справиться с этим с помощью Serverless?

Если вы знаете какой-либо пример, я буду рад взглянуть. Спасибо !

Самый полезный комментарий

Вот как я развертывал приложения Razzle в AWS Lambda с AWS CDK :

RazzleStack

import * as CDK from '@aws-cdk/core';
import * as S3 from '@aws-cdk/aws-s3';
import * as S3Deployment from '@aws-cdk/aws-s3-deployment';
import * as Lambda from '@aws-cdk/aws-lambda';
import * as APIGateway from '@aws-cdk/aws-apigateway';
import * as SSM from '@aws-cdk/aws-ssm';
import * as SecretsManager from '@aws-cdk/aws-secretsmanager';

import { ConfigProps, getParam, ModeStack } from '../helpers';

export class RazzleStack extends ModeStack {
  constructor(app: CDK.App, id: string, props: ConfigProps) {
    super(app, id, props);

    /**
     * S3 bucket to the /public folder
     */
    const publicBucketName = `my-razzle-app-bucket-public-files-${this.mode}`;
    const bucketPublicFiles = new S3.Bucket(this, publicBucketName, {
      publicReadAccess: true,
      bucketName: publicBucketName.toLowerCase(),
    });

    /**
     * Store S3 bucket name
     */
    new SSM.StringParameter(this, `MyRazzleAppBucketAssetsName${this.Mode}`, {
      description: `My Razzle App S3 Bucket Name for Assets on ${this.Mode}`,
      parameterName: `/${props.name}/S3/Assets/Name`,
      stringValue: bucketPublicFiles.bucketName,
    });

    /**
     * Store S3 domainName name
     */
    new SSM.StringParameter(this, `MyRazzleAppBucketAssetsDomainName${this.Mode}`, {
      description: `My Razzle App S3 Bucket DomainName for Assets on ${this.Mode}`,
      parameterName: `/${props.name}/S3/Assets/DomainName`,
      stringValue: bucketPublicFiles.bucketDomainName,
    });

    /**
     * Deploy public folder of build to `my-razzle-app-bucket-public-files-${this.mode}` bucket
     */
    new S3Deployment.BucketDeployment(this, `${publicBucketName}-deploy`, {
      sources: [S3Deployment.Source.asset('./build/public')],
      destinationBucket: bucketPublicFiles,
    });

    /**
     * Environment Variables for SSR Function
     */
    const environmentKeys = [
      'NODE_ENV',
      'RAZZLE_GRAPHQL_URL',
      'RAZZLE_MAPBOX_ACCESS_TOKEN',
      'RAZZLE_GOOGLE_RECAPTCHA_SITE',
      'RAZZLE_GA_ID',
    ];

    const environmentSecret = SecretsManager.Secret.fromSecretAttributes(
      this,
      `MyRazzleAppEnvironmentSecret${this.Mode}`,
      {
        secretArn: getParam(this, `MyRazzleAppSecretsArn${this.Mode}`),
      },
    );

    let environment: { [key: string]: string } = {};
    for (const key of environmentKeys) {
      environment[key] = environmentSecret.secretValueFromJson(key).toString();
    }

    /**
     * Razzle SSR Function
     */
    const myRazzleAppSsrFunction = new Lambda.Function(this, `MyRazzleAppSSRFunction${this.Mode}`, {
      description: `Lambda Function that runs My Razzle App SSR on ${this.Mode}`,
      code: Lambda.Code.fromAsset('./build', {
        exclude: ['public', 'static', '*.json'],
      }),
      handler: 'server.handler',
      runtime: Lambda.Runtime.NODEJS_12_X,
      memorySize: 512,
      timeout: CDK.Duration.seconds(5),
      environment,
      tracing: Lambda.Tracing.ACTIVE,
    });

    /**
     * Razzle ApiGateway
     */
    const razzleSsrApiGatewayName = `MyRazzleAppSSRApiGateway${this.Mode}`;
    const api = new APIGateway.RestApi(this, razzleSsrApiGatewayName, {
      description: `ApiGateway that exposes My Razzle App SSR on ${this.Mode}`,
      binaryMediaTypes: ['*/*'],
      endpointTypes: [APIGateway.EndpointType.REGIONAL],
      deployOptions: {
        stageName: this.mode,
      },
    });
    const integration = new APIGateway.LambdaIntegration(myRazzleAppSsrFunction);
    const root = api.root;
    const pathApi = api.root.addResource('{proxy+}');

    root.addMethod('GET', integration);
    pathApi.addMethod('ANY', integration);

    /**
     * Razzle ApiGateway ID
     */
    new SSM.StringParameter(this, `MyRazzleAppAPIGatewayRestId${this.Mode}`, {
      description: `My Razzle App ApiGateway ID on ${this.Mode}`,
      parameterName: `/${props.name}/APIGateway/ApiId`,
      stringValue: api.restApiId,
    });
  }
}

Все 30 Комментарий

Кто-то просто сделал это и написал об этом в Твиттере. Было бы круто

Не могу найти твит! У тебя есть это ?

Был ли мой твит - я просто заменил index.js (где происходит горячая загрузка) на

import awsServerlessExpress from 'aws-serverless-express'
import app from './server'
const binaryMimeTypes = [
  'application/octet-stream',
  'font/eot',
  'font/opentype',
  'font/otf',
  'image/jpeg',
  'image/png',
  'image/svg+xml',
]
const server = awsServerlessExpress.createServer(app, null, binaryMimeTypes)

export const handler = (event, context, callback) => {
  awsServerlessExpress.proxy(server, event, context)
}

Затем вы запускаете npm run build , копируете node_modules/ в build/ , заархивируете server.js вместе с папкой node_modules/ и загружаете это в AWS Lambda. Все в public/ затем загружается в S3 и отправляется через Cloudfront.

Для инфраструктуры шлюза Lambda -> API я использовал Terraform и следовал этому руководству: https://www.terraform.io/docs/providers/aws/guides/serverless-with-aws-lambda-and-api-gateway.html

Тем не менее, все еще не идеально - получение этой ошибки при загрузке из клиентского пакета (https://static.jobsok.io/):

image


Обновлять:
Я не дерьмо, удаление node_modules и переустановка дало мне:

File sizes after gzip:

  210.38 KB (+8 B)  build/static/js/bundle.1a960add.js
  1.96 KB           build/static/css/bundle.5865fae5.css

Без изменений кода - снова сделал клиентский пакет работоспособным

Мой handler.js (или index.js) такой же.
Затем я собираю все с помощью webpack (project + node_modules). (Для этого вам просто нужно изменить razzle.config.js)

Я работаю с бессерверным, а не с терраформом. В основном то же самое. Просто нужно импортировать относительный пакет для обработчика (по какой-то причине веб-пакет не упаковывает этот, возможно, потому что он находится в другом файле / пути, исправим позже).

Бессерверная закачка все это и все. Посмотреть проект можно здесь: https://github.com/CuistotduCoin/cuistot

Это далеко не идеально, да и вайп.

Кстати, @jaredpalmer , не могли бы вы мне помочь с конфигурацией razzle / webpack?
Я хочу включить в пакет все необходимое (включая node_modules) с максимальной производительностью. Есть примеры / советы? (С конфигурацией Typescript)

@rozenmd можешь поделиться своим razzle.config?

Шахта @romainquellec пуста - никаких дополнительных настроек, кроме тех, которые предоставляет razzle

Ну, я не знаю почему, но я не могу получить доступ к aws-serverless-express, определенному в зависимостях (может быть, больше).
И чтобы оптимизировать все это, я хочу исключить aws-sdk из окончательной сборки (он всегда доступен в лямбда-функции, хотя мы увидим это позже)

Я думаю, что это происходит из-за внешних факторов, но я не могу этого понять.

Да, у меня была такая же проблема. Моим единственным решением было заархивировать server.js со всеми node_modules.

Я не могу использовать лямбду. Официальный лимит - 50 месяцев.
Общий вопрос для всех: как связать node_modules с хорошей производительностью с razzle. Спасибо !

Мы не используем лямбда, но мы объединяем все наши node_modules, удаляя опцию externals по умолчанию в razzle.config.js. Это круто, потому что это означает, что папка сборки фактически является артефактом сборки. Затем мы помещаем его на s3, затем наша задача Jenkins загружает новую папку на каждый сервер и перезапускает pm2.

@jaredpalmer - есть ли шанс, что вы могли бы опубликовать суть с этим razzle.config.js ?

удалив опцию externals по умолчанию в razzle.config.js:

Как это ?
modify(config, { target, dev }) { if (target === 'node' && !dev) { config.externals = []; } return config; }

@romainquellec, который работает !!

module.exports = {
  modify: (config, { target, dev }, webpack) => {
    // do something to config
    const appConfig = config // stay immutable here

    if (target === 'node' && !dev) {
      appConfig.externals = []
    }

    return appConfig
  },
}

Работает. Хороший ! 👍 👍
Справедливо добавить пример! Я посмотрю на это в эти выходные. Вы не против принять участие в @rozenmd ?

К сожалению, в эти выходные у меня не будет свободного времени @romainquellec, но я буду рад пересмотреть / поработать над PR

Я закрываю это. Отправлю PR как можно скорее.

@romainquellec Вы когда-нибудь делали этот

@mbrochh В конце концов не увидел https://onlineornot.com для всего моего бессерверного рендеринга на стороне сервера.

Я также только что нашел это сообщение в блоге: https://medium.com/@RozenMD/build -your-own-app-with-react-graphql-and-serverless-architecture-part-1-server-side-rendering-f0d0144ff5f

Думаю, мне придется погрузиться в голову и попытаться следить за этим постом :)

Спасибо за сообщение!

https://maxrozen.com/2018/08/08/start-your-own-app-with-react-part-1 , вероятно, лучшая ссылка для форматирования кода.

и спасибо! :улыбка:

Кто-нибудь развернул Razzle на AWS lambda?

Я также ищу пример для Razzle на AWS lambda. Если бы кто-то сделал это и был бы рад поделиться с нами, был бы очень признателен.

Вот как я развертывал приложения Razzle в AWS Lambda с AWS CDK :

RazzleStack

import * as CDK from '@aws-cdk/core';
import * as S3 from '@aws-cdk/aws-s3';
import * as S3Deployment from '@aws-cdk/aws-s3-deployment';
import * as Lambda from '@aws-cdk/aws-lambda';
import * as APIGateway from '@aws-cdk/aws-apigateway';
import * as SSM from '@aws-cdk/aws-ssm';
import * as SecretsManager from '@aws-cdk/aws-secretsmanager';

import { ConfigProps, getParam, ModeStack } from '../helpers';

export class RazzleStack extends ModeStack {
  constructor(app: CDK.App, id: string, props: ConfigProps) {
    super(app, id, props);

    /**
     * S3 bucket to the /public folder
     */
    const publicBucketName = `my-razzle-app-bucket-public-files-${this.mode}`;
    const bucketPublicFiles = new S3.Bucket(this, publicBucketName, {
      publicReadAccess: true,
      bucketName: publicBucketName.toLowerCase(),
    });

    /**
     * Store S3 bucket name
     */
    new SSM.StringParameter(this, `MyRazzleAppBucketAssetsName${this.Mode}`, {
      description: `My Razzle App S3 Bucket Name for Assets on ${this.Mode}`,
      parameterName: `/${props.name}/S3/Assets/Name`,
      stringValue: bucketPublicFiles.bucketName,
    });

    /**
     * Store S3 domainName name
     */
    new SSM.StringParameter(this, `MyRazzleAppBucketAssetsDomainName${this.Mode}`, {
      description: `My Razzle App S3 Bucket DomainName for Assets on ${this.Mode}`,
      parameterName: `/${props.name}/S3/Assets/DomainName`,
      stringValue: bucketPublicFiles.bucketDomainName,
    });

    /**
     * Deploy public folder of build to `my-razzle-app-bucket-public-files-${this.mode}` bucket
     */
    new S3Deployment.BucketDeployment(this, `${publicBucketName}-deploy`, {
      sources: [S3Deployment.Source.asset('./build/public')],
      destinationBucket: bucketPublicFiles,
    });

    /**
     * Environment Variables for SSR Function
     */
    const environmentKeys = [
      'NODE_ENV',
      'RAZZLE_GRAPHQL_URL',
      'RAZZLE_MAPBOX_ACCESS_TOKEN',
      'RAZZLE_GOOGLE_RECAPTCHA_SITE',
      'RAZZLE_GA_ID',
    ];

    const environmentSecret = SecretsManager.Secret.fromSecretAttributes(
      this,
      `MyRazzleAppEnvironmentSecret${this.Mode}`,
      {
        secretArn: getParam(this, `MyRazzleAppSecretsArn${this.Mode}`),
      },
    );

    let environment: { [key: string]: string } = {};
    for (const key of environmentKeys) {
      environment[key] = environmentSecret.secretValueFromJson(key).toString();
    }

    /**
     * Razzle SSR Function
     */
    const myRazzleAppSsrFunction = new Lambda.Function(this, `MyRazzleAppSSRFunction${this.Mode}`, {
      description: `Lambda Function that runs My Razzle App SSR on ${this.Mode}`,
      code: Lambda.Code.fromAsset('./build', {
        exclude: ['public', 'static', '*.json'],
      }),
      handler: 'server.handler',
      runtime: Lambda.Runtime.NODEJS_12_X,
      memorySize: 512,
      timeout: CDK.Duration.seconds(5),
      environment,
      tracing: Lambda.Tracing.ACTIVE,
    });

    /**
     * Razzle ApiGateway
     */
    const razzleSsrApiGatewayName = `MyRazzleAppSSRApiGateway${this.Mode}`;
    const api = new APIGateway.RestApi(this, razzleSsrApiGatewayName, {
      description: `ApiGateway that exposes My Razzle App SSR on ${this.Mode}`,
      binaryMediaTypes: ['*/*'],
      endpointTypes: [APIGateway.EndpointType.REGIONAL],
      deployOptions: {
        stageName: this.mode,
      },
    });
    const integration = new APIGateway.LambdaIntegration(myRazzleAppSsrFunction);
    const root = api.root;
    const pathApi = api.root.addResource('{proxy+}');

    root.addMethod('GET', integration);
    pathApi.addMethod('ANY', integration);

    /**
     * Razzle ApiGateway ID
     */
    new SSM.StringParameter(this, `MyRazzleAppAPIGatewayRestId${this.Mode}`, {
      description: `My Razzle App ApiGateway ID on ${this.Mode}`,
      parameterName: `/${props.name}/APIGateway/ApiId`,
      stringValue: api.restApiId,
    });
  }
}

@jgcmarins об этой настройке razzletack. Не могли бы вы пошагово рассказать, как это использовать? Я хотел бы добавить его в документацию razzle.

https://razzle-git-canary.jared.vercel.app/deployment-options/aws

Мне нужна помощь в этом, может ли кто-нибудь помочь мне?

@fivethreeo, шаг за шагом нужно настроить CDK в учетной записи AWS, а затем запустить cdk deploy с некоторыми дополнительными флагами, и он будет работать \ o /

Я бы хотел сделать этап с доменом вместо пути. Возможно, сделайте полнофункциональный пример с rds, вращением клавиш, TypeOrm, typegraphql, Formik и jwt, но не в качестве примера в самом razzle.

В остальном ничего не упущено.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

ewolfe picture ewolfe  ·  4Комментарии

knipferrc picture knipferrc  ·  5Комментарии

corydeppen picture corydeppen  ·  3Комментарии

dizzyn picture dizzyn  ·  3Комментарии

gabimor picture gabimor  ·  3Комментарии