I would like to periodically clean up after Docker, as I use it fairly extensively and have lots of unused images/volumes.
I know about the existence of the docker system prune command, which would almost be what I need. However, I have a few containers that are run on a schedule and exit almost immediately. They are some maintenance scripts that run once per hour/once per day. If I use the 'standard' prune command, they get deleted and have to be created all over again. I also found the until= filter to be useless in this case, since the containers have been created months ago but are used daily.
Is there a way to tell Docker to delete all containers that are Exited by more than X hours? Which for example could delete all containers not used in the last day, for example.
Thanks in advance
I can't think of a way to do it with the prune command. But, how about listing all the exited containers in json format, selecting the ones that exited "day" or "month" ago with jq, and then removing them?
docker container ls -a --filter 'status=exited' --format '{{ json . }}' \
| jq --slurp --raw-output '.[] | select(.Status | contains("day") or contains("month")) | .ID' \
| xargs docker container rm
Note that just removing the exited containers won't cover ones in other non-running states. The possible states are created, restarting, running, removing, paused, exited, and dead.
The best way would be to use a container orchestrator/schedular , instead of doing things like that manually. For example you can use kubernetes cron jobs.
https://kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs/
Related
I need to run more than 70 docker containers at once. Later, these containers need to be stopped.
At the moment I can docker stop all of them with the shell command docker stop $(docker ps -f since=<last docker before>). It works OK, but if there are any containers started after mine, I have a problem as the above code will stop them too.
Is there any way I can close all of running containers with some kind of specific search?
I know there is an docker ps -f label=<some label>, but I just haven't figured out on how to use it yet.
If you're launching many containers at the same time, launch them all with
docker run --label=anyname other-docker-args-of-yours image:tag
And when you want to delete all your containers just do
docker stop $(docker ps -f label=anyname | awk 'NR>1 {print$1}')
where anyname is the label name you provide during the docker run command and
awk 'NR>1 {print$1}' ignores the column header CONTAINER_ID and just prints the values alone.
Edit-1:
I later realized that you can achieve the list of Container_ID without awk as well. I'd consider using the below line.
docker stop `docker ps -qaf label=anyname`
If you want to remove all stoppped containers also, then include a within the options, like instead of -qf use -qaf.
-q to print container IDs alone.
-a for all containers including stopped.
The documentation of docker ps and docker container ls both says "List containers", but does not mention the other command. Is there a difference between those two commands?
The output looks exactly the same:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bbe3d7158eaa flaskmysqldockerized_web "python app.py" 5 hours ago Up 18 seconds 0.0.0.0:8082->5000/tcp flaskmysqldockerized_web_1
4f7d3f0763ad mysql "docker-entrypoint..." 6 hours ago Up 18 seconds 0.0.0.0:3307->3306/tcp flaskmysqldockerized_db_1
There is no difference between docker ps and docker container ls. The new command structure (docker container <subcommand>) was added in Docker 1.13 to provider a more structured user experience when using the command line.
To my knowledge, there has not yet been any official announcement to drop support for the old-style commands (like docker ps and others), although it might be reasonable to assume this might happen at some point in the future.
This is described in a blog post accompanying the release of Docker 1.13:
Docker has grown many features over the past couple years and the Docker CLI now has a lot of commands (40 at the time of writing). Some, like build or run are used a lot, some are more obscure, like pause or history. The many top-level commands clutters help pages and makes tab-completion harder.
In Docker 1.13, we regrouped every command to sit under the logical object it’s interacting with. For example list and startof containers are now subcommands of docker container and history is a subcommand of docker image.
docker container list
docker container start
docker image history
These changes let us clean up the Docker CLI syntax, improve help text and make Docker simpler to use. The old command syntax is still supported, but we encourage everybody to adopt the new syntax.
docker ps is shorthand that stands for "docker process status", whilst docker container ls is shorthand for the more verbose docker container list.
As the accepted answer explains, there is no difference in how they work, and docker container ls is the 'newer' command, so you should probably prefer it.
Both commands actually only show running containers by default, which makes the first one (docker ps) a little more confusing as that command on its own isn't really showing 'process status'. To see the status of all containers, add the -a option for 'all' (or use --all), e.g.
docker container ls -a
older
docker ps -a or docker container ps -a
I currently use docker for my backend, and when I first start them up with
docker-compose up
I get log outputs of all 4 dockers at once, so I can see how they are interacting with each other when a request comes in. Looking like this, one request going from nginx to couchdb
The issue is now that I am running on GCE with load balancing, when a new VM spins up, it auto starts the dockers and runs normally, I would like to be able to access a load balanced VM and view the live logs, but I can not get docker to allow me this style, when I use logs, it gives me normal all white font with no label of where it came from.
Using
docker events
does nothing, it won't return any info.
tldr; what is the best way to obtain a view, same as the log output you get when running "docker-compose up"
If using docker-compose, you use
docker-compose logs --tail=0 --follow
instead of
docker logs --tail=0 --follow
This will get the output I was originally looking for.
You can see the logs for all running containers with
docker ps -q | xargs -L 1 docker logs
In theory this might work for the --follow too if xargs is ran with -P <count>, where the count is higher than the number of running containers.
I use a variation of this to live tail (--follow) all logs and indicate which log is tailing at the time. This bash includes both stdout and stderr. Note you may need to purge the /tmp dir of *.{log,err} afterwards.
for c in $(docker ps -a --format="{{.Names}}")
do
docker logs -f $c > /tmp/$c.log 2> /tmp/$c.err &
done
tail -f /tmp/*.{log,err}
Hope this helps. Logging has become so problematic these days, and other get-off-my-lawn old man rants...
Try "watch"
Here's a quick and dirty multitail/xtail for docker containers.
watch 'docker ps --format "{{.Names}}" | sort | xargs --verbose --max-args=1 -- docker logs --tail=8 --timestamps'
How this works:
watch to run every few seconds
docker ps --format "{{.Names}}" to get the names of all running containers
sort to sort them
xargs to give these names to docker logs:
docker logs to print the actual logs
Adjust parameter "--tail=8" as needed so that everything still fits on one screen.
The "xargs" methods listed above (in another user's answer) will stop working as containers are stopped and restarted. This "watch" method here does not have that problem. (But it's not great either.)
If you are using Docker Swarm, you can find your services by
docker service ls
Grap the id, and then run
docker service logs $ID -f
if the service is defined with tty: true, then you must run with the --raw flag. Notice, this wont tell you which container is giving the outputted log entry.
The docker command has a ps sub-command that emits very long lines:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6e8ec8a16da4 waisbrot/wait:latest "/wait" 4 minutes ago Exited (0) 4 minutes ago wait-for-janus-test
9dbf0739561f whoop/downsampler:master "./run.bash" 4 minutes ago Up 4 minutes 0.0.0.0:32855->4369/tcp, 0.0.0.0:32854->9100/tcp, 0.0.0.0:32853->9101/tcp, 0.0.0.0:32852->9102/tcp, 0.0.0.0:32851->9103/tcp, 0.0.0.0:32850->9104/tcp, 0.0.0.0:32849->9105/tcp, 0.0.0.0:32848->9106/tcp, 0.0.0.0:32847->9107/tcp, 0.0.0.0:32846->9108/tcp, 0.0.0.0:32845->9109/tcp, 0.0.0.0:32844->9110/tcp metrics-downsampler-test
6cf56623bb48 whoop/janus:master "./start.bash" 4 minutes ago Up 4 minutes 0.0.0.0:32843->80/tcp janus-test
882b50303d54 whoop/recalculator:master "./run.bash" 4 minutes ago Exited (1) 4 minutes ago internum-test
It can be instructed to output only specific columns:
docker ps --format "table {{.Image}}\t{{.Names}}\t{{.Ports}}\t{{.Status}}"
I'd like to be able to say docker ps and get the --format "table..." argument added on for me. Is there a nice way to do this?
I know I could say
alias dp='docker ps --format ...'
but I'd prefer to keep the sub-command.
I'm using zsh as my shell.
You can wrap docker in a function that checks for the specific subcommand and passes everything else through. (The below will actually work with not just zsh, but any POSIX-compliant shell -- a category to which zsh doesn't quite belong).
docker() {
case $1 in
ps)
shift
command docker ps --format 'table {{.Image}}\t{{.Names}}\t{{.Ports}}\t{{.Status}}' "$#"
;;
*)
command docker "$#";;
esac
}
If you wanted a more generic wrapper function (that doesn't need to know about your specific desired ps logic), that could be done as follows (note that this version is not compatible with baseline POSIX sh due to its use of local; however, this is an extension implemented even by ash and its derivatives):
docker() {
local cmd=$1; shift
if command -v "docker_$cmd" >/dev/null 2>/dev/null; then
"docker_$cmd" "$#"
else
command docker "$cmd" "$#"
fi
}
...after which any subcommand can have its own functions defined, without the wrapper needing to be modified to know about them (you could also create a script in the PATH named docker_ps, or provide the command in any other manner you choose):
docker_ps() {
command docker ps --format 'table {{.Image}}\t{{.Names}}\t{{.Ports}}\t{{.Status}}' "$#"
}
Using Docker Config
Since this is fundamentally a docker questions, not a bash question, you don't even need an alias. Docker CLI allows you to customize these commands in your own config file! From this great tip from Container 42:
Create or find your docker config file (if you've ever used docker login it should already be created.
~/.docker/config.json
Then add the default formatting for docker to use every time it runs the ps command as a top level property in the config:
{
"psFormat": "table {{.Image}}\t{{.Names}}\t{{.Ports}}\t{{.Status}}",
}
Then just run docker ps like normal:
PS Format
Docker uses go templates and has a list of the valid placeholders:
Command
Description
.ID
Container ID
.Image
Image ID
.Command
Quoted command
.CreatedAt
Time when the container was created.
.RunningFor
Elapsed time since the container was started.
.Ports
Exposed ports.
.Status
Container status.
.Size
Container disk size.
.Names
Container names.
.Labels
All labels assigned to the container.
.Label
Value of a specific label for this container.
.Mounts
Names of the volumes mounted in this container.
.Networks
Names of the networks attached to this container.
Alternative Solutions / Threads
Github Issues
Default "docker ps" output is too wide
docker ps output is so long it's unreadable
Third Party Commands
ctop - Top-like interface for container metrics
docker-pretty-ps - beautiful, colored, long output log
dockerps - A better docker ps
You can alias subcommands. With aliasing, you still get the nice zsh completions as if you were typing the full command. That's why I prefer them over functions.
The equivalent of your alias is:
alias dp='docker ps --format "table {{.Image}}\t{{.Names}}\t{{.Ports}}'\t{{.Status}}"
But the full commands seem to now be recommended, and ls has replaced ps, which makes your alias now:
alias dp='docker container ls --format "table {{.Image}}\t{{.Names}}\t{{.Ports}}'\t{{.Status}}"
It's nice to have docker aliases for everything. For this, I've been working on a set of comprehensive aliases, which would have your alias as something like:
alias ddcls='docker container ls --format "table {{.Image}}\t{{.Names}}\t{{.Ports}}\t{{.Status}}"
In my case, I needed to disable only docker login command (some folks used that command on our CI-runner breaking a generic config file for docker).
So, I added to my .bashrc:
_docker() {
if [ "$1" = "login" ]; then
echo "login is disabled, to login please update a config file manually!"
return
fi
/usr/bin/docker "$#"
}
alias docker="_docker"
What CLI commands do I need to use in order to check if the image in my private docker registry is a newer version than the one currently running on my server?
E.g. I have a container that I ran using docker run -d my.domain.com:5000/project1
and I would like to know if it is out-of-date.
Brownie points to #mbarthelemy and #amuino who put me on track. From that I was able to come up with the following bash script that others may find useful. It just checks if the tag on the registry is different from the currently executing container.
#!/bin/bash
# ensure running bash
if ! [ -n "$BASH_VERSION" ];then
echo "this is not bash, calling self with bash....";
SCRIPT=$(readlink -f "$0")
/bin/bash $SCRIPT
exit;
fi
REGISTRY="my.registry.com:5000"
REPOSITORY="awesome-project-of-awesomeness"
LATEST="`wget -qO- http://$REGISTRY/v1/repositories/$REPOSITORY/tags`"
LATEST=`echo $LATEST | sed "s/{//g" | sed "s/}//g" | sed "s/\"//g" | cut -d ' ' -f2`
RUNNING=`docker inspect "$REGISTRY/$REPOSITORY" | grep Id | sed "s/\"//g" | sed "s/,//g" | tr -s ' ' | cut -d ' ' -f3`
if [ "$RUNNING" == "$LATEST" ];then
echo "same, do nothing"
else
echo "update!"
echo "$RUNNING != $LATEST"
fi
Even when there is no command, you can use the API to check for tags on the registry and compare against what you are running.
$ curl --silent my.domain.com:5000/v1/repositories//project1/tags | grep latest
{"latest": "116f283e4f19716a07bbf48a562588d58ec107fe6e9af979a5b1ceac299c4370"}
$ docker images --no-trunc my.domain.com:5000/project1
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
my.domain.com:5000 latest 64d935ffade6ed1cca3de1b484549d4d278a5ca62b31165e36e72c3e6ab8a30f 4 days ago 583.2 MB
By comparing the ids, you can know that you are not running the latest version.
Not sure about the version but if you mean the tag of image, it can be easily checked through the registry v2 api . Note that in context of docker images tag has nothing to do with the version of software.
Use curl command in CLI
curl <docker_host_ip>:<docker_host_port>/v2/<repository_name>/tags/list
To get a list of repositories pushed on the private registry, use
curl <docker_host_ip>:<docker_host_port>/v2/_catalog
AFAIK, this is not possible right now.
The only thing I see would be to pull the registry to check if there is a new version of your image (would then have a different ID than your locally stored image):
docker pull your/image:tag
But yes, that would mean fetching the new images (if any).
If you have a look at the registry API documentation, you'll see that if you don't mind scripting a bit, you could get this information without actually downloading the image, by fetching the image tags and check if the returned ID for the tag matches the ID of the local image you have with the same tag.
That being said, having something to "check for updates" integrated into the docker CLI would be a nice addition.
I don't know if this works as advertised. Just a quick hack I just put together.
But this will at least give you a little push on how this might be done.
#!/bin/bash
container=$1
imageid=$(docker inspect --format '{{.Config.Image}}' ${container})
echo "Running version from: $(docker inspect --format '{{.Created}}' ${container})"
echo "Image version from: $(docker inspect --format '{{.Created}}' ${imageid})"
Example output:
[root#server ~]# sh version_check.sh 9e500019b9d4
Running version from: 2014-05-30T08:24:08.761178656Z
Image version from: 2014-05-01T16:48:24.163628504Z
You can use a bash script running in a cron scheduled task:
#!/bin/bash
docker_instance='YOUR_RUNNING_INSTANCE'
instance_id=$(docker ps -qa --filter name=$docker_instance)
image_name_tag=$(docker inspect $instance_id | jq -r [] |.Config.Image')
if [ "-${image_name_tag}-" != "--" ]; then
status=$(docker pull $image_name_tag | grep "Downloaded newer image")
if [ "-${status}-" != "--" ]; then
echo ">>> There is one update for this image ... "
# stop the docker instance
docker stop $docker_instance
# remove the docker instance
docker rm $docker_instance
# restart the docker using the last command, using the new image from the remote repository
run-my-docker-instance.sh
fi
fi
An older question, but this sounds like a problem that Watchtower can solve for you. It is another dockerized application that runs adjacent to your other containers and periodically check to see if their base images are updated. When they are, it downloads the new image and restarts them.
If given the correct credentials, it can work with a local registry.
FWIW I solved it with the bash script below for a while until I decided that Watchtower was the easier way to go (by the way: note the maintainer switched from v2tec to containrrr a while ago, the v2tec one isn't getting updates anymore). Watchtower gave me an easy way to schedule things without having to rely on cron (which gets blown away in a reinstall - granted, you could have something like Ansible recreate that for you, but this was easier for me). It also adds easy notifications (I like using Telegram) for updates, which I appreciate knowing about so that if something goes sideways at least I know there was an update that could be to blame.
I'm not saying this is will never cause issues, but I've been running Watchtower on various Docker hosts (3 of them, 2 in my homelab, one on Linode) for about a year now and I have yet to have an issue with it. I prefer this to having to manually update my containers on a regular basis. For me the risk of something getting screwed up is lower than the risks of running outdated containers, so this is what I chose for myself. YMMV.
I honestly don't get the apparent hate for automated update solutions like Watchtower - I see so many comments saying that you shouldn't use automated updates because they'll cause problems... I don't know what folks have been burned by - would love to hear more about where this caused problems for you! I mean that, I genuinely don't understand and would love to learn more. I keep having some vague unease about doing automated updates, but given my experience so far I can honestly only recommend it. I used to use Diun for getting notified about updates and then would go and manually update my containers. That got real old after it became a daily chore! (With ~45 different containers running, you can pretty much guarantee that at least one of them will have an update every single day.)
If I really need to keep a container from updating, I can always stick a com.centurylinklabs.watchtower.enable=false label on the container. Or you can whitelist only the containers you want automatically updated, or... There are loads of possibilities with Watchtower.
However, for reference if you still want to use it, see my script below. I used docker-compose pull to get the latest version - it does a check first to see if there is a new image, so doesn't waste a whole lot of bandwidth if there is nothing to update. It's effectively like doing the curl you guys used. Also I prefer the docker inspect -f commands to check the versions to the solutions that pipe through grep, sed, and co. since that is less likely to get broken by changes to docker inspect output format.
#!/usr/bin/env bash
cd /directory/with/docker-compose.yml/
image_name=your-awesome-image
docker-compose pull
container_version=$(docker inspect -f '{{ index .Config.Labels "org.opencontainers.image.version" }}' "$image_name")
latest_image_version=$(docker inspect -f '{{ index .Config.Labels "org.opencontainers.image.version" }}' "$image_name")
if [[ "$container_version" != "$latest_image_version" ]]; then
echo "Upgrading ${image_name} from ${container_version} to ${latest_image_version}"
docker-compose down
docker-compose up -d
fi