みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

Docker入門

はじめに

Dockerに入門してみる。

作業環境はUbuntu 20.04

環境設定

aptでも提供されているが、Install Docker Engine on Ubuntuの手順でインストールしたほうがトラブルは少なそう。

apt版のアンインストール

$ sudo apt purge docker docker-engine docker.io containerd runc

インストール

必要なパッケージのインストール

$ sudo apt install \
     apt-transport-https \
     ca-certificates \
     curl \
     gnupg-agent \
     software-properties-common

キーの設定

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

リポジトリの設定

$ sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"

インストール

$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io

dockerグループへの追加

dockerコマンドをsudoなしで実行できるようにする

$ sudo gpasswd -a ${USER} docker

一度ログアウトして、再ログインすると有効化される。

最初のコンテナ

いろいろなチュートリアルで、最初にDockerを実行するのは下記のようなコマンド。

$ docker run hello-world

docker runがいろいろなことを自動で実行するため便利だが、筆者のような初心者にはなにが起こっているか分かりづらい。

何もない状態からdocker runを実行する場合下記のようなことが行われる。

  1. イメージの取得
  2. イメージからコンテナを作成
  3. コンテナの起動
  4. コンテナへのアタッチ

dockerのコマンドでいうと下記のような感じ

  1. docker pull / docker build
  2. docker create
  3. docker start
  4. docker attach

イメージとコンテナ

Dockerといえばコンテナ。実際コンテナを作ってその上で作業するためのツールだが、コンテナを実行するためにはイメージが必要となる。

f:id:mickey_happygolucky:20210109161749p:plain
イメージとコンテナ

イメージとコンテナにはそれぞれIDが割り振られる(イメージIDとコンテナID)。これらは同じような16進数のハッシュ値なので、最初は混乱するがきちんと別物だということを理解しておく。

イメージはコンテナの金型のようなもので、1つのイメージからいくつもコンテナを実行できる。

f:id:mickey_happygolucky:20210109161837p:plain
複数のコンテナ

イメージIDは1つだが、コンテナIDは2つ必要になる。

コンテナでいろいろ作業して、使い終わったコンテナを削除することもできる。

f:id:mickey_happygolucky:20210109161921p:plain
コンテナの削除

通常コンテナで作業した内容はイメージには反映されない。

ネット上ではコンテナを実行する際下記のようなコマンドを示されることが多い。

$ docker run -it --rm ubuntu

自動的にbashが実行されubuntuが実行されたコンテナにログインされた状態になるため、ここでいろいろと作業することになる。

しかし--rmによってコンテナからexitした時点でコンテナが削除されてしまう。コンテナでの作業内容は基本的にイメージに反映されないため、作業した内容が失われてしまうことになる。

Dockerでの作業状態

Dockerで作業する場合に把握しておくべき状態をまとめる。便宜上実行状態にA-Eで名前をつけることにする。

状態 イメージ コンテナ 遷移する方法(作る時/A->E) 遷移する方法(消す時/E->A) 確認方法
A なし なし docker rmi
B 作成済み なし docker pull / docker build --rmをつけて実行したコンテナからexit / docker rm docker images
C 作成済み 停止中 docker create --rmなしのコンテナからexit docker ps -a
D 作成済み 実行中 docker start Ctrl+P Ctrl+Q でコンテナを抜ける docker ps
E 作成済み アタッチ docker attach / docker exec

大体いまどこかがわかっていれば生きていける。

よく使われるrunはこれらの状態を複数スキップというか自動実行する。

docker run

書式は下記。詳細(run)

docker run [オプション] イメージ [コマンド] [引数...]

表のAの状態でdocker runを実行すると一気にEの状態まで遷移する。ここで指定しているubuntuイメージ名

$ docker run -it ubuntu
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
da7391352a9b: Pull complete 
14428a6d4bcd: Pull complete 
2c2d948710f2: Pull complete 
Digest: sha256:c95a8e48bf88e9849f3e0f723d9f49fa12c5a00cfc6e60d2bc99d87555295e4c
Status: Downloaded newer image for ubuntu:latest
root@9f1316bc678f:/# 

--rmをつけていないので、この状態からexitすると表のCの状態に遷移する。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

実行中のコンテナはいない。

下記のコマンドで停止中のコンテナも表示する。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                      PORTS               NAMES
9f1316bc678f        ubuntu              "/bin/bash"         About a minute ago   Exited (0) 41 seconds ago                       focused_pasteur

コンテナID9f1316bc678fのコンテナが表示される。

このコンテナの元になったイメージを確認する。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              f643c72bc252        6 weeks ago         72.9MB

イメージIDがf643c72bc252のイメージが表示される。

イメージのリポジトリとタグ

先ほど便宜上イメージ名と言ったものは正確にはREPOSITORYという。

1つのリポジトリにはいくつものイメージが存在する。たとえばubuntuの過去のバージョンを選択したい場合はubuntu:18.04のように指定する。

コロン(:)のあとに続く18.04TAGといい、コマンドでREPOSITORYのみ指定した場合、最新を示すlatestというTAGが自動的に選択される。

これらのことからイメージを指定するために必要な識別子はリポジトリ名、タグ名ということになるが、イメージIDと表現を揃えるためイメージ名と呼ぶことにする。

コンテナがある状態でdocker run

停止中とはいえ既にコンテナがある状態で同じイメージに対してdocker runを実行するとどうなるか。

$ docker run -it ubuntu

exitして下記のコマンドを実行する。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
fd274bb747d1        ubuntu              "/bin/bash"         13 seconds ago      Exited (0) 9 seconds ago                        elegant_blackburn
9f1316bc678f        ubuntu              "/bin/bash"         18 minutes ago      Exited (0) 17 minutes ago                       focused_pasteur

停止中のコンテナが2つになっている。増えた方のコンテナIDはfd274bb747d1

作成済みのイメージからコンテナへアタッチされたことになる。つまり表のBからEに遷移される。

つまり一度作成したコンテナに対してはdocker runでは操作できないということ。docker runに指定するのがイメージ名なのでコンテナへの識別が必要となる操作はできない。

attach vs exec

実行中のコンテナへ接続する方法はattachexecの2つある。これらのコマンドはコンテナ識別子を指定する。(コンテナID/コンテナ名)

書式は下記。詳細(attach)詳細(exec)

docker attach [オプション] コンテナ
docker exec [オプション] コンテナ コマンド [引数...]

docker attach

docker attachはPID=1のプロセスに接続する。Ctrl+P、Ctrl+Qでデタッチした場合でも再接続可能。

$ docker start 9f1316bc678f
$ docker attach 9f1316bc678f
root@9f1316bc678f:/# echo $$
1

環境変数$$で実行中のPIDを取得できる。ここでは1になっている。

作業途中で抜けて再接続したい場合などはこちらで作業すると良さそう。

docker exec

コンテナ内の指定した任意のコマンドを実行できる。attachとは異なり新規にプロセスを作成する。

$ docker start 9f1316bc678f
$ docker exec -it 9f1316bc678f /bin/bash 
root@9f1316bc678f:/# echo $$
8

こちらに関してはCtrl+P、Ctrl+Qでデタッチした場合、このプロセスに再接続する方法がない。

実行中のコンテナで複数のターミナルを開いて作業したい場合などはこちらを使うと良さそう。

コンテナの削除

docker rmで削除する。

書式は下記。詳細(rm)

docker rm [オプション] コンテナ [コンテナ...]

削除するには下記のようにする。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
2b17b5b1a846        ubuntu              "/bin/bash"         8 seconds ago       Exited (0) 6 seconds ago                       ecstatic_jepsen
$ docker rm -f 2b17b5b1a846

-fを指定すると実行中のコンテナも削除できる。コンテナはIDでも名前でもよい。

イメージの削除

docker rmiで削除できる。

書式は下記。詳細(rmi)

docker rmi [オプション] イメージ [イメージ...]

削除するには下記のようにする。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              f643c72bc252        6 weeks ago         72.9MB
hello-world         latest              bf756fb1ae65        12 months ago       13.3kB
$ docker rmi bf756fb1ae65

よく使うオプション(-it)

docker rundocker execで、-itというオプションをつけているのをよく見かける。

Docker-docs-jaによると下記の通り。

option long name 機能
-i --interructive コンテナの STDIN にアタッチ
-t --tty 疑似ターミナル (pseudo-TTY) を割り当て

標準入力を擬似ttyに接続することでインタラクティブなシェルを実現できるとのこと。

逆に言えば単発で実行されるコマンドを実行する場合は不要

$ docker start 9f1316bc678f
$ docker exec 9f1316bc678f cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.1 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.1 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

docker runの単発実行

任意のコマンドの単発実行について触れたのでdocker runの単発実行についても触れる。

docker runもコマンドを指定することで、起動したコンテナで任意のコマンドを実行することができる。

$ docker run --rm ubuntu cat /etc/os-release

このコマンドを実行すると下記のことが実行される。

  1. イメージの取得
  2. コンテナの作成
  3. コンテナの起動
  4. コンテナ内でコマンドを実行
  5. コンテナ終了
  6. コンテナの削除

表の状態のA->B->C->D->E->D->Bという遷移が自動で行われる。

コンテナの作業内容の保存

コンテナ内でファイルを追加したり環境を構築したりてもコンテナを削除すると全て失われるが、 これらを保持したい場合は、docker commitでイメージにすることができる。

書式は下記。

docker commit [オプション] コンテナ [リポジトリ[:タグ]]

下記のように実行する。

$ docker commit 9f1316bc678f my_env:version1

まとめ

いままでDockerをなんとなく使っていたが今回まとめてみた。

runが高機能すぎて、わからなくてもなんとなく使えてしまうのが良し悪しあると思う。

attachとexecも割と混乱する。

これで君もDockerマスターだ!(嘘)