[오픈소스 활용에 대한 스터디] Podman, Buildah 관련 입니다.
안녕하세요.
서론 :
이번이 3번째 스터디 모임을 꾸리는데 이번에는 정말 잘 되었으면 합니다.
Podman 은 Docker 와 동일한 기능을 지원해주는 오픈소스 이지만 여러 확장적인 기능을 제공해줍니다.
Podman : https://github.com/containers/podman
Buildah 는 저도 아직 많은 부분은 모르지만 보다 row-level 접근 방식으로 Dockerfile(Containerfile) 빌드 기능을 제공해주는 것으로 알고 있습니다.(Dockerfile 이라는 파일명도 지원을 해주지만, Containerfile 이라는 이름으로 사용하고 있습니다. 다만 Docker에서의 호완성 측면에서는 Dockerfile 로 명명하는 것이 어느측면에서는 나을듯도 합니다.)
Buildah : https://github.com/containers/buildah
하지 않는 것:
- 해당 오픈소스들에 대한 github 링크를 달았지만, 해당 소스를 분석하려는 것은 아닙니다.
- 위에서 설명했듯이 일반적인 사용방법은 Docker 와 동일합니다. 명령어도 동일합니다.
따라서 일반적인 사용법에 대한 스터디는 아닙니다.
하려는 것:
1. Podman 에서는 binding 이라는 명칭의 기능들을 제공해줍니다.
명령어로서 Podman 을 실행 시킬 수 있습니다.
즉, bindings api 를 통해서 application 에서 이미지를 빌드하고 컨테이너를 생성하고 삭제하는 일련의 기능들을 제공해줍니다. bindings 관련 api 들을 학습하고 이를 간단한 demo 버전의 실행파일로 구동하는 것입니다.
example :
package main
import (
"context"
"fmt"
"os"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/bindings"
"github.com/containers/podman/v3/pkg/bindings/containers"
"github.com/containers/podman/v3/pkg/bindings/images"
"github.com/containers/podman/v3/pkg/specgen"
)
// 일단 v4 에서 글러온 소스도 정상작동하니 method 와 function 을 1:1 비교해보자. -> 자료로 만들어 보자.
func main() {
fmt.Println("Welcome to the Podman Go bindings tutorial")
// Get Podman socket location
sock_dir := os.Getenv("XDG_RUNTIME_DIR")
if sock_dir == "" {
sock_dir = "/var/run"
}
socket := "unix:" + sock_dir + "/podman/podman.sock"
// Connect to Podman socket
// TODO 다른 connection 도 살펴보자.
ctx, err := bindings.NewConnection(context.Background(), socket)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Pull Busybox image (Sample 1)
fmt.Println("Pulling Busybox image...")
_, err = images.Pull(ctx, "docker.io/busybox", &images.PullOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Pull Fedora image (Sample 2)
rawImage := "registry.fedoraproject.org/fedora:latest"
fmt.Println("Pulling Fedora image...")
_, err = images.Pull(ctx, rawImage, &images.PullOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// List images
imageSummary, err := images.List(ctx, &images.ListOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
var names []string
for _, i := range imageSummary {
names = append(names, i.RepoTags...)
}
fmt.Println("Listing images...")
fmt.Println(names)
// Container create
s := specgen.NewSpecGenerator(rawImage, false)
// 가상터미널 true
s.Terminal = true
r, err := containers.CreateWithSpec(ctx, s, &containers.CreateOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Container start
fmt.Println("Starting Fedora container...")
err = containers.Start(ctx, r.ID, &containers.StartOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
_, err = containers.Wait(ctx, r.ID, &containers.WaitOptions{
Condition: []define.ContainerStatus{define.ContainerStateRunning},
})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Container list
var latestContainers = 1
containerLatestList, err := containers.List(ctx, &containers.ListOptions{
Last: &latestContainers,
})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("Latest container is %s\n", containerLatestList[0].Names[0])
// Container inspect
ctrData, err := containers.Inspect(ctx, r.ID, &containers.InspectOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("Container uses image %s\n", ctrData.ImageName)
fmt.Printf("Container running status is %s\n", ctrData.State.Status)
// Container stop
fmt.Println("Stopping the container...")
err = containers.Stop(ctx, r.ID, &containers.StopOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
ctrData, err = containers.Inspect(ctx, r.ID, &containers.InspectOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("Container running status is now %s\n", ctrData.State.Status)
return
}
2. Pod 관련에 대한 학습입니다. Pod 는 쿠버네특스의 Pod 와 유사합니다. 하지만 제가 쿠버네틱스를 사용하지 않아서 얼마나 유사한지 동일한지는 확인하지는 못했습니다. Pod 는 컨테이너 오케스트레이션에서 중요한 역활을 하고 있는데, 대표적으로 (현재 지금 파악한 정도는) 5개의 네임스페이스(사실 더 많은 네임스페이스가 있지만 현재 5개만 podman 에서 지원해주는 것 같습니다.)를 isolated / unisolated 등으로 상태를 변경? 함으로써 pod 내의 리소스? 를 공유 또는 비공유 할 수 있습니다.
여러 기능들이 있지만, 이것을 이론적으로 학습하는 것을 떠나서 bindings api 를 이용하여 demo 버전으로 만들는게 목적입니다.
3. buildah 는 dockerfile 을 만들어주고 이것을 build 해주는 기능을 가지고 있습니다. 물론 더 많은 기능을 가지고 있으리라 생각됩니다. 마찬가지로 demo 버전을 만드는 것이 목적입니다만, 해당 부분은 저도 가능할지 파악은 하고 있지 못하고 있습니다.
스터디 시작점
샘플 소스를 올렸지만 해당 binding 소스는 3.x 기준으로 만들어졌습니다. 현재(5/19일 기준) 4.1 까지 github 에 올라와 있습니다. 저는 4.1 기준으로 진행하려고 하는데 그러기 위해서는 몇가지 제한사항(더해줘야 하는일)이 발생합니다. 우분투기준으로 apt install podman 으로 설치했을때 최신은 3.x 가 최신입니다. 향후 4.x 도 나오겠지만 현시점에서는 나오지 않기 때문에 github 에서 빌드해서 사용해야 하는 어려움이 있습니다. 해당 소스를 보면 알겠지만 podman 은 여러 서브 프로젝트를 담고 있습니다. 따라서 일반적인 위치에 main.go(또는 그러한 역활을 하는 go 파일) 이 없습니다. 따라서 Makefile 을 보면서 Podman 을 빌드하고 시스템에 등록? 하는 일련의 과정들이 필요합니다. 아마 스터디는 여기서 부터 시작될듯합니다.
왜 4.x 로 선택했느냐에 대한 질문을 하실 수도 있는데, podman 은 상당히 빠르게 업데이트 되는 오픈소스이고 새로운 기능이 빠르게 적용됩니다. 4.x 에서(정확히는 4.1) 에서 업데이트된 기능을 사용하고자 하기 때문입니다.
스터디시 필요 시스템 요구사항(저는 ubuntu 사용자라 ubuntu 기준으로 설명합니다.)
1. Ubuntu 22.04 LTS 설치 필요 또는 그 이상
2. golang 1.18 이상(1.16 이상 이긴한데, 1.16 일때 알수 없는 문제가 발생해서 그냥 1.18.1 로 업데이트 함.)
3. goland 20.01 버전 사용하고 있었는데 golang 1.18 goroot 설정에서 문제가 발생하여 goland 22.01 버전으로 업그레이드 함.(저는 goland 사용자라 추가했습니다.)
4. 바인딩시 코드에서 사용하는 버전과 설치되어 있는 podman 버전이 일치해야 한다. 만약 다를시 에러 발생한다. (추가설명: '바인딩시' 라는 의미는 podman 바인딩 api 를 사용해 만들어진 어플리케이션이 로컬일때는 ipc 방식으로 로컬 podman 에 접속하여 restful api 를 호출 하는 형태이고, 리모트 일때는 tcp/ip 방식으로 ip 연결을 통해서 restful api 를 호출하는 형태이다. 따라서 호출하는 바인딩 코드와 호출당하는 podman 은 버전이 같아야 한다.)
5. podman bindings 사용하여 개발시 반드시 go mod 사용해야 하며, vendor 도 사용할 것을 추천한다.
스터디 방식:
온라인으로 진행되며, 리눅스 사용자들이면 작업하는데 편하지만 windows wsl2 지원해서 상관없을듯한데 이부분은 잘 몰라서 넘어갑니다.
결과물은 github 에 공개되며 github 를 통해서 작업물 관리를 진행합니다.
연락처: seoyhaein@gmial.com, slack
진행했던 스터디 링크들:
grpc : https://github.com/seoyhaein/golang-study
dag-go : https://github.com/seoyhaein/dag-go
부디 이번 스터디는 혼자 하는 스터디 않되었으면 합니다. T.T
