Compare commits

..

7 Commits

Author SHA1 Message Date
c02572c2ef Fix minor issue with Dockerfile 2025-06-09 16:27:58 -04:00
3d5a888708 Add proper ignore files 2025-06-09 16:17:03 -04:00
21f8f61f45 Merge branch 'master' into dev-container 2025-06-09 16:15:33 -04:00
1b949309f5 Add more content 2025-06-09 16:14:42 -04:00
0b9551873f Update content 2025-06-09 12:54:23 -04:00
38375961b0 Update multistage example 2025-06-08 11:50:05 -04:00
77a19aabda Update slide deck 2025-06-08 11:46:27 -04:00
5 changed files with 228 additions and 655 deletions

View File

@ -1,2 +1,3 @@
node_modules/
dist/
slim.report.json

3
examples/react-app/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules/
dist/
slim.report.json

View File

@ -10,8 +10,10 @@ RUN yarn
FROM install-deps AS develop
WORKDIR /app
COPY --from=install-deps /app/node_modules /app/node_modules
COPY . .
# Broken, why?
#ENTRYPOINT ["yarn", "dev"]
ENTRYPOINT ["yarn", "dev", "--host=0.0.0.0"]
EXPOSE 5173

View File

@ -1,556 +0,0 @@
{
"document": "doc.report.command",
"version": "ov/command/slim/1.1",
"engine": "linux/amd64|ALP|x.1.42.2|29e62e7836de7b1004607c51c502537ffe1969f0|2025-01-16_07:48:54AM|x",
"containerized": false,
"host_distro": {
"name": "Pop!_OS",
"version": "22.04",
"display_name": "Pop!_OS 22.04 LTS"
},
"type": "slim",
"state": "done",
"target_reference": "react",
"system": {
"type": "Linux",
"release": "6.12.10-76061203-generic",
"distro": {
"name": "Alpine Linux",
"version": "3.21.3",
"display_name": "Alpine Linux v3.21"
}
},
"source_image": {
"identity": {
"id": "sha256:12e558bf38a2d45aeb557f872b631745db21f836ef3477aa191260af5cbb49c3",
"tags": [
"latest"
],
"names": [
"react:latest"
]
},
"size": 48561999,
"size_human": "49 MB",
"create_time": "2025-06-08T15:25:03Z",
"runtime_name": "docker",
"architecture": "amd64",
"exposed_ports": [
"80/tcp"
],
"os": "linux",
"labels": {
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
},
"env_vars": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.27.5",
"PKG_RELEASE=1",
"DYNPKG_RELEASE=1",
"NJS_VERSION=0.8.10",
"NJS_RELEASE=1"
],
"container_entry": {
"exe_path": ""
}
},
"minified_image_size": 9540708,
"minified_image_size_human": "9.5 MB",
"minified_image": "react:slim",
"minified_image_id": "sha256:83d848d51846cf917891131b875372b7ee75d97f80bd726d6f45e2869c94791f",
"minified_image_digest": "sha256:bdcf8a99c0c92bde711e50642d94c852d541b633317ed9045b5a6b5eedf1360d",
"minified_image_has_data": true,
"minified_by": 5.089978542472949,
"artifact_location": "/tmp/mint-state/.mint-state/images/12e558bf38a2d45aeb557f872b631745db21f836ef3477aa191260af5cbb49c3/artifacts",
"container_report_name": "creport.json",
"seccomp_profile_name": "react-seccomp.json",
"apparmor_profile_name": "react-apparmor-profile",
"image_stack": [
{
"is_top_image": true,
"id": "sha256:12e558bf38a2d45aeb557f872b631745db21f836ef3477aa191260af5cbb49c3",
"full_name": "react:latest",
"repo_name": "react",
"version_tag": "latest",
"raw_tags": [
"react:latest"
],
"create_time": "2025-06-08T15:25:03Z",
"new_size": 48561999,
"new_size_human": "49 MB",
"instructions": [
{
"type": "ADD",
"time": "2025-02-14T03:28:36Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 7834312,
"size_human": "7.8 MB",
"params": "alpine-minirootfs-3.21.3-x86_64.tar.gz /",
"command_snippet": "ADD alpine-minirootfs-3.21.3-x86_64.tar.gz /",
"command_all": "ADD alpine-minirootfs-3.21.3-x86_64.tar.gz /",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-02-13T22:15:00-05:00",
"inst_set_time_index": 0,
"inst_set_time_reverse_index": 2
},
{
"type": "CMD",
"time": "2025-02-14T03:28:36Z",
"is_nop": false,
"is_exec_form": true,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "[\"/bin/sh\"]\n",
"command_snippet": "CMD [\"/bin/sh\"]\n",
"command_all": "CMD [\"/bin/sh\"]\n",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-02-13T22:15:00-05:00",
"inst_set_time_index": 0,
"inst_set_time_reverse_index": 2
},
{
"type": "LABEL",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>",
"command_snippet": "LABEL maintainer=NGINX Docker Maintainers <d...",
"command_all": "LABEL maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "ENV",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "NGINX_VERSION=1.27.5",
"command_snippet": "ENV NGINX_VERSION=1.27.5",
"command_all": "ENV NGINX_VERSION=1.27.5",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "ENV",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "PKG_RELEASE=1",
"command_snippet": "ENV PKG_RELEASE=1",
"command_all": "ENV PKG_RELEASE=1",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "ENV",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "DYNPKG_RELEASE=1",
"command_snippet": "ENV DYNPKG_RELEASE=1",
"command_all": "ENV DYNPKG_RELEASE=1",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "RUN",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 4051586,
"size_human": "4.1 MB",
"command_snippet": "RUN set -x && \\\n\taddgroup -g 101 -S nginx &&...",
"command_all": "RUN set -x && \\\n\taddgroup -g 101 -S nginx && \\\n\tadduser -S -D -H -u 101 -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx && \\\n\tapkArch=\"$(cat /etc/apk/arch)\" && \\\n\tnginxPackages=\" nginx=${NGINX_VERSION}-r${PKG_RELEASE} \" && \\\n\tapk add --no-cache --virtual .checksum-deps openssl && \\\n\tcase \"$apkArch\" in x86_64|aarch64) set -x && \\\n\tKEY_SHA512=\"e09fa32f0a0eab2b879ccbbc4d0e4fb9751486eedda75e35fac65802cc9faa266425edf83e261137a2f4d16281ce2c1a5f4502930fe75154723da014214f0655\" && \\\n\twget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub && \\\n\tif echo \"$KEY_SHA512 */tmp/nginx_signing.rsa.pub\" | sha512sum -c -; then echo \"key verification succeeded!\"; mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; else echo \"key verification failed!\"; exit 1; fi && \\\n\tapk add -X \"https://nginx.org/packages/mainline/alpine/v$(egrep -o '^[0-9]+\\.[0-9]+' /etc/alpine-release)/main\" --no-cache $nginxPackages ;; *) set -x && \\\n\ttempDir=\"$(mktemp -d)\" && \\\n\tchown nobody:nobody $tempDir && \\\n\tapk add --no-cache --virtual .build-deps gcc libc-dev make openssl-dev pcre2-dev zlib-dev linux-headers bash alpine-sdk findutils curl && \\\n\tsu nobody -s /bin/sh -c \" export HOME=${tempDir} && \\\n\tcd ${tempDir} && \\\n\tcurl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz && \\\n\tPKGOSSCHECKSUM=\\\"c773d98b567bd585c17f55702bf3e4c7d82b676bfbde395270e90a704dca3c758dfe0380b3f01770542b4fd9bed1f1149af4ce28bfc54a27a96df6b700ac1745 *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\\\" && \\\n\tif [ \\\"\\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\\\" = \\\"\\$PKGOSSCHECKSUM\\\" ]; then echo \\\"pkg-oss tarball checksum verification succeeded!\\\"; else echo \\\"pkg-oss tarball checksum verification failed!\\\"; exit 1; fi && \\\n\ttar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz && \\\n\tcd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} && \\\n\tcd alpine && \\\n\tmake base && \\\n\tapk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk && \\\n\tabuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \" && \\\n\tcp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ && \\\n\tapk del --no-network .build-deps && \\\n\tapk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages ;; esac && \\\n\tapk del --no-network .checksum-deps && \\\n\tif [ -n \"$tempDir\" ]; then rm -rf \"$tempDir\"; fi && \\\n\tif [ -f \"/etc/apk/keys/abuild-key.rsa.pub\" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi && \\\n\tapk add --no-cache gettext-envsubst && \\\n\tapk add --no-cache tzdata && \\\n\tln -sf /dev/stdout /var/log/nginx/access.log && \\\n\tln -sf /dev/stderr /var/log/nginx/error.log && \\\n\tmkdir /docker-entrypoint.d",
"system_commands": [
"set -x",
"addgroup -g 101 -S nginx",
"adduser -S -D -H -u 101 -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx",
"apkArch=\"$(cat /etc/apk/arch)\"",
"nginxPackages=\" nginx=${NGINX_VERSION}-r${PKG_RELEASE} \"",
"apk add --no-cache --virtual .checksum-deps openssl",
"case \"$apkArch\" in x86_64|aarch64) set -x",
"KEY_SHA512=\"e09fa32f0a0eab2b879ccbbc4d0e4fb9751486eedda75e35fac65802cc9faa266425edf83e261137a2f4d16281ce2c1a5f4502930fe75154723da014214f0655\"",
"wget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub",
"if echo \"$KEY_SHA512 */tmp/nginx_signing.rsa.pub\" | sha512sum -c -; then echo \"key verification succeeded!\"; mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; else echo \"key verification failed!\"; exit 1; fi",
"apk add -X \"https://nginx.org/packages/mainline/alpine/v$(egrep -o '^[0-9]+.[0-9]+' /etc/alpine-release)/main\" --no-cache $nginxPackages ;; *) set -x",
"tempDir=\"$(mktemp -d)\"",
"chown nobody:nobody $tempDir",
"apk add --no-cache --virtual .build-deps gcc libc-dev make openssl-dev pcre2-dev zlib-dev linux-headers bash alpine-sdk findutils curl",
"su nobody -s /bin/sh -c \" export HOME=${tempDir}",
"cd ${tempDir}",
"curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz",
"PKGOSSCHECKSUM=\"c773d98b567bd585c17f55702bf3e4c7d82b676bfbde395270e90a704dca3c758dfe0380b3f01770542b4fd9bed1f1149af4ce28bfc54a27a96df6b700ac1745 *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\"",
"if [ \"$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"$PKGOSSCHECKSUM\" ]; then echo \"pkg-oss tarball checksum verification succeeded!\"; else echo \"pkg-oss tarball checksum verification failed!\"; exit 1; fi",
"tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz",
"cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE}",
"cd alpine",
"make base",
"apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk",
"abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \"",
"cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/",
"apk del --no-network .build-deps",
"apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages ;; esac",
"apk del --no-network .checksum-deps",
"if [ -n \"$tempDir\" ]; then rm -rf \"$tempDir\"; fi",
"if [ -f \"/etc/apk/keys/abuild-key.rsa.pub\" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi",
"apk add --no-cache gettext-envsubst",
"apk add --no-cache tzdata",
"ln -sf /dev/stdout /var/log/nginx/access.log",
"ln -sf /dev/stderr /var/log/nginx/error.log",
"mkdir /docker-entrypoint.d"
],
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "COPY",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 1620,
"size_human": "1.6 kB",
"params": "docker-entrypoint.sh /",
"command_snippet": "COPY docker-entrypoint.sh /",
"command_all": "COPY docker-entrypoint.sh /",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "COPY",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 2125,
"size_human": "2.1 kB",
"params": "10-listen-on-ipv6-by-default.sh /docker-entrypoint.d",
"command_snippet": "COPY 10-listen-on-ipv6-by-default.sh /docker...",
"command_all": "COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "COPY",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 389,
"size_human": "389 B",
"params": "15-local-resolvers.envsh /docker-entrypoint.d",
"command_snippet": "COPY 15-local-resolvers.envsh /docker-entryp...",
"command_all": "COPY 15-local-resolvers.envsh /docker-entrypoint.d",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "COPY",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 3018,
"size_human": "3.0 kB",
"params": "20-envsubst-on-templates.sh /docker-entrypoint.d",
"command_snippet": "COPY 20-envsubst-on-templates.sh /docker-ent...",
"command_all": "COPY 20-envsubst-on-templates.sh /docker-entrypoint.d",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "COPY",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 4619,
"size_human": "4.6 kB",
"params": "30-tune-worker-processes.sh /docker-entrypoint.d",
"command_snippet": "COPY 30-tune-worker-processes.sh /docker-ent...",
"command_all": "COPY 30-tune-worker-processes.sh /docker-entrypoint.d",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "ENTRYPOINT",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"is_exec_form": true,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "[\"/docker-entrypoint.sh\"]\n",
"command_snippet": "ENTRYPOINT [\"/docker-entrypoint.sh\"]\n",
"command_all": "ENTRYPOINT [\"/docker-entrypoint.sh\"]\n",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "EXPOSE",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "80/tcp",
"command_snippet": "EXPOSE 80/tcp",
"command_all": "EXPOSE 80/tcp",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "STOPSIGNAL",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "SIGQUIT",
"command_snippet": "STOPSIGNAL SIGQUIT",
"command_all": "STOPSIGNAL SIGQUIT",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "CMD",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"is_exec_form": true,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "[\"nginx\",\"-g\",\"daemon off;\"]\n",
"command_snippet": "CMD [\"nginx\",\"-g\",\"daemon off;\"]\n",
"command_all": "CMD [\"nginx\",\"-g\",\"daemon off;\"]\n",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "ENV",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "NJS_VERSION=0.8.10",
"command_snippet": "ENV NJS_VERSION=0.8.10",
"command_all": "ENV NJS_VERSION=0.8.10",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "ENV",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "NJS_RELEASE=1",
"command_snippet": "ENV NJS_RELEASE=1",
"command_all": "ENV NJS_RELEASE=1",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "RUN",
"time": "2025-04-16T14:50:31Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 36341673,
"size_human": "36 MB",
"command_snippet": "RUN set -x && \\\n\tapkArch=\"$(cat /etc/apk/arc...",
"command_all": "RUN set -x && \\\n\tapkArch=\"$(cat /etc/apk/arch)\" && \\\n\tnginxPackages=\" nginx=${NGINX_VERSION}-r${PKG_RELEASE} nginx-module-xslt=${NGINX_VERSION}-r${DYNPKG_RELEASE} nginx-module-geoip=${NGINX_VERSION}-r${DYNPKG_RELEASE} nginx-module-image-filter=${NGINX_VERSION}-r${DYNPKG_RELEASE} nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${NJS_RELEASE} \" && \\\n\tapk add --no-cache --virtual .checksum-deps openssl && \\\n\tcase \"$apkArch\" in x86_64|aarch64) apk add -X \"https://nginx.org/packages/mainline/alpine/v$(egrep -o '^[0-9]+\\.[0-9]+' /etc/alpine-release)/main\" --no-cache $nginxPackages ;; *) set -x && \\\n\ttempDir=\"$(mktemp -d)\" && \\\n\tchown nobody:nobody $tempDir && \\\n\tapk add --no-cache --virtual .build-deps gcc libc-dev make openssl-dev pcre2-dev zlib-dev linux-headers libxslt-dev gd-dev geoip-dev libedit-dev bash alpine-sdk findutils curl && \\\n\tsu nobody -s /bin/sh -c \" export HOME=${tempDir} && \\\n\tcd ${tempDir} && \\\n\tcurl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz && \\\n\tPKGOSSCHECKSUM=\\\"c773d98b567bd585c17f55702bf3e4c7d82b676bfbde395270e90a704dca3c758dfe0380b3f01770542b4fd9bed1f1149af4ce28bfc54a27a96df6b700ac1745 *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\\\" && \\\n\tif [ \\\"\\$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\\\" = \\\"\\$PKGOSSCHECKSUM\\\" ]; then echo \\\"pkg-oss tarball checksum verification succeeded!\\\"; else echo \\\"pkg-oss tarball checksum verification failed!\\\"; exit 1; fi && \\\n\ttar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz && \\\n\tcd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE} && \\\n\tcd alpine && \\\n\tmake module-geoip module-image-filter module-njs module-xslt && \\\n\tapk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk && \\\n\tabuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \" && \\\n\tcp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ && \\\n\tapk del --no-network .build-deps && \\\n\tapk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages ;; esac && \\\n\tapk del --no-network .checksum-deps && \\\n\tif [ -n \"$tempDir\" ]; then rm -rf \"$tempDir\"; fi && \\\n\tif [ -f \"/etc/apk/keys/abuild-key.rsa.pub\" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi && \\\n\tapk add --no-cache curl ca-certificates",
"system_commands": [
"set -x",
"apkArch=\"$(cat /etc/apk/arch)\"",
"nginxPackages=\" nginx=${NGINX_VERSION}-r${PKG_RELEASE} nginx-module-xslt=${NGINX_VERSION}-r${DYNPKG_RELEASE} nginx-module-geoip=${NGINX_VERSION}-r${DYNPKG_RELEASE} nginx-module-image-filter=${NGINX_VERSION}-r${DYNPKG_RELEASE} nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${NJS_RELEASE} \"",
"apk add --no-cache --virtual .checksum-deps openssl",
"case \"$apkArch\" in x86_64|aarch64) apk add -X \"https://nginx.org/packages/mainline/alpine/v$(egrep -o '^[0-9]+.[0-9]+' /etc/alpine-release)/main\" --no-cache $nginxPackages ;; *) set -x",
"tempDir=\"$(mktemp -d)\"",
"chown nobody:nobody $tempDir",
"apk add --no-cache --virtual .build-deps gcc libc-dev make openssl-dev pcre2-dev zlib-dev linux-headers libxslt-dev gd-dev geoip-dev libedit-dev bash alpine-sdk findutils curl",
"su nobody -s /bin/sh -c \" export HOME=${tempDir}",
"cd ${tempDir}",
"curl -f -L -O https://github.com/nginx/pkg-oss/archive/${NGINX_VERSION}-${PKG_RELEASE}.tar.gz",
"PKGOSSCHECKSUM=\"c773d98b567bd585c17f55702bf3e4c7d82b676bfbde395270e90a704dca3c758dfe0380b3f01770542b4fd9bed1f1149af4ce28bfc54a27a96df6b700ac1745 *${NGINX_VERSION}-${PKG_RELEASE}.tar.gz\"",
"if [ \"$(openssl sha512 -r ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz)\" = \"$PKGOSSCHECKSUM\" ]; then echo \"pkg-oss tarball checksum verification succeeded!\"; else echo \"pkg-oss tarball checksum verification failed!\"; exit 1; fi",
"tar xzvf ${NGINX_VERSION}-${PKG_RELEASE}.tar.gz",
"cd pkg-oss-${NGINX_VERSION}-${PKG_RELEASE}",
"cd alpine",
"make module-geoip module-image-filter module-njs module-xslt",
"apk index --allow-untrusted -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk",
"abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \"",
"cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/",
"apk del --no-network .build-deps",
"apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages ;; esac",
"apk del --no-network .checksum-deps",
"if [ -n \"$tempDir\" ]; then rm -rf \"$tempDir\"; fi",
"if [ -f \"/etc/apk/keys/abuild-key.rsa.pub\" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi",
"apk add --no-cache curl ca-certificates"
],
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-04-16T10:45:00-04:00",
"inst_set_time_index": 1,
"inst_set_time_reverse_index": 1
},
{
"type": "RUN",
"time": "2025-06-08T15:25:02Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 127131,
"size_human": "127 kB",
"command_snippet": "RUN apk add curl && \\\n\trm -rf /var/cache/apk...",
"command_all": "RUN apk add curl && \\\n\trm -rf /var/cache/apk/*",
"system_commands": [
"apk add curl",
"rm -rf /var/cache/apk/*"
],
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-06-08T11:15:00-04:00",
"inst_set_time_index": 2,
"inst_set_time_reverse_index": 0
},
{
"type": "COPY",
"time": "2025-06-08T15:25:03Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 195526,
"size_human": "196 kB",
"params": "/app/dist /usr/share/nginx/html/",
"command_snippet": "COPY /app/dist /usr/share/nginx/html/",
"command_all": "COPY /app/dist /usr/share/nginx/html/",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-06-08T11:15:00-04:00",
"inst_set_time_index": 2,
"inst_set_time_reverse_index": 0
},
{
"type": "EXPOSE",
"time": "2025-06-08T15:25:03Z",
"is_nop": false,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "80/tcp",
"command_snippet": "EXPOSE 80/tcp",
"command_all": "EXPOSE 80/tcp",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-06-08T11:15:00-04:00",
"inst_set_time_index": 2,
"inst_set_time_reverse_index": 0
},
{
"type": "ENTRYPOINT",
"time": "2025-06-08T15:25:03Z",
"is_nop": false,
"is_exec_form": true,
"local_image_exists": false,
"layer_index": 0,
"size": 0,
"params": "[\"nginx\",\"-g\",\"daemon off;\"]\n",
"command_snippet": "ENTRYPOINT [\"nginx\",\"-g\",\"daemon off;\"]\n",
"command_all": "ENTRYPOINT [\"nginx\",\"-g\",\"daemon off;\"]\n",
"comment": "buildkit.dockerfile.v0",
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-06-08T11:15:00-04:00",
"inst_set_time_index": 2,
"inst_set_time_reverse_index": 0
},
{
"type": "HEALTHCHECK",
"time": "2025-06-08T15:25:03Z",
"is_last_instruction": true,
"is_nop": false,
"local_image_exists": true,
"layer_index": 0,
"size": 0,
"params": "&{[\"CMD-SHELL\" \"\\\"/usr/bin/curl localhost\\\" || exit 1\"] \"5s\" \"5s\" \"5s\" \"0s\" '\\x03'}",
"command_snippet": "HEALTHCHECK --interval=5s --timeout=5s --sta...",
"command_all": "HEALTHCHECK --interval=5s --timeout=5s --start-period=5s --retries=3 CMD \\/usr/bin/curl localhost\\ || exit 1",
"comment": "buildkit.dockerfile.v0",
"raw_tags": [
"react:latest"
],
"is_buildkit_instruction": true,
"inst_set_time_bucket": "2025-06-08T11:15:00-04:00",
"inst_set_time_index": 2,
"inst_set_time_reverse_index": 0
}
]
}
],
"image_created": true,
"image_build_engine": "internal"
}

317
slides.md
View File

@ -51,17 +51,62 @@ transition: fade-out
layout: center
---
## Why Containers?
# Who is this for?
- "It works on my machine" is a thing of the past
- Containers are lightweight and portable
- Boot in milliseconds
- Ideal for reproducible dev environments
## About you
- Some experience with Docker/containers
- Some experience with BASH
- Want to better understand how containers work
---
transition: fade-out
layout: center
---
## Follow Along
**Example Repo** - https://git.hackanooga.com/mikeconrad/demystifying-docker
---
transition: fade-out
layout: center
---
<img src="https://miro.medium.com/v2/resize:fit:400/format:webp/1*Ibnwjo9LtUFxRY1MZgOcvg.png"/>
---
transition: fade-out
layout: center
---
## Common Use cases for containers
- Reproducible dev environments
- Testing in CI/CD environments
- Better "Portability" of application code
- Snapshot of application code at specific point in time
---
transition: fade-out
layout: center
---
## How we use containers
- PR builds (Preview Environments).
<br />
<br />
### Allows us to
- Test changes in isolated environments
- Simplify complex dev environment setups
- (frontend/backend services, databases, object storage, etc)
<!--A single VM in the cloud running `Docker Compose` with `Traefik` and `Sablier` allows us to have multiple ephemeral preview environments running at any given time.-->
---
transition: fade-out
layout: center
---
## Containers vs Virtual Machines
| Feature | VM | Container |
@ -71,7 +116,7 @@ layout: center
| Isolation | Strong | Process-level |
| Portability | Medium | Very High |
In reality we often use containers and vm's together. Containers run inside of VM's for better security and isolation, especially in cloud and multi tenant environments.
In reality we use containers and vm's together. Containers run inside of VM's for better security and isolation, especially in cloud and multi tenant environments.
---
transition: fade-out
@ -80,17 +125,35 @@ layout: center
## What is Docker?
- A tool to build and run containers
- Docker engine runs containers using OS features:
- Namespaces
- cgroups
- Union file systems
- Uses images layered from base -> app code
- Written in GO
- Uses Client/Server model with REST API (`docker cli` and `dockerd`)
- Eco system of tools (Compose, Swarm, etc)
- Public Image Registry (Dockerhub)
- Docker client typically runs on same machine as server but doesn't have to
---
transition: fade-out
layout: center
---
## What is Docker?
- A tool to build and run containers
- Containers are exclusive to Linux
- Docker engine runs containers using Linux features like:
- Namespaces
- cgroups
- Union file systems
- Container runs from an image layered with base image and application code
---
transition: fade-out
layout: center
---
## Common Use Cases
- Reproducible Dev environments (dev containers)
- Preview/PR environments (ephemeral test environments)
- Legacy applications or applications with complex environment setups
---
transition: fade-out
@ -99,10 +162,11 @@ layout: center
## Docker Architecture
Docker Engine (Server) <-- REST API --> Docker CLI (Client)
Docker CLI (Client) <-- REST API --> Docker Engine (Server)
<img src="https://docs.docker.com/get-started/images/docker-architecture.webp" width="700" />
<!-- ![Docker Architecture](https://docs.docker.com/get-started/images/docker-architecture.webp) -->
[https://docs.docker.com/get-started/docker-overview/]
---
transition: fade-out
@ -113,7 +177,10 @@ layout: center
- **Namespaces**: isolate PID, net, mount, etc.
- **cgroups**: control CPU, memory, IO
- **UnionFS**: layered filesystem (AUFS, OverlayFS)
- **UnionFS**: layered filesystem (OverlayFS)
<!--
Overlayfs is the default. This allows Docker to use a layered approach. In this example the bottom layer or lowerdir is the "filesytem" from the image. The upperdir is the container filesystem (not persisted by default) and the merged is volume/bind mounts.
-->
![UnionFS diagram](https://docs.docker.com/engine/storage/drivers/images/overlay_constructs.webp)
@ -122,6 +189,94 @@ transition: fade-out
layout: center
---
## Bind/Volume Mounts
- 2 most common storage mechanisms
- Different use cases and security implications
---
transition: fade-out
layout: center
---
## Bind Mounts
- Mounting files/directories from the host machine directly into a container (merged overlayfs layer).
- Processes inside container can modify files on host system.
- Bind mounts are strongly tied to the host
- Best for things like dev containers where you need to mount source code into container and have hot reload, etc.
## Bind Mount Example
```bash
$ docker run --mount type=bind,src=/home/mikeconrad/projects/example/app,dst=/app,ro nginx # ro for ReadOnly
$ docker run --volume /home/mikeconrad/projects/example/app:/app nginx
```
<!-- https://docs.docker.com/engine/storage/bind-mounts/ -->
<!--
These terms are oftentimes used interchangebly and can be confusing but it is important to understand the difference.
You need to be careful when using bind mounts because by default, the processes running in the container will have read/write access to your filesystem.
This could cause some issues if code running inside the container is malicious or is compromised.
It is also possible to mount the files as read-only so that the container has access to read them but not write. This is better for security.
Bind mounts also "overwrite" the container/image filesystem layers. So for example mounting ./some-files/test:/etc/passwd would overwrite the /etc/passwd file in the container
The directory inside the container does not need to exist for this to work. If the directory does not exist inside of the container filesystem it will be created with the contents.
-->
---
transition: fade-out
layout: center
---
## Volume Mount Example
```bash
$ docker run --name postgrestest \
--mount type=volume,src=postgresData,dst=/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=postgres \
--rm postgres:16
$ docker run --name postgrestest \
--volume postgresData:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=postgres \
--rm postgres:16
```
```bash
$ docker volume inspect postgresData
[
{
"CreatedAt": "2025-06-08T10:39:12-04:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/postgresData/_data",
"Name": "postgresData",
"Options": null,
"Scope": "local"
}
]
```
- Docker creates a volume named postgresData and mounts that directory inside the container.
<!-- https://docs.docker.com/engine/storage/bind-mounts/ -->
---
transition: fade-out
layout: center
---
## Volume mounts
- Created and managed by the Docker Daemon
- Volume data is stored on host filesystem but managed by Docker.
- Used for persistent data.
<!--
It is possible to modify the data directly via normal tools but unsupported and can cause unintended side-effects due to the overlayfs storage driver.
An example would be creating a postgres volume for persistent database storage.
-->
---
transition: fade-out
layout: center
---
## Anatomy of a Dockerfile
```dockerfile
@ -133,6 +288,11 @@ COPY . .
EXPOSE 3000
CMD ["npm", "start"]
```
```bash
mikeconrad@pop-os:~/projects/demystifying-docker/examples/react
$ docker build -t react-app .
```
- Starts with a base image
- Copy files and install deps
@ -143,33 +303,34 @@ transition: fade-out
layout: center
---
## Dockerfile Best Practices
## Multi Stage builds
```dockerfile
# Stage 1: Build the Go binary
FROM golang:1.24.2-alpine AS builder
# Set working directory inside the build container
# Stage 1 - Define Base image
FROM node:22-alpine AS base
# Stage 2 Install dependencies
FROM base AS install-deps
WORKDIR /app
COPY package*.json /app/
RUN yarn
# Stage 3 Development
FROM install-deps AS develop
WORKDIR /app
COPY go.mod ./
RUN go mod download
COPY . .
# Build the Go binary statically
RUN CGO_ENABLED=0 GOOS=linux go build -o docker-api-proxy .
# Stage 2: Run binary in minimal container
FROM scratch
# Copy binary from builder
COPY --from=builder /app/docker-api-proxy /usr/local/bin/docker-api-proxy
# Run binary
ENTRYPOINT ["docker-api-proxy"]
EXPOSE 80
ENTRYPOINT ["yarn", "dev", "--host=0.0.0.0"]
EXPOSE 5173
```
- Use specific versions, not `latest`
- Combine commands to reduce layers
- Use `.dockerignore`
- Prefer slim or alpine images
- Run as non-root user if possible
```bash
$ docker build -t react .
$ docker run --rm -P react
```
<!--
Run docker image and demonstrate dev container functionality. Attach to the running container
via VSCode extension and make changes to code. Note that it updates in real time in the browser.
Kill the container and start a new one. Note that the files do not persist. Need volume/bind mounts
For that.
-->
---
transition: fade-out
layout: center
@ -180,64 +341,26 @@ layout: center
- Define multi-container apps in one file
- Great for local dev and staging (and production!)
```yaml
name: traefik_secure
services:
socket-proxy:
image: git.hackanooga.com/mikeconrad/docketproxy:latest
container_name: socket-proxy
networks:
- traefik
- socket_proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
restart: unless-stopped
environment:
- ALLOWED_NETWORKS=traefik_secure_traefik
traefik:
image: traefik:latest
container_name: traefik
command:
- "--log.level=INFO"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--providers.docker=true"
- "--providers.docker.endpoint=tcp://socket-proxy:8000"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.traefik.address=:8080"
- "--api.insecure=true"
- "--api.dashboard=true"
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`traefik.docker.localhost`)"
- "traefik.http.routers.api.entrypoints=web"
- "traefik.http.routers.api.service=api@internal"
ports:
- "80:80"
- "8080:8080"
networks:
- traefik
- socket_proxy
depends_on:
- socket-proxy
restart: unless-stopped
whoami:
image: traefik/whoami
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"
networks:
traefik: {}
socket_proxy:
driver: bridge
internal: true
enable_ipv6: false
```
---
transition: fade-out
layout: center
---
## Q/A
-
---
transition: fade-out
layout: center
---
## Resources
- [Slide Deck (including examples)](https://git.hackanooga.com/mikeconrad/demystifying-docker-v2)
- [DocketProxy (Docker socket proxy)](https://git.hackanooga.com/mikeconrad/docketproxy)
- [SlimToolkit (Optimize and secure containers)](https://github.com/slimtoolkit/slim)
## VSCode plugins
https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker
https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers
https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-containers