Skip to content

Commit 1ea6754

Browse files
committed
Add healthcheck endpoints and scripts
1 parent af4bca0 commit 1ea6754

14 files changed

Lines changed: 241 additions & 20 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/http"
7+
"os"
8+
"time"
9+
)
10+
11+
func main() {
12+
13+
port, exists := os.LookupEnv("PORT")
14+
if !exists {
15+
port = "8080"
16+
}
17+
18+
client := http.Client{
19+
Timeout: 2 * time.Second,
20+
}
21+
22+
resp, err := client.Get("http://localhost:" + port + "/ping")
23+
if err != nil {
24+
log.Fatal(err)
25+
}
26+
27+
// Print the HTTP Status Code and Status Name
28+
fmt.Println("HTTP Response Status:", resp.StatusCode, http.StatusText(resp.StatusCode))
29+
30+
if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
31+
fmt.Println("HTTP Status is in the 2xx range")
32+
} else {
33+
fmt.Println("Argh! Broken")
34+
os.Exit(1)
35+
}
36+
}

‎05-example-web-application/api-golang/main.go‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,11 @@ func main() {
4242
"now": tm,
4343
})
4444
})
45-
r.Run() // listen and serve on 0.0.0.0:8080
45+
46+
r.GET("/ping", func(c *gin.Context) {
47+
tm = database.GetTime(c)
48+
c.JSON(200, "pong")
49+
})
50+
51+
r.Run() // listen and serve on 0.0.0.0:8080 (or "PORT" env var)
4652
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
var http = require('http');
2+
3+
var options = {
4+
timeout: 2000,
5+
host: 'localhost',
6+
port: process.env.PORT || 3000,
7+
path: '/ping',
8+
};
9+
10+
var request = http.request(options, (res) => {
11+
console.info('STATUS: ' + res.statusCode);
12+
process.exitCode = res.statusCode === 200 ? 0 : 1;
13+
process.exit();
14+
});
15+
16+
request.on('error', function (err) {
17+
console.error('ERROR', err);
18+
process.exit(1);
19+
});
20+
21+
request.end();

‎05-example-web-application/api-node/src/index.js‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const express = require('express');
44
const morgan = require('morgan');
55

66
const app = express();
7-
const port = 3000;
7+
const port = process.env.PORT || 3000;
88

99
// setup the logger
1010
app.use(morgan('tiny'));
@@ -16,6 +16,10 @@ app.get('/', async (req, res) => {
1616
res.send(response);
1717
});
1818

19+
app.get('/ping', async (_, res) => {
20+
res.send('pong');
21+
});
22+
1923
const server = app.listen(port, () => {
2024
console.log(`Example app listening on port ${port}`);
2125
});

‎05-example-web-application/client-react/nginx.conf‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
server {
22
listen 80;
3+
location /ping {
4+
access_log off;
5+
add_header 'Content-Type' 'text/plain';
6+
return 200 "pong";
7+
}
38
location /api/golang/ {
49
resolver 127.0.0.1;
510
proxy_set_header X-Forwarded-Host $host;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Pin specific version for stability
2+
# Use separate stage for building image
3+
# Use debian for easier build utilities
4+
FROM golang:1.19-bullseye AS build
5+
6+
# Add non root user
7+
RUN useradd -u 1001 nonroot
8+
9+
WORKDIR /app
10+
11+
# Copy only files required to install dependencies (better layer caching)
12+
COPY go.mod go.sum ./
13+
14+
# Use cache mount to speed up install of existing dependencies
15+
RUN --mount=type=cache,target=/go/pkg/mod \
16+
--mount=type=cache,target=/root/.cache/go-build \
17+
go mod download
18+
19+
COPY . .
20+
21+
# Compile healthcheck
22+
RUN go build \
23+
-ldflags="-linkmode external -extldflags -static" \
24+
-tags netgo \
25+
-o healthcheck \
26+
./healthcheck/healthcheck.go
27+
28+
# Compile application during build rather than at runtime
29+
# Add flags to statically link binary
30+
RUN go build \
31+
-ldflags="-linkmode external -extldflags -static" \
32+
-tags netgo \
33+
-o api-golang
34+
35+
# Use separate stage for deployable image
36+
FROM scratch
37+
38+
# Set gin mode
39+
ENV GIN_MODE=release
40+
41+
WORKDIR /
42+
43+
# Copy the passwd file
44+
COPY --from=build /etc/passwd /etc/passwd
45+
46+
# Copy the healthcheck binary from the build stage
47+
COPY --from=build /app/healthcheck/healthcheck healthcheck
48+
49+
# Copy the app binary from the build stage
50+
COPY --from=build /app/api-golang api-golang
51+
52+
# Use nonroot user
53+
USER nonroot
54+
55+
# Indicate expected port
56+
EXPOSE 8080
57+
58+
CMD ["/api-golang"]

‎06-building-container-images/api-golang/Makefile‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ build-N:
1111

1212
.PHONY: build-all
1313
build-all:
14-
for number in 0 1 2 3 4 5 6 ; do \
14+
for number in 0 1 2 3 4 5 6 7; do \
1515
N=$$number $(MAKE) build-N; \
1616
done
1717

@@ -21,6 +21,6 @@ push-N:
2121

2222
.PHONY: push-all
2323
push-all:
24-
for number in 0 1 2 3 4 5 6 ; do \
24+
for number in 0 1 2 3 4 5 6 7; do \
2525
N=$$number $(MAKE) push-N; \
2626
done
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Pin specific version for stability
2+
# Use alpine for reduced image size
3+
FROM node:19.4-alpine
4+
5+
# Set NODE_ENV
6+
ENV NODE_ENV production
7+
8+
# Specify working directory other than /
9+
WORKDIR /usr/src/app
10+
11+
# Copy only files required to install
12+
# dependencies (better layer caching)
13+
COPY package*.json ./
14+
15+
# Install only production dependencies
16+
# Use cache mount to speed up install of existing dependencies
17+
RUN --mount=type=cache,target=/usr/src/app/.npm \
18+
npm set cache /usr/src/app/.npm && \
19+
npm ci --only=production
20+
21+
# Use non-root user
22+
# Use --chown on COPY commands to set file permissions
23+
USER node
24+
25+
# Copy the healthcheck script
26+
COPY --chown=node:node ./healthcheck/ .
27+
28+
# Copy remaining source code AFTER installing dependencies.
29+
# Again, copy only the necessary files
30+
COPY --chown=node:node ./src/ .
31+
32+
# Indicate expected port
33+
EXPOSE 3000
34+
35+
CMD [ "node", "index.js" ]
36+
37+
# TODO: Use multi-stage with distroless image or chainguard image?
38+
# https://github.com/GoogleContainerTools/distroless/blob/main/examples/nodejs/Dockerfile
39+
# https://edu.chainguard.dev/chainguard/chainguard-images/reference/node/overview/

‎06-building-container-images/api-node/Makefile‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ build-N:
1111

1212
.PHONY: build-all
1313
build-all:
14-
for number in 0 1 2 3 4 5 6 7 ; do \
14+
for number in 0 1 2 3 4 5 6 7 8; do \
1515
N=$$number $(MAKE) build-N; \
1616
done
1717

@@ -21,6 +21,6 @@ push-N:
2121

2222
.PHONY: push-all
2323
push-all:
24-
for number in 0 1 2 3 4 5 6 7; do \
24+
for number in 0 1 2 3 4 5 6 7 8; do \
2525
N=$$number $(MAKE) push-N; \
2626
done

‎08-running-containers/docker-compose.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ services:
1010
api-node:
1111
build:
1212
context: ../05-example-web-application/api-node/
13-
dockerfile: ../../06-building-container-images/api-node/Dockerfile.7
13+
dockerfile: ../../06-building-container-images/api-node/Dockerfile.8
1414
init: true
1515
depends_on:
1616
- db

0 commit comments

Comments
 (0)