Skip to content

Commit

Permalink
feat: update Dockerfile and add new server.js setup
Browse files Browse the repository at this point in the history
  • Loading branch information
jimbrig committed Oct 31, 2024
1 parent 5915b82 commit 2933257
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 77 deletions.
81 changes: 47 additions & 34 deletions app/.dockerignore
Original file line number Diff line number Diff line change
@@ -1,34 +1,47 @@
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/

**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.next
**/.cache
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
**/build
**/dist
LICENSE
README.md
# Version control
.git
.gitignore
.github

# Dependencies
node_modules
.pnpm-store

# Build outputs
build
dist

# Development files
.env
.env.*
*.log
npm-debug.log*
.cache

# IDE and editor files
.vscode
.idea
*.swp
*.swo

# Test files
__tests__
*.test.js
*.spec.js
coverage

# Documentation
docs
*.md

# Temporary files
.DS_Store
*.tmp
*.temp

# Allow specific files/folders
!package.json
!pnpm-lock.yaml
!server.js
!app/
!public/
8 changes: 8 additions & 0 deletions app/.gcloudignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.git
.gitignore
node_modules/
npm-debug.log
README.md
.env
*.test.js
coverage/
94 changes: 51 additions & 43 deletions app/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
ARG NODE_VERSION=20.12.2
ARG NODE_VERSION=18
ARG PNPM_VERSION=9.0.5

################################################################################
# Use node image for base image for all stages.
FROM node:${NODE_VERSION}-alpine as base
# Base stage for shared node/pnpm setup
FROM node:${NODE_VERSION}-alpine AS base

# Associate with GitHub repository
LABEL org.opencontainers.image.source=https://github.com/noclocks/bastienlaw-remix
Expand All @@ -16,59 +16,67 @@ RUN --mount=type=cache,target=/root/.npm \
npm install -g pnpm@${PNPM_VERSION}

################################################################################
# Create a stage for installing production dependecies.
FROM base as deps

# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.local/share/pnpm/store to speed up subsequent builds.
# Leverage bind mounts to package.json and pnpm-lock.yaml to avoid having to copy them
# into this layer.
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
--mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --prod --frozen-lockfile
# Dependencies stage - install all dependencies
FROM base AS deps

# Copy package files
COPY package.json pnpm-lock.yaml ./

# Install dependencies with a more flexible approach
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --prod

################################################################################
# Create a stage for building the application.
FROM deps as build
# Builder stage - build the application
FROM base AS builder

# Copy package files
COPY package.json pnpm-lock.yaml ./

# Download additional development dependencies before building, as some projects require
# "devDependencies" to be installed to build. If you don't need this, remove this step.
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
--mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
# Install all dependencies (including dev deps)
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install

# Copy the rest of the source files into the image.
# Copy source code
COPY . .
# Run the build script.

# Build the application
RUN pnpm run build

# List build output for debugging
RUN echo "Build directory structure:" && \
find build -type f

################################################################################
# Create a new stage to run the application with minimal runtime dependencies
# where the necessary files are copied from the build stage.
FROM base as final
# Runner stage - final production image
FROM node:${NODE_VERSION}-alpine AS runner

# Use production node environment by default.
# ENV NODE_ENV production
ENV NODE_ENV development
# Use production node environment
ENV NODE_ENV=production
ENV HOST=0.0.0.0
ENV PORT=3000

# # Run the application as a non-root user.
USER node
WORKDIR /usr/src/app

# Copy package.json so that package manager commands can be used.
COPY package.json .
# Install only the packages needed to run the server
COPY package.json pnpm-lock.yaml ./
RUN npm install --global pnpm@${PNPM_VERSION} && \
pnpm install --prod

# Copy the production dependencies from the deps stage and also
# the built application from the build stage into the image.
COPY --from=deps /usr/src/app/node_modules ./node_modules
COPY --from=build /usr/src/app/build ./build
# Copy server.js and the build directory
COPY server.js ./
COPY --from=builder /usr/src/app/build ./build
COPY --from=builder /usr/src/app/public ./public

ENV HOST '0.0.0.0'
ENV PORT 8080
# List directories for debugging
RUN echo "Final build structure:" && \
find . -type f

# Run the application as a non-root user.
USER node

# Expose the port that the application listens on.
EXPOSE 8080
EXPOSE 3000

# Run the application.
CMD ["pnpm", "run", "serve"]
# Run the application using the express server
CMD ["node", "server.js"]
74 changes: 74 additions & 0 deletions app/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createRequestHandler } from "@remix-run/express";
import { installGlobals } from "@remix-run/node";
import compression from "compression";
import express from "express";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
import fs from 'fs';

const __dirname = dirname(fileURLToPath(import.meta.url));

installGlobals();

const viteDevServer =
process.env.NODE_ENV === "production"
? undefined
: await import("vite").then((vite) =>
vite.createServer({
server: { middlewareMode: true },
})
);

const app = express();

// Add health check endpoint
app.get('/_health', (req, res) => {
res.status(200).send('OK');
});

app.use(compression());

// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
app.disable("x-powered-by");

// handle asset requests
if (viteDevServer) {
app.use(viteDevServer.middlewares);
} else {
app.use(
"/assets",
express.static(join(__dirname, "build/client/assets"), { immutable: true, maxAge: "1y" })
);
}
app.use(express.static(join(__dirname, "build/client"), { maxAge: "1h" }));

// Log build directory contents for debugging
console.log('Build directory contents:');
try {
console.log('Client directory:', fs.readdirSync(join(__dirname, 'build/client')));
} catch (error) {
console.error('Error reading build directory:', error);
}

// handle SSR requests
app.all(
"*",
createRequestHandler({
build: viteDevServer
? () => viteDevServer.ssrLoadModule("virtual:remix/server-build")
: { default: { default: { handler: (req, res) => res.sendFile(join(__dirname, 'build/client/index.html')) } } },
})
);

const port = process.env.PORT || 3000;
const server = app.listen(port, () => {
console.log(`Express server listening on port ${port}`);
});

// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM signal received: closing HTTP server');
server.close(() => {
console.log('HTTP server closed');
});
});

0 comments on commit 2933257

Please sign in to comment.