Skip to content

Commit

Permalink
feat: go rootless (#13)
Browse files Browse the repository at this point in the history
* Backend working with nodemon

* Backend w/o nodemon

* Fix backend probes

* Add terrible health check

* Roll health check into main routing

* Caddy

* Shuffle dependencies

* Troubleshoot backend test fail
  • Loading branch information
DerekRoberts authored Sep 22, 2023
1 parent b314ad5 commit ebd57ea
Show file tree
Hide file tree
Showing 8 changed files with 404 additions and 52 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr-open.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
overwrite: true
parameters: -p MIN_REPLICAS=1 -p MAX_REPLICAS=2
triggers: ('backend/' 'frontend/')
verification_path: /api
verification_path: /health
- name: frontend
file: frontend/openshift.deploy.yml
overwrite: true
Expand Down
27 changes: 18 additions & 9 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
# Build
FROM node:alpine AS build
# Build static files
# Node Bullseye has npm
FROM node:20.7.0-bullseye-slim AS build
ENV NODE_ENV production

# Copy and build
WORKDIR /app
COPY . .
RUN apk add --no-cache python3 g++ make
RUN npm install -g nodemon # Install nodemon globally
RUN npm install
RUN npm i --ignore-scripts --no-update-notifer --omit=dev


# User and startup
# Deploy container
# Distroless has node, but not npm
FROM gcr.io/distroless/nodejs20-debian11:nonroot AS deploy
ENV NODE_ENV production

# Copy over app
WORKDIR /app
COPY --from=build /app ./

# Ports, health check and non-root user
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/:5000
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/:5000 || exit 1

#CMD
CMD ["npm", "start"]
# Start up command with 50MB of heap size, each application needs to determine what is the best value. DONT use default as it is 4GB.
CMD ["--max-old-space-size=50", "/app/index"]
10 changes: 9 additions & 1 deletion backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const cors = require('cors');
const dotenv =require('dotenv');
const indexRoutes = require("./routes/indexRoutes");
const mailRoutes = require("./routes/mailRoutes");
const healthRoute = require('./routes/health');

dotenv.config({
path: './.env'
Expand All @@ -21,7 +22,7 @@ const ipWhitelistMiddleware = (req, res, next) => {
clientIp = clientIp.substr(7);
}

if (isLoopbackIp(clientIp) || (isValidIp(clientIp) && whitelist.includes(clientIp))) {
if (isLoopbackIp(clientIp) || (isValidIp(clientIp) && whitelist.includes(clientIp)) || isHealthCheck(req.url)) {
// If the IP is the loopback address or in the whitelist, allow the request to proceed
next();
} else {
Expand All @@ -35,6 +36,11 @@ function isLoopbackIp(ip) {
return ip === '::1' || ip === '127.0.0.1' || ip === '::ffff:127.0.0.1';
}

// Helper function to let health checks through
function isHealthCheck(path) {
return path === '/health';
}

// Helper function to validate if an IP address is valid
function isValidIp(ip) {
// Regular expression to validate IPv4 and IPv6 addresses, excluding loopback addresses
Expand All @@ -57,6 +63,8 @@ app.use(cors(corsOptions));
app.use('/api/', indexRoutes);
app.use('/api/mail', mailRoutes);

app.use('/health', healthRoute);

app.listen(5000, () => {
console.log('Backend server is running on port 5000');
});
16 changes: 8 additions & 8 deletions backend/openshift.deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ objects:
- name: CHES_TOKEN_URL
value: ${CHES_TOKEN_URL}
ports:
- containerPort: 3000
- containerPort: 5000
protocol: TCP
resources:
requests:
Expand All @@ -148,8 +148,8 @@ objects:
memory: "${MEMORY_LIMIT}"
readinessProbe:
httpGet:
path: /api
port: 3000
path: /health
port: 5000
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 2
Expand All @@ -160,8 +160,8 @@ objects:
successThreshold: 1
failureThreshold: 3
httpGet:
path: /api
port: 3000
path: /health
port: 5000
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 30
Expand All @@ -174,10 +174,10 @@ objects:
name: "${NAME}-${ZONE}-${COMPONENT}"
spec:
ports:
- name: 3000-tcp
- name: 5000-tcp
protocol: TCP
port: 80
targetPort: 3000
targetPort: 5000
selector:
deploymentconfig: "${NAME}-${ZONE}-${COMPONENT}"
- apiVersion: route.openshift.io/v1
Expand All @@ -189,7 +189,7 @@ objects:
spec:
host: "${NAME}-${ZONE}-${COMPONENT}.${DOMAIN}"
port:
targetPort: 3000-tcp
targetPort: 5000-tcp
to:
kind: Service
name: "${NAME}-${ZONE}-${COMPONENT}"
Expand Down
17 changes: 17 additions & 0 deletions backend/routes/health.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const express = require("express");
const router = express.Router({});
router.get('/', async (_req, res, _next) => {
const check = {
uptime: process.uptime(),
message: 'OK',
timestamp: Date.now()
};
try {
res.send(check);
} catch (error) {
check.message = error;
res.status(503).send();
}
});
// export router with all routes included
module.exports = router;
47 changes: 18 additions & 29 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
### Use this section while builds are broken
## Use this section once builds are fixed

FROM node:20-bullseye
FROM node:20.7.0-bullseye-slim AS build

# Build static files
WORKDIR /app
COPY . .
RUN npm ci
CMD ["npm", "run", "start"]

RUN npm ci --ignore-scripts && \
npm run build && \
rm -rf node_modules

### Use this section once builds are fixed
# Caddy
FROM caddy:2.7.4-alpine
ENV LOG_LEVEL=info

# FROM node:20-slim AS build
# Copy static files and config
COPY --from=build /app/build/ /srv
COPY Caddyfile /etc/caddy/Caddyfile

# # Build static files
# WORKDIR /app
# COPY . .
# CA certs and Caddy format
RUN apk add --no-cache ca-certificates && \
caddy fmt --overwrite /etc/caddy/Caddyfile

# RUN npm ci --ignore-scripts && \
# npm run build && \
# rm -rf node_modules

# # Caddy
# FROM caddy:2.7.4-alpine
# ENV LOG_LEVEL=info

# # Copy static files and config
# COPY --from=build /app/build/ /srv
# COPY Caddyfile /etc/caddy/Caddyfile

# # CA certs and Caddy format
# RUN apk add --no-cache ca-certificates && \
# caddy fmt --overwrite /etc/caddy/Caddyfile

# # User, port and healthcheck
# USER 1001
# EXPOSE 3000 3001
# HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/:3001/health
# User, port and healthcheck
USER 1001
EXPOSE 3000 3001
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/:3001/health
Loading

0 comments on commit ebd57ea

Please sign in to comment.