ทำ CICD ให้กับ Golang บน Gitlab กันเหอะ

DevOps Jun 03, 2019

เราได้เรียนรู้การใช้งาน Gitlab-ci กันมาพอสมควร คราวนี้ผมจะทำให้ดูว่าเราจะเขียน .gitlab-ci.yaml ยังไงได้บ้าง โดยในครั้งนี้สำหรับสาวก Golang ว่าเราจะทำ CICD ได้ยังไงผ่าน gitlab

อันนี้เป็นโค็ดตัวอย่างที่ผมได้รองทำไว้สามารถ clone มาลองใช้งานได้เลย https://gitlab.com/twin-opensource/golang-cicd

เรามาดูที่ไฟล์ .gitlab-ci.yaml กันว่าเป็นยังไงบ้าง

variables:
  PACKAGE_PATH: /go/src/gitlab.com/twin-opensource/golang-cicd

.inject_gopath: &inject_gopath
  before_script:
    - mkdir -p $(dirname $PACKAGE_PATH)
    - ln -s $CI_PROJECT_DIR $PACKAGE_PATH
    - cd $PACKAGE_PATH

stages:
  - dep
  - build
  - release

dep:
  stage: dep
  image: golang:1.10-alpine3.7
  <<: *inject_gopath
  script:
    - apk add --no-cache curl git
    - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
    - chmod +x /go/bin/dep
    - dep ensure -v -vendor-only
  artifacts:
    name: "vendor-$CI_PIPELINE_ID"
    paths:
      - vendor/
    expire_in: 1 hour

build:
  stage: build
  image: golang:1.10-alpine3.7
  dependencies:
    - dep
  <<: *inject_gopath
  script:
    - GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o main .
  artifacts:
    name: "main-$CI_PIPELINE_ID"
    paths:
      - main
    expire_in: 1 hour

release:
  stage: release
  image: docker:latest
  services:
    - docker:dind
  dependencies:
    - build
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build --pull -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME

โดยขั้นตอนการทำงานจะเป็นในลักษณะแบบนี้

อธิบายค่าต่างๆ

จะขออธิบายส่วนต่างๆ ใน .gitlab-ci.yaml ให้อ่านกัน

  • variable เป็นการสร้างตัวแปรขึ้นมาเพื่อใช้งาน โดยในกรณีนี้ผมจะสร้าง gopath ไว้ให้ถูกต้อง
  • .inject_gopath เป็นใช้งาน Anchors เพื่อให้สามารถ reuse คำสั่งมาใช้งานซ้ำได้ภายในไฟล์ yaml ซึ่งผมจะให้มันสร้าง directory ตาม PACKAGE_PATH แล้วทำการสร้าง link จากโค็ดเราทั้งหมดไปวางใน PACKAGE_PATH
  • stage เป็นการบอกว่าในโปรเจ็คนี้จะมี 3 stage ได้แก่ dep, build และ release

ใช้ dep ติดตั้ง vendor/

dep:
  stage: dep
  image: golang:1.10-alpine3.7
  <<: *inject_gopath
  script:
    - apk add --no-cache curl git
    - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
    - chmod +x /go/bin/dep
    - dep ensure -v -vendor-only
  artifacts:
    name: "vendor-$CI_PIPELINE_ID"
    paths:
      - vendor/
    expire_in: 1 hour

ในส่วนนี้ของ dep job ผมจะใช้ dep ซึ่งเป็น package manager ของ Golang ทำการติดตั้ง Liberies ต่างๆ โดยมันจะได้ vendor/ ก็ให้เราทำการ artifacts เก็บไว้โดยมีระยะเวลาหมดอายุ 1 ชั่วโมง

Build ไฟล์ main.go

build:
  stage: build
  image: golang:1.10-alpine3.7
  dependencies:
    - dep
  <<: *inject_gopath
  script:
    - GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o main .
  artifacts:
    name: "main-$CI_PIPELINE_ID"
    paths:
      - main
    expire_in: 1 hour

หลังจากเราได้ vendor/ มาแล้ว ในส่วนของ build job ผมก็จะทำการโหลด vendor/ มาจาก dep job โดยใช้ dependencies: แล้วตามด้วยชื่อ job

dependencies:
  - dep

แล้วค่อยทำการ build golang GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o main . จากนั้นจะได้ไฟล์ main ผมก็เอามาเก็บลง artifacts เหมือนเดิม

สร้างเป็น Docker image

release:
  stage: release
  image: docker:latest
  services:
    - docker:dind
  dependencies:
    - build
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build --pull -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME

ในส่วนของ release job ผมจะทำการโหลดไฟล์ main มาจาก build job แล้วก็นำมาทำ docker image จากนั้นก็โยนขึ้นไปเก็บไว้ที่ registry.gitlab.com โดยใน Dockerfile จะเป็นแบบนี้

FROM alpine:3.8
LABEL name="Golang CICD" \
    version="1.0.0" \
    org.label-schema.vcs-url="https://gitlab.com/twin-opensource/golang-cicd" \
    org.label-schema.vendor="Twin synergy"
RUN apk --no-cache add ca-certificates \
    && apk add --update tzdata \
    && cp /usr/share/zoneinfo/Asia/Bangkok /etc/localtime \
    && apk del tzdata
COPY main /usr/local/bin/app
EXPOSE 8080
CMD ["/usr/local/bin/app"]

จะสังเกตุเห็นว่า ผมใช้ alpine เป็น os หลักเพื่อให้ไฟล์ image มันเล็ก แล้วแค่ COPY main ที่ได้มาใส่ไว้ จากนั้นก็ run มันขึ้นมา เราก็จะได้ golang application ที่ขนาดโครตเล็กมาใช้งานแล้ว

ทดสอบการใช้งานหน่อย

ลองเรียกใช้ด้วย docker จากนั้นทดสอบดู

$ docker run -p 8080:8080 registry.gitlab.com/twin-opensource/golang-cicd:master
$ curl http://localhost:8080/greeting
Hello, world!

เย้ ได้ Hello, world มาแล้ว

ขอให้สนุกกับการเขียน gitlab-ci นะครับ

Arnon Kijlerdphon

IT manager & DevOps @Twin Synergy Co.,Ltd

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.