/
build_app
executable file
·154 lines (132 loc) · 6.26 KB
/
build_app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/bin/bash
# Tested with Docker version 18.06.0-ce, build 0ffa825
set -e
APP_NAME="demoapp"
DOCKERFILE="docker/Dockerfile.app"
DOCKER_CONTEXT="react-webpack-rails-tutorial"
RESET_BUILD="$1"
# If you are working with a team, make sure you pull the latest image first:
# docker pull demoapp/app:latest
# Reset the base image after this many rsync diff layers
MAX_DIFF_LAYERS_COUNT=30
# Reset the base image when rsync diff layers are more than x% of the base layer
MAX_DIFF_SIZE_PERCENTAGE=80
echo "Fetching info about previous docker builds..."
# Build base images first
if [ -z "$(docker images -q $APP_NAME/ruby-node 2> /dev/null)" ]; then
docker build -t "$APP_NAME/ruby-node" -f docker/Dockerfile.ruby-node .
fi
if [ -z "$(docker images -q $APP_NAME/base 2> /dev/null)" ]; then
docker build -t "$APP_NAME/base" -f docker/Dockerfile.base .
fi
LATEST_BUILD_ID="$(docker images -q $APP_NAME/app:latest 2> /dev/null)"
BASE_ASSETS_BUILD_ID="$(docker images -q $APP_NAME/app:base-assets-build 2> /dev/null)"
if [ -z "$BASE_ASSETS_BUILD_ID" ] || [ -z "$LATEST_BUILD_ID" ]; then
echo "No previous builds, starting from $APP_NAME/base..."
# If there is no previous build, use the base image
docker tag "$APP_NAME/base" "$APP_NAME/app:base-webpack-build"
docker tag "$APP_NAME/base" "$APP_NAME/app:base-assets-build"
docker tag "$APP_NAME/base" "$APP_NAME/app:latest-assets-build"
INITIAL_BUILD=true
fi
# Dummy secret key for assets task
SECRET_KEY_BASE=231bf79489c63f8c8facd7bf27db1c25\
82a42a7f4302fccdb74ef35bc5dc91fb4e19dbf167f3003b\
db4073818dfab4a9916890d193d535a7be458dbef1609800
# Build i18n for React app. This could be done in the Docker image
# but it just makes everything more complicated. We get much better
# caching when the webpack build doesn't depend on bundle install.
# ----------------------------------------------------------------------------------------
# NOTE: For demo purposes, I've commented out this step and committed the generated files.
# ----------------------------------------------------------------------------------------
# echo "Running rake react_on_rails:locale locally to prepare i18n for webpack..."
# (cd "$DOCKER_CONTEXT" && rake react_on_rails:locale)
docker build \
--target webpack \
-t "$APP_NAME/app:latest-webpack-build" \
-f "$DOCKERFILE" \
"$DOCKER_CONTEXT"
docker build \
--target assets \
-t "$APP_NAME/app:latest-assets-build" \
-f "$DOCKERFILE" \
--build-arg "SECRET_KEY_BASE=${SECRET_KEY_BASE}" \
"$DOCKER_CONTEXT"
# Reset to the gems stage, copy in /app, remove unneeded files, squash layers
docker build \
-t "$APP_NAME/app:current" \
-f "$DOCKERFILE" \
--build-arg "SECRET_KEY_BASE=${SECRET_KEY_BASE}" \
--squash \
"$DOCKER_CONTEXT"
if [ -z "$INITIAL_BUILD" ] && [ -z "$RESET_BUILD" ]; then
IMAGE_LAYERS="$(docker history --human=false $APP_NAME/app:latest \
--format="{{.Size}} {{.CreatedBy}} {{.Comment}}" 2>/dev/null || true)"
DIFF_LAYERS_COUNT="$(echo "$IMAGE_LAYERS" | grep -c "rsync --recursive" || true)"
DIFF_LAYERS_SIZE="$(
echo "$IMAGE_LAYERS" | grep "rsync --recursive" |
awk 'BEGIN{sum=0} {sum+=$1} END {print sum}'
)"
BASE_LAYER_SIZE="$(
echo "$IMAGE_LAYERS" | grep "merge sha256" |
awk 'BEGIN{sum=0} {sum+=$1} END {print sum}'
)"
DIFF_PERCENTAGE=0
if [ "$BASE_LAYER_SIZE" -gt 0 ]; then
DIFF_PERCENTAGE=$((100 * DIFF_LAYERS_SIZE / BASE_LAYER_SIZE))
fi
echo "Found $DIFF_LAYERS_COUNT rsync diff layers in latest image"
echo "=> Diff layers size: $(echo "$DIFF_LAYERS_SIZE" | numfmt --to=iec)B"
echo "=> Base layer size: $(echo "$BASE_LAYER_SIZE" | numfmt --to=iec)B"
echo "=> Diff layers / Base layer: $DIFF_PERCENTAGE%"
if [ "$DIFF_LAYERS_COUNT" -ge "$MAX_DIFF_LAYERS_COUNT" ]; then
OVER_MAX_DIFF_LAYERS="true"
fi
if [ "$DIFF_PERCENTAGE" -gt "$MAX_DIFF_SIZE_PERCENTAGE" ]; then
OVER_MAX_PERCENTAGE="true"
fi
fi
if [ -n "$INITIAL_BUILD" ] || [ -n "$RESET_BUILD" ] ||
[ "$OVER_MAX_DIFF_LAYERS" = "true" ] || [ "$OVER_MAX_PERCENTAGE" = "true" ]
then
if [ "$INITIAL_BUILD" ]; then
echo "Tagging $APP_NAME/app:base-webpack-build and :base-assets-build..."
docker tag "$APP_NAME/app:latest-webpack-build" "$APP_NAME/app:base-webpack-build"
docker tag "$APP_NAME/app:latest-assets-build" "$APP_NAME/app:base-assets-build"
elif [ -n "$RESET_BUILD" ]; then
echo "Clearing all diff layers, setting this build as the latest image..."
elif [ "$OVER_MAX_DIFF_LAYERS" = "true" ]; then
echo "Reached the maximum number of rsync diff layers ($MAX_DIFF_LAYERS_COUNT)"
echo "Resetting base image..."
else
echo "Diff layers add up to more than $MAX_DIFF_SIZE_PERCENTAGE% of the base layer"
echo "Resetting base image..."
fi
docker tag "$APP_NAME/app:current" "$APP_NAME/app:latest"
else
echo "Creating a diff layer between :latest and :current..."
echo "=> To reset the base image and remove all diff layers, run:"
echo " docker tag $APP_NAME/app:current $APP_NAME/app:latest"
# Remove existing volume (if exists)
docker container rm "$APP_NAME-current-volume" > /dev/null 2>&1 || true
# Create a new container from :current with /app as a volume
docker run --name="$APP_NAME-current-volume" \
--volume /app "$APP_NAME/app:current" true
# Find the host directory for the /app volume
BUILD_VOLUME_DIR="$(docker inspect "$APP_NAME-current-volume" \
-f '{{ (index .Mounts 0).Source }}')"
# Use rsync to add/remove changed files in the diff layer container
# Adds another rsync layer to the latest image
docker container rm "$APP_NAME-diff-layer" > /dev/null 2>&1 || true
docker run --name="$APP_NAME-diff-layer" -it \
--volume "$BUILD_VOLUME_DIR:/app-current" \
"$APP_NAME/app:latest" \
rsync --recursive --links --progress --checksum --delete --itemize-changes \
--exclude log \
/app-current/ /app/
# Commit the updated files as a new layer
docker commit "$APP_NAME-diff-layer" "$APP_NAME/app:latest"
DIFF_LAYER_SIZE="$(docker history "$APP_NAME/app:latest" --format="{{.Size}}" | head -n1)"
echo "====> New diff layer size: $DIFF_LAYER_SIZE"
fi
echo "Finished building $APP_NAME/app:latest"