Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Empty file added scenarios/georgetown/README.md
Empty file.
85 changes: 85 additions & 0 deletions scenarios/georgetown/ansible/playbook-debian.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
# ansible-playbook playbook-debian.yml -u admin --private-key=/path/to/private/key -i "$ip,"
- name: copy files to remote server
hosts: all
vars:
admin_home: "{{ ansible_env.HOME | default('/home/admin') }}"
tasks:
# OS
- name: update packages and install lsof
become: true
package:
# This is weird in that it does not exist in the offical docs
# but this module just passes params down to the underlying
# package manager module where `update_cache` is normally a thing so
# it ends up working....
update_cache: yes
state: latest
name:
- lsof
- util-linux # Ensure that fallocate is installed

- name: create log file
become: true
file:
path: /var/log/fallocate.log
state: touch
owner: admin
group: admin

- name: cronjob
cron:
name: "reboot"
special_time: reboot
job: "{{ admin_home }}/badlog.py &"

# check.sh
- name: Create {{ admin_home }}/agent directory
ansible.builtin.file:
path: "{{ admin_home }}/agent"
owner: admin
group: admin
mode: a+wx
state: directory

- name: Copy georgetown systemd files
become: true
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
with_items:
- { src: ../files/mock-build.service, dest: /etc/systemd/system/mock-build.service }
- { src: ../files/secondary-service.service, dest: /etc/systemd/system/secondary-service.service }

- name: Copy georgetown script files
become: false
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: "{{ item.mode | default('0644') }}"
with_items:
- { src: ../files/secondary-service.sh, dest: "{{ admin_home }}/secondary-service.sh", mode: '0755' }
- { src: ../files/mock-build-run.sh, dest: "{{ admin_home }}/mock_build.sh", mode: '0755' }

- name: Start mock build application
become: true
systemd:
name: mock-build
state: started

- name: Start secondary service
become: true
systemd:
name: secondary-service
state: started
enabled: yes

- name: copy check.sh
copy:
src: ../files/check.sh
dest: "{{ admin_home }}/agent/check.sh"

- name: set check.sh
file:
path: "{{ admin_home }}/agent/check.sh"
mode: "+x"
34 changes: 34 additions & 0 deletions scenarios/georgetown/files/check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/bash

BUILD_MOUNT_POINT="/tmp/ephemeral-build"

# Check if the build mount point exists
function check_build_mount_point {
if [ ! -d $BUILD_MOUNT_POINT ]; then
echo -b "NO"
exit 1
fi
}

# Function to check if both example-build-artifact.txt and
# secondary-artifact.txt exist in the build mount point
# BOTH files need to exist concurrently
function check_build_artifacts {
BUILD_FILES=(
"example-build-artifact.txt"
"secondary-artifact.txt"
)
for file in "${BUILD_FILES[@]}"; do
if [ ! -f "$BUILD_MOUNT_POINT/$file" ]; then
echo -b "NO"
exit 1
else
echo -b "OK"
exit 0
fi
done
}

# Check if one or both functions executed successfull
check_build_mount_point
check_build_artifacts
13 changes: 13 additions & 0 deletions scenarios/georgetown/files/mock-build-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/env/bin bash

MOUNT_POINT="/tmp/ephemeral-build"
EXAMPLE_BUILD_ARTIFACT="example-build-artifact.txt"
MAX_BUILD_ARTIFACT_SIZE=10M

# Create the example build file
touch $MOUNT_POINT/$EXAMPLE_BUILD_ARTIFACT

# Use fallocate to allocate the maximum size of the file
fallocate -l "$MAX_SIZE" "$MOUNT_POINT/$EXAMPLE_BUILD_ARTIFACT"

trap
9 changes: 9 additions & 0 deletions scenarios/georgetown/files/mock-build.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Unit]
Description=Mock Build Application
After=network.target

[Service]
ExecStart={{ admin_home }}/mock_build.sh

[Install]
WantedBy=multi-user.target
9 changes: 9 additions & 0 deletions scenarios/georgetown/files/secondary-service.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Unit]
Description=Secondary service Application
After=network.target

[Service]
ExecStart={{ admin_home }}/secondary-service.sh

[Install]
WantedBy=multi-user.target
2 changes: 2 additions & 0 deletions scenarios/georgetown/files/secondary-service.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Secondary service that will continually run in the background
# and try to place a file on tmp artifact directory that is too big to fit on disk
67 changes: 67 additions & 0 deletions scenarios/georgetown/packer/aws-debian11.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Debian

packer {
required_plugins {
amazon = {
version = "= 1.2.1"
source = "github.com/hashicorp/amazon"
}
}
}

source "amazon-ebs" "debian" {
ami_name = "scenario-1-saintjohn"
instance_type = "t3a.nano"
region = "${var.region}"
vpc_id = "${var.vpc_id}"
subnet_id = "${var.subnet_id}"
associate_public_ip_address = true
source_ami = "${var.source_ami}"
ssh_username = "admin"
}

build {
name = "debian-build"
sources = [
"source.amazon-ebs.debian"
]

# OS & scenario packages
provisioner "shell" {
inline = [
"echo Update packages...",
"sudo apt-get update",
"sudo apt-get install -y lsof",
]
}

# badlog.py
provisioner "file" {
source = "../files/badlog.py"
destination = "/tmp/badlog.py"
}

provisioner "shell" {
inline = [
"mv /tmp/badlog.py /home/admin/badlog.py",
"chmod +x /home/admin/badlog.py",
"sudo touch /var/log/bad.log",
"sudo chown admin: /var/log/bad.log",
"echo '@reboot /home/admin/badlog.py &' | crontab -",
]
}

# check.sh
provisioner "file" {
source = "../files/check.sh"
destination = "/tmp/check.sh"
}

provisioner "shell" {
inline = [
"sudo mv /tmp/check.sh /home/admin/agent/check.sh",
"sudo chmod +x /home/admin/agent/check.sh",
]
}

}
25 changes: 25 additions & 0 deletions scenarios/georgetown/packer/variables.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# variables for Packer file, adapt to your AWS region, base image, vpc, subnet

variable "region" {
type = string
default = "us-east-1"
}

# tested with source Debian 11 image HVM 64-bit (x86)
# change to one in your region
variable "source_ami" {
type = string
default = "ami-"
}

# change to your vpc
variable "vpc_id" {
type = string
default = "vpc-"
}

# change to your subnet
variable "subnet_id" {
type = string
default = "subnet-"
}
110 changes: 110 additions & 0 deletions scenarios/kingston/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# "Kingston": Docker Image Build Optimization

## Introduction

Welcome to the Kingston scenario! This challenge focuses on optimizing Docker image build times for a Ruby on Rails application. You'll be working with a real-world Dockerfile that has several optimization opportunities.

## Scenario Description

You have been given a Ruby on Rails application with a poorly optimized Dockerfile that takes a long time to build. Your task is to optimize the Docker build process to achieve **at least a 10% reduction in build time** compared to the base implementation.

## The Challenge

The scenario includes:
- **Dockerfile.base**: An unoptimized Dockerfile with multiple inefficiencies
- **Dockerfile.optimized**: A pre-optimized version that you can study and improve further
- **Sample Rails app**: A minimal Rails application for testing builds
- **Timing script**: Automated build time comparison tool

## Common Docker Optimization Techniques

Here are some optimization strategies you might consider:

### 1. **Layer Caching Optimization**
- Order instructions from least to most frequently changing
- Copy dependency files before application code
- Use `.dockerignore` to exclude unnecessary files

### 2. **Reduce Layer Count**
- Combine multiple RUN statements
- Chain commands with `&&`
- Clean up in the same layer where packages are installed

### 3. **Multi-stage Builds**
- Separate build dependencies from runtime dependencies
- Use a smaller base image for the final stage
- Copy only necessary artifacts between stages

### 4. **Base Image Selection**
- Use more recent, optimized base images
- Consider Alpine Linux for smaller images
- Use official language-specific images when appropriate

### 5. **Package Management**
- Use `--no-install-recommends` for apt packages
- Clean package caches in the same layer
- Pin package versions for reproducible builds

### 6. **Build Context Optimization**
- Minimize build context size
- Use `.dockerignore` effectively
- Avoid sending unnecessary files to Docker daemon

## Getting Started

1. **Explore the current setup:**
```bash
cd /home/admin/docker-optimization
ls -la
```

2. **Examine the base Dockerfile:**
```bash
cat Dockerfile.base
```

3. **Study the optimized version:**
```bash
cat Dockerfile.optimized
```

4. **Run the build time comparison:**
```bash
./time_builds.sh
```

## Success Criteria

The scenario is considered complete when:
- Both Dockerfiles build successfully
- The optimized build is **at least 10% faster** than the base build
- The optimization maintains the same functionality

## Tips for Success

- **Start with the obvious wins**: Look for repeated `RUN` commands, unnecessary downloads, and poor layer ordering
- **Use Docker's build cache effectively**: Structure your Dockerfile to maximize cache hits
- **Monitor build progress**: Use `docker build --progress=plain` to see detailed build steps
- **Test iteratively**: Make small changes and test frequently
- **Consider the application needs**: Don't break functionality while optimizing

## Validation

Run the check script to validate your solution:
```bash
/home/admin/agent/check.sh
```

This will verify that your optimization achieves the required 10% improvement in build time.

## Files in this Scenario

- `Dockerfile.base` - The unoptimized Dockerfile (starting point)
- `Dockerfile.optimized` - An optimized version to study and improve
- `time_builds.sh` - Script to measure and compare build times
- `setup_scenario.sh` - Initial scenario setup script
- `Gemfile`, `Gemfile.lock` - Rails application dependencies
- `entrypoint.sh` - Docker container entrypoint script
- Sample Rails application files for realistic build testing

Good luck with your Docker optimization challenge!
Loading