Cube Core
This guide walks you through deploying Cube with Docker.
This is an example of a production-ready deployment, but real-world deployments can vary significantly depending on desired performance and scale.
Prerequisites
Configuration
Create a Docker Compose stack by creating a docker-compose.yml
. A
production-ready stack would at minimum consist of:
- One or more Cube API instance
- A Cube Refresh Worker
- A Cube Store Router node
- One or more Cube Store Worker nodes
An example stack using BigQuery as a data source is provided below:
Using macOS or Windows? Use CUBEJS_DB_HOST=host.docker.internal
instead of
localhost
if your database is on the same machine.
version: "2.2"
services:
cube_api:
restart: always
image: cubejs/cube:v%CURRENT_VERSION
ports:
- 4000:4000
environment:
- CUBEJS_DB_TYPE=bigquery
- CUBEJS_DB_BQ_PROJECT_ID=cubejs-bq-cluster
- CUBEJS_DB_BQ_CREDENTIALS=<BQ-KEY>
- CUBEJS_DB_EXPORT_BUCKET=cubestore
- CUBEJS_CUBESTORE_HOST=cubestore_router
- CUBEJS_API_SECRET=secret
volumes:
- .:/cube/conf
depends_on:
- cube_refresh_worker
- cubestore_router
- cubestore_worker_1
- cubestore_worker_2
cube_refresh_worker:
restart: always
image: cubejs/cube:v%CURRENT_VERSION
environment:
- CUBEJS_DB_TYPE=bigquery
- CUBEJS_DB_BQ_PROJECT_ID=cubejs-bq-cluster
- CUBEJS_DB_BQ_CREDENTIALS=<BQ-KEY>
- CUBEJS_DB_EXPORT_BUCKET=cubestore
- CUBEJS_CUBESTORE_HOST=cubestore_router
- CUBEJS_API_SECRET=secret
- CUBEJS_REFRESH_WORKER=true
volumes:
- .:/cube/conf
cubestore_router:
restart: always
image: cubejs/cubestore:v%CURRENT_VERSION
environment:
- CUBESTORE_WORKERS=cubestore_worker_1:10001,cubestore_worker_2:10002
- CUBESTORE_REMOTE_DIR=/cube/data
- CUBESTORE_META_PORT=9999
- CUBESTORE_SERVER_NAME=cubestore_router:9999
volumes:
- .cubestore:/cube/data
cubestore_worker_1:
restart: always
image: cubejs/cubestore:v%CURRENT_VERSION
environment:
- CUBESTORE_WORKERS=cubestore_worker_1:10001,cubestore_worker_2:10002
- CUBESTORE_SERVER_NAME=cubestore_worker_1:10001
- CUBESTORE_WORKER_PORT=10001
- CUBESTORE_REMOTE_DIR=/cube/data
- CUBESTORE_META_ADDR=cubestore_router:9999
volumes:
- .cubestore:/cube/data
depends_on:
- cubestore_router
cubestore_worker_2:
restart: always
image: cubejs/cubestore:v%CURRENT_VERSION
environment:
- CUBESTORE_WORKERS=cubestore_worker_1:10001,cubestore_worker_2:10002
- CUBESTORE_SERVER_NAME=cubestore_worker_2:10002
- CUBESTORE_WORKER_PORT=10002
- CUBESTORE_REMOTE_DIR=/cube/data
- CUBESTORE_META_ADDR=cubestore_router:9999
volumes:
- .cubestore:/cube/data
depends_on:
- cubestore_router
Set up reverse proxy
In production, the Cube API should be served over an HTTPS connection to ensure security of the data in-transit. We recommend using a reverse proxy; as an example, let's use NGINX (opens in a new tab).
You can also use a reverse proxy to enable HTTP 2.0 and GZIP compression
First we'll create a new server configuration file called nginx/cube.conf
:
server {
listen 443 ssl;
server_name cube.my-domain.com;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ecdh_curve secp384r1;
# Replace the ciphers with the appropriate values
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384 OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256";
ssl_prefer_server_ciphers on;
ssl_certificate /etc/ssl/private/cert.pem;
ssl_certificate_key /etc/ssl/private/key.pem;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
location / {
proxy_pass http://cube:4000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Then we'll add a new service to our Docker Compose stack:
services:
...
nginx:
image: nginx
ports:
- 443:443
volumes:
- ./nginx:/etc/nginx/conf.d
- ./ssl:/etc/ssl/private
Don't forget to create a ssl
directory with the cert.pem
and key.pem
files
inside so the Nginx service can find them.
For automatically provisioning SSL certificates with LetsEncrypt, this blog post (opens in a new tab) may be useful.
Security
Use JSON Web Tokens
Cube can be configured to use industry-standard JSON Web Key Sets for securing its API and limiting access to data. To do this, we'll define the relevant options on our Cube API instance:
If you're using queryRewrite
for access control,
then you must also configure
scheduledRefreshContexts
so the refresh workers
can correctly create pre-aggregations.
services:
cube_api:
image: cubejs/cube:v%CURRENT_VERSION
ports:
- 4000:4000
environment:
- CUBEJS_DB_TYPE=bigquery
- CUBEJS_DB_BQ_PROJECT_ID=cubejs-bq-cluster
- CUBEJS_DB_BQ_CREDENTIALS=<BQ-KEY>
- CUBEJS_DB_EXPORT_BUCKET=cubestore
- CUBEJS_CUBESTORE_HOST=cubestore_router
- CUBEJS_API_SECRET=secret
- CUBEJS_JWK_URL=https://cognito-idp.<AWS_REGION>.amazonaws.com/<USER_POOL_ID>/.well-known/jwks.json
- CUBEJS_JWT_AUDIENCE=<APPLICATION_URL>
- CUBEJS_JWT_ISSUER=https://cognito-idp.<AWS_REGION>.amazonaws.com/<USER_POOL_ID>
- CUBEJS_JWT_ALGS=RS256
- CUBEJS_JWT_CLAIMS_NAMESPACE=<CLAIMS_NAMESPACE>
volumes:
- .:/cube/conf
depends_on:
- cubestore_worker_1
- cubestore_worker_2
- cube_refresh_worker
Securing Cube Store
All Cube Store nodes (both router and workers) should only be accessible to Cube API instances and refresh workers. To do this with Docker Compose, we simply need to make sure that none of the Cube Store services have any exposed
Monitoring
All Cube logs can be found by through the Docker Compose CLI:
docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------------------------------------------
cluster_cube_1 docker-entrypoint.sh cubej ... Up 0.0.0.0:4000->4000/tcp,:::4000->4000/tcp
cluster_cubestore_router_1 ./cubestored Up 3030/tcp, 3306/tcp
cluster_cubestore_worker_1_1 ./cubestored Up 3306/tcp, 9001/tcp
cluster_cubestore_worker_2_1 ./cubestored Up 3306/tcp, 9001/tcp
docker-compose logs
cubestore_router_1 | 2021-06-02 15:03:20,915 INFO [cubestore::metastore] Creating metastore from scratch in /cube/.cubestore/data/metastore
cubestore_router_1 | 2021-06-02 15:03:20,950 INFO [cubestore::cluster] Meta store port open on 0.0.0.0:9999
cubestore_router_1 | 2021-06-02 15:03:20,951 INFO [cubestore::mysql] MySQL port open on 0.0.0.0:3306
cubestore_router_1 | 2021-06-02 15:03:20,952 INFO [cubestore::http] Http Server is listening on 0.0.0.0:3030
cube_1 | 🚀 Cube API server (%CURRENT_VERSION) is listening on 4000
cubestore_worker_2_1 | 2021-06-02 15:03:24,945 INFO [cubestore::cluster] Worker port open on 0.0.0.0:9001
cubestore_worker_1_1 | 2021-06-02 15:03:24,830 INFO [cubestore::cluster] Worker port open on 0.0.0.0:9001
Update to the latest version
Find the latest stable release version (currently v%CURRENT_VERSION
) from
Docker Hub (opens in a new tab). Then update your docker-compose.yml
to use
the tag:
version: "2.2"
services:
cube_api:
image: cubejs/cube:v%CURRENT_VERSION
ports:
- 4000:4000
environment:
- CUBEJS_DB_TYPE=bigquery
- CUBEJS_DB_BQ_PROJECT_ID=cubejs-bq-cluster
- CUBEJS_DB_BQ_CREDENTIALS=<BQ-KEY>
- CUBEJS_DB_EXPORT_BUCKET=cubestore
- CUBEJS_CUBESTORE_HOST=cubestore_router
- CUBEJS_API_SECRET=secret
volumes:
- .:/cube/conf
depends_on:
- cubestore_router
- cube_refresh_worker
Extend the Docker image
If you need to use npm packages with native extensions inside the cube.js
configuration file, you'll need to build your own Docker image.
You can do this by first creating a Dockerfile
and a corresponding
.dockerignore
:
touch Dockerfile
touch .dockerignore
Add this to the Dockerfile
:
FROM cubejs/cube:latest
COPY . .
RUN npm install
And this to the .dockerignore
:
node_modules
npm-debug.log
schema
cube.js
.env
Then start the build process by running the following command:
docker build -t <YOUR-USERNAME>/cubejs-custom-build .
Finally, update your docker-compose.yml
to use your newly-built image:
version: "2.2"
services:
cube_api:
image: <YOUR-USERNAME>/cubejs-custom-build
ports:
- 4000:4000
environment:
- CUBEJS_DB_TYPE=bigquery
- CUBEJS_DB_BQ_PROJECT_ID=cubejs-bq-cluster
- CUBEJS_DB_BQ_CREDENTIALS=<BQ-KEY>
- CUBEJS_DB_EXPORT_BUCKET=cubestore
- CUBEJS_CUBESTORE_HOST=cubestore_router
- CUBEJS_API_SECRET=secret
volumes:
- .:/cube/conf
# Prevent dev dependencies leaking
- .empty:/cube/conf/node_modules/@cubejs-backend/
depends_on:
- cubestore_router
- cube_refresh_worker