-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
781 additions
and
306 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
title: "Deployment" | ||
weight: 2 | ||
showtoc: false | ||
--- | ||
|
||
# Deployment Guides | ||
|
||
This section contains guides for deploying a Vendure application to production. | ||
|
||
We are planning to publish specific guides for popular platforms soon. For now, you can find platform-specific information in our [Deployment discussion category](https://github.com/vendure-ecommerce/vendure/discussions/categories/deployment). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
--- | ||
title: "Deploying the Admin UI" | ||
showtoc: true | ||
--- | ||
|
||
## Deploying the Admin UI | ||
|
||
If you have customized the Admin UI with extensions, you should [compile your extensions ahead-of-time as part of the deployment process]({{< relref "/docs/plugins/extending-the-admin-ui" >}}#compiling-as-a-deployment-step). | ||
|
||
### Deploying a stand-alone Admin UI | ||
|
||
Usually, the Admin UI is served from the Vendure server via the AdminUiPlugin. However, you may wish to deploy the Admin UI app elsewhere. Since it is just a static Angular app, it can be deployed to any static hosting service such as Vercel or Netlify. | ||
|
||
Here's an example script that can be run as part of your host's `build` command, which will generate a stand-alone app bundle and configure it to point to your remote server API. | ||
|
||
This example is for Vercel, and assumes: | ||
|
||
* A `BASE_HREF` environment variable to be set to `/` | ||
* A public (output) directory set to `build/dist` | ||
* A build command set to `npm run build` or `yarn build` | ||
* A package.json like this: | ||
```json | ||
{ | ||
"name": "standalone-admin-ui", | ||
"version": "0.1.0", | ||
"private": true, | ||
"scripts": { | ||
"build": "ts-node compile.ts" | ||
}, | ||
"devDependencies": { | ||
"@vendure/ui-devkit": "^1.4.5", | ||
"ts-node": "^10.2.1", | ||
"typescript": "~4.3.5" | ||
} | ||
} | ||
``` | ||
|
||
```TypeScript | ||
// compile.ts | ||
import { compileUiExtensions } from '@vendure/ui-devkit/compiler'; | ||
import { DEFAULT_BASE_HREF } from '@vendure/ui-devkit/compiler/constants'; | ||
import path from 'path'; | ||
import { promises as fs } from 'fs'; | ||
|
||
/** | ||
* Compiles the Admin UI. If the BASE_HREF is defined, use that. | ||
* Otherwise, go back to the default admin route. | ||
*/ | ||
compileUiExtensions({ | ||
outputPath: path.join(__dirname, 'build'), | ||
baseHref: process.env.BASE_HREF ?? DEFAULT_BASE_HREF, | ||
extensions: [ | ||
/* any UI extensions would go here, or leave empty */ | ||
], | ||
}) | ||
.compile?.() | ||
.then(() => { | ||
// If building for Vercel deployment, replace the config to make | ||
// api calls to api.example.com instead of localhost. | ||
if (process.env.VERCEL) { | ||
console.log('Overwriting the vendure-ui-config.json for Vercel deployment.'); | ||
return fs.writeFile( | ||
path.join(__dirname, 'build', 'dist', 'vendure-ui-config.json'), | ||
JSON.stringify({ | ||
apiHost: 'https://api.example.com', | ||
apiPort: '443', | ||
adminApiPath: 'admin-api', | ||
tokenMethod: 'cookie', | ||
defaultLanguage: 'en', | ||
availableLanguages: ['en', 'de'], | ||
hideVendureBranding: false, | ||
hideVersion: false, | ||
}), | ||
); | ||
} | ||
}) | ||
.then(() => { | ||
process.exit(0); | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
--- | ||
title: "Getting data into production" | ||
showtoc: true | ||
weight: 4 | ||
--- | ||
|
||
# Getting data into production | ||
|
||
Once you have set up your production deployment, you'll need some way to get your products and other data into the system. | ||
|
||
The main tasks will be: | ||
|
||
1. Creation of the database schema | ||
2. Importing initial data like roles, tax rates, countries etc. | ||
3. Importing catalog data like products, variants, options, facets | ||
4. Importing other data used by your application | ||
|
||
## Creating the database schema | ||
|
||
The first item - creation of the schema - can be automatically handled by TypeORM's `synchronize` feature. Switching it on for the initial | ||
run will automatically create the schema. This can be done by using an environment variable: | ||
|
||
```TypeScript {hl_lines=[5]} | ||
export const config: VendureConfig = { | ||
// ... | ||
dbConnectionOptions: { | ||
type: 'postgres', | ||
synchronize: process.env.DB_SYNCHRONIZE, | ||
host: process.env.DB_HOST, | ||
port: process.env.DB_PORT, | ||
username: process.env.DB_USER, | ||
password: process.env.DB_PASSWORD, | ||
database: process.env.DB_DATABASE, | ||
}, | ||
// ... | ||
}; | ||
``` | ||
|
||
Set the `DB_SYNCHRONIZE` variable to `true` on first start, and then after the schema is created, set it to `false`. | ||
|
||
## Importing initial & catalog data | ||
|
||
Importing initial and catalog data can be handled by Vendure `populate()` helper function - see the [Importing Product Data guide]({{< relref "importing-product-data" >}}). | ||
|
||
## Importing other data | ||
|
||
Any kinds of data not covered by the `populate()` function can be imported using a custom script, which can use any Vendure service or service defined by your custom plugins to populate data in any way you like. See the [Stand-alone scripts guide]({{< relref "stand-alone-scripts" >}}). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
--- | ||
title: "Horizontal scaling" | ||
showtoc: true | ||
weight: 2 | ||
--- | ||
|
||
# Horizontal scaling | ||
|
||
"Horizontal scaling" refers to increasing the performance capacity of your application by running multiple instances. | ||
|
||
This type of scaling has two main advantages: | ||
|
||
1. It can enable increased throughput (requests/second) by distributing the incoming requests between multiple instances. | ||
2. It can increase resilience because if a single instance fails, the other instances will still be able to service requests. | ||
|
||
As discussed in the [Server resource requirements guide]({{< relref "server-resource-requirements" >}}), horizontal scaling can be the most cost-effective way of deploying your Vendure server due to the single-threaded nature of Node.js. | ||
|
||
In Vendure, both the server and the worker can be scaled horizontally. Scaling the server will increase the throughput of the GraphQL APIs, whereas scaling the worker can increase the speed with which the job queue is processed by allowing more jobs to be run in parallel. | ||
|
||
## Multi-instance configuration | ||
|
||
In order to run Vendure in a multi-instance configuration, there are some important configuration changes you'll need to make. The key consideration in configuring Vendure for this scenario is to ensure that any persistent state is managed externally from the Node process, and is shared by all instances. Namely: | ||
|
||
* The JobQueue should be stored externally using the [DefaultJobQueuePlugin]({{< relref "default-job-queue-plugin" >}}) (which stores jobs in the database) or the [BullMQJobQueuePlugin]({{< relref "bull-mqjob-queue-plugin" >}}) (which stores jobs in Redis), or some other custom JobQueueStrategy. **Note:** the BullMQJobQueuePlugin is much more efficient than the DefaultJobQueuePlugin, and is recommended for production applications. | ||
* A custom [SessionCacheStrategy]({{< relref "session-cache-strategy" >}}) must be used which stores the session cache externally (such as in the database or Redis), since the default strategy stores the cache in-memory and will cause inconsistencies in multi-instance setups. [Example Redis-based SessionCacheStrategy]({{< relref "session-cache-strategy" >}}) | ||
* When using cookies to manage sessions, make sure all instances are using the _same_ cookie secret: | ||
```TypeScript | ||
const config: VendureConfig = { | ||
authOptions: { | ||
cookieOptions: { | ||
secret: 'some-secret' | ||
} | ||
} | ||
} | ||
``` | ||
* Channel and Zone data gets cached in-memory as this data is used in virtually every request. The cache time-to-live defaults to 30 seconds, which is probably fine for most cases, but it can be configured in the [EntityOptions]({{< relref "entity-options" >}}#channelcachettl). | ||
|
||
## Using Docker or Kubernetes | ||
|
||
One way of implementing horizontal scaling is to use Docker to wrap your Vendure server & worker in a container, which can then be run as multiple instances. | ||
|
||
Some hosting providers allow you to provide a Docker image and will then run multiple instances of that image. Kubernetes can also be used to manage multiple instances | ||
of a Docker image. | ||
|
||
For a more complete guide, see the [Using Docker guide]({{< relref "using-docker" >}}). | ||
|
||
## Using PM2 | ||
|
||
[PM2](https://pm2.keymetrics.io/) is a process manager which will spawn multiple instances of your server or worker, as well as re-starting any instances that crash. PM2 can be used on VPS hosts to manage multiple instances of Vendure without needing Docker or Kubernetes. | ||
|
||
PM2 must be installed on your server: | ||
|
||
```sh | ||
npm install pm2@latest -g | ||
``` | ||
|
||
Your processes can then be run in [cluster mode](https://pm2.keymetrics.io/docs/usage/cluster-mode/) with the following command: | ||
|
||
```sh | ||
pm2 start ./dist/index.js -i 4 | ||
``` | ||
|
||
The above command will start a cluster of 4 instances. You can also instruct PM2 to use the maximum number of available CPUs with `-i max`. | ||
|
||
Note that if you are using pm2 inside a Docker container, you should use the `pm2-runtime` command: | ||
|
||
```Dockerfile | ||
# ... your existing Dockerfile config | ||
RUN npm install pm2 -g | ||
CMD ["pm2-runtime", "app.js", "-i", "max"] | ||
``` |
Binary file not shown.
116 changes: 116 additions & 0 deletions
116
docs/content/deployment/production-configuration/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
--- | ||
title: 'Production configuration' | ||
showtoc: true | ||
weight: 0 | ||
--- | ||
|
||
# Production configuration | ||
|
||
This is a guide to the recommended configuration for a production Vendure application. | ||
|
||
## Environment variables | ||
|
||
Keep sensitive information or context-dependent settings in environment variables. In local development you can store the values in a `.env` file. For production, you should use the mechanism provided by your hosting platform to set the values for production. | ||
|
||
The default `@vendure/create` project scaffold makes use of environment variables already. For example: | ||
|
||
```TypeScript | ||
const IS_DEV = process.env.APP_ENV === 'dev'; | ||
``` | ||
|
||
The `APP_ENV` environment variable can then be set using the admin dashboard of your hosting provider: | ||
|
||
{{< figure src="./env-var-ui.webp" title="A typical UI for setting env vars" >}} | ||
|
||
If you are using [Docker or Kubernetes]({{< relref "using-docker" >}}), they include their own methods of setting environment variables. | ||
|
||
## Superadmin credentials | ||
|
||
Ensure you set the superadmin credentials to something other than the default of `superadmin:superadmin`. Use your hosting platform's environment variables to set a **strong** password for the Superadmin account. | ||
|
||
```TypeScript | ||
import { VendureConfig } from '@vendure/core'; | ||
|
||
export const config: VendureConfig = { | ||
authOptions: { | ||
tokenMethod: ['bearer', 'cookie'], | ||
superadminCredentials: { | ||
identifier: process.env.SUPERADMIN_USERNAME, | ||
password: process.env.SUPERADMIN_PASSWORD, | ||
}, | ||
}, | ||
// ... | ||
}; | ||
``` | ||
|
||
## API hardening | ||
|
||
It is recommended that you install and configure the [HardenPlugin]({{< relref "harden-plugin" >}}) for all production deployments. This plugin locks down your schema (disabling introspection and field suggestions) and protects your Shop API against malicious queries that could otherwise overwhelm your server. | ||
|
||
Install the plugin: | ||
|
||
```sh | ||
npm install @vendure/harden-plugin | ||
|
||
# or | ||
|
||
yarn add @vendure/harden-plugin | ||
``` | ||
|
||
Then add it to your VendureConfig: | ||
|
||
```TypeScript | ||
import { VendureConfig } from '@vendure/core'; | ||
import { HardenPlugin } from '@vendure/harden-plugin'; | ||
|
||
const IS_DEV = process.env.APP_ENV === 'dev'; | ||
|
||
export const config: VendureConfig = { | ||
// ... | ||
plugins: [ | ||
HardenPlugin.init({ | ||
maxQueryComplexity: 500, | ||
apiMode: IS_DEV ? 'dev' : 'prod', | ||
}), | ||
// ... | ||
] | ||
}; | ||
``` | ||
|
||
{{< alert primary >}} | ||
For a detailed explanation of how to best configure this plugin, see the [HardenPlugin docs]({{< relref "harden-plugin" >}}). | ||
{{< /alert >}} | ||
|
||
## ID Strategy | ||
|
||
By default, Vendure uses auto-increment integer IDs as entity primary keys. While easier to work with in development, sequential primary keys can leak information such as the number of orders or customers in the system. | ||
|
||
For this reason you should consider using the UuidIdStrategy for production. | ||
|
||
```TypeScript | ||
import { UuidIdStrategy, VendureConfig } from '@vendure/core'; | ||
|
||
export const config: VendureConfig = { | ||
entityIdStrategy: new UuidIdStrategy(), | ||
// ... | ||
} | ||
``` | ||
|
||
Another option, if you wish to stick with integer IDs, is to create a custom [EntityIdStrategy]({{< relref "entity-id-strategy" >}}) which uses the `encodeId()` and `decodeId()` methods to obfuscate the sequential nature of the ID. | ||
|
||
## Database Timezone | ||
|
||
Vendure internally treats all dates & times as UTC. However, you may sometimes run into issues where dates are offset by some fixed amount of hours. E.g. you place an order at 17:00, but it shows up in the Admin UI as being placed at 19:00. Typically, this is caused by the timezone of your database not being set to UTC. | ||
|
||
You can check the timezone in **MySQL/MariaDB** by executing: | ||
|
||
```SQL | ||
SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP); | ||
``` | ||
and you should expect to see `00:00:00`. | ||
|
||
In **Postgres**, you can execute: | ||
```SQL | ||
show timezone; | ||
``` | ||
and you should expect to see `UTC` or `Etc/UTC`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
--- | ||
title: 'Server resource requirements' | ||
showtoc: true | ||
weight: 1 | ||
--- | ||
|
||
## Server resource requirements | ||
|
||
### RAM | ||
|
||
The Vendure server and worker process each use around 200-300MB of RAM when idle. This figure will increase under load. | ||
|
||
The total RAM required by a single instance of the server depends on your project size (the number of products, variants, customers, orders etc.) as well as expected load (the number of concurrent users you expect). As a rule, 512MB per process would be a practical minimum for a smaller project with low expected load. | ||
|
||
### CPU | ||
|
||
CPU resources are generally measured in "cores" or "vCPUs" (virtual CPUs) depending on the type of hosting. The exact relationship between vCPUs and physical CPU cores is out of the scope of this guide, but for our purposes we will use "CPU" to refer to both physical and virtual CPU resources. | ||
|
||
Because Node.js is single-threaded, a single instance of the Vendure server or worker will not be able to take advantage of multiple CPUs. For example, if you set up a server instance running with 4 CPUs, the server will only use 1 of those CPUs and the other 3 will be wasted. | ||
|
||
Therefore, when looking to optimize performance (for example, the number of requests that can be serviced per second), it makes sense to scale horizontally by running multiple instances of the Vendure server. See the [Horizontal Scaling guide]({{< relref "horizontal-scaling" >}}). | ||
|
||
## Load testing | ||
|
||
It is important to test whether your current server configuration will be able to handle the loads you expect when you go into production. There are numerous tools out there to help you load test your application, such as: | ||
|
||
- [k6](https://k6.io/) | ||
- [Artillery](https://www.artillery.io/) | ||
- [jMeter](https://jmeter.apache.org/) |
Oops, something went wrong.