tonykang22 / study

0 stars 0 forks source link

[나만의 컨테이너 환경 만들기] 나만의 컨테이너 생성 #104

Open callmeaxxe opened 1 year ago

callmeaxxe commented 1 year ago

5. (실습) 나만의 컨테이너 생성 - 소개

image

image

image

callmeaxxe commented 1 year ago

6. (실습) 나만의 컨테이너 생성 - namespace

요구사항 : 호스트명 변경, 프로세스 ID 변경, 프로세스 리스트 정보 변경

1단계: "run" 명령어 전달 시 run 함수 실행

package main

import (
    "fmt"
    "os"
)

// docker           run image <CMD> <ARG>
// go run main.go   run       <CMD> <ARG>

// Step1: 명령어 종류에 따른 함수 실행. "run" 명령어 전달 시 run 함수 실행.

func main() {
    switch os.Args[1] {
    case "run":
        run()
    default:
        os.Exit(1)
    }
}

func run() {
    fmt.Printf("Running: %v\n", os.Args[2:])
}
실행 결과
root@mobius-pub:~/projects/box# go run main.go run ls -l
Running: [ls -l]

2단계: 새로운 프로세스에서 명령어 실행

package main

import (
    "fmt"
    "os"
    "os/exec"
)

// docker           run image <CMD> <ARG>
// go run main.go   run       <CMD> <ARG>

// Step2: 새로운 프로세스에서 명령어 실행  예) ls -l

func main() {
    switch os.Args[1] {
    case "run":
        run()
    default:
        os.Exit(1)
    }
}

func run() {
    fmt.Printf("Running: %v\n", os.Args[2:])
    cmd := exec.Command(os.Args[2], os.Args[3:]...)
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout

    cmd.Run()
}
실행 결과
root@mobius-pub:~/projects/box# go run main.go run ls -l
Running: [ls -l]
total 12
-rw-r--r-- 1 root root 171 Sep  8 16:18 go.mod
-rw-r--r-- 1 root root 499 Sep  8 16:18 go.sum
-rw-r--r-- 1 root root 477 Sep  8 18:34 main.go

3단계: 새로운 UTS 설정 추가→hostname 변경

package main

import (
    "fmt"
    "os"
    "os/exec"
    "syscall"
)

// docker           run image <CMD> <ARG>
// go run main.go   run       <CMD> <ARG>

// Step3: 새로운 UTS 설정 추가. Clone flag 추가 NEW UTS namespace. hostname 수동 변경 실습.
// 실습
// $ go run . run /bin/sh
// $ hostname box

func main() {
    switch os.Args[1] {
    case "run":
        run()
    default:
        os.Exit(1)
    }
}

func run() {
    fmt.Printf("Running: %v\n", os.Args[2:])
    cmd := exec.Command(os.Args[2], os.Args[3:]...)
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout

    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS,
    }

    must(cmd.Run())
}

// 에러 대응
func must(err error) {
    if err != nil {
        panic(err)
    }
}
실행 결과
root@mobius-pub:~/projects/box# go run main.go run /bin/bash
Running: [/bin/bash]
root@mobius-pub:~/projects/box# hostname
mobius-pub
# hostname 변경
root@mobius-pub:~/projects/box# hostname container
root@mobius-pub:~/projects/box# hostname
container
# 다른 터미널에서는 hostname 이 기존 값으로 노출되는것 확인 (ns 분리되어)

4단계: 컨테이너 환경 시작 시 호스트명을 container로 변경

package main

import (
    "fmt"
    "os"
    "os/exec"
    "syscall"
)

// docker           run image <CMD> <ARG>
// go run main.go   run       <CMD> <ARG>

// Step4: 컨테이너 환경 시작시 호스트명을 container로 변경.
// $ go run . run /bin/sh
// $ hostname

func main() {
    switch os.Args[1] {
    case "run":
        run()
    case "child":
        child()
    default:
        os.Exit(1)
    }
}

func run() {
    fmt.Printf("Running: %v\n", os.Args[2:])
    // cmd := exec.Command(os.Args[2], os.Args[3:]...)
    // 자기 자신을 하나 더 실행한 프로세스 내에서 child 를 실행
    cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...)
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout

    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS,
    }

    must(cmd.Run())
}

func child() {
    fmt.Printf("Running child: %v\n", os.Args[2:])

    // hostname 설정
    syscall.Sethostname([]byte("container"))

    cmd := exec.Command(os.Args[2], os.Args[3:]...)
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout

    must(cmd.Run())
}

func must(err error) {
    if err != nil {
        panic(err)
    }
}
실행 결과
root@mobius-pub:~/projects/box# go run main.go run /bin/bash
Running: [/bin/bash]
Running child: [/bin/bash]

5단계: 컨테이너 환경에서 ps명령 실행 시 제한된 프로세스 정보만 조회. 루트 파일 시스템 변경

package main

import (
    "fmt"
    "os"
    "os/exec"
    "syscall"
)

// docker            run image <CMD> <ARG>
// go run main.go   run       <CMD> <ARG>

// Step5: 컨테이너 환경에서 ps명령 실행 시 제한된 프로세스 정보만 조회. 루트 파일 시스템 변경.
//        실습으로 ps, cat /os-release 명령 실행.

func main() {
    switch os.Args[1] {
    case "run":
        run()
    case "child":
        child()
    default:
        os.Exit(1)
    }
}

func run() {
    fmt.Printf("Running: %v as %d\n", os.Args[2:], os.Getpid())
    cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...)
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout

    // NEWPID : new PID ns, NEWNS : mount ns
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
    }

    must(cmd.Run())
}

func child() {
    fmt.Printf("Running child: %v as %d\n", os.Args[2:], os.Getpid())

    // hostname 설정
    syscall.Sethostname([]byte("container"))

    /* 루트 파일시스템 다운로드
    # https://github.com/tianon/docker-brew-ubuntu-core/raw/88ba31584652db8b96a29849ea2809d99ce3cc31/focal/ubuntu-focal-oci-amd64-root.tar.gz
    # mkdir /tmp/ubuntu
    # tar zxf ubuntu-focal-oci-amd64-root.tar.gz -C /tmp/ubuntu
    */
    // 루트 파일시스템 변경 (change root)
    syscall.Chroot("/tmp/ubuntu")
    syscall.Chdir("/")
    // mount 옵션으로 proc fs mount
    syscall.Mount("proc", "proc", "proc", 0, "")
    // 함수 종료 전 실행을 위한 defer (unmount)
    defer syscall.Unmount("proc", 0)

    cmd := exec.Command(os.Args[2], os.Args[3:]...)
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout

    must(cmd.Run())

}

func must(err error) {
    if err != nil {
        panic(err)
    }
}
실행 결과
root@mobius-pub:~/projects/box# go run main.go run /bin/bash
Running: [/bin/bash] as 639301
Running child: [/bin/bash] as 1
# proc fs 를 새로 마운트해줘야 함
root@container:~/projects/box# ps aux | head -5
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.0 167288 11264 ?        Ss   Sep04   0:15 /sbin/init
root           2  0.0  0.0      0     0 ?        S    Sep04   0:00 [kthreadd]
root           3  0.0  0.0      0     0 ?        I<   Sep04   0:00 [rcu_gp]
root           4  0.0  0.0      0     0 ?        I<   Sep04   0:00 [rcu_par_gp]

# proc fs 마운트를 위한 코드 추가된 결과 (잘 안됨..)
root@mobius-pub:~/projects/box# go run main.go run /bin/bash
Running: [/bin/bash] as 642316
Running child: [/bin/bash] as 1
groups: cannot find name for group ID 121
root@container:/# echo $$
6
callmeaxxe commented 1 year ago

07. (실습) 나만의 컨테이너 생성 - cgroup

요구사항: Process 갯수 제한

image

cgroup 관련 추가 코드
func cg() {
    // 리눅스 커널 cgroup 기능을 사용하기 위해 path 에 접근
    cgroups := "/sys/fs/cgroup/"
    pids := filepath.Join(cgroups, "pids")
    // `linux_campus` control group 생성
    os.Mkdir(filepath.Join(pids, "linux_campus"), 0755)
    // PID max 값을 20개로 지정
    must(ioutil.WriteFile(filepath.Join(pids, "linux_campus/pids.max"), []byte("20"), 0700))
    must(ioutil.WriteFile(filepath.Join(pids, "linux_campus/notify_on_release"), []byte("1"), 0700))
    // 자기 자신의 프로세스 정보 등록
    must(ioutil.WriteFile(filepath.Join(pids, "linux_campus/cgroup.procs"), []byte(strconv.Itoa(os.Getpid())), 0700))
}
main.go
package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "os/exec"
    "path/filepath"
    "strconv"
    "syscall"
)

// docker           run image <CMD> <ARG>
// go run main.go   run       <CMD> <ARG>

// Step1: cgroup을 사용하여 컨테이너 내 process 갯수 제한.
// 실습
// $ go run . run /bin/sh
// $ :(){ :|:& };:

func main() {
    switch os.Args[1] {
    case "run":
        run()
    case "child":
        child()
    default:
        os.Exit(1)
    }
}

func run() {
    fmt.Printf("Running: %v as %d\n", os.Args[2:], os.Getpid())
    cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...)
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout

    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
    }

    cmd.Run()
}

func child() {
    fmt.Printf("Running child: %v as %d\n", os.Args[2:], os.Getpid())

    cg()

    syscall.Sethostname([]byte("container"))

    syscall.Chroot("/tmp/ubuntu")
    syscall.Chdir("/")
    syscall.Mount("proc", "proc", "proc", 0, "")
    defer syscall.Unmount("proc", 0)

    cmd := exec.Command(os.Args[2], os.Args[3:]...)
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout

    must(cmd.Run())

}

func cg() {
    cgroups := "/sys/fs/cgroup/"
    pids := filepath.Join(cgroups, "pids")
    os.Mkdir(filepath.Join(pids, "linux_campus"), 0755)
    must(ioutil.WriteFile(filepath.Join(pids, "linux_campus/pids.max"), []byte("20"), 0700))
    must(ioutil.WriteFile(filepath.Join(pids, "linux_campus/notify_on_release"), []byte("1"), 0700))
    must(ioutil.WriteFile(filepath.Join(pids, "linux_campus/cgroup.procs"), []byte(strconv.Itoa(os.Getpid())), 0700))
}

func must(err error) {
    if err != nil {
        panic(err)
    }
}
root@mobius-pub:~# cd /sys/fs/cgroup/
root@mobius-pub:/sys/fs/cgroup# ls
blkio  cpu  cpuacct  cpu,cpuacct  cpuset  devices  freezer  hugetlb  memory  net_cls  net_cls,net_prio  net_prio  perf_event  pids  rdma  systemd  unified
root@mobius-pub:/sys/fs/cgroup# cd pids/
root@mobius-pub:/sys/fs/cgroup/pids# ls
cgroup.clone_children  cgroup.procs  cgroup.sane_behavior  linux_campus  notify_on_release  release_agent  system.slice  tasks  user.slice
root@mobius-pub:/sys/fs/cgroup/pids# cd linux_campus/
root@mobius-pub:/sys/fs/cgroup/pids/linux_campus# ls
cgroup.clone_children  cgroup.procs  notify_on_release  pids.current  pids.events  pids.max  tasks
root@mobius-pub:/sys/fs/cgroup/pids/linux_campus# cat pids.max 
20
root@mobius-pub:/sys/fs/cgroup/pids/linux_campus# cat tasks 
4673
4675
4676
4677
4678
4679
root@mobius-pub:/sys/fs/cgroup/pids/linux_campus# cat pids.current 
20
root@mobius-pub:/sys/fs/cgroup/pids/linux_campus# 

image