iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 16
0
DevOps

遠離 DevOops系列 第 16

[Day 16] Oops!Golang - CI/CD with Data Race Detector

Data Race

是個非常難找的錯誤類型之一

Data races are among the most common and hardest to debug types of bugs in concurrent systems. A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write. - golang 官網

講到 Data Race 大家可以來看 weiweiwesley的這篇 - Day10 Race Condition

今天我們的目的是提前偵測到 Data Race,避免程式有產生 Data Race的狀況!

要怎麼將Data Race Detector導入到CI/CD流程內呢?

先來體驗使用 Data Race Detector

golang code

package main

import "fmt"

func main() {
	c := make(chan bool)
	m := make(map[string]string)
	go func() {
		m["1"] = "a" // First conflicting access.
		c <- true
	}()
	m["2"] = "b" // Second conflicting access.
	<-c
	for k, v := range m {
		fmt.Println(k, v)
	}
	fmt.Println("Hello, ithome")
}

執行

go run -race main.go

馬上就能看到 data race的警告

==================
WARNING: DATA RACE
Write at 0x00c000092180 by goroutine 7:
  runtime.mapassign_faststr()
      /usr/local/go/src/runtime/map_faststr.go:202 +0x0
  main.main.func1()
      /ithome/main.go:9 +0x5d

Previous write at 0x00c000092180 by main goroutine:
  runtime.mapassign_faststr()
      /usr/local/go/src/runtime/map_faststr.go:202 +0x0
  main.main()
     /ithome/main.go:12 +0xc6

Goroutine 7 (running) created at:
  main.main()
      /ithome/main.go:8 +0x97
==================
2 b
1 a
Found 1 data race(s)

還有另一種情況是
造成 data race的code 可能需要某些條件才會被執行到!

package main

import "fmt"

func main() {
	_, err := fmt.Println("Hello, ithome")
	if err != nil {
		gorace()
	}
}

func gorace() {
	c := make(chan bool)
	m := make(map[string]string)
	go func() {
		m["1"] = "a" // First conflicting access.
		c <- true
	}()
	m["2"] = "b" // Second conflicting access.
	<-c
	for k, v := range m {
		fmt.Println(k, v)
	}
}

所以建議若有用goroutine的專案需要go build -race 的版本放在某一站去執行。

dockerfile

FROM golang:1.14

WORKDIR /ithome
COPY . /ithome
RUN cd /ithome && go build -race

CMD ["./ithome"]
==================
WARNING: DATA RACE
Write at 0x00c000066250 by goroutine 7:
  runtime.mapassign_faststr()
      /usr/local/go/src/runtime/map_faststr.go:202 +0x0
  main.main.func1()
      /ithome/main.go:9 +0x5d

Previous write at 0x00c000066250 by main goroutine:
  runtime.mapassign_faststr()
      /usr/local/go/src/runtime/map_faststr.go:202 +0x0
  main.main()
      /ithome/main.go:12 +0xc9

Goroutine 7 (running) created at:
  main.main()
      /ithome/main.go:8 +0x9a
==================
2 b
1 a
Hello, ithome
Found 1 data race(s)

加入到CI/CD流程內

example for drone:
Drone Ppipeline

kind: pipeline
type: docker
name: default

steps:
  - name: golangci-lint
    image: golangci/golangci-lint:v1.31.0-alpine
    commands:
      - golangci-lint run

  - name: gcp_push_image
    image: docker:dind
    volumes:
    - name: docker
      path: /var/run/docker.sock
    commands:
    - docker build --no-cache --pull --force-rm -t ithome/${DRONE_REPO_NAME}:latest -f go.dockerfile .
    

volumes:
- name: docker
  host:
    path: /var/run/docker.sock

Oops - image選擇注意

如果你是用alpine版本,是無法build race的唷!

Status: Downloaded newer image for golang:1.14-alpine
 ---> 0223ac8ea40d
Step 2/5 : WORKDIR /ithome
 ---> Running in 2af0d6741a36
Removing intermediate container 2af0d6741a36
 ---> 0c4a85e3a1d2
Step 3/5 : COPY . /ithome
 ---> 793671813d82
Step 4/5 : RUN cd /ithome && go build -race
 ---> Running in 1a5c3093f6aa
# runtime/cgo
exec: "gcc": executable file not found in $PATH

Oops - 影響效能

使用race去build出來的執行檔,效能跟一般的會有落差!若有要壓測的需求(測race 無相關的需求),不建議使用race build出來的執行檔去跑唷!!!

所以不建議在「正式」環境上使用唷!要請讀者們特別注意!/images/emoticon/emoticon70.gif


上一篇
[Day 15] Oops!Golang - Linter
下一篇
[Day 17] Oops!Golang - 讓我們來抓出吃資源的兇手!
系列文
遠離 DevOops30

尚未有邦友留言

立即登入留言