Commit | Line | Data |
---|---|---|
29a35d1b S |
1 | #!/bin/bash |
2 | ||
3 | # Copyright (c) 2014 Spotify AB. | |
4 | # | |
5 | # Licensed to the Apache Software Foundation (ASF) under one | |
6 | # or more contributor license agreements. See the NOTICE file | |
7 | # distributed with this work for additional information | |
8 | # regarding copyright ownership. The ASF licenses this file | |
9 | # to you under the Apache License, Version 2.0 (the | |
10 | # "License"); you may not use this file except in compliance | |
11 | # with the License. You may obtain a copy of the License at | |
12 | # | |
13 | # http://www.apache.org/licenses/LICENSE-2.0 | |
14 | # | |
15 | # Unless required by applicable law or agreed to in writing, | |
16 | # software distributed under the License is distributed on an | |
17 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
18 | # KIND, either express or implied. See the License for the | |
19 | # specific language governing permissions and limitations | |
20 | # under the License. | |
21 | ||
22 | # This script attempts to garbage collect docker containers and images. | |
23 | # Containers that exited more than an hour ago are removed. | |
24 | # Images that have existed more than an hour and are not in use by any | |
25 | # containers are removed. | |
26 | ||
27 | # Note: Although docker normally prevents removal of images that are in use by | |
28 | # containers, we take extra care to not remove any image tags (e.g. | |
29 | # ubuntu:14.04, busybox, etc) that are used by containers. A naive | |
30 | # "docker rmi `docker images -q`" will leave images stripped of all tags, | |
31 | # forcing users to re-pull the repositories even though the images | |
32 | # themselves are still on disk. | |
33 | ||
34 | # Note: State is stored in $STATE_DIR, defaulting to /var/lib/docker-gc | |
35 | ||
36 | set -o nounset | |
37 | set -o errexit | |
38 | ||
39 | GRACE_PERIOD_SECONDS=${GRACE_PERIOD_SECONDS:=3600} | |
40 | STATE_DIR=${STATE_DIR:=/var/lib/docker-gc} | |
41 | DOCKER=${DOCKER:=docker} | |
42 | EXCLUDE_FROM_GC=${EXCLUDE_FROM_GC:=/etc/docker-gc-exclude} | |
43 | if [ ! -f "$EXCLUDE_FROM_GC" ] | |
44 | then | |
45 | EXCLUDE_FROM_GC=/dev/null | |
46 | fi | |
47 | ||
48 | EXCLUDE_IDS_FILE="exclude_ids" | |
49 | ||
50 | function date_parse { | |
51 | if date --utc >/dev/null 2>&1; then | |
52 | # GNU/date | |
53 | echo $(date -u --date "${1}" "+%s") | |
54 | else | |
55 | # BSD/date | |
56 | echo $(date -j -u -f "%F %T" "${1}" "+%s") | |
57 | fi | |
58 | } | |
59 | ||
60 | ||
61 | # Elapsed time since a docker timestamp, in seconds | |
62 | function elapsed_time() { | |
63 | # Docker 1.5.0 datetime format is 2015-07-03T02:39:00.390284991 | |
64 | # Docker 1.7.0 datetime format is 2015-07-03 02:39:00.390284991 +0000 UTC | |
65 | utcnow=$(date -u "+%s") | |
66 | without_ms="${1:0:19}" | |
67 | replace_t="${without_ms/T/ }" | |
68 | epoch=$(date_parse "${replace_t}") | |
69 | echo $(($utcnow - $epoch)) | |
70 | } | |
71 | ||
72 | function compute_exclude_ids() { | |
73 | # Find images that match patterns in the EXCLUDE_FROM_GC file and put their | |
74 | # id prefixes into $EXCLUDE_IDS_FILE, prefixed with ^ | |
75 | ||
76 | PROCESSED_EXCLUDES="processed_excludes.tmp" | |
77 | # Take each line and put a space at the beginning and end, so when we | |
78 | # grep for them below, it will effectively be: "match either repo:tag | |
79 | # or imageid". Also delete blank lines or lines that only contain | |
80 | # whitespace | |
81 | sed 's/^\(.*\)$/ \1 /' $EXCLUDE_FROM_GC | sed '/^ *$/d' > $PROCESSED_EXCLUDES | |
82 | # The following looks a bit of a mess, but here's what it does: | |
83 | # 1. Get images | |
84 | # 2. Skip header line | |
85 | # 3. Turn columnar display of 'REPO TAG IMAGEID ....' to 'REPO:TAG IMAGEID' | |
86 | # 4. find lines that contain things mentioned in PROCESSED_EXCLUDES | |
87 | # 5. Grab the image id from the line | |
88 | # 6. Prepend ^ to the beginning of each line | |
89 | ||
90 | # What this does is make grep patterns to match image ids mentioned by | |
91 | # either repo:tag or image id for later greppage | |
92 | $DOCKER images \ | |
93 | | tail -n+2 \ | |
94 | | sed 's/^\([^ ]*\) *\([^ ]*\) *\([^ ]*\).*/ \1:\2 \3 /' \ | |
95 | | grep -f $PROCESSED_EXCLUDES 2>/dev/null \ | |
96 | | cut -d' ' -f3 \ | |
97 | | sed 's/^/^/' > $EXCLUDE_IDS_FILE | |
98 | } | |
99 | ||
100 | # Change into the state directory (and create it if it doesn't exist) | |
101 | if [ ! -d "$STATE_DIR" ] | |
102 | then | |
103 | mkdir -p $STATE_DIR | |
104 | fi | |
105 | cd "$STATE_DIR" | |
106 | ||
107 | # Verify that docker is reachable | |
108 | $DOCKER version 1>/dev/null | |
109 | ||
110 | # List all currently existing containers | |
111 | $DOCKER ps -a -q --no-trunc | sort | uniq > containers.all | |
112 | ||
113 | # List running containers | |
114 | $DOCKER ps -q --no-trunc | sort | uniq > containers.running | |
115 | ||
116 | # compute ids of containers to exclude from GC | |
117 | compute_exclude_ids | |
118 | ||
119 | # List containers that are not running | |
120 | comm -23 containers.all containers.running > containers.exited | |
121 | ||
122 | # Find exited containers that finished at least GRACE_PERIOD_SECONDS ago | |
123 | echo -n "" > containers.reap.tmp | |
124 | cat containers.exited | while read line | |
125 | do | |
126 | EXITED=$(${DOCKER} inspect -f "{{json .State.FinishedAt}}" ${line}) | |
127 | ELAPSED=$(elapsed_time $EXITED) | |
128 | if [[ $ELAPSED -gt $GRACE_PERIOD_SECONDS ]]; then | |
129 | echo $line >> containers.reap.tmp | |
130 | fi | |
131 | done | |
132 | cat containers.reap.tmp | sort | uniq > containers.reap | |
133 | ||
134 | # List containers that we will keep. | |
135 | comm -23 containers.all containers.reap > containers.keep | |
136 | ||
137 | # List images used by containers that we keep. | |
138 | # This may be both image id's and repo/name:tag, so normalize to image id's only | |
139 | cat containers.keep | | |
140 | xargs -n 1 $DOCKER inspect -f '{{.Config.Image}}' 2>/dev/null | | |
141 | sort | uniq | | |
142 | xargs -n 1 $DOCKER inspect -f '{{.Id}}' 2>/dev/null | | |
143 | sort | uniq > images.used | |
144 | ||
145 | # List images to reap; images that existed last run and are not in use. | |
146 | $DOCKER images -q --no-trunc | sort | uniq > images.all | |
147 | comm -23 images.all images.used | grep -v -f $EXCLUDE_IDS_FILE > images.reap || true | |
148 | ||
149 | # Reap containers. | |
150 | xargs -n 1 $DOCKER rm --volumes=true < containers.reap &>/dev/null || true | |
151 | ||
152 | # Reap images. | |
153 | xargs -n 1 $DOCKER rmi < images.reap &>/dev/null || true |