Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
dbbe985
ISSUE-90 section outline
ivorscott Nov 14, 2021
84936ae
ISSUE-90 add entrypoint/command introduction and lecture titles
ivorscott Nov 15, 2021
ac60742
ISSUE-90 refine Lecture 1
ivorscott Nov 28, 2021
d7b5a99
ISSUE-90 clean up folder
ivorscott Nov 28, 2021
7cb1959
ISSUE-90 add Lecture 2
ivorscott Nov 28, 2021
1b4432a
ISSUE-90 add Lecture 3
ivorscott Nov 28, 2021
1bf1d42
ISSUE add note to Lecture 1
ivorscott Nov 29, 2021
743f780
ISSUE-90 add 4 rules for CMD and ENTRYPOINT
ivorscott Nov 29, 2021
8f533b6
ISSUE-90 replace images with text
ivorscott Jan 23, 2022
1f54a0e
ISSUE-90 fix terminology
ivorscott Jan 25, 2022
5d40757
ISSUE-90 add exercises
ivorscott Jan 30, 2022
5465263
ISSUE-90 fix readme and bash script linting issues
ivorscott Feb 9, 2022
b116f60
adding gha
BretFisher Jan 16, 2022
16cee52
fixing lints (#95)
BretFisher Jan 16, 2022
4a311a9
Fixing lints (#96)
BretFisher Jan 16, 2022
76d6d32
Fixing lints, again (#97)
BretFisher Jan 16, 2022
80f40a2
ISSUE-90 update markdown linter
ivorscott Feb 13, 2022
229e59b
Merge branch 'main' into ISSUE-90
ivorscott Feb 13, 2022
72c73f5
ISSUE-90 fix markdown linter
ivorscott Feb 13, 2022
7bed733
ISSUE-90 fix linter, restructure folder and readme
ivorscott Feb 13, 2022
986d4de
ISSUE-90 add quiz and update section structure
ivorscott Feb 13, 2022
6993077
fix wording
ivorscott Feb 13, 2022
7236918
fix typos
ivorscott Feb 13, 2022
4ad1b57
Merge branch 'main' into ISSUE-90
BretFisher May 23, 2023
2ad6589
Merge branch 'main' into ISSUE-90
BretFisher Feb 13, 2024
f57e492
more section adding on entrypoint-cmd
BretFisher Dec 4, 2024
db8091e
Merge branch 'main' into ISSUE-90
BretFisher Dec 4, 2024
743b219
adding bad shell script example
BretFisher Jan 19, 2025
31a88cd
improving references and img cleanup
BretFisher Feb 10, 2025
4843ccc
Big update with entrypoint section launch
BretFisher Feb 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/linters/.markdown-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ MD013:
heading_line_length: 100
# check code blocks?
code_blocks: false

# MD033/html - Inline HTML
MD033:
allowed_elements: ["details", "summary", "br", "small"]

# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
MD024:
allow_different_nesting: true
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mysql-data
wordpress-data
registry-data
sample-data

NEXT
##
##
##
Expand Down Expand Up @@ -152,4 +152,4 @@ $RECYCLE.BIN/
.dccache

# reference pdf's
references/*.pdf
references/*.pdf
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions dockerfiles/entrypoint/assignment01/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# ENTRYPOINT Assignment 01

> Creating CLI programs with Docker ENTRYPOINT

This assignment has two different Dockerfiles for you to build that are meant to run a single execuable.

1. The cmatrix subdirectory uses the Alpine Linux image and installs the cmatrix screen saver.
2. The apachebench subdirectory uses the Ubuntu image and installs the apache2-utils package for running the ab benchmarking tool.

6 changes: 6 additions & 0 deletions dockerfiles/entrypoint/assignment01/apachebench/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# this file should list everything you don't want coppied into the docker image
# format is the same as .gitignore (in fact, I often start one by just copying .gitignore)

.git
answer

12 changes: 12 additions & 0 deletions dockerfiles/entrypoint/assignment01/apachebench/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# use the README.md file for requirements to build this image
# If you get stuck, the answer/ directory has the solution
FROM ubuntu:latest

RUN apt-get update && \
apt-get install -y apache2-utils && \
rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["ab"]

CMD ["-n", "10", "-c", "2", "https://www.bretfisher.com/"]

23 changes: 23 additions & 0 deletions dockerfiles/entrypoint/assignment01/apachebench/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ENTRYPOINT Assignment 01 Part 2 - ApacheBench

> Create an image for the ApacheBench HTTP server benchmarking tool

This Dockerfile Assignment will create a container image for the ApacheBench HTTP server benchmarking tool. This is a simple tool that can be used to test the performance of a web server by sending a large number of requests to it and measuring the response time.

We'll use the Ubuntu image with the apt-get package manager.

## Requirements

- No source code is needed, so we won't need to COPY any files into the image.
- Use the `ubuntu` image as the base image. You could pin to a specific version (LTS versions end in .04, so `:20.04`), or use `:latest`.
- Use the `apt-get` package manager to install the `apache2-utils` package, which contains the `ab` ApacheBench tool.
- Create an ENTRYPOINT that runs the `ab` command.
- Create an CMD that gives some default arguments to the `ab` command. For example, `-n 10 -c 2 https://www.bretfisher.com/`. `-n 10` means 10 requests total, and `-c 2` means 2 concurrent requests.
- OR, an easer way for tools like ab, curl, httping, that always need a URL added at the CLI, I find it easier to add some default arguments to the ENTRYPOINT, and leave the CMD as `["--help"]` so that all the user needs to do is add the URL at the end of the `docker run` command. If they forget to override the CMD, they'll get the help output.

## Tips

- Tip: Ubuntu Linux uses the `apt` package manager. It requires a three step process (commands) with 1. update the db cache, 2. install packages, 3. cleanup the cache files. You can chain these three commands together with &&, so a typical install would look like `apt-get update && apt-get install -y <list-of-packages> && rm -rf /var/lib/apt/lists/*` and you can add line breaks for readability with a backslash `\` at the end of the Dockerfile line.
- ApacheBench expects a URL as the last argument, so you'll need to pass that in when you run the container, and it always needs http:// or https:// in the URL, and a trailing slash after the domain name.
- Tip: As most Linux CLI tools, ab has a --help option, so I welcome you to explore the options and customize the CMD instruction to your liking. Remember you could always build this image, then overwride the ENTRYPOINT at runtime and then play around with ab by running `docker run -it --entrypoint sh <image-name>`

11 changes: 11 additions & 0 deletions dockerfiles/entrypoint/assignment01/apachebench/answer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM ubuntu

# Install the necessary packages
RUN apt-get update && apt-get install -y \
apache2-utils \
&& rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["ab", "-n", "10", "-c", "2"]

CMD ["https://www.bretfisher.com/"]

6 changes: 6 additions & 0 deletions dockerfiles/entrypoint/assignment01/cmatrix/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# this file should list everything you don't want coppied into the docker image
# format is the same as .gitignore (in fact, I often start one by just copying .gitignore)

.git
answer

10 changes: 10 additions & 0 deletions dockerfiles/entrypoint/assignment01/cmatrix/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# use the README.md file for requirements to build this image
# If you get stuck, the answer/ directory has the solution
FROM alpine:latest

RUN apk add --no-cache cmatrix

ENTRYPOINT ["cmatrix"]

CMD ["-abs", "-C", "red"]

23 changes: 23 additions & 0 deletions dockerfiles/entrypoint/assignment01/cmatrix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ENTRYPOINT Assignment 01 Part 1 - cmatrix

> Bulding a Matrix screensaver as a Docker Image

While this is just a fun tool, it's a good way to practice creating a Docker image for a single binary.

All we need to get started is the latest alpine image and a quick understanding of the apk package manager.

## Requirements for this Dockerfile

- No source code is needed, so we won't need to COPY any files into the image.
- Start FROM the `alpine` image. You can also practice pinning to a recent version (found on Docker Hub's alpine image page.) or `:latest`. I tend to care less about FROM image pinning for CLI tools like this.
- Use the `apk` package manager to install the `cmatrix` package.
- You'll want to set the ENTRYPOINT to run `cmatrix` when the container starts.
- Let's make this image easy to run by adding a `CMD` instruction with some default cmatrix arguments. For example, `-abs -C red`.
- The cmatrix tool needs a terminal to run, so you need to ensure docker provisions a tty. Always use the `-it` flags with this image when running the container, or you'll get a "Error opening terminal" error.

## Tips

- Tip: Alpine Linux uses the `apk` package manager, not `apt` or `yum`. The command to install a package is `apk add <package>`.
- Tip: Use the `--no-cache` apk flag to avoid caching the package index locally, so a command would look like `apk add --no-cache <package>`.
- Tip: As most Linux CLI tools, cmatrix has a --help option, so I welcome you to explore the options and customize the CMD instruction to your liking. Remember you could always build this image, then overwride the ENTRYPOINT at runtime and then play around with cmatrix by running `docker run -it --entrypoint sh <image-name>`

7 changes: 7 additions & 0 deletions dockerfiles/entrypoint/assignment01/cmatrix/answer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM alpine

RUN apk add --no-cache cmatrix

ENTRYPOINT ["cmatrix"]

CMD ["-abs"]
6 changes: 6 additions & 0 deletions dockerfiles/entrypoint/assignment02/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# this file should list everything you don't want coppied into the docker image
# format is the same as .gitignore (in fact, I often start one by just copying .gitignore)

.git
answer

17 changes: 17 additions & 0 deletions dockerfiles/entrypoint/assignment02/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# use the README.md file for requirements to build this image
# If you get stuck, the answer/ directory has the solution
FROM python:slim

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

VOLUME /app/data

ENTRYPOINT ["./docker-entrypoint.sh"]

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
23 changes: 23 additions & 0 deletions dockerfiles/entrypoint/assignment02/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ENTRYPOINT Assignment 02

> Running startup scripts with ENTRYPOINT

This assignment has you building a Dockerfile from scratch to perform startup tasks with an ENTRYPOINT script. This script will perform a few actions before starting the FastAPI Python web API server.

## Dockerfile Requirements

- This is a Python app, so you should start with the `python:slim` image. I always prefer slim images when available, as the default language images are usually too big and bloated for real-world use.
- Set the working directory to `/app`.
- Python apps often keep their dependencies in a requirements.txt file, so let's copy that in first, so we can install dependencies before we copy all the other files in.
- Install the dependencies with a run command for `pip install --no-cache-dir -r requirements.txt`
- Now let's copy all files in after requirements are installed.
- This app requires persistant data, so lets create a volume statment for `/app/data`.
- Set the entrypoint to run the startup script `./docker-entrypoint.sh`.
- Set the cmd to start the uvicorn web server with `uvicorn main:app --host 0.0.0.0 --port 8000`.

## Testing the Image

- Once this image builds, you can run it with a `-p 8000:8000` to make the web server available on http://localhost:8000
- The most interesting URL for testing is the built-in docs of FastAPI at http://localhost:8000/docs
- Python pip has a `--no-cache-dir` option to avoid caching the package index locally, so a command would look like `pip install --no-cache-dir -r requirements.txt`.
- Checkout the `docker-entrypoint.sh` script for the startup tasks. It's a simple script that checks several common things like ensuring the `/app/data` directory exists and copying over any initial data files (which you might want to peak at, 😎).
18 changes: 18 additions & 0 deletions dockerfiles/entrypoint/assignment02/answer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM python:slim

WORKDIR /app

# Copy requirements first for better docker layer caching
COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the application
COPY . .

VOLUME /app/data

ENTRYPOINT ["./docker-entrypoint.sh"]

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions dockerfiles/entrypoint/assignment02/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/sh
set -e

# seed the mounted volume with the default images from /app/data-default if it's empty
# Docker will actually auto-copy any files in the image in /app/data to a freshly mounted volume
# but this doesn't work in other Container runtimes and can be confusing to use,
# so we'll just do it manually here

if [ -z "$(ls -A /app/data)" ]; then
cp -r /app/data-default/* /app/data/
fi

# If /run/secrets/ exists, turn any mounted secrets found into environment variables
# NOTE: this is not used for this Python app, but it's a good example of how to do it

if [ -d "/run/secrets" ]; then
for secret in /run/secrets/*; do
if [ -f "$secret" ]; then
# Get just the filename without the path
name=$(basename "$secret")
# Read contents and set as env var
export "$name"="$(cat $secret)"
fi
done
fi

# use envsubst to replace any environment variables in the config file
# NOTE: this is not used for this Python app, but it's a good example of how to do it with envsubst

if [ -f "/app/data/config.json" ]; then
envsubst < /app/data/config.json > /app/data/config.json.tmp
mv /app/data/config.json.tmp /app/data/config.json
fi

# if $PORT is not set, quit with an error
# NOTE: this is not used for this Python app, but it's a good example of how to do it

# if [ -z "$PORT" ] ; then
# echo "ERROR: PORT must be set"
# exit 1
# fi

# run the app by passing execution to the Dockerfile CMD
exec "$@"
83 changes: 83 additions & 0 deletions dockerfiles/entrypoint/assignment02/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from pydantic import BaseModel
import os
from datetime import datetime

app = FastAPI()

# Create data directory if it doesn't exist
os.makedirs("/app/data", exist_ok=True)

# Enable CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, replace with your frontend URL
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# Example data model
class Item(BaseModel):
name: str
description: str | None = None

@app.get("/")
async def root():
return {"message": "Welcome to FastAPI"}

@app.get("/api/download/{filename}")
async def download_file(filename: str):
file_path = os.path.join("/app/data", filename)
if not os.path.exists(file_path):
raise HTTPException(status_code=404, detail="File not found")
return FileResponse(file_path, filename=filename)

@app.get("/api/items")
async def get_items():
try:
# Get list of files in /app/data directory
files = os.listdir("/app/data")

# Get detailed information for each file
file_details = []
for filename in files:
file_path = os.path.join("/app/data", filename)
stats = os.stat(file_path)
file_details.append({
"filename": filename,
"size": stats.st_size,
"created": datetime.fromtimestamp(stats.st_ctime).isoformat(),
"modified": datetime.fromtimestamp(stats.st_mtime).isoformat(),
"download_url": f"http://localhost:8000/api/download/{filename}"
})

return file_details
except Exception as e:
return {"error": str(e)}

@app.post("/api/upload")
async def upload_file(file: UploadFile = File(...)):
# Create unique filename with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{timestamp}_{file.filename}"

# Save file to /app/data directory
file_path = os.path.join("/app/data", filename)

try:
# Read file content
content = await file.read()
# Write to disk
with open(file_path, "wb") as f:
f.write(content)

return {
"filename": filename,
"size": len(content),
"content_type": file.content_type
}
except Exception as e:
return {"error": str(e)}
4 changes: 4 additions & 0 deletions dockerfiles/entrypoint/assignment02/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fastapi>=0.115.8
uvicorn>=0.34.0
python-multipart>=0.0.20
pydantic>=2.10.6
3 changes: 3 additions & 0 deletions dockerfiles/entrypoint/entrypoint-1/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM busybox:latest

ENTRYPOINT ["hostname"]
10 changes: 10 additions & 0 deletions dockerfiles/entrypoint/entrypoint-cmd-1/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM ubuntu:latest

RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["curl"]

CMD ["--help"]
8 changes: 8 additions & 0 deletions dockerfiles/entrypoint/entrypoint-cmd-2/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM python:slim
USER www-data
WORKDIR /var/www/html
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENTRYPOINT ["./startup.sh"]
CMD ["python", "app.py"]
10 changes: 10 additions & 0 deletions dockerfiles/entrypoint/entrypoint-cmd-2/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello, World!'

if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
1 change: 1 addition & 0 deletions dockerfiles/entrypoint/entrypoint-cmd-2/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flask
9 changes: 9 additions & 0 deletions dockerfiles/entrypoint/entrypoint-cmd-2/startup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#! /bin/bash

# do some startup stuff
echo "Checking permissions on /var/www/html/upload"
mkdir -p /var/www/html/upload
chown -R www-data:www-data /var/www/html/upload

# Start the application set in CMD
exec "$@"
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading