Skip to content

Commit

Permalink
Separate api and web docker images (#390)
Browse files Browse the repository at this point in the history
* chore: separate api and web docker images

* ci: adjust docker build and push

* generate pruned web docker image

* adjust migrations, static and public dirs

* optimize api image and bundle using ncc

* use slim variant

* chore(lint): ignore mjs config files

* chore(web): use `path` module to get next config file's current directory

* ci: bump docker/[email protected] and cleanup

* feat!: Use new api and web docker images repositories

IMPORTANT: blossomlabs/blobscan docker image is now deprecated.
Use blossomlabs/blobscan-api and blossomlabs/blobscan-web.

environment vars:
Removed: BLOBSCAN_TAG
Added: BLOBSCAN_WEB_TAG, BLOBSCAN_WEB_TAG

* chore: separate prisma into its own package

* chore(docker): include prisma cli dep in image

* chore(docker): use nextjs `standalone` build output when building docker images only

* chore(rest-api-server): create postbuild script to add missing swagger assets to dist

* chore: add `dist` folder to build outputs

* refactor(rest-api-server): rearrange scripts

* chore: add `start` task to turbo's pipeline

* chore(db): remove target binaries as build already includes necessary ones

* add stage comments to Dockerfile

* use for loop in postbuild.ts

* use alpine for api image

* Revert "chore(docker): include prisma cli dep in image"

This reverts commit 85a7e1e.

* Revert "chore: separate prisma into its own package"

This reverts commit 998816f.

* Include prisma is a prod dependency

* Add prisma binary and dependencies to docker image

* clean docker stuff

---------

Co-authored-by: PJColombo <[email protected]>
  • Loading branch information
PabloCastellano and PJColombo authored Aug 2, 2024
1 parent dbe414b commit f1484d7
Show file tree
Hide file tree
Showing 24 changed files with 301 additions and 134 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ docker-compose*.yml
README.md
LICENSE
node_modules
.changeset
.env*
.env.example
.git*
.next
.vscode
out
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
# @see https://www.prisma.io/docs/reference/database-reference/connection-urls
DATABASE_URL=postgresql://blobscan:s3cr3t@localhost:5432/blobscan_dev?schema=public

BLOBSCAN_TAG=next
BLOBSCAN_WEB_TAG=next
BLOBSCAN_API_TAG=next
INDEXER_TAG=master

### blobscan website
Expand Down
42 changes: 25 additions & 17 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,46 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: blossomlabs/blobscan
tags: |
type=ref,event=branch
type=ref,event=tag
type=semver,pattern={{version}}
type=raw,value=latest,enable=${{ github.ref_name == 'next' }}
type=raw,value=stable,enable=${{ github.ref_name == 'master' }}
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Set tag prefix
id: prefix
run: |
if [[ ${{ github.ref_name }} == 'master' ]]; then
echo "::set-output name=TAG_SUFFIX::stable"
elif [[ ${{ github.ref_name }} == 'next' ]]; then
echo "::set-output name=TAG_SUFFIX::latest"
fi
- name: Set Github ENV variables
shell: bash
run: |
echo "BUILD_TIMESTAMP=$(date '+%F %H:%M:%S')" >> $GITHUB_ENV
echo "GIT_COMMIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
- name: Build and push
uses: docker/build-push-action@v4
- name: Build and push (api)
uses: docker/[email protected]
with:
context: .
target: api
tags: blossomlabs/blobscan-api:${{ steps.prefix.outputs.TAG_SUFFIX }}
push: true
build-args: |
BUILD_TIMESTAMP=${{ env.BUILD_TIMESTAMP }}
GIT_COMMIT=${{ env.GIT_COMMIT }}
- name: Build and push (web)
uses: docker/[email protected]
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
target: web
tags: blossomlabs/blobscan-web:${{ steps.prefix.outputs.TAG_SUFFIX }}
push: true
build-args: |
BUILD_TIMESTAMP=${{ env.BUILD_TIMESTAMP }}
GIT_COMMIT=${{ env.GIT_COMMIT }}
119 changes: 90 additions & 29 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,47 +1,108 @@
#FROM node:20-alpine as builder
# Pinned due to https://github.com/nodejs/docker-node/issues/2009
FROM node:20-alpine3.18
FROM node:20-alpine3.18 as base

ADD docker-entrypoint.sh /

# stage: deps
FROM base as deps

ARG BUILD_TIMESTAMP
ENV BUILD_TIMESTAMP=$BUILD_TIMESTAMP
ARG GIT_COMMIT
ENV GIT_COMMIT=$GIT_COMMIT
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
# Do not perform environment variable validation during build time
ENV SKIP_ENV_VALIDATION=true

RUN apk add bash curl
RUN npm install -g pnpm
RUN npm install -g pnpm turbo
WORKDIR /app
RUN mkdir -p /tmp/blobscan-blobs && chmod 777 /tmp/blobscan-blobs

# pnpm fetch does require only lockfile
COPY pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/root/.pnpm-store/v3 pnpm fetch -r
COPY . /prepare
WORKDIR /prepare

ADD . ./
RUN --mount=type=cache,id=pnpm,target=/root/.pnpm-store/v3 pnpm install -r
RUN turbo prune @blobscan/web --docker --out-dir /prepare/web
RUN turbo prune @blobscan/rest-api-server --docker --out-dir /prepare/api

# Do not perform environment variable validation during build time
RUN SKIP_ENV_VALIDATION=true npm run build
# stage: web-builder
FROM deps AS web-builder

WORKDIR /app

RUN mkdir -p /tmp/blobscan-blobs
RUN chown node:node . /tmp/blobscan-blobs -R
ENV NEXT_BUILD_OUTPUT standalone

ADD docker-entrypoint.sh /
USER node
COPY --from=deps /prepare/web/json .
COPY --from=deps /prepare/web/pnpm-lock.yaml .

RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm fetch -r
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile

COPY --from=deps /prepare/web/full .

# Copy original which includes pipelines
COPY --from=deps /prepare/turbo.json .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm build --filter=@blobscan/web

# stage: web
FROM base AS web
RUN apk add bash
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=web-builder --chown=nextjs:nodejs /app/node_modules/prisma ./node_modules/prisma
COPY --from=web-builder --chown=nextjs:nodejs /app/node_modules/@prisma ./node_modules/@prisma
COPY --from=web-builder --chown=nextjs:nodejs /prepare/api/full/packages/db/prisma/migrations ./migrations
COPY --from=web-builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=web-builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=web-builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public

ENTRYPOINT ["/docker-entrypoint.sh"]
USER nextjs

EXPOSE 3000
ENV PORT 3000

ENTRYPOINT ["/docker-entrypoint.sh", "web"]
CMD ["--help"]

# FROM node:18-alpine AS production
# RUN npm install -g pnpm
# WORKDIR /app
# COPY --chown=node --from=builder /app/apps/web/next.config.mjs ./
# COPY --chown=node --from=builder /app/apps/web/package.json ./
# COPY --chown=node --from=builder /app/apps/web/postcss.config.cjs ./
# COPY --chown=node --from=builder /app/apps/web/tailwind.config.ts.json ./
# COPY --chown=node --from=builder /app/apps/web/tsconfig.json ./
# COPY --chown=node --from=builder /app/apps/web/public ./public
# COPY --chown=node --from=builder /app/apps/web/.next ./.next
# COPY --chown=node --from=builder /app/pnpm-lock.yaml /app/pnpm-workspace.yaml /app/turbo.json ./
# COPY --chown=node --from=builder /app/node_modules ./node_modules
# USER node
#
# CMD ["pnpm", "start"]
# stage: api-builder
FROM deps AS api-builder

WORKDIR /app
COPY --from=deps /prepare/api/json .
COPY --from=deps /prepare/api/pnpm-lock.yaml .

RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm fetch -r
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile

COPY --from=deps /prepare/api/full .

# Copy original which includes pipelines
COPY --from=deps /prepare/turbo.json .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm build --filter=@blobscan/rest-api-server

# stage: api
FROM base as api
RUN apk add bash
WORKDIR /app

ENV NODE_ENV production

COPY --from=api-builder /app/node_modules/prisma ./node_modules/prisma
COPY --from=api-builder /app/node_modules/@prisma ./node_modules/@prisma
COPY --from=api-builder /app/apps/rest-api-server/dist ./
COPY --from=api-builder /prepare/api/full/packages/db/prisma/migrations ./migrations

EXPOSE 3001
ENV PORT 3001

ADD docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh", "api"]
CMD ["--help"]
10 changes: 7 additions & 3 deletions apps/rest-api-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
"version": "0.4.2",
"private": true,
"scripts": {
"build": "ncc build src/index.ts --target es2020 --minify",
"postbuild": "ts-node ./scripts/postbuild.ts",
"clean": "git clean -xdf node_modules",
"dev": "pnpm with-env ts-node-dev --require ./src/scripts/print-banner.ts --require ./src/scripts/init-bucket.ts --require ./src/scripts/instrumentation.ts --respawn --transpile-only --exit-child ./src/index.ts",
"dev": "pnpm with-env ts-node-dev --require ./scripts/init-bucket.ts --respawn --transpile-only --exit-child ./src/index.ts",
"lint": "eslint .",
"lint:fix": "pnpm lint --fix",
"start": "pnpm with-env ts-node --require ./src/scripts/print-banner.ts --require ./src/scripts/instrumentation.ts src/index.ts",
"start": "pnpm with-env node dist/index.js",
"profile": "pnpm with-env node -r ts-node/register --prof --require ./src/scripts/instrumentation.ts src/index.ts",
"with-env": "dotenv -e ../../.env --",
"type-check": "tsc --noEmit"
Expand All @@ -34,7 +36,9 @@
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/morgan": "^1.9.4",
"@types/swagger-ui-express": "^4.1.3"
"@types/swagger-ui-express": "^4.1.3",
"@vercel/ncc": "^0.38.1",
"zx": "^8.1.4"
},
"eslintConfig": {
"root": true,
Expand Down
File renamed without changes.
40 changes: 40 additions & 0 deletions apps/rest-api-server/scripts/postbuild.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { $, path, glob } from "zx";

async function main() {
const prismaFiles = await glob(path.join("dist", "client", "*"));

await $`mv ${prismaFiles} dist`;

console.log("postbuild: Prisma client files moved to `dist` directory");

const swaggerUiDist = path.join(
__dirname,
"..",
"..",
"..",
"node_modules",
"swagger-ui-dist"
);

const files = [
"swagger-ui.css",
"swagger-ui-bundle.js",
"swagger-ui-standalone-preset.js",
"favicon-16x16.png",
"favicon-32x32.png"
];

for (const file of files) {
await $`cp ${path.join(swaggerUiDist, file)} dist`;
}

console.log("postbuild: Swagger UI assets copied to `dist` directory");
}

main().then(
() => process.exit(0),
(err) => {
console.error(err);
process.exit(1);
}
);
8 changes: 0 additions & 8 deletions apps/rest-api-server/sentry.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { env } from "@blobscan/env";

function run() {
export function printBanner() {
console.log(" ____ _ _");
console.log("| __ )| | ___ | |__ ___ ___ __ _ _ __");
console.log("| _ \\| |/ _ \\| '_ \\/ __|/ __/ _` | '_ \\");
Expand All @@ -11,5 +11,3 @@ function run() {

env.display();
}

run();
4 changes: 4 additions & 0 deletions apps/rest-api-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ import {
import { env } from "@blobscan/env";
import { collectDefaultMetrics } from "@blobscan/open-telemetry";

import "./instrumentation";
import { printBanner } from "./banner";
import { logger } from "./logger";
import { morganMiddleware } from "./morgan";
import { openApiDocument } from "./openapi";
import { setUpSyncers } from "./syncers";

collectDefaultMetrics();

printBanner();

const closeSyncers = setUpSyncers();

const app = express();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
import * as Sentry from "@sentry/node";

import { env } from "@blobscan/env";
import { setUpOpenTelemetry } from "@blobscan/open-telemetry";

Sentry.init({
dsn: env.SENTRY_DSN_API,
tracesSampleRate: 1.0,
});

if (env.TRACES_ENABLED) {
setUpOpenTelemetry("blobscan_rest_api", {
instrumentations: [new ExpressInstrumentation()],
Expand Down
6 changes: 4 additions & 2 deletions apps/rest-api-server/src/syncers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { env } from "@blobscan/env";
import type { BaseSyncer } from "@blobscan/syncers";
import {
DailyStatsSyncer,
Expand All @@ -6,7 +7,6 @@ import {
createRedisConnection,
} from "@blobscan/syncers";

import { env } from "@blobscan/env";
import { logger } from "./logger";
import { getNetworkDencunForkSlot } from "./utils";

Expand Down Expand Up @@ -56,7 +56,9 @@ export function setUpSyncers() {

for (const syncer of syncers) {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
teardownPromise = teardownPromise.finally(() => syncer.close());
teardownPromise = teardownPromise.finally(async () => {
await syncer.close();
});
}

return teardownPromise;
Expand Down
2 changes: 1 addition & 1 deletion apps/rest-api-server/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"module": "commonjs"
}
},
"include": ["src", "sentry.ts"]
"include": ["src", "scripts"]
}
Loading

0 comments on commit f1484d7

Please sign in to comment.