hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

コンテナイメージの作り方(Containerfile 編)



1 はじめに

イメージの作成は、以下の方法があります。

  • Containerfileを使う方法
  • Podmanのcommitコマンドを使う方法

ここでは、Containerfileを使用したイメージの作り方について説明します。Containerfileでイメージを作成する際には、buildコマンドを使用します。書式は以下の通りです。

$ podman build -t Podmanイメージ名 Containerfile格納ディレクトリ

参考情報:Dockerfile リファレンス — Docker-docs-ja 24.0 ドキュメント

2 検証環境

サーバのAlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.2 (Turquoise Kodkod)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-284.11.1.el9_2.x86_64

3 podmanのインストール方法

podmanパッケージをインストールします。

[root@server ~]# dnf -y install podman

podmanコマンドの版数を確認します。

[root@server ~]# podman -v
podman version 4.9.4-rhel

4 ディレクティブ

ディレクティブとは、Containerfile内に記述するコマンドのことです。ディレクティブを使用すると、コンテナにソフトウェアをインストールしたり、ホストのファイルをコンテナ内にコピーしたりすることができます。

ディレクティブ 概要
FROM ベースとなるコンテナイメージを指定します
COPY ホストのファイル、ディレクトリ、URLをコンテナにコピーします。ADD と異なる点は、リモートURL の指定不可、アーカイブファイルを自動で展開しません
ADD ホストのファイル、ディレクトリ、URLをコンテナにコピーします。ADDはCOPYに比べ多機能ですが、シンプルなコピーにはCOPYを使うことが推奨されているようです
RUN イメージのビルド中に実行するコマンドを指定します。具体的には、イメージの構築に必要なソフトウェアのインストール、設定変更、ファイルのダウンロードなどを行うことができます
ENTRYPOINT コンテナが起動したときに実行されるメインプロセスを指定するために使用します
CMD コンテナが起動するときに実行するコマンドのデフォルトのパラメータを指定します
EXPOSE コンテナがパケットを待ち受けるポート番号を指定するために使用します。パケットはlistenシステムコールを実行して待ち受けます
ENV コンテナ内で環境変数を設定するために使用します。これにより、コンテナ内のプロセスが利用する環境変数を定義できます。ENV ディレクティブで設定した環境変数は、結果として作成されたイメージから実行したコンテナでも維持されます
LABEL イメージに任意のラベル(キー・バリュー形式で指定)を付けるために使用します。ラベルは、プロジェクトでイメージを管理するために使用しま。例えば、誰が作成したイメージなのかといった情報をイメージに付けることができます
WORKDIR コンテナ内の作業ディレクトリを変更するために使用します
USER USERディレクティブで指定したユーザー権限でコンテナ内のプロセスやコマンドを実行します

5 FROMディレクティブの使い方

イメージの初期状態を確認します。

[user1@server ~]$ podman images
REPOSITORY  TAG         IMAGE ID    CREATED     SIZE

Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )

[user1@server ~]$ vi Containerfile
[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux

イメージをビルドします。-t(タグ)オプションは、ビルドするイメージの名前(test_image)を指定します。最後に指定するパラメータは、Containerfile を格納したディレクトリを示します。ここではピリオド(.)を指定しているため、podman コマンドを実行する場所と同じディレクトリに Containerfile があることを意味しています。

[user1@server ~]$ podman build -t test_image .
STEP 1/1: FROM docker.io/library/almalinux
Trying to pull docker.io/library/almalinux:latest...
Getting image source signatures
Copying blob 587e68e1d836 done   |
Copying config 8109fa501e done   |
Writing manifest to image destination
COMMIT test_image
--> 8109fa501eaf
Successfully tagged localhost/test_image:latest
Successfully tagged docker.io/library/almalinux:latest
8109fa501eaf5af622946e7c5c033cb7aef816c3267b1c17c7b62b34fea099a5

イメージを確認すると、イメージ(test_image)が作成されたことがわかります。

[user1@server ~]$ podman images
REPOSITORY                   TAG         IMAGE ID      CREATED      SIZE
localhost/test_image         latest      8109fa501eaf  5 weeks ago  191 MB
docker.io/library/almalinux  latest      8109fa501eaf  5 weeks ago  191 MB

次の検証のため、イメージを削除します。

[user1@server ~]$ podman rmi -f $(podman images -q)

6 COPY/ADDディレクティブの使い方

6.1 COPY ディレクティブの使い方

ホストからコンテナにコピーするファイルを作成します。

[user1@server ~]$ vi test.sh
[user1@server ~]$ cat test.txt
12345

作成したファイルの所有者と所有グループを確認します。ホスト環境では、user1 でファイルを作成しているため、所有者および所有グループが user1 になっていることが確認できます。

[user1@server ~]$ ls -l test.txt
-rw-r--r--. 1 user1 user1 6  7月  8 20:20 test.txt

Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )
・ホストで作成したファイル(test.txt)をコンテナの /tmpにコピーする(COPY )

[user1@server ~]$ vi Containerfile
[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
COPY test.txt /tmp

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

ビルドしたイメージを確認します。

[user1@server ~]$ podman images
REPOSITORY                   TAG         IMAGE ID      CREATED        SIZE
localhost/test_image         latest      4d4957e1ef46  2 seconds ago  191 MB
docker.io/library/almalinux  latest      8109fa501eaf  6 weeks ago    191 MB

ビルドしたイメージ(test_image)からコンテナを起動します。

[user1@server ~]$ podman run -dit --name test1 test_image
ce501fb90adaa9828d45f3ad02fb2e24b242048228f42dbc095cfb4fa1265fcc

コンテナでbashを実行します。

[user1@server ~]$  podman exec -it test1 bash
[root@ce501fb90ada /]#

ホストからコンテナにコピーしたファイルを確認します。ファイルの所有者、所有グループはrootになっています。ファイルの所有者、所有グループの変更方法は後述します。

[root@ce501fb90ada /]# ls -l /tmp/test.txt
-rw-r--r--. 1 root root 6 Jul  8 11:20 /tmp/test.txt

コンテナからぬけます。

[root@ce501fb90ada /]# exit
exit

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)

次は、コンテナ内にコピーしたファイルの所有者、所有グループをuser1に変更してみます。

[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
RUN adduser user1
COPY test.txt /tmp
RUN chown user1:user1 /tmp/test.txt

コンテナを起動してファイル(/tmp/test.txt)の所有者および所有グループを確認すると user1 に変更されていることが確認できます。

[root@45abd84d6676 /]# ls -l /tmp/test.txt
-rw-r--r--. 1 user1 user1 6 Jul  8 11:20 /tmp/test.txt

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)

6.2 ADDディレクティブの使い方

6.2.1 圧縮ファイルのコピー

(1) 事前準備(圧縮ファイルの作成)
テスト用のファイルを作成します。

[user1@server ~]$ vi test.txt
[user1@server ~]$ cat test.txt
12345

テスト用の圧縮ファイルを作成します。

[user1@server ~]$ tar -zcvf test.tar.gz test.txt
test.txt

(2) ADDディレクティブの動作確認
Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )
・ホストで作成した圧縮ファイル(test.tar.gz )を解凍してコンテナの/tmpにコピーする(ADD)

[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
ADD test.tar.gz /tmp/

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

ビルドしたイメージ(test_image)からコンテナを起動します。

[user1@server ~]$ podman run -dit --name test1 test_image
23fb9dc21856fd4dec4d02f1310a74d02468213c456dc66dd270bd39ed3b43ca

コンテナでbashを実行します。

[user1@server ~]$ podman exec -it test1 bash
[root@23fb9dc21856 /]#

ADD ディレクティブは圧縮ファイルを解凍してコンテナにコピーするため、ホストで作成した圧縮ファイルがコンテナ内で解凍されることが確認できます。

[root@23fb9dc21856 /]# cat /tmp/test.txt
12345

コンテナからぬけます。

[root@23fb9dc21856 /]# exit
exit

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)
6.2.2 ディレクトリのコピー

ホストでテスト用のディレクトリ、ファイルを作成します。

[user1@server ~]$ mkdir host_dir
[user1@server ~]$ echo "12345" > host_dir/test1.txt
[user1@server ~]$ echo "67890" > host_dir/test2.txt

Containerfileを使って、次の内容のイメージを作成します。
・(FROM )
・ホストで作成したディレクトリ(host_dir )をコンテナの/tmpにコピーする(ADD)

[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
ADD host_dir /tmp

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

ビルドしたイメージ(test_image)からコンテナを起動します。

[user1@server ~]$ podman run -dit --name test1 test_image
ba931b22c906117c514d604b1a556cbc933665b4e39e3500d8e081a0b0c37183

コンテナでbashを実行します。

[user1@server ~]$ podman exec -it test1 bash
[root@ba931b22c906 /]#

コンテナで/tmp配下のファイルを確認します。ホストのhost_dir 配下に作成したファイルがコンテナの/tmp配下にコピーされていることが確認できます。

[root@ba931b22c906 /]# ls -l /tmp/*
-rw-r--r--. 1 root root 6 Jul 14 00:24 /tmp/test1.txt
-rw-r--r--. 1 root root 6 Jul 14 00:24 /tmp/test2.txt

コンテナからぬけます。

[root@ba931b22c906 /]# exit
exit

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)

7 RUNディレクティブの使い方

Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )
・iprouteパッケージをイメージにインストールする(RUN )

[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
RUN dnf -y install iproute

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

ビルドしたイメージを確認します。

[user1@server ~]$ podman images
REPOSITORY                   TAG         IMAGE ID      CREATED         SIZE
localhost/test_image         latest      f28aa2f7799c  54 seconds ago  238 MB
docker.io/library/almalinux  latest      8109fa501eaf  5 weeks ago     191 MB

ビルドしたイメージ(test_image)からコンテナを起動します。

[user1@server ~]$ podman run -dit --name test1 test_image
db3023492a4caf5ce9b0b653bc5b2ecb6cba5065ccf68de12e6ed268bc44631c

コンテナでbashを実行します。

[user1@server ~]$ podman exec -it test1 bash
[root@db3023492a4c /]#

コンテナでパッケージを確認すると、iprouteパッケージがインストールされていることが確認できます。

[root@db3023492a4c /]# rpm -qa|grep iproute
iproute-6.2.0-6.el9_4.x86_64

コンテナからぬけます。

[root@db3023492a4c /]# exit
exit

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)

8 ENTRYPOINT/CMDディレクティブの使い方

Dockerfile リファレンス — Docker-docs-ja 24.0 ドキュメントによると、ENTRYPOINTディレクティブは以下の書式があります。 exec 形式が推奨されているようです。ここでも、ディレクティブはexec 形式を使用します。

  • exec 形式(推奨されている形式)
ENTRYPOINT ["実行ファイル", "パラメータ1", "パラメータ2"]
  • shell 形式
ENTRYPOINT コマンド パラメータ1 パラメータ2

Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )
・コンテナ起動時に実行するプログラムを指定する(ENTRYPOINT )
・ENTRYPOINT で指定するコマンドのパラメータを指定する(CMD )

[user1@server ~]$ vi Containerfile
[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
ENTRYPOINT ["ping","-c","1"]
CMD ["example.com"]

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

もう1つターミナルを開いてtcpdumpを実行します。-iはインタフェース名を指定します。お使いの環境に合わせて適宜変更してください。なお。、tcpdumpの詳細な使い方は、tcpdumpの使い方(基本編) - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# tcpdump -i enp1s0 icmp -n

ビルドしたイメージ(test_image)からコンテナを起動します。

[user1@server ~]$ podman run --name test1 test_image

tcpdumpの実行結果を確認すると、example.com(93.184.215.14)宛てのping実行結果が確認できます。

[root@server ~]# tcpdump -i enp1s0 icmp -n
22:21:10.429828 IP 192.168.122.87 > 93.184.215.14: ICMP echo request, id 20, seq 1, length 64
22:21:10.524670 IP 93.184.215.14 > 192.168.122.87: ICMP echo reply, id 20, seq 1, length 64

digコマンドで確認すると、example.comIPアドレスは93.184.215.14であることが確認できます。なお、digコマンドの使い方は、digコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# dig example.com +short
93.184.215.14

次の検証のため、コンテナを削除します。

[user1@server ~]$ podman rm test1 --force

次に、コンテナ起動時にパラメータを明示的に指定します。指定するパラメータは、デフォルトゲートウェイIPアドレス(192.168.122.1)です。

[user1@server ~]$ podman run --name test1 test_image 192.168.122.1

tcpdumpの実行結果を確認すると、example.comに対するpingの結果ではなく、デフォルトゲートウェイIPアドレス(192.168.122.1)に対するpingの結果であることが確認できます。つまり、CMDで指定したパラメータがコンテナ起動時に指定したパラメータで上書きされたことがわかります。

[root@server ~]# tcpdump -i enp1s0 icmp -n
22:27:21.353640 IP 192.168.122.87 > 192.168.122.1: ICMP echo request, id 24, seq 1, length 64
22:27:21.353973 IP 192.168.122.1 > 192.168.122.87: ICMP echo reply, id 24, seq 1, length 64

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)

9 EXPOSEディレクティブの使い方

Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )
・コンテナのhttpdが80番ポートでリッスンする(EXPOSE )

[user1@server ~]$ vi Containerfile
[user1@server ~]$ cat Containerfile
FROM httpd:latest
EXPOSE 80

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

作成したイメージを確認します。

[user1@server ~]$ podman images
REPOSITORY               TAG         IMAGE ID      CREATED         SIZE
localhost/test_image     latest      72416f67c6ae  36 seconds ago  152 MB
docker.io/library/httpd  latest      c0c20df5e7be  5 days ago      152 MB

ビルドしたイメージ(test_image)からコンテナを起動します。このとき、-pオプションを使って、ホストの8080番ポートへのアクセスをコンテナの80番ポートにマッピングします。

[user1@server ~]$ podman run -d -p 8080:80 --name test1 test_image
1f8a374faf80f2bd28b48710de04b74f8d2cdc2ef98489f4e6d027136fc271d4

ポートのマッピングを確認します。ホストの任意(0.0.0.0)のIPアドレスに対する8080番ポートへのアクセスが、コンテナの80番ポートへのアクセスに変換されることが確認できます。

[user1@server ~]$ podman port test1
80/tcp -> 0.0.0.0:8080

curlコマンドを実行してホストの8080番ポートにアクセスしてみます。コンテナのhttpdからの応答を確認することができます。

[user1@server ~]$ curl http://localhost:8080
<html><body><h1>It works!</h1></body></html>

コンテナからぬけます。

[root@db3023492a4c /]# exit
exit

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)

10 ENVディレクティブの使い方

Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )
環境変数(TEST)に文字列("Hello, World!")を設定する(ENV )
環境変数に設定した文字列をechoコマンドで出力する(CMD )

[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
ENV TEST="Hello, World!"
CMD echo $TEST

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

ビルドしたイメージ(test_image)からコンテナを起動します。コンテナを起動する際に、環境変数TESTに設定した文字列が出力されることを確認できます。

[user1@server ~]$ podman run --name test1 test_image
Hello, World!

あと始末をします。

[user1@server ~]$ podman rm test1
test1

コンテナを実行する際に、設定された環境変数を上書きすることもできます。例えば、環境変数TESTに"bye"を設定してコンテナを起動してみます。この場合、コンテナが起動する際に、”Hello, World!”ではなく"bye"が出力されることを確認できます。

[user1@server ~]$ podman run -e TEST=bye --name test1 test_image
bye

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)

11 LABELディレクティブの使い方

Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )
・イメージにラベルをつける(LABEL )。ラベルの内容は、作成者(maintainer)、イメージのバージョン(version)、イメージの作成日(2024/7/10)

[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
LABEL maintainer="hana_shin@example.com"
LABEL version="1.0"
LABEL created="2024-07-10"

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

Podmanのinspectコマンドとjqコマンドを併用して、イメージに付けたラベルを確認してみます。なお、io.buildah.versionはBuildahのバージョン情報を示しており、ビルドツールが自動的に追加するものです。

[user1@server ~]$ podman inspect test_image --format '{{json .Config.Labels}}' | jq
{
  "created": "2024-07-10",
  "io.buildah.version": "1.33.7",
  "maintainer": "hana_shin@example.com",
  "version": "1.0"
}

次の検証のため、イメージを削除します。

[user1@server ~]$ podman rmi -f $(podman images -q)

12 WORKDIRディレクティブの使い方

Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )
・作業ディレクトリを /etc に変更する(WORKDIR )
・ファイル(test.conf)に文字列を書き込む(RUN )

[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
WORKDIR /etc
RUN echo "Hello" > test.conf

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

ビルドしたイメージ(test_image)からコンテナを起動します。

[user1@server ~]$ podman run -dit --name test1 test_image
734466e173c07719688d9deb0304eacc4ece68bdb3cbc58685b47954142c8eae

コンテナでbashを実行します。

[user1@server ~]$ podman exec -it test1 bash
[root@734466e173c0 etc]#

作業ディレクトリを確認すると /etc であることが確認できます。

[root@734466e173c0 etc]# pwd
/etc

ファイル(test.conf)に書き込んだ内容を確認すると、"Hello"であることが確認できます。

[root@734466e173c0 etc]# cat test.conf
Hello

コンテナからぬけます。

[root@734466e173c0 etc]# exit
exit

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)

13 USERディレクティブの使い方

Containerfileを使って、次の内容のイメージを作成します。
・ベースイメージにAlmaLinuxを使用する(FROM )
・ユーザー(user2)を作成する(RUN )
・実行ユーザーをuser2に変更する(USER )
・作業ディレクトリを/home/user2に変更する(USER )

[user1@server ~]$ cat Containerfile
FROM docker.io/library/almalinux
RUN adduser user2
USER user2
WORKDIR /home/user2

イメージをビルドします。

[user1@server ~]$ podman build -t test_image .

ビルドしたイメージ(test_image)からコンテナを起動します。

[user1@server ~]$ podman run -dit --name test1 test_image
ae3345f70a53062894ff5b050ff48fe7225553d0799d97f2e38baddee92a1692

コンテナでbashを実行します。

[user1@server ~]$ podman exec -it test1 bash
[user2@ae3345f70a53 ~]$

idコマンドを使用してユーザー名を確認してみます。ユーザ名がuser2であることが確認できます。

[user2@ae3345f70a53 ~]$ id
uid=1000(user2) gid=1000(user2) groups=1000(user2)

次に、pwdコマンドを使用して現在の作業ディレクトリを確認すると、作業ディレクトリが/home/user2であることが確認できます。

[user2@ae3345f70a53 ~]$ pwd
/home/user2

コンテナからぬけます。

[user2@ae3345f70a53 ~]$ exit
exit

次の検証のため、コンテナ、イメージを削除します。

[user1@server ~]$ podman rm test1 --force
[user1@server ~]$ podman rmi -f $(podman images -q)

Y 参考図書

今回の記事執筆にあたり参考にした図書は以下のものです。

単行本


Docker実践ガイド 第3版 (impress top gear)

電子書籍



Software Design (ソフトウェアデザイン) 2023年11月号 [雑誌]


Docker実践ガイド 第3版 impress top gearシリーズ