Skip to content
Merged
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
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!
98 changes: 98 additions & 0 deletions scenarios/kingston/ansible/playbook-debian.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
# ansible-playbook playbook-debian.yml -u admin --private-key=/path/to/private/key -i "$ip,"
- name: Setup Docker Image Build Optimization scenario
hosts: all
tasks:
# OS packages and Docker installation
- name: Update packages and install prerequisites
become: true
package:
update_cache: yes
state: latest
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- bc

- name: Add Docker GPG key
become: true
apt_key:
url: https://download.docker.com/linux/debian/gpg
state: present

- name: Add Docker repository
become: true
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable"
state: present

- name: Install Docker
become: true
package:
update_cache: yes
state: latest
name:
- docker-ce
- docker-ce-cli
- containerd.io

- name: Add admin user to docker group
become: true
user:
name: admin
groups: docker
append: yes

- name: Start and enable Docker service
become: true
systemd:
name: docker
state: started
enabled: yes

# Scenario files setup
- name: Create /home/admin/agent directory
ansible.builtin.file:
path: /home/admin/agent
owner: admin
group: admin
mode: a+wx
state: directory

- name: Copy Docker optimization scenario files
become: false
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: "{{ item.mode | default('0644') }}"
with_items:
- { src: ../files/Dockerfile.base, dest: /home/admin/Dockerfile.base }
- { src: ../files/Dockerfile.optimized, dest: /home/admin/Dockerfile.optimized }
- { src: ../files/Gemfile, dest: /home/admin/Gemfile }
- { src: ../files/Gemfile.lock, dest: /home/admin/Gemfile.lock }
- { src: ../files/entrypoint.sh, dest: /home/admin/entrypoint.sh, mode: '0755' }
- { src: ../files/sidekiq_shutdown.rb, dest: /home/admin/sidekiq_shutdown.rb, mode: '0755' }
- { src: ../files/time_builds.sh, dest: /home/admin/time_builds.sh, mode: '0755' }
- { src: ../files/setup_scenario.sh, dest: /home/admin/setup_scenario.sh, mode: '0755' }

- name: Copy check.sh to agent directory
copy:
src: ../files/check.sh
dest: /home/admin/agent/check.sh
mode: '0755'

- name: Run scenario setup script
become: false
shell: /home/admin/setup_scenario.sh
args:
creates: /home/admin/docker-optimization

- name: Copy timing script to scenario directory
become: false
copy:
src: ../files/time_builds.sh
dest: /home/admin/docker-optimization/time_builds.sh
mode: '0755'
49 changes: 49 additions & 0 deletions scenarios/kingston/files/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# .dockerignore for optimized Docker builds
.git
.gitignore
README.md
Dockerfile*
.dockerignore
*.md

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage/

# node_modules
node_modules/

# Test files
test/
spec/
**/*_test.rb
**/*_spec.rb

# Development dependencies
.bundle/vendor
tmp/
log/
public/system/
storage/

# Editor files
.vscode/
.idea/
*.swp
*.swo
*~

# OS files
.DS_Store
Thumbs.db
59 changes: 59 additions & 0 deletions scenarios/kingston/files/Dockerfile.base
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
FROM ubuntu:16.04
ENV RUBY_VERSION=2.3.8
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
apt-get install -y curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev \
sqlite3 \
git \
libxml2-dev \
libxslt1-dev \
libcurl4-openssl-dev \
software-properties-common \
libffi-dev \
tzdata \
# These three are wkhtmltopdf dependencies
wget libfontconfig1 libxrender1 \
libmysqlclient-dev openssl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get install -y nodejs

RUN curl -O https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.8.tar.gz && \
tar -xzvf ruby-2.3.8.tar.gz && \
cd ruby-2.3.8 && \
./configure --disable-install-doc && \
make && \
make install && \
cd / && \
rm -rf /tmp/ruby-2.3.8 ruby-2.3.8.tar.gz

# This thing is used by wicked_pdf to generate pdfs from html
RUN wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.3/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz && \
tar vxf wkhtmltox-0.12.3_linux-generic-amd64.tar.xz && \
cp wkhtmltox/bin/wk* /usr/bin/

COPY . /app

WORKDIR /app

COPY Gemfile Gemfile.lock ./

# nokogiri needs to be installed separately or it will fail
RUN gem install bundler -v 1.17.0 && \
gem install rake -v 13.0.6 --force && \
gem install nokogiri -v '1.9.1'

RUN bundle install && \
bundle config --delete bin && \
rake rails:update:bin && \
bundle binstubs rails

COPY . /app
RUN chmod +x /app/entrypoint.sh /app/sidekiq_shutdown.rb

RUN bundle exec rake RAILS_ENV=production assets:precompile

CMD ["bundle","exec","rails","server","-p","8000"]
Loading