11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

さくらインターネットAdvent Calendar 2021

Day 1

Docker CLIで「コマンド置換」を活用した一括操作

Last updated at Posted at 2021-11-30

概要

シェル上で docker psdocker kill コマンドの実行にあたり、 $(コマンド) という書き方(コマンド置換)を使うと、操作が簡単になる場合があります。たとえば、複数のコンテナをまとめて停止するためには、 docker ps を実行後に docker kill 35e 1f5 ... を実行する手順が必要ですが、これを docker kill $(docker ps -q) だけで済ませます。

前提条件

  • Linux や macOS 上のシェル環境で利用できます。
  • Windows は PowerShell や WSL でのみ。コマンドプロンプトでは利用できません。

コマンド置換とは

あるコマンドを実行する時、そのコマンドの中で $(コマンド) の書き方をすると、それを別のコマンド出力に置き換えられる書き方が、コマンド置換(command substitution)です。

たとえば ls コマンドを実行すると

$ ls
file1  file2

このようにファイル一覧が出ます。

これを、コマンド置換を使えば、次のように書き換えできます。

$ echo $(ls)
file1 file2

ポイントは $(ls) の記述とは、 ls コマンドの表示結果( file1 file2 )を $(ls) 文字で置き換えられ、結果的に echo file1 file2 を実行したのと同じ結果となります。

$ echo file1 file2
file1 file2

なお、 `コマンド` のようにバッククォート文字で囲む書き方もあります(例: `docker ps -q`)。ただし、こちらはコマンド部分に ` を含められないレガシー(旧来形式)の書き方です。

Docker でコマンド置換を活用

コマンド置換を docker コマンドで使うと、以下のような作業が簡単になります。

  • 直前に作成した docker コンテナを直ちに操作する
  • 実行中の Docker コンテナをすべて停止する
  • Docker コンテナやイメージを全削除する

また、CI 環境などで、シェルスクリプトを利用した処理の自動化にも、このコマンド置換が利用できます。

直前に作成した Docker コンテナを直ちに操作する

Docker コンテナの起動直後、対象のコンテナに docker logs でログ(標準出力やエラー)を見たいときや、docker exec を使ったり、 docker top で実行中のプロセスを表示したい場合があります。

通常であれば、2つのステップが必要です。

  1. 何らかの方法で、コンテナ ID またはコンテナ名を知る
  2. docker コマンドで、コンテナ ID かコンテナ名で、対象のコンテナを操作する

たとえば、 docker logs コマンドでログを見るには docker logs interesting_sanderson のようにコンテナ名(この例では interesting_sanderson)、あるいは、コンテナIDを指定する必要があります。つまり、コンテナの操作には、何らかの方法で事前にコンテナ ID かコンテナ名を知っておかなくてはいけません。

ちなみに、この課題を簡単に解決するのは、 docker run --name コンテナ名 を使い、コンテナに予め名前を与える方法です。1つの Docker ホスト上で、コンテナ名を重複して実行できません。そうしておけば、シェルスクリプト上でも、間違いなくコマンドが実行できます。しかし、この方法では、同じコンテナ名で同時に処理が出来ないため、意図せず自動実行できない場合や、それを回避するための工夫が必要になる場合も考えられます。

ですが、このような方法を使わなくても、コマンド置換を使えば、直前に操作したコンテナIDを直ちに知れます。

例として、Nginx コンテナをデタッチドモード(バックグラウンド)で実行するケースを取り上げます。

$ docker run -itdP nginx
0c28d6afc0f9ba9a1f73ae7769493c437bba8583fb2c769df6ba2cb4c2202381
$ 

デタッチドモードであれば、コマンド実行直後に 0c28d.... で始まるコンテナ ID が表示されています。

通常、ここでログを見ようとすると docker logs 0c28 のように入力しますが、コマンド置換を使えば docker logs $(docker ps -lq) という決まり切ったコマンドで表示できます。

$ docker logs $(docker ps -lq)
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2021/11/30 22:36:25 [notice] 1#1: using the "epoll" event method
(省略)

ここで何をしているかを知るには、 $(コマンド) にあたる docker ps -lq の理解が欠かせません。

まず、 docker ps -l コマンドは docker ps --lastest の省略系で、 -l オプションは、直近に作成したコンテナ(状態が、起動・停止かに拘わらず)を表示します。

$  docker ps -l
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                     NAMES
0c28d6afc0f9   nginx     "/docker-entrypoint.…"   6 minutes ago   Up 6 minutes   0.0.0.0:49158->80/tcp, :::49158->80/tcp   keen_stonebraker

そして docker ps -l -qdocker ps --lastest --quiet の省略系で、 -q オプションはコンテナ ID しか表示しません。

$ docker ps -l -q
0c28d6afc0f9

つまり、 docker logs $(docker ps -lq) の実行とは、この例における docker logs 「コマンドdocker ps -lqの実行結果」 であり、 $( ) で囲まれた部分は実行した出力に置換されますから、結果的に docker logs 0c28d6afc0f9 を実行するのと、同じ処理を意味します。

この方法を使えば、直前に操作したコンテナ ID が分からなくても、手動でコマンドを実行して確認しなくても操作できます。さらに、シェルスクリプトなど、自動的にコンテナ操作をしたい場合にも有用です。

実行中の Docker コンテナをすべて停止する

次のように、複数の Docker コンテナが起動している状態があるとします。

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                     NAMES
0c28d6afc0f9   nginx     "/docker-entrypoint.…"   12 minutes ago   Up 12 minutes   0.0.0.0:49158->80/tcp, :::49158->80/tcp   keen_stonebraker
35ecc5c7355b   nginx     "/docker-entrypoint.…"   3 months ago     Up 3 months     0.0.0.0:49157->80/tcp, :::49157->80/tcp   inspiring_merkle
e6b88055e48c   nginx     "/docker-entrypoint.…"   3 months ago     Up 3 months     0.0.0.0:49156->80/tcp, :::49156->80/tcp   quirky_mirzakhani
1f51ba1a4dfd   nginx     "/docker-entrypoint.…"   3 months ago     Up 3 months     0.0.0.0:49155->80/tcp, :::49155->80/tcp   determined_shaw
703b701ae8d3   nginx     "/docker-entrypoint.…"   3 months ago     Up 3 months     0.0.0.0:49154->80/tcp, :::49154->80/tcp   interesting_sanderson
7f5d7a4bd06e   nginx     "/docker-entrypoint.…"   3 months ago     Up 3 months     0.0.0.0:49153->80/tcp, :::49153->80/tcp   youthful_ramanujan

ここでは6つの Nginx コンテナが起動中です。これらをまとめて停止(kill)する場合を考えましょう。通常であれば、次の手順が必要です。

  1. docker ps でコンテナ一覧を表示する(既に実行済み)
  2. docker kill で、停止したいコンテナのIDを指定する

この例であれば docker kill 0c2 35e e6b 1f5 715 と入力する必要があります。

ですが、これもコマンド置換であれば、次のコマンドで済みます。

$ docker kill $(docker ps -q)
0c28d6afc0f9
e6b88055e48c
1f51ba1a4dfd
703b701ae8d3
7f5d7a4bd06e

Docker コンテナをまとめて削除する

停止中のコンテナを全削除するには docker container prune コマンドがあります。個人で利用しているPC上で、雑に停止中のコンテナ(正確には、コンテナ用のイメージレイヤやログや設定情報)を一括削除するには楽ですが、社内や学内の共用環境で実行するのは躊躇します。

そういう場合でも、コマンド置換と docker ps のフィルタによって問題を解決できます。

doker ps では -f または --filter オプションによって( リファレンス )、status や 元イメージの情報によるフィルタができます。

たとえば、 alpine:latest イメージを使ったコンテナ一覧は次のようになります( -a オプションの追加により、起動・停止中に拘わらず、すべてを表示)。

$ docker ps -a -f ancestor=alpine:latest
CONTAINER ID   IMAGE     COMMAND       CREATED        STATUS                     PORTS     NAMES
6873bcaf4879   alpine    "/bin/sh"     2 hours ago    Exited (137) 2 hours ago             hardcore_tu
b5c731e558f7   alpine    "/bin/sh"     2 hours ago    Exited (137) 2 hours ago             clever_antonelli
fa14d1f00ae7   alpine    "/bin/ash"    3 months ago   Exited (0) 3 months ago              crazy_lehmann
c9b814f42f7c   alpine    "/bin/bash"   3 months ago   Created                              pedantic_merkle

これらだけを削除するには、これまで見てきた通りコマンド置換を使えば簡単です。

$ docker rm -f $(docker ps -a -f ancestor=alpine:latest -q)
6873bcaf4879
b5c731e558f7
fa14d1f00ae7
c9b814f42f7c

実際に運用している環境では、フィルタはイメージ名だけでなく、状態など組みあわせるなど、工夫が必要になる場合もあるでしょう。

Docker イメージをまとめて削除

以前であれば、このコマンド置換を使い docker rmi -f $(docker image ls -q) のように実行していましたが、最近では便利なコマンドがあります。

親子関係を持たない、不要と思われる中間イメージ(dangling images)を一括で消すコマンドは docker image prune です( リファレンス )。このコマンドを実行しても、Docker Hub のようなリポジトリからダウンロードしたイメージは残しておけます。

$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
$ docker images
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
nginx         latest    dd34e67e3371   3 months ago   133MB
alpine        latest    021b3423115f   3 months ago   5.6MB
ubuntu        latest    1318b700e415   4 months ago   72.8MB
hello-world   latest    d1165f221234   9 months ago   13.3kB

この環境では、もともと余分なイメージはなかったようです。

便利なのは、-a オプションの付与です。Docker コンテナで使われていないイメージを削除します。さらに、 -f オプションも付ければ確認のプロンプトも出ませんので、シェルスクリプトでの処理にも向いています。

$ docker image prune -a -f
Deleted Images:
untagged: hello-world:latest
untagged: hello-world@sha256:0fe98d7debd9049c50b597ef1f85b7c1e8cc81f59c8d623fcb2250e8bec85b38
deleted: sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726
deleted: sha256:f22b99068db93900abe17f7f5e09ec775c2826ecfe9db961fea68293744144bd
untagged: alpine:latest
untagged: alpine@sha256:eb3e4e175ba6d212ba1d6e04fc0782916c08e1c9d7b45892e9796141b1d379ae
deleted: sha256:021b3423115ff662225e83d7e2606475217de7b55fde83ce3447a54019a77aa2
deleted: sha256:bc276c40b172b1c5467277d36db5308a203a48262d5f278766cf083947d05466

Total reclaimed space: 5.609MB

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
nginx        latest    dd34e67e3371   3 months ago   133MB
ubuntu       latest    1318b700e415   4 months ago   72.8MB

この環境では nginx と ubuntu のコンテナが実行中のため残っていますが、それ以外の不要なイメージは削除されています。

ポイント

docker で $(コマンド) によるコマンド置換を使えば、通所の作業が楽になる場合や、シェルスクリプトでのコマンド実行にも使えます。

参考

11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?