공식홈페이지 접속: https://go.dev/
클라우드 서버에 다운로드하므로 tar 다운로드 링크 복사
$ wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
# 이후 Docs 참조해 진행. 관리자 권한으로 실행
$ sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz
$ vim $HOME/.profile
# 아래의 내용 추가
export PATH=$PATH:/usr/local/go/bin
$ source ~/.profile
$ go version
go version go1.23.0 linux/amd64
이후 VS code extension에서 go plugin 설치(클라우드에)
# hello world 위한 디렉토리 생성
$ mkdir -p ~/projects/box
# go 모듈 생성
$ go mod init example/box
go: creating new go.mod: module example/box
$ ls -l
total 4
-rw-rw-r-- 1 ubuntu ubuntu 30 Sep 5 04:46 go.mod
$ vi main.go
main.go
package main
import "fmt"
func main() {
fmt.Println("Hello Go")
}
main.go 실행
$ go run main.go
Hello Go
외부패키지 import
import (
"fmt"
"rsc.io/quote"
)
패키지 의존성 관리
$ go mod tidy
go: finding module for package rsc.io/quote
go: downloading rsc.io/quote v1.5.2
go: found rsc.io/quote in rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
main.go 실행
$ go run main.go
Ahoy, world!
package main
import (
"fmt"
"time"
)
func readword(ch chan string) {
fmt.Println("Type a word, then hit Enter.")
var word string
fmt.Scanf("%s", &word)
ch <- word
}
func printchar() {
// while문과 유사
for {
fmt.Printf(".")
time.Sleep(2 * time.Second)
}
}
func main() {
// 지연처리. 앱 종료 후 처리됨
defer fmt.Println("===== BYE..")
// 새로운 스레드 생성
go printchar()
// 채널 생성
ch := make(chan string)
go readword(ch)
// Switch와 유사
select {
case word := <-ch:
fmt.Println("\nReceived: ", word)
}
}
주요 go 커맨드
# go 바이너리 빌드
$ go build
# go 빌드/실행
$ go run
# 새로운 모듈 생성
$ go mod init
# 필요한 의존성 설치/필요없는 의존성 삭제
$ go mod tidy
리눅스의 Namespace/Cgroups 기능 이용
Go 기반으로 환경 구성 -> 독립된 환경에서 쉘 명령어 실행
CRI 런타임: Container runtime interface -> 컨테이너 런타임이 쿠버네티스의 kubelet과 어떻게 통신해야 하는지
OCI 런타임: Open container initiative -> 컨테이너 포맷과 런타임의 표준화
$ docker run images <CMD> <ARGS>
$ go run main.go run <CMD> <ARG>
호스트명, 프로세스ID, 프로세스 리스트 정보 isolation -> UTS, PID, Mount namespace
step1
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() {
// argument 전부 출력
fmt.Printf("Running: %v\n", os.Args[2:])
}
$ go run . run ls -l
Running: [ls -l]
step2
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()
}
$ go run . run pwd
Running: [pwd]
/home/ubuntu/projects/box
step3
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)
}
}
# bash 셀로 컨테이너 진입해서 host명을 바꿈
$ go run . run /bin/bash
Running: [/bin/bash]
$ hostname
ip-{ip}
$ hostname container
$ hostname
container
# 다른 쉘에서 확인하면 별도의 namespace이기 때문에 container host가 잡히지 않음
$ hostname
ip-{ip}
step4
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:]...)
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)
}
}
# hostname 변경된것을 확인
$ go run .run /bin/bash
Running: [/bin/bash]
Running child: [/bin/bash]
root@container
step5
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
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
*/
// 루트 파일시스템 변경
// 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 must(err error) {
if err != nil {
panic(err)
}
}
$ go run . run /bin/bash
Running: [/bin/bash] as 869604
Running child: [/bin/bash] as 1
$ echo $$
4
$ ps aux | head -5
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 1.1 22212 11008 ? Ss Sep04 0:10 /usr/lib/systemd/systemd --sys
# 격리되지 않았음 .루트 파일시스템 다운로드 필요(컨테이너 내부)
$ wget {github 파일시스템}
$ mkdir /tmp/ubuntu
$ tar zxf ubuntu-focal-oci-amd64-root.tar.gz -C /tmp/ubuntu/
$ ls -l /tmp/ubuntu
total 60
lrwxrwxrwx 1 root root 7 Apr 5 2022 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Apr 15 2020 boot
drwxr-xr-x 2 root root 4096 Apr 5 2022 dev
drwxr-xr-x 30 root root 4096 Apr 5 2022 etc
drwxr-xr-x 2 root root 4096 Apr 15 2020 home
# 이제, 위의 go 코드에서 루트 파일시스템 변경코드 주석해제
# 컨테이너 탈출
$ exit
$ go run . run /bin/bash
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 1225424 1920 ? Sl 10:20 0:00 /proc/self/exe child /bin/bash