Skip to content

Commit af90321

Browse files
committed
fix: eliminate duplicate Docker layers to reduce image size
- Move user creation before COPY commands - Use COPY --chown to set ownership in single layer - Remove redundant chown operations that duplicated layers - Add analyze-layers.sh script to detect layer redundancy - This should reduce image size by ~30-40% (removes duplicate /opt/openspp layer)
1 parent c27fcad commit af90321

File tree

3 files changed

+130
-14
lines changed

3 files changed

+130
-14
lines changed

Dockerfile

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,6 @@ ENV LANG=en_US.UTF-8 \
128128
LANGUAGE=en_US:en \
129129
LC_ALL=en_US.UTF-8
130130

131-
# Copy OpenSPP installation from installer stage
132-
COPY --from=installer /opt/openspp /opt/openspp
133-
COPY --from=installer /usr/bin/openspp-* /usr/bin/
134-
COPY --from=installer /etc/openspp /etc/openspp
135-
136131
# Create openspp user and group with consistent IDs
137132
# Check if GID 1000 exists, if so use a different one (9999)
138133
RUN (groupadd -r -g 1000 openspp 2>/dev/null || groupadd -r openspp) && \
@@ -141,6 +136,11 @@ RUN (groupadd -r -g 1000 openspp 2>/dev/null || groupadd -r openspp) && \
141136
-s /bin/bash \
142137
-m openspp
143138

139+
# Copy OpenSPP installation from installer stage with correct ownership
140+
COPY --chown=openspp:openspp --from=installer /opt/openspp /opt/openspp
141+
COPY --from=installer /usr/bin/openspp-* /usr/bin/
142+
COPY --chown=openspp:openspp --from=installer /etc/openspp /etc/openspp
143+
144144
# Create necessary directories with proper permissions
145145
RUN mkdir -p \
146146
/var/lib/openspp \
@@ -151,8 +151,7 @@ RUN mkdir -p \
151151
/var/lib/openspp \
152152
/var/log/openspp \
153153
/mnt/extra-addons \
154-
/opt/openspp \
155-
/etc/openspp
154+
/opt/openspp/data
156155

157156
# Copy configuration and entrypoint scripts
158157
COPY --chown=openspp:openspp config/odoo.conf /etc/openspp/odoo.conf

Dockerfile.slim

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,6 @@ ENV LANG=en_US.UTF-8 \
112112
LANGUAGE=en_US:en \
113113
LC_ALL=en_US.UTF-8
114114

115-
# Copy OpenSPP installation from installer stage
116-
COPY --from=installer /opt/openspp /opt/openspp
117-
COPY --from=installer /usr/bin/openspp-* /usr/bin/
118-
COPY --from=installer /etc/openspp /etc/openspp
119-
120115
# Create openspp user and group with consistent IDs
121116
# Check if GID 1000 exists, if so use a different one (9999)
122117
RUN (groupadd -r -g 1000 openspp 2>/dev/null || groupadd -r openspp) && \
@@ -125,6 +120,11 @@ RUN (groupadd -r -g 1000 openspp 2>/dev/null || groupadd -r openspp) && \
125120
-s /bin/bash \
126121
-m openspp
127122

123+
# Copy OpenSPP installation from installer stage with correct ownership
124+
COPY --chown=openspp:openspp --from=installer /opt/openspp /opt/openspp
125+
COPY --from=installer /usr/bin/openspp-* /usr/bin/
126+
COPY --chown=openspp:openspp --from=installer /etc/openspp /etc/openspp
127+
128128
# Create necessary directories with proper permissions
129129
RUN mkdir -p \
130130
/var/lib/openspp \
@@ -135,8 +135,7 @@ RUN mkdir -p \
135135
/var/lib/openspp \
136136
/var/log/openspp \
137137
/mnt/extra-addons \
138-
/opt/openspp \
139-
/etc/openspp
138+
/opt/openspp/data
140139

141140
# Copy configuration and entrypoint scripts
142141
COPY --chown=openspp:openspp config/odoo.conf /etc/openspp/odoo.conf

analyze-layers.sh

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/bin/bash
2+
# ABOUTME: Script to analyze Docker image layers and find redundancy
3+
# ABOUTME: Helps identify duplicate layers and optimization opportunities
4+
5+
set -e
6+
7+
# Colors
8+
RED='\033[0;31m'
9+
GREEN='\033[0;32m'
10+
YELLOW='\033[1;33m'
11+
BLUE='\033[0;34m'
12+
NC='\033[0m'
13+
14+
# Default image
15+
IMAGE="${1:-openspp:test}"
16+
17+
echo -e "${BLUE}Docker Layer Analysis for: $IMAGE${NC}"
18+
echo "================================================"
19+
20+
# Check if image exists
21+
if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then
22+
echo -e "${RED}Error: Image '$IMAGE' not found${NC}"
23+
echo "Usage: $0 [image-name]"
24+
exit 1
25+
fi
26+
27+
# 1. Show layer sizes
28+
echo -e "\n${YELLOW}Layer Sizes:${NC}"
29+
docker history "$IMAGE" --format "table {{.Size}}\t{{.CreatedBy}}" | head -20
30+
31+
# 2. Find duplicate sizes
32+
echo -e "\n${YELLOW}Checking for duplicate layer sizes:${NC}"
33+
docker history "$IMAGE" --format "{{.Size}}" | \
34+
grep -v "<missing>" | \
35+
sort | uniq -d | while read size; do
36+
if [ ! -z "$size" ] && [ "$size" != "0B" ]; then
37+
echo -e "${RED}Found duplicate size: $size${NC}"
38+
echo "Layers with this size:"
39+
docker history "$IMAGE" --format "{{.Size}}\t{{.CreatedBy}}" | grep "^$size"
40+
fi
41+
done
42+
43+
# 3. Calculate total image size
44+
echo -e "\n${YELLOW}Image Size Analysis:${NC}"
45+
SIZE=$(docker image inspect "$IMAGE" --format='{{.Size}}')
46+
SIZE_MB=$((SIZE / 1024 / 1024))
47+
echo "Total image size: ${SIZE_MB}MB"
48+
49+
# 4. Show largest layers
50+
echo -e "\n${YELLOW}Top 5 Largest Layers:${NC}"
51+
docker history "$IMAGE" --format "table {{.Size}}\t{{.CreatedBy}}" | \
52+
grep -v SIZE | \
53+
sort -hr | \
54+
head -5
55+
56+
# 5. Check for common anti-patterns
57+
echo -e "\n${YELLOW}Checking for common anti-patterns:${NC}"
58+
59+
# Check for chown after COPY
60+
if docker history "$IMAGE" --no-trunc | grep -q "COPY.*from=.*installer" && \
61+
docker history "$IMAGE" --no-trunc | grep -q "chown.*openspp"; then
62+
echo -e "${RED}⚠ Warning: Found COPY followed by chown - consider using COPY --chown${NC}"
63+
fi
64+
65+
# Check for multiple apt-get updates
66+
APT_UPDATES=$(docker history "$IMAGE" --no-trunc | grep -c "apt-get update" || true)
67+
if [ "$APT_UPDATES" -gt 2 ]; then
68+
echo -e "${YELLOW}⚠ Found $APT_UPDATES apt-get update commands - consider combining${NC}"
69+
fi
70+
71+
# Check for rm commands that might be ineffective
72+
if docker history "$IMAGE" --no-trunc | grep -q "rm -rf /var/lib/apt/lists"; then
73+
echo -e "${GREEN}✓ Good: Cleaning apt cache${NC}"
74+
fi
75+
76+
# 6. Suggest dive tool if not installed
77+
if ! command -v dive &> /dev/null; then
78+
echo -e "\n${BLUE}For detailed layer analysis, install dive:${NC}"
79+
echo " brew install dive # macOS"
80+
echo " wget https://github.com/wagoodman/dive/releases/latest/download/dive_*_linux_amd64.deb"
81+
echo " sudo dpkg -i dive_*.deb # Ubuntu/Debian"
82+
echo ""
83+
echo "Then run: dive $IMAGE"
84+
else
85+
echo -e "\n${GREEN}Dive is installed. Run 'dive $IMAGE' for interactive analysis${NC}"
86+
fi
87+
88+
# 7. Export detailed analysis
89+
echo -e "\n${YELLOW}Exporting detailed analysis...${NC}"
90+
REPORT_FILE="layer-analysis-$(date +%Y%m%d-%H%M%S).txt"
91+
{
92+
echo "Docker Layer Analysis Report"
93+
echo "Image: $IMAGE"
94+
echo "Date: $(date)"
95+
echo "================================"
96+
echo ""
97+
echo "Full Layer History:"
98+
docker history --no-trunc "$IMAGE"
99+
echo ""
100+
echo "Layer SHA256 Hashes:"
101+
docker inspect "$IMAGE" | jq '.[0].RootFS.Layers'
102+
} > "$REPORT_FILE"
103+
104+
echo -e "${GREEN}Detailed report saved to: $REPORT_FILE${NC}"
105+
106+
# Summary
107+
echo -e "\n${BLUE}Summary:${NC}"
108+
LAYERS=$(docker history "$IMAGE" | wc -l)
109+
echo "- Total layers: $LAYERS"
110+
echo "- Image size: ${SIZE_MB}MB"
111+
112+
# Optimization suggestions
113+
echo -e "\n${BLUE}Optimization Tips:${NC}"
114+
echo "1. Use COPY --chown instead of separate chown commands"
115+
echo "2. Combine multiple RUN commands with && to reduce layers"
116+
echo "3. Order commands from least to most frequently changing"
117+
echo "4. Clean package manager cache in the same RUN command"
118+
echo "5. Use multi-stage builds to exclude build dependencies"

0 commit comments

Comments
 (0)