ENECHANGE Developer Blog

ENECHANGE開発者ブログ

experimental な Docker 環境を利用するための手順

ENECHANGE チーフエンジニアの cuzic こと、川西です。

最近、誕生日を迎えました。 かなり今さらですが、バースデーケーキの砂糖・甘さがもたらす幸福感、至福感はすごいですね。 甘いものが好きな人が多い理由が分かったような気がしました。

別記事でまた執筆する予定の buildkite の導入を現在進めているのですが、それに先行して、experimental な機能を活用した上で Docker 環境を構築しなおしたので、その内容について、紹介します。

はじめに

私は約3年ほど前に enechange の Dockerfile や docker-compose.yml を作成しました。 その設定を今見直すと、技術の発展もあり、古びたところが多数あるな、と感じていました。

  • BuildKit が正式採用され、有効化すれば、パフォーマンスの向上、ビルドキャッシュの向上などにより、Docker 環境を構築するときの待ち時間を短縮できる。
  • SSH キーを環境変数経由で受け渡ししているが、Docker BuildKit のシークレット機能を使って、もっとセキュアに受け渡しできる。
  • docker-compose.yml の中で volumes_from などの deprecate となった古い機能を使っており、廃止したい。

そこで docker-compose を全面的に書き換えることを決意しました。 実施した内容について、紹介します。

前半は最新の Docker インストールの話です。 インストール済みの方は飛ばして後半からお読みください。

最新の Docker のインストール

私は Debian を使っています。 最新の Docker を利用するため、Docker 公式の apt リポジトリを導入しました。

Docker 公式の apt リポジトリの導入

Docker の gpg キーの導入

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

docker の公式 apt リポジトリを追加

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

apt リポジトリ情報のアップデート

$ sudo apt update

最新の docker-ce のインストール

$ sudo apt install docker-ce

docker version の確認

$ docker --version
Docker version 19.03.8, build afacb8b7f0

docker グループにログインユーザを追加

$ sudo groupadd docker
$ sudo usermod -aG docker $USER

ログアウト&ログインのやり直し。 (リブート等でも OK)

docker の動作を確認

$ docker run hello-world

ここで、

Hello from Docker!

と表示されれば成功です。

docker を自動起動するように設定

systemd の場合)

$ sudo systemctl enable docker

ここまでで、最新の Docker が利用できるように設定できました。

最新の docker-compose のインストール

次に最新の docker-compose をインストールします。

$ pip install docker-compose

インストールした docker-compose のバージョンを確認します。

$ docker-compose --version
docker-compose version 1.25.4, build unknown

作成した Docker ファイルとその説明

今回、下記のような Docker ファイルを設定しました。

# syntax = docker/dockerfile:experimental
FROM prograils/ruby-node-chrome:2.6.5

ENV DEBIAN_FRONTEND=noninteractive

RUN rm -f /etc/apt/apt.conf.d/docker-clean; \
  echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' \
  > /etc/apt/apt.conf.d/keep-cache

RUN \
  --mount=type=cache,target=/var/cache/apt \
  --mount=type=cache,target=/var/lib/apt \
    apt update -qq \
  && apt install -y build-essential \
                       libpq-dev \
                       postgresql-client \
                       libzmq3-dev \
                       libtool-bin \
                       libappindicator3-1 \
                       fonts-libe
                       libasound2 \
                       libnspr4 \
                       libnss3 \
                       libxss1 \
                       xdg-utils \
                       libgdbm-dev \
                       curl \
                       unzip \
                       tzdata \
                       ucommon-utils # for md5sum

WORKDIR ~/

RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
  && unzip awscliv2.zip \
  && ./aws/install \
  && rm awscliv2.zip

RUN echo 'if [ -e /shared/.bashrc ]; then' >> ~/.bashrc \
  && echo '  source /shared/.bashrc' >> ~/.bashrc \
  && echo 'fi' >> ~/.bashrc

# Add the wait-for-it.sh script for waiting on dependent containers
RUN \
  curl https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh > /usr/local/bin/wait-for-it.sh \
  && chmod +x /usr/local/bin/wait-for-it.sh

RUN mkdir -p ~/.ssh \
  && ssh-keyscan github.com > ~/.ssh/known_hosts\
  && chmod 0700 ~/.ssh

1行目

# syntax = docker/dockerfile:experimental

により、 Docker の experimental な機能を利用可能になります。

具体的に利用している experimental な機能について後ほど詳しく説明します。

2行目

FROM prograils/ruby-node-chrome:2.6.5

今回の構築しようとしているコンテナは主に継続的インテグレーションでの自動テスト執行での利用が目的でした。 Ruby on Rails と Node.JS、google chrome を利用する必要があったので、それらがインストール済みの Dockerイメージである ruby-node-chrome を使うことにしました。apt install の待ち時間を短縮でき、さまざまな試行錯誤をより効率的にできます。

4行目

ENV DEBIAN_FRONTEND=noninteractive

この設定はのちほどインストールする tzdata で、インタラクティブシェルが表示されないように設定しています。

https://stackoverflow.com/questions/44331836/aptapt-get-install-tzdata-noninteractive

6行目~30行目

RUN rm -f /etc/apt/apt.conf.d/docker-clean; \
  echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' \
  > /etc/apt/apt.conf.d/keep-cache

RUN \
  --mount=type=cache,target=/var/cache/apt \
  --mount=type=cache,target=/var/lib/apt \
  apt update -qq \
  && apt install -y build-essential \
                       libpq-dev \
                       postgresql-client \
                       libzmq3-dev \
                       libtool-bin \
                       libappindicator3-1 \
                       fonts-libe
                       libasound2 \
                       libnspr4 \
                       libnss3 \
                       libxss1 \
                       xdg-utils \
                       libgdbm-dev \
                       curl \
                       unzip \
                       tzdata \
                       ucommon-utils # for md5sum

こちらは、 buildkit のドキュメントに書かれた例を参考にしています。

apt install するときに Docker experimental の機能を活用し、毎回 apt が deb パッケージファイルをダウンロードすることなく、 cache を利用できるようになります。 再構築をより短時間にできるようになります。

以降の行

この行以降での処理内容は、今回の記事の主題である、 Docker の Experimental 機能の利用とは、異なるトピックとなりますので割愛します。

docker-compose ファイル

docker-compose のファイルでも、これまで利用していなかった Docker の secrets 機能を活用するように修正しました。

secrets 機能を利用する前のやり方

今回、作成する Docker イメージでは、自社の private リポジトリ内の gem を利用しています。そのため、ssh 経由で github に git ログインできる必要があります。

Docker イメージの中で SSH を利用する方法として、コマンドラインから環境変数経由で ssh のプライベートキーの情報を受け渡しする手法がありました。 具体的には下記のようなコマンドで docker-compose を実行します。

$ docker-compose build \
  --build-arg PRI_SSH_KEY="$(cat ~/.ssh/id_rsa)"\
  --build-arg PUB_SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"

これは Docker ファイルの中にプライベートキーを残さず実現できる方法ですが、

  • コマンドラインに毎回指定する必要があり、実行コマンドが長くなる
  • プライベートキーが不要な他のコンテナ内からも参照可能

という問題がありました。

Docker の secrets 機能

Docker の secrets 機能を使うと秘匿したい情報にアクセスが必要なコンテナからのみ、その情報にアクセスできるようにすることができます。

今回は ssh-agent と組み合わせることでコンテナ内から SSH できるようしました。 直接 SSHプライベートキーを受け渡ししておらず、コンテナは Docker の secrets 機能を使って、ホスト上にある ssh-agent が生成したソケットファイルを参照しているだけです。 非常にセキュアに SSH 可能な環境を構築できています。

具体的には、docker-compose.yml 内で下記のような記述をしました。 (今回の説明で必要な内容以外はすべて割愛しています)

services:
  web:
    secrets:
    - ssh_auth_sock
    environment:
      SSH_AUTH_SOCK: /run/secrets/ssh_auth_sock

secrets:
  ssh_auth_sock:
    file: $SSH_AUTH_SOCK

下にある secrets のセクションで、 ssh_auth_sock というシークレット名で 環境変数 $SSH_AUTH_SOCK が指し示す先のファイルを Docker シークレットとしてアクセスできるように設定しています。環境変数 $SSH_AUTH_SOCK の値は /tmp/ssh-Z2Fq3F4qRuiy/agent.1021 といった ssh-agent が作成するソケットファイルのファイル名です。

コンテナ内では Docker シークレットを /run/secrets/[シークレット名] というファイル名でアクセスすることができます。 この例では ssh_auth_sock という名前ですので、 /run/secrets/ssh_auth_sock を環境変数 SSH_AUTH_SOCK に設定しています。 これで、ホストの ssh-agent を活用して、Docker コンテナ内で ssh が可能になります。

Docker compose からの docker experimental 機能の利用

BuildKit を利用して docker-compose を実行するために、いくつか環境変数の設定などが必要となります。

  export DOCKER_BUILDKIT=1
  export COMPOSE_DOCKER_CLI_BUILD=1
  ssh-agent docker-compose  up

環境変数 DOCKER_BUILDKIT を設定することで BuildKit を有効化できます。環境変数以外にも /etc/docker/daemon.json に記述することで有効化する方法もあります。

さらに環境変数 COMPOSE_DOCKER_CLI_BUILD を設定することで、docker-compose で、最新の機能を利用することができます。

また、今回の設定では ssh-agent 経由で ssh アクセスすることを前提していますので、 ssh-agent の引数として、docker-compose コマンドを実行しています。

まとめ

今回の記事では、最新の docker を導入し、 BuildKit などの最新機能を活用して docker-compose を使えるようにする方法を紹介しました。

こうすることで、docker の構築のときの待ち時間を短縮できますし、よりセキュアで安全な方法で SSH の認証情報をコンテナと受け渡しできるため、安心できます。