diff --git a/.github/workflows/build-and-publish-container-image.yaml b/.github/workflows/build-and-publish-container-image.yaml index 67fb06a9..f75a1008 100644 --- a/.github/workflows/build-and-publish-container-image.yaml +++ b/.github/workflows/build-and-publish-container-image.yaml @@ -14,7 +14,7 @@ jobs: env: CONTAINER_REGISTRY: docker.io - CONTAINER_IMAGE: paulbouwer/hello-kubernetes + CONTAINER_IMAGE: ${{ github.repository }} steps: diff --git a/Makefile b/Makefile index c7be66fe..6ad3e85a 100644 --- a/Makefile +++ b/Makefile @@ -4,26 +4,46 @@ IMAGE_VERSION ?= $(shell cat src/app/package.json | jq -r .version) IMAGE_MAJOR_VERSION = $(shell echo "$(IMAGE_VERSION)" | cut -d '.' -f1 ) IMAGE_MINOR_VERSION = $(shell echo "$(IMAGE_VERSION)" | cut -d '.' -f2 ) IMAGE = $(REGISTRY)/$(REPOSITORY)/hello-kubernetes +BUILD_ARCH ?= amd64 arm arm64 riscv64 +BUILD_IMAGE_LIST:=$(shell echo $(BUILD_ARCH) | sed -e "s~[^ ]*~$(IMAGE):$(IMAGE_VERSION)\-&~g") .PHONY: scan-for-vulns scan-for-vulns: - trivy image --format template --template "@/trivy/contrib/sarif.tpl" $(IMAGE):$(IMAGE_VERSION) + for arch in $(BUILD_ARCH); do \ + if [ "$${arch}" = $$(uname -m) ]; then \ + trivy image --format template --template "@/trivy/contrib/sarif.tpl" $(IMAGE):$(IMAGE_VERSION)-$${arch}; \ + fi; \ + done .PHONY: build-images build-images: build-image-linux .PHONY: build-image-linux -build-image-linux: - docker build --no-cache \ +build-image-linux: + cp Dockerfile build/docker/$${arch} ; \ + DOCKER_ARGS=""; \ + for arch in $(BUILD_ARCH); do \ + if [ "$${arch}" = "riscv64" ]; then \ + DOCKER_ARGS="--build-arg=BASE=riscv64/alpine:edge"; \ + fi; \ + DOCKER_BUILDKIT=1 \ + docker build --no-cache \ + --platform $${arch} \ --build-arg IMAGE_VERSION="$(IMAGE_VERSION)" \ --build-arg IMAGE_CREATE_DATE="`date -u +"%Y-%m-%dT%H:%M:%SZ"`" \ --build-arg IMAGE_SOURCE_REVISION="`git rev-parse HEAD`" \ - -f src/app/Dockerfile -t "$(IMAGE):$(IMAGE_VERSION)" src/app; + $${DOCKER_ARGS} \ + -f src/app/Dockerfile -t "$(IMAGE):$(IMAGE_VERSION)-$${arch}" src/app; \ + done .PHONY: push-image push-image: - docker tag $(IMAGE):$(IMAGE_VERSION) $(IMAGE):$(IMAGE_MAJOR_VERSION); \ - docker tag $(IMAGE):$(IMAGE_VERSION) $(IMAGE):$(IMAGE_MAJOR_VERSION).$(IMAGE_MINOR_VERSION); \ - docker push $(IMAGE):$(IMAGE_VERSION); \ - docker push $(IMAGE):$(IMAGE_MAJOR_VERSION); \ - docker push $(IMAGE):$(IMAGE_MAJOR_VERSION).$(IMAGE_MINOR_VERSION) \ No newline at end of file + for arch in $(BUILD_ARCH); do \ + docker push $(IMAGE):$(IMAGE_VERSION)-$${arch}; \ + done; \ + docker manifest create --amend $(IMAGE):$(IMAGE_VERSION) $(BUILD_IMAGE_LIST); \ + docker manifest create --amend $(IMAGE):$(IMAGE_MAJOR_VERSION) $(BUILD_IMAGE_LIST); \ + docker manifest create --amend $(IMAGE):$(IMAGE_MAJOR_VERSION).$(IMAGE_MINOR_VERSION) $(BUILD_IMAGE_LIST); \ + docker manifest push --purge $(IMAGE):$(IMAGE_VERSION); \ + docker manifest push --purge $(IMAGE):$(IMAGE_MAJOR_VERSION); \ + docker manifest push --purge $(IMAGE):$(IMAGE_MAJOR_VERSION).$(IMAGE_MINOR_VERSION); \ diff --git a/src/app/Dockerfile b/src/app/Dockerfile index a61d1b77..4fc2e958 100644 --- a/src/app/Dockerfile +++ b/src/app/Dockerfile @@ -1,4 +1,5 @@ -FROM node:16-alpine3.13 +ARG BASE=alpine:3.18 +FROM ${BASE} ARG IMAGE_CREATE_DATE ARG IMAGE_VERSION @@ -17,6 +18,10 @@ LABEL org.opencontainers.image.title="Hello Kubernetes!" \ org.opencontainers.image.source="https://github.com/paulbouwer/hello-kubernetes.git" \ org.opencontainers.image.revision=$IMAGE_SOURCE_REVISION +RUN addgroup -g 1000 node && \ + adduser -u 1000 -G node -s /bin/sh -D node && \ + apk add -U --no-cache nodejs npm + # Create app directory RUN mkdir -p /usr/src/app WORKDIR /usr/src/app diff --git a/src/app/README.md b/src/app/README.md index 6cf40c09..acde9451 100644 --- a/src/app/README.md +++ b/src/app/README.md @@ -28,4 +28,3 @@ The application relies on the following files for configuration and operational | File | Required | Information | Description | | ---- | -------- | ----------- | ----------- | | package.json | Yes | `.version` | The release version is used when the CONTAINER_IMAGE env is not provided. | -| info.json | Yes | `.containerImageArch` | The container image architecture is used for display. This file will be overwritten in future versions as part of the container image build process when multi-arch images are supported. | \ No newline at end of file diff --git a/src/app/info.json b/src/app/info.json deleted file mode 100644 index 911ef762..00000000 --- a/src/app/info.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "containerImageArch": "linux/amd64" -} \ No newline at end of file diff --git a/src/app/server.js b/src/app/server.js index 303358d8..46535230 100644 --- a/src/app/server.js +++ b/src/app/server.js @@ -33,8 +33,8 @@ var podName = process.env.KUBERNETES_POD_NAME || os.hostname(); var nodeName = process.env.KUBERNETES_NODE_NAME || '-'; var nodeOS = os.type() + ' ' + os.release(); var applicationVersion = JSON.parse(fs.readFileSync('package.json', 'utf8')).version; -var containerImage = process.env.CONTAINER_IMAGE || 'paulbouwer/hello-kubernetes:' + applicationVersion -var containerImageArch = JSON.parse(fs.readFileSync('info.json', 'utf8')).containerImageArch; +var containerImage = process.env.CONTAINER_IMAGE || 'paulbouwer/hello-kubernetes:' + applicationVersion; +var containerImageArch = os.platform() + '/' + os.arch(); logger.debug(); logger.debug('Configuration'); @@ -79,4 +79,4 @@ logger.debug('-----------------------------------------------------'); app.listen(port, function () { logger.info("Listening on: http://%s:%s", podName, port); -}); \ No newline at end of file +});