« ^ »

EmacsでのDockerイメージのビルド方法を改善する

所要時間: 約 2分

以前「Emacsのdockerfile-modeでimageのデフォルト値が入力されるようにカスタマイズする」という取り組みをした。今はもう少し改良して次のようなコードを .emacs.d/init.el に含めている。

(use-package docker :ensure t :defer t)
(use-package docker-compose-mode :ensure t :defer t)
;; (use-package dockerfile-mode :ensure t :defer t)

;; (el-get-bundle dockerfile-mode :url "[email protected]:TakesxiSximada/dockerfile-mode.git" :type "git")
(el-get-bundle gist:73383aaf81656737fa533dd39dcb27a8:docker-compose-up-services :type "git")

dockerfile-modeでvternを使用するため関数を上書きする。

(require 'dockerfile-mode)

(defun dockerfile-get-docker-image-from-inbuffer ()
  "# iamge: DockerImageName"
  (interactive)
  (let ((image-name-line (save-excursion
		      (goto-char (point-min))
		      (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))
    (s-trim (car (cdr (s-split ":" image-name-line))))))


(defun dockerfile-read-image-name ()
  "Read a docker image name."
  (ido-completing-read "Image name: "
		       dockerfile-image-name-history
		       nil nil nil nil
		       (dockerfile-get-docker-image-from-inbuffer)))


(defun dockerfile-build-buffer (image-name &optional no-cache)
  "Build an image called IMAGE-NAME based upon the buffer.

If prefix arg NO-CACHE is set, don't cache the image.
The build string will be of the format:
`sudo docker build --no-cache --tag IMAGE-NAME --build-args arg1.. -f filename directory`"
  (interactive (list (dockerfile-read-image-name)
		     (not (y-or-n-p "Using cache?"))))
  (save-buffer)
  (vterm-command
   (format
    "%s%s build --ssh=default %s %s %s -f %s %s"  ;; FIX
    (if dockerfile-use-sudo "sudo " "")
    dockerfile-mode-command
    (if no-cache "--no-cache" "")
    (dockerfile-tag-string image-name)
    (dockerfile-build-arg-string)
    (shell-quote-argument (dockerfile-standard-filename (buffer-file-name)))
    (shell-quote-argument (dockerfile-standard-filename default-directory)))
   default-directory))

(define-key dockerfile-mode-map (kbd "C-c C-c") #'dockerfile-build-buffer)

この修正は大きく2つの理由があったように思う。

1つ目は、dockerコマンドの出力には制御コードが多く含まれるため、制御コードを適切に処理しないバッファ等に出力してしまうと、内容が全く分からなくなる。そのため、制御コードを処理できるよう、vtermを使ってコマンドを実行するものだった。

2つ目は、ビルドするイメージのリポジトリやイメージ名やタグといった情報を手で入力する事を防ぐためだった。これはDockerfileの先頭に # image: XXXX のような記述を挿入する事で、ビルド時にその値を読み取るようにするものだった。

2つ目のこの方法には多少問題があった。Dockerfileの先頭にコメントとしてタグ名を指定しなければならない。個人的に管理しているものはこれでも問題なかったのだが、プロジェクトによってはイメージのレポジトリが本番とstagingで分かれていて、そもそもこの方法が取れない場合がある。またDockerfileは公開したいけれど、イメージのレポジトリは公開したくない場合なども対応に困る状況になった。

dockerfile-mode.el をちゃんと読むと、イメージ名の指定については既に考慮されている事に気が付いた。Dockerイメージ名は dockerfile-image-name という変数が利用される。 dockerfile-mode では、この値を .dir-locals.el で指定する事を想定しているようだ。例えば次の Dockerfile.dir-locals.el を同じディレクトリに配置しておくと、 .dir-locals.el で指定している testing というイメージ名でビルドできる。

FROM python:3.12.1-slim-bookworm
EXPOSE 8000
CMD ["python" , "-m" , "http.server"]
((dockerfile-mode . ((dockerfile-image-name . "testing"))))
.dir-locals.el