We are not waiting for future, We create the future

Tags


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

3rd June 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 นะครับ

IT manager & DevOps @Twin Synergy Co.,Ltd

View Comments