Containers Lab¶
45 min Beginner
In this lab you'll build a container image for a small web service, run it, inspect it, and push it to a public registry.
The lab doesn't hand you every command. Each step describes what to do. Work out the command yourself, then expand the solution to check your answer.
Prerequisites¶
- A container engine:
- A free Quay.io account, used to push your image in Part 4.
Part 1: Verify your setup¶
-
Print the version of your container engine.
-
Run the
quay.io/podman/hellocontainer. Because the image isn't on your machine yet, it's pulled from the registry first. -
List the images stored on your machine.
Part 2: Build an image¶
Create the application¶
Create a new directory named greeting for the application:
Save the following as main.go. It's a small HTTP service that returns a JSON greeting and reads its message from an environment variable:
package main
import (
"encoding/json"
"log"
"net/http"
"os"
)
func main() {
message := os.Getenv("GREETING")
if message == "" {
message = "Welcome to the Cloud Native Bootcamp!"
}
http.HandleFunc("/greeting", func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "World"
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": message,
"name": name,
})
})
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
})
log.Println("greeting service listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Save this as go.mod:
You don't need Go installed. The build happens inside a container.
Write the Containerfile¶
A Containerfile (Docker calls it a Dockerfile, and both tools accept either name) describes how to build the image. Save this as Containerfile:
# Stage 1: compile the Go binary with Red Hat's Go toolchain image
FROM registry.access.redhat.com/ubi9/go-toolset AS builder
COPY --chown=1001:0 . .
RUN CGO_ENABLED=0 go build -o greeting .
# Stage 2: copy only the binary into a minimal runtime image
FROM registry.access.redhat.com/ubi9/ubi-micro
COPY --from=builder /opt/app-root/src/greeting /usr/local/bin/greeting
EXPOSE 8080
USER 1001
CMD ["greeting"]
FROMsets the base image. It starts each stage of the build.COPYcopies files into the image.--from=buildercopies from an earlier stage instead of from your directory.RUNruns a command at build time.EXPOSEdocuments the port the app listens on.USERruns the app as a non-root user. OpenShift ignores this and assigns a random UID, which is why the app doesn't depend on a particular user.CMDis the command that runs when the container starts.
This is a multi-stage build. The first stage contains the whole Go toolchain, which is several hundred MB. The final image contains only the compiled binary on top of ubi-micro, so it's around 30 MB. Smaller images pull faster, start faster, and have fewer packages that could contain vulnerabilities.
Build it¶
-
Build the image from the current directory and tag it
greeting:v1.0.0. -
List your images again and compare the size of
greetingwithgo-toolset.
Part 3: Run and inspect the container¶
-
Run the image in the background (detached), name the container
greeting, and map port8080on your machine to port8080in the container.Port already in use?
If something else on your machine uses port 8080, map a different host port, for example
-p 9080:8080, and use that port in the steps below. -
Call the service:
-
Try to run a second container with the same name. What happens?
-
List the running containers.
-
Inspect the container to see its full configuration: environment variables, network settings, mounts and state. Then use a Go template to print only its IP address and state.
-
Show the container's logs.
-
Run a command inside the running container to see which user the app runs as.
-
Stop and remove the container, then start a new one with the
GREETINGenvironment variable set to a message of your choice, and call it again.Solution
podman stop greeting podman rm greeting podman run -d --name greeting -p 8080:8080 -e GREETING="Hello from my container" greeting:v1.0.0 curl "localhost:8080/greeting?name=John"This is how containers are configured: the same image, with different settings supplied at runtime. Kubernetes does the same with ConfigMaps and Secrets.
Part 4: Push the image to a registry¶
A container registry stores and distributes images. Docker Hub, Quay.io and GitHub Container Registry are public registries. OpenShift also has a built-in internal registry.
-
Log in to Quay.io.
-
Tag your image for your Quay repository:
quay.io/<your-username>/greeting:v1.0.0. The tag includes the registry hostname, which tells the engine where to push. -
Push the image.
-
On quay.io, open the new
greetingrepository and, under Settings, make it public. New repositories are private by default. -
Remove the local images, then pull your image back from Quay.
Building on Apple Silicon (M-series Macs)?
Images you build on an ARM Mac are arm64 by default, but most OpenShift clusters run on amd64 (x86_64) nodes. Pods fail there with exec format error. Build for the target platform, or build a multi-arch image that covers both:
Cleanup¶
Keep the greeting image on Quay. The Image Registry Lab deploys it to OpenShift.
What you learned¶
- Building images with a Containerfile and multi-stage builds
- Running, inspecting and configuring containers
- Tagging and pushing images to a registry
- Building images for the right CPU architecture