Ubuntu Weekly Recipe

第458回UbuntuでDocker再入門

高速で軽量な仮想環境を構築できるDockerは、その利便性から、ソフトウェアの開発者にとってもシステムの管理者にとっても使い方を知っておくべきソフトウェアとしての地位を確立しつつあります。今回はより新しいDockerをUbuntu上で使う上で、気をつけるべきことをおさらいします。

UbuntuにおけるDocker

2017年7月追記:Dockerのリリースポリシーの変更にあわせて、リポジトリのURLやパッケージ名が変更になりました。本記事は新しい手順に更新済みです。記事中の「docker-engineパッケージ」「docker-ceパッケージ」と読み替えてください。

Dockerはカーネルのコンテナ技術などを利用して、アプリケーションをサンドボックス環境の中で動かす仕組みです。Dockerでは最小限のルートファイルシステムである「Dockerのベースイメージ」の上に、各種ソフトウェアのインストール手順や設定手順をファイルとして記述したDockerfileを用いて環境を構築します。Dockefileさえあれば、機械的に何度でもいろいろなところで同じ環境を再構築できるのです。折しも現在発売中の『Software Design 2017年2月号』の第1特集は「いまはじめるDocker」と題したDocker特集になっていますので、より応用的な使い方を知りたい場合はそちらを参照してください[1]⁠。

さてそのDockerを使うためには、まずDockerエンジンと呼ばれるデーモンとそのDockerエンジンを操作するクライアントをインストールする必要があります[2]⁠。UbuntuにDockerをインストールする方法は、実はいくつか存在します。

  • Docker公式のdocker-ce(旧称:docker-engine)パッケージをインストールする方法
  • Ubuntu公式リポジトリにあるdocker.ioパッケージをインストールする方法
  • snapを使ってdockerパッケージをインストールする方法

最新版を使いたいのであれば、Docker公式のパッケージを使う方法が一番確実です。常に最新のリリースに追随していますし、そのパッケージの作りもUbuntuの流儀から外れてはいません。

最新版にこだわりがないのであれば、公式リポジトリのパッケージをインストールする方法が一番簡単でしょう。なにせ公式リポジトリにパッケージがあるので、インストールはaptコマンドを実行するだけです。Dockerは昨今のシステムでは必須とも言えるコンポーネントになっているため、LTSへのSRUも積極的に行われています。末尾の「.1」のリリース以降のいずれかのタイミングでUbuntu 16.04 LTSにも導入されますので、2017年2月頭の時点でインストールできるのは「1.12.3」です。また、おそらく近いうちに「1.12.6」に更新される見込みです。よって、常にリリースされたばかりのバージョンが必要という用途でなければ、Ubuntuリポジトリのパッケージも選択肢に入ってくるでしょう。

ちなみにdocker.ioパッケージにはLXD上でDockerを使うためのパッチが当たっています。LXDの上でDockerを使いたいのであれば、Ubuntuのdocker.ioパッケージを使うと良いでしょう。

snap版は、新しいパッケージングシステムである「snap」でパッケージ化されたDockerです。snapではアプリケーションやサービスをすべてAppArmorやseccomp、cgroup、namespaceなどを用いて「ホストから隔離した環境」上で実行する仕組みになっています。現時点でのsnap版のdockerパッケージは、⁠このような隔離環境でもDockerがきちんと動く」ことを示す技術的デモに近い位置づけですので、現時点でこれを通常のデスクトップやサーバーで使う意味はありません。たとえばIoT向けのUbuntu Coreシステム上で、Dockerを用いたサービスを立ち上げたいといった用途であれば、意味が出てくるでしょう[3]⁠。

Dokcer公式のdocker-engineパッケージ

Docker公式のdocker-engineパッケージについては、Dockerのドキュメントにとても丁寧なインストール手順が記載されています。この手順に従えば何の問題もありません。ここではいくつか細かい注意点を付記しておきましょう。

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

まず最初に、最低限必要なパッケージをインストールします。

$ sudo apt update
$ sudo apt install curl apt-transport-https ca-certificates \
    software-properties-common linux-image-generic linux-image-extra-$(uname -r)

curlはこのあとリポジトリのGPG鍵をダウンロードするために使用します。サーバー版だと最初からインストールされていますが、デスクトップ版だと入っていないかもしれません。apt-transport-httpsとca-certificatesはDockerのリポジトリがHTTPSであるために必要なパッケージです。software-properties-commonはadd-apt-repositoryコマンドのためにインストールしています。ただし普通にUbuntuをインストールすれば、いずれのパッケージも最初からインストールされているはずです。

最後の2つのカーネル関連のパッケージは、Storage Driverとしてaufsを使いたいかOverlayFSを使いたい場合に必要となります。Dockerのインストールドキュメントには「⁠⁠インストールしない)強い理由がないのであればインストールしましょう」と書いてありますので、特に理由がなければインストールしておきましょう。ちなみにデスクトップ版であれば最初からインストールされています。

DockerのStorage Driverは、Dockerイメージを保存するための仕組みです。過去にはaufsが使われていましたが、最近はOverlayFSやDevice Mapperなど複数のStorage Driverをサポートするようになりました。Select a storage driverには個々のStorage Driverの特性や選択基準が記載されていますので、必要に応じて確認すると良いでしょう。Ubuntuの場合、上記カーネルパッケージをインストールしているとaufsを使うようになります。

「linux-image-generic」⁠linux-image-extra-VERSION」そしてDockerのドキュメントにある「linux-image-extra-virtual」の関係についても説明しておきましょう。Ubuntuにおいてカーネル本体とカーネルモジュールは「linux-image-VERSION-generic」パッケージによって提供されます[4]⁠。⁠linux-image-generic」「リポジトリにある最新のバージョンのパッケージに依存している」ため、⁠linux-image-generic」をインストールしておけばパッケージの更新によって常に最新のパッケージがインストールされます。

カーネルモジュールのうち起動時には使わないものは「linux-image-extra-VERSION」に分離してパッケージ化されています。しかしながら「linux-image-generic」「linux-image-VERSION」「linux-image-extra-VERSION」の両方に依存しているため、普通のデスクトップやサーバーにおいてはこれらを区別することはありません。それに対して仮想環境で使われる「linux-image-virtual」「linux-image-VERSION」のみに依存しています。これは「linux-image-extra-VIRSION」で提供するハードウェアドライバーのほとんどは仮想環境では不要だからです。以前は「linux-image-virtual」「linux-image-generic」とは別のカーネルだったのですが、14.04ぐらいからカーネルイメージそのものは同一でパッケージの依存関係だけ変えている状態になっています。そのため「linux-image-extra-virtual」をインストールすることと「linux-image-generic」をインストールすることは本質的には同じです。

Dockerで使うaufsのドライバーは「linux-image-extra-VERSION」に入っています。仮想マシンやクラウドインスタンスなど「linux-image-virtual」を使っている環境で、最新の「linux-image-extra-VERSION」をインストールするために上記では「linux-image-generic」を指定しているわけです。ちなみに「linux-image-generic」は常に「リポジトリにある最新のバージョンのlinux-image-extra-VERSIONのパッケージ」に依存しています。しかしながら何らかの理由で「現在そのホストで使っているカーネルパッケージのバージョン」「リポジトリの最新バージョン」と異なることもあるでしょう。そこで上記ではlinux-image-extra-$(uname -r)と指定することで「現在使っているカーネルバージョン」のaufsドライバーもインストールしています。

上記を踏まえた上で、カーネルパッケージを明示的にインストールするかどうかを個々の環境にあわせて判断してください。

リポジトリ鍵とリポジトリリストの導入

次にリポジトリのGPG鍵とリポジトリのリストを登録しましょう。

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
OK
$ apt-key fingerprint 0EBFCD88
pub   4096R/0EBFCD88 2017-02-22
      フィンガー・プリント = 9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid                  Docker Release (CE deb) 
sub   4096R/F273FCD8 2017-02-22

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

apt-keyコマンドを使えばリポジトリ鍵を管理できます。ただし普段使うことはあまりありません。ちなみにadd-apt-repositoryコマンドはPPAの追加でよく使われていますが、上記のように任意のリポジトリURLを/etc/apt/sources.listに追加するような使い方も可能です。この使い方で注意しなくてはならないのは、PPAのように/etc/apt/sources.list.d/以下に新規にファイルを追加するのではなく、/etc/apt/sources.listの末尾に指定した文字列をそのまま追記するということです。別ファイルで管理したい場合は、次のようにechoコマンドとteeコマンドを組み合わせる方法を使用しましょう。

$ echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
    | sudo tee /etc/apt/sources.list.d/docker.list

将来的にDocker自体が不要になり、リポジトリも使わなくなったら、パッケージやリポジトリURLだけでなくリポジトリ鍵も削除しておきましょう。リポジトリの鍵は次のコマンドで削除できます。

$ sudo apt-key del 0EBFCD88

ここ0EBFCD88は鍵IDであり、apt-key list出力結果の中で個々の鍵の作成日時の前に表示される文字列です。

docker-engineパッケージのインストール

リポジトリの準備ができたらパッケージをインストールします。

$ sudo apt install docker-ce

Ubuntu 16.04 LTSにインストールした場合、systemdのサービスファイルがインストールされて、自動的にサービスが立ち上がります。また再起動後も自動起動する設定になっています。

$ ps -fe | grep docker
root     11731     1  1 18:05 ?        00:00:00 /usr/bin/dockerd -H fd://
root     11741 11731  0 18:05 ?        00:00:00 docker-containerd (略)
$ systemctl is-enabled docker
enabled

Dockerクライアントがデーモンを操作するためには、sudoコマンドを使って管理者権限を取得するか、dockerグループに所属する必要があります。dockerグループに所属しておけば、sudoなしにコンテナの生成や操作ができますので何かと便利です。このdockerグループはdocker-engineインストール時に自動的に作成されていますので、あとはユーザーをグループに追加してログインしなおすだけです[5]⁠。

$ getent group docker
docker:x:999:
$ sudo usermod -aG docker $USER
(ログインし直す)
$ docker info
(Dockerデーモンの状態が表示される)

これでインストールは完了です。実際にシンプルなhello-worldコンテナを立ち上げてみましょう。

$ docker search hello-world
NAME                                      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
hello-world                               Hello World! (an example of minimal Docker...   248       [OK]
tutum/hello-world                         Image to test docker deployments. Has Apac...   31                   [OK]
(以下略)

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
78445dd45222: Pull complete
Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://cloud.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              48b5124b2768        3 weeks ago         1.84 kB

Ubuntuのdocker.ioパッケージ

Ubuntuのdocker.ioパッケージのインストールは、Ubuntu 16.04 LTS上であれば非常に簡単で、ただdocker.ioパッケージをインストールするだけです[6]⁠。

$ sudo apt install docker.io
$ getent group docker
docker:x:136:
$ systemctl is-enabled docker
enabled

docker-engineパッケージと同様にsystemdの設定ファイルのインストールや自動起動も設定されていますし、dockerグループも作成されます。ちなみにDocker公式のdocker-engineパッケージはgroupaddコマンドでグループを作っているのに対して、Ubuntuのdocker.ioパッケージはaddgroupコマンドでグループを作っているため、グループIDの番号付けのルールが異なっていることに注意してください。docker-engineパッケージと同様に、dockerグループに自分のアカウントを追加しておきましょう。

$ sudo usermod -aG docker $USER
(ログインし直す)
$ docker info
(Dockerデーモンの状態が表示される)

基本的にDocker公式のdocker-engineパッケージとUbuntuのdocker.ioパッケージは共存できません。どちらか一方をインストールする時は他方はインストールしないでおきましょう。

プロキシの設定

DockerデーモンはHTTPS経由でDocker HubのAPIなどにアクセスします。そのため環境によってはデーモンそのものにもプロキシの設定が必要です。プロキシを設定する方法はいくつか存在しますが、ここではsystemdのドロップインファイルを使ったカスタマイズ方法を紹介しましょう。

systemdの設定ファイルは/etc/systemd/system/lib/systemd/systemなどに保存されています。Ubuntuの場合、これらはパッケージによって提供され、原則として内容を変更しなくても「きちんと動く」ようになっています。しかしながら何らかの理由で一部の設定を変えたいことがあるでしょう。/etc以下の設定ファイルとして登録されたファイルであれば、内容を変更したとしてもパッケージのアップデートによって更新される心配はありませんし、何らかの理由で更新されるとしても通知が出るようになっています。しかしながら/lib以下のような設定ファイルとしては扱われないファイルはパッケージの更新によって内容が変わり得ます。

systemdには設定ファイルを部分的に変更する仕組みとして「ドロップイン」が存在します。これはたとえば/etc/systemd/system以下にUnitファイル名.dというディレクトリを作成し、拡張子が.confのファイルを置いておけば、設定ファイルとして追加で読み込まれるという仕組みです。変更したい部分だけを別ファイルにできるため管理が楽になりますし、パッケージのアップデートで更新される心配もありません[7]⁠。

DockerデーモンのUnit名はdocker.service⁠設定ファイルは/lib/systemd/system/docker.serviceですので、docker.service.dという名前でドロップインファイル用のディレクトリを作成します。

$ sudo mkdir -p /etc/systemd/system/docker.service.d
$ cat <<EOF | sudo tee /etc/systemd/system/docker.service.d/proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:8080/"
Environment="HTTPS_PROXY=https://proxy.example.com:8080/"
Environment="NO_PROXY=localhost,127.0.0.1"
EOF

プロキシの設定は環境変数を指定するだけです。またDockerのローカルリポジトリはプロキシを経由せずにアクセスするようNO_PROXYの設定も行なっています。

systemdの設定をリロードし、実際に設定が反映されていることを確認しておきましょう。問題なさそうならsystemctl restartでDockerデーモンを再起動します。

$ sudo systemctl daemon-reload
$ systemctl show --property=Environment docker
Environment=HTTP_PROXY=http://proxy.example.com:8080/ HTTPS_PROXY=https://proxy.example.com:8080/ NO_PROXY=localhost,127.0.0.1
$ sudo systemctl restart docker

ちなみにsystemctl statusを使えば、実際にドロップインとして使用しているファイルも確認できます。

$ systemctl status docker
(中略)
  Drop-In: /etc/systemd/system/docker.service.d
           └─proxy.conf
(後略)

上記以外の方法として、/etc/default/dockerから環境変数を渡す設定手順も存在します。ただしDocker公式のdocker-engineパッケージの場合はUpstartもしくはSysV Init環境でのみこの設定ファイルを使用し、systemdのUnitファイルからはこの設定ファイルを読まないポリシーにしているようです。Ubuntuリポジトリのdocker.ioパッケージの場合は、/lib/systemd/system/docker.serviceにおいてEnvironmentFileにこのファイルを指定しているため、systemdでも反映されます。各自の環境に合わせて設定手順を選ぶと良いでしょう。

さて次回はようやくNVIDIA Dockerの話……ではなく、同じコンテナ技術をつかったLXD上でDockerを使う方法を紹介します。

おすすめ記事

記事・ニュース一覧