반응형

 

작성일: 2024년 5월 8일

 

 

Go 언어로 코드를 작성하다보면, 다른 사람이 작성한 코드(즉, golang package)를 가져다 쓸 일이 생기는데

아래의 순서로 Golang package를 검색하는 것을 추천한다.

 

1)  Golang 표준 패키지 찾아보기

Golang 표준 패키지 검색 :   이 링크를 클릭 ( https://pkg.go.dev/ )

 

2)  Awesome Go 에서 패키지 찾아보기

"Awesome Go" 에서 관리되는 패키지 목록 검색 :  이 링크를 클릭 ( https://github.com/avelino/awesome-go )

 

 

많은 Go package가 위 2개 저장소에서 제공되니까, 일단 package를 검색해보고 가져다 쓰도록 하자.

위에 없는 package는 구글링해서 쓰면 되겠지만, 패키지의 구현 완성도는 믿을 수 없을 듯 ㅡㅡ;

 

 


 

반응형

 

작성일: 2024년 5월 8일

 

 

설치 작업을 시작하기 전에...

주의 - macOS에 기본 제공되는 VIM을 사용하는 경우 -> Homebrew로 재설치

모든 경우에 해당되는지 모르겠지만, 내 경우 macOS에 기본 제공되는 vim editor를 사용했더니

아래처럼 vim editor 화면에 E319 에러가 떴다.

VIM E319 Error

 

VIM E319 Error

 

아래와 같은 해결 방식이 정석인지는 모르겠지만, 내 경우에는 문제없이 vim과 vim-go 등 vim plugin들이 잘 동작했다.

따라서 macOS를 사용하는 사용자라면, Homebrew를 통해서 vim을 재설치하는 것을 추천한다.

 

$ brew remove vim
$ brew cleanup
$ brew install vim

 

그리고 macOS에 설치된 기본 vim editor보다 homebrew를 통해 설치된 vim editor의 실행 순서를 앞 당기기위 해서 아래와 같이 .zshrc 파일에 2줄을 추가한다.

 

$ cat ~/.zshrc
... 중간 생략 ...
alias vi=/opt/homebrew/Cellar/vim/9.1.0350/bin/vim
alias vim=/opt/homebrew/Cellar/vim/9.1.0350/bin/vim
... 중간 생략 ...

 

 

주의 - vim 버전 확인

$ brew info vim

 

만약, vim 8.0 이상이 아닌 경우라면 vim을 최신 버전으로 재설치

$ brew unlink vim
$ brew uninstall vim
$ brew install vim

 

 


 

 

 

공식 문서를 한번쯤 읽어주는 예절...  (Official Documents)

 

fatih/vimgo 공식 문서 열기

 

 


 

vim-plug 설치

vim-plug는 vim에서 사용할 수 있는 plugin manager로써 plugin을 설치하고 사용할 수 있게 해준다.

$ curl -fLo ~/.vim/autoload/plug.vim \
--create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

 

vim-go 및 기타 plugin 설치

${HOME}/.vimrc 파일에 설치할 plugin을 정의한다.

$ vi ~/.vimrc

... 중간 생략 ...

" ------------------------------------------
" vim-go 설정
" -----------------------------------------
filetype plugin indent on

" vim-Plug Plugin List
call plug#begin('~/.vim/plugged')

" 자동완성을 해주는 Plugin.
" :CoCInstall coc-<lang> 으로 지원되는 Language 설치가 가능
" NOTE: 내 경우는 아래 neoclide/coc.nvim을 설치하지 않았다.
"       왜냐하면, 나는 node.js를 사용하지 않기 때문.
" Plug 'neoclide/coc.nvim', {'branch': 'release'}

" 많이 사용되는 plugin list
Plug 'majutsushi/tagbar'
Plug 'vim-airline/vim-airline'
Plug 'tpope/vim-fugitive'

" vim-go 사용을 위한 Plugin
Plug 'fatih/vim-go', { 'do': ':GoUpdateBinaries' }

" 자동 완성 Plugin 이다.
Plug 'SirVer/ultisnips'
Plug 'maralla/completor.vim'

" Misc utilities
Plug 'milkypostman/vim-togglelist'
Plug 'AndrewRadev/splitjoin.vim'
Plug 'mhinz/vim-startify'

" preview plugin
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } " fzf 사용시 꼭 설치;  ~/.fzf 에서 ./install --all 실행해야 함.
Plug 'junegunn/fzf.vim'

" Initialize plugin system
call plug#end()

... 중간 생략 ...

 

 

${HOME}/.vimrc 파일을 저장하고, 아래와 vim 편집기를 실행 후 vim 내부 명령을 수행한다.

$ vim 

... 중간 생략 ...

:PlugInstall

 

수십 초 정도 시간이 흐르면서 plugin을 설치할 것이다. (시간이 많이 걸리니까 다른 일을 하면서 신경을 끄는게 좋다)

vim plugins 설치 화면

 

 

 

 

vim-go 관련 설정 (개인 취향에 따라 설정 값 정하기)

아래 vim-go 관련 설정은 개인 취향에 따라 값을 정하면 된다.

 

$ vim ~/.vimrc

... 중간 생략 ...

" -------------------------------------------------------
" NOTE: *.go 파일을 저장할 때 Auto formatting하고, 
"       관련 go package를 자동으로 .go 소스 코드 파일에 추가해준다.
let g:go_fmt_command = "goimports"
let g:go_list_type = "quickfix"
let g:go_addtags_transform = "camelcase"

let g:go_autodetect_gopath = 1
let g:go_highlight_types = 1
let g:go_highlight_fields = 1
let g:go_highlight_functions = 1
let g:go_highlight_function_calls = 1
let g:go_highlight_extra_types = 1
let g:go_highlight_generate_tags = 1
let g:go_highlight_operators = 1
let g:go_auto_type_info = 1
let g:go_auto_sameids = 1

" NOTE: 문서를 Popup window 형태로 보여주기 (:GoDoc 명령)
let g:go_doc_popup_window = 1 

" NOTE: go source code 작성할 때, 실시간으로 자동 완성할 추천 문장을 보여줌.
let g:completor_filetype_map = {}
let g:completor_filetype_map.go = {'ft': 'lsp', 'cmd': 'gopls - remote=auto'}

" NOTE: quickfix 이동 및 open/close
nnoremap <C-n> :cnext<CR>
nnoremap <C-p> :cprevious<CR>
nnoremap <LocalLeader>q :call ToggleQuickfixList()<CR>

" NOTE: Coverage toggle
nnoremap <LocalLeader>c :GoCoverageToggle<CR>

" NOTE: Go 프로그램을 build, run, test하는 명령에 대한 설정
autocmd FileType go nnoremap <Tab>b :GoBuild<CR>
autocmd FileType go nnoremap <Tab>r :GoRun<CR>
autocmd FileType go nnoremap <Tab><Tab>r :GoRun %<CR>

autocmd FileType go nnoremap <Tab>t :GoTest<CR>
autocmd FileType go nnoremap <Tab><Tab>t :GoTestFunc<CR>
autocmd FileType go nnoremap <Tab>c :GoCoverageToggle<CR>
" -------------------------------------------------------

... 중간 생략 ...

 

 

vim-go 및 관련 plugin 설치 완료

 

 

간단하게 vim-go 동작 유무 확인하기

 

아래와 같이 새 파일 my-main.go 를 편집해보다.

$ vim  my-main.go

 

아래와 같이 기본 소스 코드 틀이 만들어진 .go 파일이 자동으로 생긴다.

vim-go가 자동으로 만들어준 새 소스 코드 파일

 

 

 

 

vim-go 사용법

 

 

많은 분이 잘 정리했으니까, 아래 문서를 보는 것을 추천!

 

A 문서 링크 열기

 

B 문서 링크 열기

 

 

 

 

 


 

반응형
작성일: 2024년 5월 8일

 

Go 언어를 2015~2020년 사이에 사용하고, 그 뒤로 거의 쓰지 않다가 오늘 go-pktgen 소스 코드를 보다가

몇년 사이 golang에 많은 변화가 있다는 것을 알았다.

내가 2015년에 책으로 배운 Go 언어에 대한 지식만으로 프로그래밍하면 큰코 다치겠다 ㅋㅋ

(회사 동료들이 같이 Go 언어를 사용하면, 같이 성장할텐데... 회사 동료들은 C, C++ 찬양론자들이라서... ㅠㅠ)

 

새로운 Golang Feature도 파악하고, 다시 기억도 좀 살리기 위해 Golang 학습 문서를 봐야겠다

오늘 기준으로 볼만한 온라인 문서를 찾아보니... 아래와 같다.

 

 

Tucker의 Go 언어 프로그래밍 (2021년, 공봉식)

2024년 5월 8일 기준으로 이 책을 eBook으로 구입해서 보고 있는데...

내가 9년 전에 구입해서 봤던 책보다는 3배 정도 설명이 자세하고 친절하다.

9년 전에 구입한 Go 언어 책이 미울 정도이다. (그 책을 읽은 시간이 아깝다 ㅠㅠ)

종이 책(또는 eBook) 중에서는 이 책이 가장 괜찮은 것 같다. (나의 주관적 느낌)

 

Tucker의 Go 언어 프로그래밍 (책 표지)

 

 

아래 화면은 내가 구입한 eBook이고 macOS용 Crema 앱으로 보고 있는 중이다.

컴퓨터학(Compute Science) 전공하고 있는 대학생, 또는 컴퓨터학을 수료한 졸업생이라면 쉽게 읽힐 수 있는 책이다.

왜냐고? 책 중간 중간에 Memory 구조와 Go source code를 설명하는 부분이 자주 등장하는데,

약간이라도 CPU, Memory 구조 및 동작 방식을 알고 보는 것이 재미있다.

예를 들어, 아래 빨간 동그라미로 표시한 부분과 같은 표현이 익숙하고 잘 이해가 되는 분이라면 이 책을 재미있게 읽을 수 있다.

 

Tucker의 Go 언어 프로그래밍 (eBook으로 구입해서 macOS용 Crema로 보고 있음)

참고: 저작권 침해가 될 것 같아서 위와 같이 책 내용을 가렸습니다.

 

Tucker의 Go 언어 프로그래밍 (eBook으로 구입해서 macOS용 Crema로 보고 있음)

 

아무튼 이 책은 적극 추천~~~
(이 책의 저자, 출판사로부터 받은 거 없음. 그냥 개인적 소신임)

 

 

Tucker의 Go 언어 프로그래밍 / 동영상 강의

YouTube 채널, Playlist: https://www.youtube.com/playlist?list=PLy-g2fnSzUTBHwuXkWQ834QHDZwLx6v6j

 

 

 

Effective Go (Go Official Site)

영어 문서: https://go.dev/doc/effective_go 

한국어 문서: https://gosudaweb.gitbooks.io/effective-go-in-korean/content/

 

 

 

 

Facebook 그룹

가끔 Facebook을 통해서 공식 행사를 알려준다. (예를 들어, GopherCon Korea 2024 같은 이벤트)

https://www.facebook.com/groups/golangko

 

 

 

 


 

반응형

작성일: 2023년 9월 20일

 

 

 

Client 장비에 network port가 여러개 있는 경우, 특정 network port를 지정하여 IP 패킷을 전송하고 싶을 때가 있다.

이럴 때, source IP address를 binding하면 특정 network port를 통해 IP 패킷이 전송된다.

참고:
  일반적으로 Target IP address로 가기 위한 routing path 및 network port는 
  OS에 있는 Routing table을 통해서 자동으로 결정된다.
  그러나 Target IP address로 가기 위한 routing path가 1개가 아닌 2개 이상인 경우에는
  어느 network port를 통해서 IP 패킷이 나갈지 예측하기 어렵다.  
package main

import (
    "fmt"
    "io/ioutil"
    "net"
    "net/http"
    "time"
)


func main() {
##
## NOTE:  14.33.80.179를 Source IP address로 지정한다. (즉, Source IP address binding)
##
    localAddr, err := net.ResolveIPAddr("ip", "14.33.80.179")
    if err != nil {
        panic(err)
    }

    localTCPAddr := net.TCPAddr{
        IP: localAddr.IP,
    }

    d := net.Dialer{
        LocalAddr: &localTCPAddr,
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
    }

    tr := &http.Transport{
        Proxy:               http.ProxyFromEnvironment,
        Dial:                d.Dial,
        TLSHandshakeTimeout: 10 * time.Second,
    }

    webclient := &http.Client{Transport: tr}

    // Use NewRequest so we can change the UserAgent string in the header
    req, err := http.NewRequest("GET", "https://www.naver.com", nil)
    if err != nil {
        panic(err)
    }

    res, err := webclient.Do(req)
    if err != nil {
        panic(err)
    }

    fmt.Println("DEBUG", res)
    defer res.Body.Close()

    content, err := ioutil.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%s", string(content))
}

 

 

 

 


 

반응형

Go Language로 특정 Process의 CPU, Memory 사용량을 계산하고 싶다면,

아래의 코드를 Build해서 사용하면 된다.

 

참고: Linux, MacOS, Unix, Windows 모두 동작하는 코드임.

 

 

// Filename: proc_usage.go

package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"math"
	"os/exec"
	"path"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

const (
	statTypePS   = "ps"
	statTypeProc = "proc"
)

// SysInfo will record cpu and memory data
type SysInfo struct {
	CPU    float64
	Memory float64
}

// Stat will store CPU time struct
type Stat struct {
	utime  float64
	stime  float64
	cutime float64
	cstime float64
	start  float64
	rss    float64
	uptime float64
}

type fn func(int) (*SysInfo, error)

var fnMap map[string]fn
var platform string
var history map[int]Stat
var historyLock sync.Mutex
var eol string

// Linux platform
var clkTck float64 = 100    // default
var pageSize float64 = 4096 // default

func init() {
	platform = runtime.GOOS
	if eol = "\n"; strings.Index(platform, "win") == 0 {
		platform = "win"
		eol = "\r\n"
	}
	history = make(map[int]Stat)
	fnMap = make(map[string]fn)
	fnMap["darwin"] = wrapper("ps")
	fnMap["sunos"] = wrapper("ps")
	fnMap["freebsd"] = wrapper("ps")
	fnMap["openbsd"] = wrapper("proc")
	fnMap["aix"] = wrapper("ps")
	fnMap["linux"] = wrapper("proc")
	fnMap["netbsd"] = wrapper("proc")
	fnMap["win"] = wrapper("win")

	if platform == "linux" || platform == "netbsd" || platform == "openbsd" {
		initProc()
	}
}

func initProc() {
	clkTckStdout, err := exec.Command("getconf", "CLK_TCK").Output()
	if err == nil {
		clkTck = parseFloat(formatStdOut(clkTckStdout, 0)[0])
	}

	pageSizeStdout, err := exec.Command("getconf", "PAGESIZE").Output()
	if err == nil {
		pageSize = parseFloat(formatStdOut(pageSizeStdout, 0)[0])
	}

}

func wrapper(statType string) func(pid int) (*SysInfo, error) {
	return func(pid int) (*SysInfo, error) {
		return stat(pid, statType)
	}
}

func formatStdOut(stdout []byte, userfulIndex int) []string {
	infoArr := strings.Split(string(stdout), eol)[userfulIndex]
	ret := strings.Fields(infoArr)
	return ret
}

func parseFloat(val string) float64 {
	floatVal, _ := strconv.ParseFloat(val, 64)
	return floatVal
}

func statFromPS(pid int) (*SysInfo, error) {
	sysInfo := &SysInfo{}
	args := "-o pcpu,rss -p"
	if platform == "aix" {
		args = "-o pcpu,rssize -p"
	}
	stdout, _ := exec.Command("ps", args, strconv.Itoa(pid)).Output()
	ret := formatStdOut(stdout, 1)
	if len(ret) == 0 {
		return sysInfo, errors.New("Can't find process with this PID: " + strconv.Itoa(pid))
	}
	sysInfo.CPU = parseFloat(ret[0])
	sysInfo.Memory = parseFloat(ret[1]) * 1024
	return sysInfo, nil
}

func statFromProc(pid int) (*SysInfo, error) {
	sysInfo := &SysInfo{}
	uptimeFileBytes, err := ioutil.ReadFile(path.Join("/proc", "uptime"))
	if err != nil {
		return nil, err
	}
	uptime := parseFloat(strings.Split(string(uptimeFileBytes), " ")[0])

	procStatFileBytes, err := ioutil.ReadFile(path.Join("/proc", strconv.Itoa(pid), "stat"))
	if err != nil {
		return nil, err
	}
	splitAfter := strings.SplitAfter(string(procStatFileBytes), ")")

	if len(splitAfter) == 0 || len(splitAfter) == 1 {
		return sysInfo, errors.New("Can't find process with this PID: " + strconv.Itoa(pid))
	}
	infos := strings.Split(splitAfter[1], " ")
	stat := &Stat{
		utime:  parseFloat(infos[12]),
		stime:  parseFloat(infos[13]),
		cutime: parseFloat(infos[14]),
		cstime: parseFloat(infos[15]),
		start:  parseFloat(infos[20]) / clkTck,
		rss:    parseFloat(infos[22]),
		uptime: uptime,
	}

	_stime := 0.0
	_utime := 0.0

	historyLock.Lock()
	defer historyLock.Unlock()

	_history := history[pid]

	if _history.stime != 0 {
		_stime = _history.stime
	}

	if _history.utime != 0 {
		_utime = _history.utime
	}
	total := stat.stime - _stime + stat.utime - _utime
	total = total / clkTck

	seconds := stat.start - uptime
	if _history.uptime != 0 {
		seconds = uptime - _history.uptime
	}

	seconds = math.Abs(seconds)
	if seconds == 0 {
		seconds = 1
	}

	history[pid] = *stat
	sysInfo.CPU = (total / seconds) * 100
	sysInfo.Memory = stat.rss * pageSize
	return sysInfo, nil
}

func stat(pid int, statType string) (*SysInfo, error) {
	switch statType {
	case statTypePS:
		return statFromPS(pid)
	case statTypeProc:
		return statFromProc(pid)
	default:
		return nil, fmt.Errorf("Unsupported OS %s", runtime.GOOS)
	}
}

// GetStat will return current system CPU and memory data
func GetStat(pid int) (*SysInfo, error) {
	sysInfo, err := fnMap[platform](pid)
	return sysInfo, err
}

 

 

 

// Filename: main.go

package main

import (
    "os"
    "fmt"
    "time"
    "strconv"
)

func main() {
    myPid, _ := strconv.Atoi(os.Args[1])

    for i:= 0; i < 100; i++ {
        sysInfo, _ := GetStat(myPid)
        fmt.Println("CPU Usage     :", sysInfo.CPU)
        fmt.Println("Mem Usage(RSS):", sysInfo.Memory)
        time.Sleep(5 * time.Second)
    }
}

 

위와 같이 Go source code를 모두 작성했다면, 아래처럼 build하고 실행하면 된다.

 

$ go mod init andrew.space/proc_usage
go: creating new go.mod: module andrew.space/proc_usage
go: to add module requirements and sums:
	go mod tidy
    
$ go mod tidy

$ go build

$ ./proc_usage 4314
CPU Usage     : 52.92167225853122
Mem Usage(RSS): 2.018664448e+09
CPU Usage     : 39.800000000000004
Mem Usage(RSS): 2.018664448e+09
CPU Usage     : 47.30538922366738
Mem Usage(RSS): 2.018664448e+09
...
...

 

top 명령으로 본 것과 결과가 동일했다.

반응형

 

 

Kubernetes 또는 OCP 클러스터에 내가 만든 App을 Deploy하고, 이 App이 Prometheus에서 Scraping하도록 설정하고 싶을 때가 있다.

그럴 때 아래 문서를 따라하면 Metric Exporting, Scraping이 잘 된다.

(단, ServiceMonitor 리소스 설정은 아래 문서에서 살짝 놓친 부분이 있으니까, 이 블로그 페이지의 마지막에 있는 예제를 따를 것)

 

 

모니터링 OpenShift Container Platform 4.10 | Red Hat Customer Portal

이 문서에서는 OpenShift Container Platform에서 Prometheus를 구성 및 사용하는 방법에 대한 지침을 설명합니다.

access.redhat.com

 

위 문서에서 7.2 사용자 정의 프로젝트에 대한 메트릭 컬렉션 설정 부분을 보면 된다.

 

 

 


 

 

또는 같은 내용이지만, Page 단위로 분리된 문서를 보고 싶다면 아래 Web Page를 보는 것도 추천 !!!

(다시 말하지만, 위 문서랑 완전히 똑같은 내용이고 Chapter 별로 Page를 분리한 문서 Form이다)

 

 

7.2. 사용자 정의 프로젝트에 대한 메트릭 컬렉션 설정 OpenShift Container Platform 4.10 | Red Hat Customer Por

Access Red Hat’s knowledge, guidance, and support through your subscription.

access.redhat.com

 

 


 

 

 

주의:
  위 문서가 대체로 절차를 잘 설명하고 있지만, ServiceMonitor 리소스에 대한 설정이 이상하다.
  그래서 ServiceMonitor 리소스는 내가 별도로 만들었다.

 

 

웹 문서의 설명과 다른 부분만 아래와 같이 Comment를 추가했다. 

Comment가 붙어 있는 라인만 잘 수정하면, 잘 동작한다.

 

##
## File name: servicemonitor.yaml
##

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    k8s-app: almighty
  name: almighty
  namespace: openshift-monitoring  ## Note: 반드시 이 namespace로 설정해야 한다.
spec:
  endpoints:
  - interval: 15s
    port: web
    scheme: http
    path: /metrics       ## 이 설정을 꼭 넣자.
  selector:
    matchLabels:
      app: almighty      ## 이 부분이 Pod, Service의 label 값과 같은지 꼭 확인해야 한다.
  namespaceSelector: 
    matchNames:
      - almighty         ## Service 리소스의 값과 같은지 확인해야 한다.

 

servicemonitor.yaml 파일을 작성하면, 아래와 같이 kubectl 명령으로 kubernetes cluster에 적용한다.

$ kubectl apply -f servicemonitor.yaml

 


그런데 여기서 생각해볼 것이 있다.
만약, Service에 포함된 Pod가 2개 이상일 때는 위 ServiceMonitor처럼 Scrapping을 시도하면
2개의 Pod 중에서 1개의 Pod만 Scrapping에 응답하기 때문에 반쪽 짜리 Scrapping이 되는 문제가 있다.
Pod가 10개라면, 1개 Pod 입장에서 본다면 Scrapping 인터벌(주기)는 10배로 더 길어진다.

따라서  Service에 포함되는 Pod가 2개 이상일 때는 ServiceMonitor 보다 PodMonitor 조건을 사용하는 것이 좋다.
PodMonitor를 사용하면, Prometheus가 각 Pod마다 접근해서 Scrapping한다.

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: almighty
  labels:
    app: almighty
## FIXME
  namespace: openshift-monitoring
spec:
  selector:
    matchLabels:
## FIXME
      app: almighty
  podMetricsEndpoints:
## FIXME
  - port: web
    path: /metrics
    interval: 7s   ## 테스트를 해보면, 1s 로 설정해도 잘 동작한다.
    scheme: http
  namespaceSelector:
    matchNames:
## FIXME
      - almighty

 

 

 

 

 

Prometheus Web UI를 열고, 아래와 같이 Qeury(PromQL)을 입력한다.

## Example 1
http_request_duration_seconds_bucket{code="200",handler="found"}

## Example 2
irate(http_requests_total{namespace="almighty", code="200"}[5m])

 

 

irate 함수를 사용한 PromQL

 

 

 

위와 같이 챠트가 잘 그려지면, Prometheus의 Scraper가 사용자 App의 Metrics을 잘 Scraping하고 있는 것이다.

 

 

 

참고:  Prometheus Metrics 테스트를 위한 Example App

아래 Go Source Code를 이용하는 것이 제일 테스트하기 편하다. (적극 추천)

 

 

GitHub - brancz/prometheus-example-app: Go app that exposes metrics about its HTTP handlers.

Go app that exposes metrics about its HTTP handlers. - GitHub - brancz/prometheus-example-app: Go app that exposes metrics about its HTTP handlers.

github.com

 

 

 

 

 

 

 


 

 

 

 

Troubleshooting (문제 해결)

 

 

위 예시대로 사용하려 했는데 잘 동작하지 않는다면, 아래 설정 작업을 따라해볼 것 !!!

 

Prometheus의 Cluster Role Binding 설정 작업

만약, Kubernetes 또는 OCP를 초기 구축하고 나서 User App에 대한 Metrics을 Scrapping할 수 있는 Role을 Prometheus ServiceAccount에 부여하지 않았다면, 꼭 Cluster Role과 ClusterRoleBinding을 설정해줘야 한다.

Cluster Role Binding 작업이 안 되어 있으면, 위에서 예제로 설명했던 ServiceMonitoring, PodMonitoring을 모두 

"endpoints is forbidden" 에러가 발생할 것이다. (아래 Error Log을 참고)

 

Prometheus Error Log 예시

$ kubectl logs -f -n openshift-monitoring prometheus-k8s-0

ts=2022-08-10T03:29:51.795Z caller=log.go:168 level=error component=k8s_client_runtime func=ErrorDepth msg="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:471: Failed to watch *v1.Pod: failed to list *v1.Pod: pods is forbidden: User \"system:serviceaccount:openshift-monitoring:prometheus-k8s\" cannot list resource \"pods\" in API group \"\" in the namespace \"almighty\""

 

위 에러를 없애기 위해 아래와 같이 ClusterRole, ClusterRoleBinding 리소스를 만들어야 한다.

##
## Cluster Role 생성을 위한 YAML 파일 작성
##

$ cat prometheus-cluster-role.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: my-prometheus
rules:
- apiGroups: [""]
  resources:
  - pods
  - services
  - endpoints
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources:
  - configmaps
  verbs: ["get"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]


##
## Cluster Role Binding 생성을 위한 YAML 파일 작성
##

$ cat prometheus-cluster-role-binding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-prometheus
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: clusterlogging-collector-metrics
subjects:
- kind: ServiceAccount
  name: prometheus-k8s
  namespace: openshift-monitoring

##
##
##

$ kubectl apply -f prometheus-cluster-role.yaml

$ kubectl apply -f prometheus-cluster-role-binding.yaml

 

위 설명에 대한 자세한 정보는 아래 GitHub의 Prometheus RBAC 챕터를 참고.

 

 

GitHub - prometheus-operator/prometheus-operator: Prometheus Operator creates/configures/manages Prometheus clusters atop Kubern

Prometheus Operator creates/configures/manages Prometheus clusters atop Kubernetes - GitHub - prometheus-operator/prometheus-operator: Prometheus Operator creates/configures/manages Prometheus clus...

github.com

위readme.md 내용을 화면 캡처한 것!

 

 

참고: 다른 블로거의 글

Red Hat 문서에 오류가 있어서 한참 헤메고 있을 때, 아래 블로그 글에서 해법을 찾았다 ^^

쉽게 예제를 만들어서 설명하고 있어서, 이해하기 좋다.

 

 

 

prometheus-operator | loliot

prometheus-operator

wiki.loliot.net

 

 

반응형

 

참고:
Go Module과 Package를 사용하는 방법이 Go Version에 따라 다를 수 있다.
아래 예제는 Go 1.17을 이용한 예제이고, 아마 Go 1.11 이상을 사용하는 경우라면 아래 설명이 잘 맞을 것이다.

 

여담:
나는 본업이 Go Language를 사용해서 개발하는 개발자가 아니다보니, 2~3년에 한번 잠깐 GoLang을 이용해서 개발하려고 하면 Go Package와 Module 때문에 시간을 허비하게 된다.
그래서 오늘은 기왕 스터디한 것을 2~3년 뒤에 또 GoLang을 사용할 일이 있을 경우, 쉽게 PackageModule 개념을 습득하기 위해 예제를 통해서 메모를 할까 한다.

 


 

Go PKG를 다루기 위해서는 크게 3가지 방식이 있다.

 

방식 A

  $GOPATH 경로를 설정하고, 이 $GOPATH 경로 안에서만 Package를 사용하는 방법. (암묵적 Module 사용하기)

 

방식 B    <-- 가장 많은 개발자가 선호하는 방식이 아닐까 생각한다.

  Module을 명시적으로 선언(go mod init x.y.z)하고, 내가 원하는 폴더 어디에서든 Go Source Code를 작성하는 방법.

 

방식 C

  go ven를 이용해서 현재 디렉토리 하위에서 모든 Package를 사용하는 방법.

 

각 Package 사용 방식에 대해서 아래에서 세부적으로 다룬다.

 

 

$GOPATH 경로를 선언하여 Package를 사용하는 방식

설명보다는 예제 코드를 보는 것이 좋을 듯~~~

 

$  vi  ~/.bash_profile
...
export GOPATH=$(go env GOPATH)
...

$  .  ~/.bash_profile
$  echo  $GOPATH
/Users/sejong/go

$  cd  $GOPATH
##
## 이제부터는 $GOPATH/src 디렉토리 밑에서만 코드를 작성해야 한다.
##
$  mkdir  -p src/myexample
$  cd  src/myexample
$  mkdir  myPkgAlpha
$  cd  myPkgAlpha
$  vi  example.go

package myPkgAlpha

import "fmt"

# 주의: 패키지 외부에서 참조할 수 있도록 함수 이름은 대문자로 시작해야 한다.

func MyExamplePrint() {
  fmt.Println("My Alpha PKG")
}

$  cd  ..
$  vi  main.go

package main

import (
  "fmt"
  "myexample/myPkgAlpha"
)

func main() {
  fmt.Println("This is main package.")
  myPkgAlpha.MyExamplePrint()
}

$  go  mod  init  myexample
$  go  mod  tidy
$  go  build

$  ./myexample
This is main package.
My Alpha PKG

$  tree
.
├── go.mod
├── main.go
├── myPkgAlpha
│   └── example.go
└── myexample
$

 

 

 

 

$GOPATH 경로가 아닌, 나만의 별도의 Project 폴더에서 Go Module 사용하는 방식

Go module을 사용하면 $GOPATH 경로가 아닌 아무 곳(디렉토리)에서 Go Source Code를 작성할 수 있는 자유가 생긴다.

긴 설명은 생략하고, 아래 예제처럼 Go Package와 Module을 사용하면 된다.

 

##
##  새로운 프로젝트를 위한 폴더를 만들자~
##
$  mkdir  go-mod-pkg-example
$  cd  go-mod-pkg-example

##
##  Go Package를 만들자.  (myPkgAA)
##  주의사항: package 이름과 folder 이름을 동일하게 작성해야 한다.
##
$  mkdir  myPkgAA
$  cd  myPkgAA

##
## 아래와 같이 myPkgAA 패키지에 대한 코드를 작성한다.
##
$  vi  example.go

package myPkgAA

const (
    COMPANY  = "Sejong Inc"
    LOCATION = "Seoul Korea"
)

##
##  Package가 1개만 있으면 심심하니까, 하나 더 만들자. (mypkg_bb)
##

$  cd  ..
$  mkdir  mypkg_bb
$  cd  mypkg_bb

##
## 아래와 같이 mypkg_bb 패키지에 대한 코드를 작성한다.
##
$  vi  example.go

package mypkg_bb

import (
    "fmt"
)

// NOTE: 함수 이름은 대문자로 시작해야 한다.
func DoSomething(company string, location string) {
    fmt.Println("[In mypkg_bb]")
    fmt.Println("  Company  :", company)
    fmt.Println("  Location :", location)
}


##
##  위에서 작성한 Package를 사용하는 Go Module Code를 작성하자.
##

$  go  mod  init  myexample.cncf/sejong/myexample
$  vi  main.go

// NOTE: package 이름을 main 으로 한다.
package main

// NOTE: 이 module의 이름이 "myexample.cncf/sejong/myexample" 이었기 때문에
//       myPkgAA 패키지를 import하려면 아래처럼 경로를 작성해야 한다.
import (
    "myexample.cncf/sejong/myexample/myPkgAA"
    "myexample.cncf/sejong/myexample/mypkg_bb"
)

func main() {
    mypkg_bb.DoSomething(myPkgAA.COMPANY, myPkgAA.LOCATION)
}

## 솔직히, 위 예제 패키지(로컬 패키지)만 이용하는 경우에는 아래 tidy 명령을 필요 없다.
## 그러나 main.go 파일에서 외부 pkg를 import 한 것도 포함했다면,
## 아래처럼 tidy 명령을 수행해야 한다.
$  go  mod  tidy

$  go  build

$  ./myexample
[In mypkg_bb]
  Company  : Sejong Inc
  Location : Seoul Korea
  
$  tree
.
├── go.mod
├── main.go
├── myPkgAA
│   └── example.go
├── myexample
└── mypkg_bb
    └── example.go

$

 

 

Vendor  방식의 패키지 관리

 

vendor 디렉토리에 특정 버전의 외부 패키지들을 저장 시킨 뒤 빌드에 참여시킴으로써 버전 일관성 문제를 해결 할 수 있다.

그리고 다운로드 불가한 외부 패키지를 어떻게든 한번만 구할 수만 있다면 다운로드 불가 문제도 피할 수 있다.

Container Image처럼 내가 작성한 Go Module 소스 코드가 있는 폴더에 의존성이 있는 모든 Package를 vendor/ 폴더 디렉토리에 다운로드 받는 다는 것이 가장 큰 특징이다.

 

자세한 내용은 아래 Go Module Vendoring 문서를 참고할 것 !!!

 

https://go.dev/ref/mod#go-mod-vendor

 

Go Modules Reference - The Go Programming Language

 

go.dev

 

## 코드를 작성한 상태에서 아래 명령을 실행~

$  mkdir MyExampleApp
$  vi main.go 
...
...

$  go mod vendor

$  ls
main.go   vendor/

 

 

 

 

반응형

 

REST API Server

Go 기본 Package만 사용해서 REST API Server 만들기

일단, 아래 블로그가 예제 코드를 복사해서 실습하기 좋게되어 있다.

https://woony-sik.tistory.com/12

 

Golang REST API 만들기

오늘은 Golang으로 간단한 REST API를 만드는 방법을 쓸까 한다. 바로 시작하자 우선은 Directory를 하나 만들고 시작 mkdir rest go module 등록 go mod init noah.io/ark/rest main.go 생성 touch main.go Direc..

woony-sik.tistory.com

 

 

gorilla package의 mux를 이용해서 REST API Server 만들기

이 블로그는 자동 Test하는 Code까지 포함되어 있다.

따라서 상용 Software PKG 개발할 때, 참고하면 좋다.

 

https://velog.io/@soosungp33/Golang%EC%9C%BC%EB%A1%9C-%EC%9B%B9%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B04

 

 

Golang으로 웹서버 만들기(4)

RESTful API - GET과 POST 다뤄보기

velog.io

 

 

 

REST API Client

go-resty 라는 'REST client library'를 이용하면, 쉽게 REST API Client App을 개발할 수 있다.

아래 GitHub에 Case by Case로 Example이 있기 때문에 장황한 설명보다는 아래 Web Docs에 있는 Example Code를 보고 이해하고 따라해보는 것이 좋을 듯하다.

https://github.com/go-resty/resty
 

GitHub - go-resty/resty: Simple HTTP and REST client library for Go

Simple HTTP and REST client library for Go. Contribute to go-resty/resty development by creating an account on GitHub.

github.com

 

go-resty를 소개한 블로그이다. 쉽게 설명했으니까 한번 읽어보면 좋을 것이다.

go-resty를 사용하면, JSON Type의 Request / Response 를 Marshal, Unmarshal할 필요가 없다.

 

https://wookiist.dev/104
 

[Go/Golang] Go의 HTTP & REST Client 라이브러리 - Resty

Go의 HTTP & REST Client 라이브러리 - Resty API Client 이전 포스팅에서 다뤘던 Echo는 Go의 Web Framework입니다. Echo로 구현한 프로그램은 API Server 등으로 동작할 수 있고, 큰 어려움 없이 Web Server로..

wookiist.dev

 

 

 

 

 

기타 Reference 하면 좋을 Blog

 

https://doitnow-man.tistory.com/259

 

[Go Lang] 4. 실전 - http server + swagger 만들기

목표 web framework를 사용하여 간단한 web api server를 만들어 보겠습니다. 배포는 추후 포스트에서 다루겠습니다. 개발 환경 - ubnutu 18.04 - go version go1.16.3 linux/amd64 (업그레이드 방법: golang.org..

doitnow-man.tistory.com

 

 

블로그 작성자: sejong.jeonjo@gmail.com

 

 

+ Recent posts