Podman REST APIを使用してコンテナにファイルをコピーする方法
はじめに
Podmanは、コンテナ管理のための強力なツールです。本記事では、PythonからPodman REST APIを使用してコンテナにファイルをコピーする方法について解説します。
特に、公式ドキュメントには記載されていない重要な点について詳しく説明します。
Podman REST APIの基本
Podman はコンテナのためのツールですが REST API を提供しており、プログラマブルに制御できます。ここでは Python で扱える podmanモジュールを使用します。
このモジュールは基本的な操作をサポートしていますが、コンテナ内にファイルをコピーするAPIは含まれていません。
コンテナへのファイルコピーの実現
モジュールにない操作はPodman HTTP APIドキュメントを参照すれば実装は難しくありません。ファイルコピーについても記載されています。
しかし、ファイルコピーについてはドキュメントの記載が間違っており、その通りにやっても動作しません。
問題点
結論から言うと、ファイルコピーについてはドキュメントに記載されているContent-Typeが正しくありませんでした。これに類似する問題については、Podmanのissue #21861でも報告されています。
正常だと主張する HTTP Status を返すものの、求める動作をしていない状況です。
解決策
ひとつひとつトラブルシューティングを行いました。
まずは podman の cli が動作するだろうと考え、以下のコマンドで正常にファイルをコピーできることを確認しました。
podman container cp ./trial.py sandbox:/home
コピーの確認:
podman container exec -it sandbox /bin/bash
# ls -al /home
CLIの実装調査
動作確認が取れたので cli のコードを読んでいきます。
Podmanのソースコードを調査した結果、containers/archive.goが該当する処理を行っていることが分かりました。
DoRequest で HTTP リクエストを送っています。DoRequestの実装を見るとheaderを受け取るようになっていますが、archive.goからはnilが渡されていました。
このことから、ドキュメントに記載があったJSON形式での送信は不要であることが判明しました。
Pythonによる実装例
原因が判明したので requests モジュールで実装します。
以下に、PythonでPodman REST APIを使用してコンテナにファイルをコピーする完全な実装例を示します
from podman import PodmanClient
import tarfile
import io
def create_tar_string(file_name: str, file_content: str) -> str:
"""tarファイルを作成し、文字列として返す"""
buffer = io.BytesIO()
with tarfile.open(fileobj=buffer, mode="w") as tar:
info = tarfile.TarInfo(name=file_name)
info.size = len(file_content)
file_bytes = file_content.encode('utf-8')
tar.addfile(info, io.BytesIO(file_bytes))
tar_string = buffer.getvalue().decode('utf-8')
return tar_string
uri = "unix:///run/user/1000/podman/podman.sock"
with PodmanClient(base_url=uri) as client:
version = client.version()
print("Release: ", version["Version"])
print("Compatible API: ", version["ApiVersion"])
print("Podman API: ", version["Components"][0]["Details"]["APIVersion"], "\n")
image_name = "docker.io/library/python:3.11-slim"
client.images.pull(image_name)
container_name = "sandbox"
container = client.containers.create(
image_name,
name="sandbox",
command=["python", "-c", "import time; time.sleep(3600)"], # コンテナを起動したまま保持
detach=True
)
# コンテナの起動
container.start()
# ファイルコピーするために tarfile を作成
tar_string = create_tar_string("trial.py", "print('Hello, World!')")
# 【ファイルコピー】
response = client.api.put(f"/containers/{container_name}/archive", params={"path": "/home"}, data=tar_string)
print(response.status_code)
print(response.text)
# コンテナ内で転送したファイルをそのまま実行
exit_code, output = container.exec_run("python /home/trial.py")
print(f"Exit code: {exit_code}")
print(f"Output: {output.decode()}")
# コンテナの停止と削除
container.stop()
container.remove()
このコードは以下の手順で動作します:
- Podmanクライアントの初期化
- Pythonイメージのプル(docker.ioは省略できません)
- コンテナの作成と起動
- 転送したいファイルの Tar file 化
- ファイルのコピー
- コピーしたファイルを実行
- コンテナの停止と削除
重要なポイント
- ファイルコピーAPIではPUTメソッドでの通信になります。またJSONを送信しません
-
create_tar_string
関数を使用して、コピーするファイルをtar形式に変換します - ファイルコピーには
client.api.put
メソッドを使用し、クエリパラメータとしてコピー先のパスを指定します - ファイルが正しくコピーされなかった場合でも200ステータスコードが返される可能性があるため、実際にファイルが存在するか確認することをおすすめします
まとめ
Podman REST APIを使用してコンテナにファイルをコピーする方法について解説しました。公式ドキュメントには記載されていない重要な点もありますが、本記事で紹介した方法を使用することで、ファイルコピーを実装できます。