Emacs
tramp
docker
EmacsDay 19

docker-tramp.el でdockerコンテナのファイルを読み書きする

はじめに

docker-tramp.el はdockerコンテナ内に存在するファイルを、Emacsから直接読み書きできるようにするライブラリです。使い慣れたEmacsでコンテナ内のファイルを編集できるようになるので編集効率が上がり、dockerでの試行錯誤が劇的にやりやすくなります。

この記事では docker-tramp.el の使い方について説明します。

docker-tramp.el の必要性

dockerコンテナ内のファイルを読み書きしたいという状況には以下のような場面があると思います。

  1. 他人が作成したdockerイメージの内容を理解する(ディレクトリ構成や設定ファイルの内容など)。
  2. 独自のdockerイメージをビルドする際に設定ファイルやスクリプトなどを試行錯誤する。

1は初めて利用するツールやサーバアプリケーションをdockerでインストールしたような場面です。dockerイメージがどのように動作しているのかを把握するために、コンテナ内のファイル、ディレクトリを閲覧したくなります。

2は独自にカスタムしたdockerイメージをビルドする場面です。dockerイメージをビルドするときには、カスタムした設定ファイルや、エントリポイントとなる独自のスクリプトをイメージに含めたりします。それらのファイルの変更が意図通り動作しているか確認するのには試行錯誤が必要になります。

通常dockerでコンテナ内のファイルを読み書きするには

  1. docker exec -it sh などでコンテナ内にログインする。
  2. docker cp でコンテナ内のファイルをコピーし、ローカルで読み書きする。

の2通りの方法があります。しかし1の方法ではコンテナごとに利用できるプログラムが異なり、vi はおろか less さえインストールされていないコンテナもあります。2の方法はローカルのプログラムで読み書きできますが、コンテナに変更を反映するのに再度 docker cp でコンテナに書き戻す必要があり、手間がかかります。

このような場面で docker-tramp.el を使用すると、使い慣れたEmacsで直接コンテナ内のファイルを読み書きできるようになります。当然Emacsのシンタックスハイライトが効くのでとても読みやすくなります。Emacsの機能は大体そのまま透過的に使用できるので、検索、置換などなんでもありです。

TRAMP

docker-tramp.el の説明に入る前にその前提である TRAMP について説明しましょう。

TRAMPのマニュアルには

TRAMP stands for "Transparent Remote (file) Access, Multiple Protocol".

と書かれています。その名の通りEmacsで様々なプロトコルを通じて透過的なリモートアクセスを実現する組み込みのライブラリです。例えばEmacsで /ssh:user@host:/path/to/file というファイルを開こうとすると、SSH経由で user@host というリモートホストにログインし、リモートホストに存在する /path/to/file というファイルを開くことになります。

sshplink といったSSH系のプロトコルは外部ライブラリを入れることなく利用できます。TRAMPは独自のプロトコルで通信するように拡張する仕組みがあり、この仕組でdocker向けに拡張したのが docker-tramp.el になります。

docker-tramp.el により、TRAMPの仕組みに乗っかってdockerホストからdockerコンテナのファイルを透過的に扱えるようになります。

docker-tramp.el

それでは docker-tramp.el の使い方について説明します。

インストール

docker-tramp.el はMELPAに登録されているので、インストールは

M-x package-install docker-tramp

だけでOKです。

init.elに以下の設定を加えると docker-tramp.el が使えるようになります1

~/.emacs.d/init.el
(require 'docker-tramp-compat)

コンテナ内のファイルにアクセスする

ではコンテナ内のファイルにアクセスしてみます。前提としてnginxコンテナを2台動かしている状態とします。

$ docker run -d --name nginx1 nginx
$ docker run -d --name nginx2 nginx

コンテナ内のファイルを開くには find-file (C-x C-f) で

/docker:user@container:/path/to/file

を指定します。user はコンテナにアクセスするユーザで、省略するとコンテナのデフォルトユーザになります。container 部分はコンテナのIDかNAMESを指定できます。

container 部分で <TAB>C-i を押下すると、現在稼働中のコンテナを補完してくれます。また /path/to/file 部分ではコンテナ内のファイル、ディレクトリ名を補完できます。

実際にコンテナ内の /etc/nginx にアクセスする様子をgifにしてみました。

docker-tramp-example.gif

コンテナ内のディレクトリ (/etc/nginx) にアクセスしたので、Diredでバッファが開きます。それから conf というキーワードで検索して /etc/nginx/nginx.conf ファイルを開くまでを記録しています。

このようにバッファ内の操作はただのEmacsなので、普段と変わらない使い勝手でコンテナ内のファイルを読み書きすることができます。

Ad-hoc multi-hops

TRAMPには Ad-hoc multi-hops という仕組みがあります。これは多段でTRAMPによるリモートアクセスする記法です。これができると何が嬉しいかというと、別マシンのdockerホストで稼働しているコンテナにアクセスできるようになります。

例えば以下のように docker_host 上で稼働している containerlocalmachine からアクセスしたいとします。

                            +-----------+
                            | container | <-----\
+--------------+          +---------------+   docker
| localmachine | --ssh--> |  docker_host  | ----/
+--------------+          +---------------+ 

この場合 find-file (C-x C-f) で

/ssh:user@docker_host|docker:container:/path/to/file

というファイル名を指定します。実際にアクセスしてみた様子は以下のようになります。

docker-tramp-multihop-example.gif

これはWindowsマシンのEmacsからplink(Putty)でVagrant上のUbuntu(dockerホスト)にアクセスし、さらにUbuntu上で動作している nginx1 コンテナにアクセスしています。

残念ながらmulti-hopsの場合はコンテナ名が補完できないようですが、一旦バッファを開いたあとならファイル名の補完自体はできます。

これでdockerホストが別ホストにある場合も安心です。

TIPS

コンテナ名で補完する

コンテナの補完をIDではなくNAMESでしてほしい場合は docker-tramp-use-namest にします。

(require 'docker-tramp-compat)
(set-variable 'docker-tramp-use-names t)

docker-tramp-complete-names.png

docker コマンドは sudo なしで実行できる前提

docker-tramp.el は sudo なしで docker コマンドを実行できる前提で作られているようです。ドキュメントを参考にして sudo なしで実行できるようにしておきましょう。

まとめ

docker-tramp.el を利用してdockerコンテナ内のファイルにアクセスする方法を紹介しました。docker-tramp.el によってdockerでの試行錯誤がやりやすくなるので、非常におすすめです。

環境

本稿の確認に使用した動作環境は以下のとおりです。

$ emacs --version
GNU Emacs 25.3.1
Copyright (C) 2017 Free Software Foundation, Inc.
GNU Emacs comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of GNU Emacs
under the terms of the GNU General Public License.
For more information about these matters, see the file named OPYING.

$ docker --version
Docker version 17.09.1-ce, build 19e2cf6

$ cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.3 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.3 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

現状docker for WindowsのようなWindows環境ではうまく動作しないようです。https://github.com/emacs-pe/docker-tramp.el/issues/7

Windows環境は以下のとおりです。

エディション: Windows 10 Pro
バージョン: 1709
OSビルド: 16299.125

Docker for Windows

Version: 17.09.1-ce-win42 (14687)
Channel: stable
Commit: 3176a6a

  1. バージョン2.3より前のTRAMPにはAlpineコンテナでうまく動作しないバグがあるため、docker-tramp-compat.el を使用している。TRAMPのバージョンが2.3以上になれば docker-tramp.el を直接使ってもよい。https://github.com/emacs-pe/docker-tramp.el/issues/11