# Podman ###### tags: `container`, `Podman` 目前比較多人用的是 [docker](https://www.docker.com/) 工具來進行 container 管理,但我想用 [Podman](https://podman.io/)。原因是因為 Open Source,且 Podman 是 [Open Container Initiative](https://opencontainers.org/) (OCI) 提供的工具。 有能力的話,可以自己加功能、修 Bug,並送回 upstream。但我還不會 [Go](https://golang.org/) 就是了 :P * Podman 和 Docker 基本概念、指令可以互通使用 * 可以使用相同的 image registry,例如 [docker.io/library](https://hub.docker.com/) * Docker 要先用 systemctl 起 docker.service,再下指令;但 Podman 直接是指令 base,可以直接下指令使用。 ## 安裝 ```shell= pacman -S podman ``` ## Pull image ```shell= $ podman pull docker.io/library/alpine:3.14 $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/library/alpine 3.14 021b3423115f 7 days ago 5.87 MB ``` ## Have the shell in a container ```shell= $ podman run -it --name test docker.io/library/alpine:3.14 sh / # $ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 649b5756f07f docker.io/library/alpine:3.14 sh 25 seconds ago Exited (0) 24 seconds ago test $ podman start test test $ podman attach test / # ``` ## Build an image with Dockerfile ```shell= $ cat python-dockerfile FROM docker.io/library/alpine:3.14 RUN apk add --no-cache python3 $ podman build -t mypython -f python-dockerfile STEP 1: FROM docker.io/library/alpine:3.14 STEP 2: RUN apk add python3 --no-cache fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz (1/13) Installing libbz2 (1.0.8-r1) (2/13) Installing expat (2.4.1-r0) (3/13) Installing libffi (3.3-r2) (4/13) Installing gdbm (1.19-r0) (5/13) Installing xz-libs (5.2.5-r0) (6/13) Installing libgcc (10.3.1_git20210424-r2) (7/13) Installing libstdc++ (10.3.1_git20210424-r2) (8/13) Installing mpdecimal (2.5.1-r1) (9/13) Installing ncurses-terminfo-base (6.2_p20210612-r0) (10/13) Installing ncurses-libs (6.2_p20210612-r0) (11/13) Installing readline (8.1.0-r0) (12/13) Installing sqlite-libs (3.35.5-r0) (13/13) Installing python3 (3.9.5-r1) Executing busybox-1.33.1-r3.trigger OK: 56 MiB in 27 packages STEP 3: COMMIT mypython --> 20332d09dbe Successfully tagged localhost/mypython:latest 20332d09dbe5b483818a106bf5e72bceb478c2274686db476bb3097f0d8a0895 $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/mypython latest 20332d09dbe5 58 seconds ago 54.2 MB docker.io/library/python 3-alpine3.14 d4d6be1b90ec 34 hours ago 48 MB docker.io/library/alpine 3.14 021b3423115f 7 days ago 5.87 MB ``` 有趣的是,發現自己用 alpine 當 base 裝 python3,最後 build 出來的 image 比直接從 dockerhub 上拉一樣版本的 Python image [python:3-alpine3.14](https://hub.docker.com/_/python) 還要肥大!當然,也不是所有的 case 都是這樣,但至少 Python image 是這樣。 ## Add and execute a Python application in a container directly ```shell= $ cat pyhello-dockerfile FROM docker.io/library/python:3-alpine3.14 COPY hello.py /usr/bin/ $ cat hello.py #!/usr/bin/env python3 print("Hello World!") $ chmod +x hello.py $ podman build -t python-hello -f pyhello-dockerfile STEP 1: FROM docker.io/library/python:3-alpine3.14 STEP 2: COPY hello.py /usr/bin/ STEP 3: COMMIT python-hello --> 4d28af87192 Successfully tagged localhost/python-hello:latest 4d28af87192c0d1940d615c0b9a8b9a82590fd4181b8dd0e9ca5bb1a7ea43b7f $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/python-hello latest 4d28af87192c 9 seconds ago 48 MB docker.io/library/python 3-alpine3.14 d4d6be1b90ec 34 hours ago 48 MB docker.io/library/alpine 3.14 021b3423115f 7 days ago 5.87 MB $ podman run --name pyhello python-hello hello.py Hello World! ``` ## Have a bottle HTTP server in a container Reference: [Running Bottle Apps in Docker Containers on macOS](https://www.fullstackpython.com/blog/first-steps-bottle-web-apps-docker-containers.html) ```shell= $ ls app/ app.py requirements.txt $ cat app/requirements.txt bottle $ cat app/app.py #!/usr/bin/env python3 from bottle import route, run @route('/') def hello_world(): return "Hello, world! (From Full Stack Python)" if __name__ == "__main__": run(host="0.0.0.0", port=8080, debug=True, reloader=True) $ chmod +x app/app.py $ cat bottle-dockerfile FROM docker.io/library/python:3-alpine3.14 WORKDIR /app COPY app /app RUN pip install --no-cache-dir -r requirements.txt $ podman build -t mybottle -f bottle-dockerfile STEP 1: FROM docker.io/library/python:3-alpine3.14 STEP 2: WORKDIR /app --> 4b04293273b STEP 3: COPY app /app --> fb59b39cb01 STEP 4: RUN pip install --no-cache-dir -r requirements.txt Collecting bottle Downloading bottle-0.12.19-py3-none-any.whl (89 kB) Installing collected packages: bottle Successfully installed bottle-0.12.19 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv STEP 5: COMMIT mybottle --> fe155ca85e1 Successfully tagged localhost/mybottle:latest fe155ca85e13658653ab20d29f4c4b52558c39919c656ad37a4b372edd063d5b $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/mybottle latest fe155ca85e13 About a minute ago 55 MB docker.io/library/python 3-alpine3.14 d4d6be1b90ec 34 hours ago 48 MB docker.io/library/alpine 3.14 021b3423115f 7 days ago 5.87 MB ``` 測試一下,要導 port 進 container: ```shell= $ podman run -p 5000:8080 --name testbottle mybottle /app/app.py Bottle v0.12.19 server starting up (using WSGIRefServer())... Listening on http://0.0.0.0:8080/ Hit Ctrl-C to quit. 10.0.2.100 - - [14/Aug/2021 07:55:25] "GET / HTTP/1.1" 200 38 ``` 另一個 terminal ```shell= $ curl localhost:5000 Hello, world! (From Full Stack Python) ``` 也可把 container 起成像是 keep alive standalone daemon: ```shell= $ podman run -d -p 5000:8080 --name testbottle mybottle /app/app.py 76f1bc5f3a31a2cef4d1d3a751d01cee04ab55abecb65c09a8a046600898b47e $ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 76f1bc5f3a31 localhost/mybottle:latest /app/app.py 8 seconds ago Up 8 seconds ago 0.0.0.0:5000->8080/tcp testbottle # Stop the container $ podman stop testbottle testbottle $ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 76f1bc5f3a31 localhost/mybottle:latest /app/app.py 3 minutes ago Exited (137) 45 seconds ago 0.0.0.0:5000->8080/tcp testbottle ``` 可以看 standalone container 裡 process 的 log: ```shell= $ podman logs mybottle Bottle v0.12.19 server starting up (using WSGIRefServer())... Listening on http://0.0.0.0:8080/ Hit Ctrl-C to quit. 10.0.2.100 - - [14/Aug/2021 14:43:14] "GET / HTTP/1.1" 200 38 ``` 也可以另外連進 standalone container,並取得、執行 shell ```shell= $ podman exec -it mybottle sh /app # ``` ## Secrets Container 裡的程式有時會需要一些敏感資料,如帳號、密碼。可以用 `-e --env` 設定 container 環境變數的方式,pass 一些參數。這可以搭配 Continuous Delivery (CD) 服務的 secret store。 ```shell= $ podman run --rm -it -e ENVTEST=iamtest alpine sh / # echo $ENVTEST iamtest ``` 另外,也可以用一些 Container 管裡工具的 secret store 功能,如 Podman、docker、[Kubernetes](https://kubernetes.io/) 和 cloud service provider 都有提供類似功能。 * [Podman secret command](http://docs.podman.io/en/latest/secret.html) * [Podman - Secrets](https://blog.while-true-do.io/podman-secrets/) 以 Podman 為例,可以用 `--secret` pass secret object 進 container,Podman 會在 container 裡 mount 該 secret object 為一個 file `/run/secrets/<secret object's name>`: ```shell= $ echo "iamsensitive" | podman secret create SecureData - $ podman secret inspect SecureData [ { "ID": "ade70aaccf022dd93d039f1df", "CreatedAt": "2021-08-14T23:16:28.261537528+08:00", "UpdatedAt": "2021-08-14T23:16:28.261537528+08:00", "Spec": { "Name": "SecureData", "Driver": { "Name": "file", "Options": null } } } ] $ podman run --rm --secret SecureData alpine cat /run/secrets/SecureData iamsensitive ``` ## Bind mount a volume into the container 利用 volume 的機制,把 host 上的 folder 掛進 container 裡: ```shell= $ mkdir /tmp/data $ date | tee /tmp/data/mydata Sun Aug 15 11:55:01 AM CST 2021 $ podman run --rm -it -v /tmp/data:/home/dev alpine sh / # ls -l /home/dev/ total 4 -rw-r--r-- 1 root root 32 Aug 15 03:55 mydata / # cat /home/dev/mydata Sun Aug 15 11:55:01 AM CST 2021 / # ``` ## Reference * MariaDB 在 dockerhub 上有很完整的[教學範例](https://hub.docker.com/_/mariadb) (以 docker 為例)