PodmanでHello, World
以降はすべてRHEL 9.2環境での実行例です。まず

which podman

を実行して、Podmanが入っていない場合はインストールします。 DockerのコマンドをPodmanに翻訳してくれるpodman-dockerというパッケージも入れておきます (基本的な使い方の範囲ではDocker互換コマンドが必要になる機会はないかもしれませんが)。

sudo dnf install -y podman podman-docker
podman -v

バージョンが「podman version 4.4.1」のように表示されればインストールはOKです。
早速、hello-world というイメージをダウンロードして実行してみます。

podman run hello-world

!... Hello Podman World ...!

         .--"--.
       / -     - \
      / (O)   (O) \
   ~~~| -=(,Y,)=- |
    .---. /`  \   |~~
 ~/  o  o \~~~~.----. ~~
  | =(X)= |~  / (O (O) \
   ~~~~~~~  ~| =(Y_)=-  |
  ~~~~    ~~~|   U      |~~

このように絵が出てくれば実行確認もOKです。

podman ps -a
podman rm f663eddf993e

実行中のコンテナは通常「podman ps」で見れるのですが、hello-worldは絵を出すとすぐ終了(停止)してしまいます。 そこで停止状態のコンテナも表示される「podman ps -a」を使用しています。
確認が終わってコンテナを削除する場合は podman rmコマンドを使います。 上の例ではコンテナに名前を付けていないので podman ps -a で表示されるIDを指定しています。 このIDは一意に特定できる限り短縮して指定できます。 例えば"f6"で始まるIDが環境に他になければ、「podman rm f6」でもこのコンテナを削除できます。

podman run --name myhelloworld hello-world

のようにコンテナに名前を付けることができます。その場合は

podman rm myhelloworld

と名前を指定してコンテナを削除できます。
さらに hello-world イメージも削除する場合は

podman rmi hello-world

と podman rmi コマンドを使います。


不要なイメージの削除
イメージのビルドを繰り返していくとディスク領域を消費します。
以下のコマンドを実行すると、タグが付いておらず、コンテナから参照されていないイメージを一括削除できます。

podman image prune


トラブルシューティング
OCI Error: unknown version specified
Podmanと、Podmanが呼び出すcrunというプログラムのバージョンに不整合があると起きるエラーのようです。
両者を揃って最新版にアップデートすると解決するかも。

sudo dnf update podman
sudo dnf update crun

ちなみにメッセージに出てくるOCIとはOpen Container Initiativeの略らしく、 Oracle Cloudや、Oracleの接続APIを指すアレとは全く無関係です。


CentOS7のイメージを取得し、コンテナ内にTomcat9を手動で導入する

podman pull centos:7
podman images

まずホスト側の作業です。「centos:7」というイメージをダウンロードします。 ダウンロード済のイメージの一覧は podman imagesで確認できます。

mkdir /home/ec2-user/tomcat-container
mkdir /home/ec2-user/tomcat-container/logs
podman run -it -d -p 8888:8080 -v /home/ec2-user/tomcat-container/logs:/share/logs --name tomcat centos:7

コンテナ内のTomcatのログをホスト側から見れるように、ログ出力ディレクトリを作ります。 上の例では、コンテナの/share/logsをホスト側の /home/ec2-user/tomcat-container/logs にマウントしています。 またコンテナの名前はtomcat、イメージはcentos:7、 -it はbash等で対話型の接続を許可するオプション、-dはバックグラウンドで実行するオプションです。 そして -p 8888:8080は、コンテナ側のポート8080をホスト側の8888に接続しています。 つまりコンテナの中でTomcatをポート8080で動かしておき、 利用者がこのホストのポート8888にアクセスするとTomcatの処理が実行されることになります。

mkdir ~/install
cd ~/install
sudo dnf install wget
wget http://www-eu.apache.org/dist/tomcat/tomcat-9/v9.0.79/bin/apache-tomcat-9.0.79.tar.gz
podman cp apache-tomcat-9.0.79.tar.gz tomcat:/opt/
podman exec -it tomcat /bin/bash

wgetでTomcat公式からバイナリ配布をダウンロードし、コンテナ内の/optにコピーした上で、 bashを起動してコンテナ内に入ります。ここからコンテナ側(ゲスト側)の操作になります。 先頭に#文字を付けて区別します。

# whoami
root

# yum install -y java
# cd /opt/
# tar zxf apache-tomcat-9.0.79.tar.gz
# cd apache-tomcat-9.0.79
# ./bin/startup.sh

centos:7イメージにbashで入るとユーザーはrootになるようです。 ここではそのままrootの作業で構いません。 Javaをインストールし、先ほど/optに配置したtar.gzを展開して、 ログの出力位置は特に変えずにそのまま起動してみます。 Tomcatのデフォルトのポート番号は8080です。
この状態で、別のPC上のブラウザで http://hostname:8888/ にアクセスして、猫の絵のホームページが表示されることを確認します。
Tomcatのログはデフォルトで /opt/apache-tomcat-9.0.79/logs に出力されますが、 この状態だとホスト側からはログが見れません。 そこでログの出力先を /share/logs に変更することで、 ホスト側からは /home/ec2-user/tomcat-container/logs のパスで見れるようにします。

# cd /opt/apache-tomcat-9.0.79/conf
# cp -i logging.properties logging.properties.orig
# sed -i -e "s/\${catalina.base}\/logs/\/share\/logs/g" logging.properties

centos:7の中にはvimもnanoもエディタらしき物は何にも入っていません。 Javaと同様に導入してもいいのですが、ここではsedコマンドを使って、 Tomcatの logging.properties に含まれる「${catalina.base}/logs」 という文字列を「/share/logs」に置き換えています(記載個所は1個所です)。

# cd ../bin
# ./shutdown.sh
# ./startup.sh
# ls -la /share/logs/
ls: cannot open directory .: Permission denied

Tomcatを停止して、再起動するとログが見れるようになったはず…と思ったら問題発生。 どうもホスト側のOS(つまりRHEL 9.2)でSELinuxモードが有効になっているためらしいです。
回避するにはマウント先の末尾にzを付ける必要があるらしいのでそうします。 一回コンテナを削除して、作り直します。

podman stop tomcat
podman rm tomcat
podman run -it -d -p 8888:8080 -v /home/ec2-user/tomcat-container/logs:/share/logs:z --name tomcat centos:7

以降はbashでコンテナに入る所からやり直します。これで
  • ブラウザで http://hostname:8888/ にアクセスして、猫の絵のホームページが表示される
  • コンテナ内の/share/logsにTomcatのログが出力される
  • ホスト側(RHEL9.2)に戻ったとき、/home/ec2-user/tomcat-container/logs にTomcatのログが出力される
の3点を確認します。 確認したら、Tomcatはshutdownしておきます。


コンテナからイメージを作り、コンテナを増殖させる
次にこのコンテナからDockerイメージを作ってみます。

podman stop tomcat
cd /home/ec2-user/tomcat-container
podman commit tomcat tomcat-image
podman images

ホスト側の作業です。最初にpodman stopでコンテナを停止してから作業します。
podman commitコマンドでコンテナ tomcat からイメージ tomcat-imageが作られていることを確認します。
確認できたら次に、tomcat-image を使ってコンテナ tomcat-no2 を作ってみます。

mkdir /home/ec2-user/tomcat-container/logs2
podman run -it -d -p 8889:8080 -v /home/ec2-user/tomcat-container/logs2:/share/logs:z --name tomcat-no2 tomcat-image
podman ps
podman exec -it tomcat-no2 /bin/bash

bashでコンテナに入ります。 先ほど設定済のコンテナをコピーした物なので、 今度は既にTomcatはインストール済で、/share/logsにログが出るよう設定済です。 つまりTomcatを起動するだけでOKの状態になっています。

# cd /opt/apache-tomcat-9.0.79/bin
# ./startup.sh

これで先ほどと同様
  • ブラウザで http://hostname:8889/ にアクセスして、猫の絵のホームページが表示される
  • コンテナ内の/share/logsにTomcatのログが出力される
  • ホスト側(RHEL9.2)に戻ったとき、/home/ec2-user/tomcat-container/logs2 にTomcatのログが出力される
の3点を確認します。

podman stop tomcat-no2


PodmanでPostgreSQLを起動
PostgreSQLを起動するサンプルをネットで探すとPodという物を作っている事例が多いですが、今回はシンプルな例で試してみます。

podman run -d --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=temppass postgres

なお、このコマンドだと、作られたPGDATAをコンテナ外に持ち出せません。 持ち出すためには先の例のように-vでPGDATAの位置をmountする必要があります。
起動オプションについては、公式に説明があります。 Dockerイメージpostgresの中は、ユーザーpostgresとデータベースpostgresがinitdbコマンドで作成済の状態から利用可能となっています。

podman exec -it postgres /bin/bash
# psql -U postgres
postgres=# (以下を入力)
create table scores (name varchar not null primary key, score integer not null);
insert into scores(name,score) values('Taro', 80);
insert into scores(name,score) values('Jiro', 75);
select * from scores;
exit
# exit

コンテナ内に入ってpsqlで動作を確認します。コンテナの中にはpsqlは当然インストール済です。

sudo dnf install -y postgresql

ホスト側でも動作を確認したいですが、ホスト側にはpsqlがまだ入ってないので、インストールします。 余談ですが、postgresqlでクライアント、postgresql-serverでサーバが入る模様です。MySQLやMariaDBと同じですね。

psql -U postgres
psql: error: could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

あれ?今度は同じコマンドでは接続できません。 Unix domain socketという文字がある通り、 コンテナ外⇒内のログインはソケットではなくネットワークアクセスになるので以下のようにする必要があります。

PGPASSWORD=temppass psql -h localhost -p 5432 -U postgres
postgres=# (以下を入力)
select * from scores;
exit;

psqlの際のPWを設定する環境変数は POSTGRES_PASSWORD ではなく PGPASSWORD なので注意が必要です。 上のコマンドは対話的にパスワードを入力せずに実行する常套手段で、 上のように書けばPGPASSWORD変数のexportは不要のようです。
ちなみに上で作ったscores表は、データベースpostgresのpublicスキーマの中に作られています。

コンテナの中に入って、設定を見てみます。

podman exec -it postgres /bin/bash
# env | grep PG
------
PG_MAJOR=15
PG_VERSION=15.4-1.pgdg120+1
PGDATA=/var/lib/postgresql/data
------
# cd $PGDATA
# more postgresql.conf
# more pg_hba.conf

初期設定でネットワークアクセスをlistenしており、localhostからは入れるようになっています。
ちなみに pg_hba.conf などを編集したい、と思っても、postgresイメージの中にはエディタがvimもnanoも入っていません。 しかも先のcentosと違い、yumもdnfも使えません。どうすれば…と思ったら

# cat /etc/debian-version

「Debian GNU/Linux 12」と表示されます。 そう、postgresイメージのOSはDebian系なので、apt-getでエディタがインストールできます。

# apt-get install nano vim


PodmanでMariaDBを起動

cd ~/mariadb
mkdir podwork
nano Dockerfile

まず作業用ディレクトリをどこかに作り、Dockerfile、my.cnf、init.sqlという3つのファイルを作ります。
◆Dockerfile
------
FROM mariadb:latest

COPY ./my.cnf   /etc/mysql/conf.d/my.cnf
COPY ./init.sql /docker-entrypoint-initdb.d/

VOLUME [ "/var/lib/mysql" ]
------
◆my.cnf
------
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
------
◆init.sql
------
create database if not exists nkdb;
use nkdb;
CREATE TABLE ITEM_CATEGORIES(CATEGORY_ID INTEGER NOT NULL PRIMARY KEY, CATEGORY_NAME VARCHAR(100));
INSERT INTO ITEM_CATEGORIES(CATEGORY_ID, CATEGORY_NAME) VALUES (1, 'アイスクリーム');
------
設定方法の詳細は公式に書いてあります。
COPYで指定する2ファイルの主ファイル名は実際には何でもよく、指定のコピー先にそれぞれ .cnf, .sqlの拡張子で配置されたファイルは、起動時に自動的に実行されるそうです。

podman build -t mariadb-test-image .
podman images

これでDockerfileの記述をもとに、イメージ mariadb-test-image が作成されました。

podman run --name mariadb -e MARIADB_ROOT_PASSWORD=temprootpass -dp 3306:3306 \
 -e MARIADB_DATABASE=nkdb -e MARIADB_USER=nkdbuser -e MARIADB_PASSWORD=tempuserpass mariadb-test-image
podman ps
podman exec -it mariadb /bin/bash

これでbashで入れれば起動成功ですが、ここで次のようなエラーが出る場合があります。

Error: can only create exec sessions on running containers: container state improper

これが出た場合、一旦コンテナを削除し、podman runの-dオプションを削って再度実行してみます。

podman stop mariadb
podman rm mariadb
podman run --name mariadb -e MARIADB_ROOT_PASSWORD=temprootpass -p 3306:3306 \
 -e MARIADB_DATABASE=nkdb -e MARIADB_USER=nkdbuser -e MARIADB_PASSWORD=tempuserpass mariadb-test-image

2023-08-27 08:44:27+00:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh:
running /docker-entrypoint-initdb.d/init.sql
ERROR 1064 (42000) at line 3: You have an error in your SQL syntax;
check the manual that corresponds to your MariaDB server version for the right syntax to use near ')' at line 1

なるほど。このようにinit.sql内に文法エラーがあると教えてくれます。 この例では、CREATE TABLE文のVARCHAR型の列に最大文字数を指定していなかった(※TEXTは省略できる) のでそこが文法エラーになっていました。原因が分かったら、

podman stop mariadb
podman rm mariadb
podman rmi mariadb-test-image

コンテナとイメージを削除し、init.sqlを修正して、再度上の手順をやり直します。

podman exec -it mariadb /bin/bash
#

今度は入れたぞ。

# mariadb -u root -p
Enter password:

MariaDB[(none)]> use nkdb
MariaDB[nkdb]> select * from ITEM_CATEGORIES;

コンテナの中には入れて、同じくmariadbコマンドでデータベースに接続できて、 さらにinit.sqlで作成を指示しておいた ITEM_CATEGORIES が自動で作られていることを確認します。

# mariadb -u nkdbuser -p

同様に通常ユーザーでも接続できることを確認します。

次にホスト側から接続してみます。

sudo dnf install -y mariadb
mariadb -h localhost -P 3306 -u nkdbuser -ptemprootpass
Enter password:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)

MariaDBの場合、localhostではエラーになります。

mariadb --host=127.0.0.1 -P 3306 -u root -ptemprootpass

接続できました。つまり、ソケット接続ではなくTCP接続すればOKでした。

Misc. Topics Top

(first uploaded 2023/09/03 last updated 2024/09/01, URANO398)