Simple gitlab CI/CD with docker

年初挑了bitbucket + jenkins作為docker內部簡易自動化建置與部署的設置
那時候由於jenkins是內網的服務,bitbucket沒辦法送webhook進來,無法實作pipeline
自建bitbucket也是可以,但有沒有一個服務是一條龍做完的呢?就是gitlab
由於gitlab community edition (簡稱gitlab-ce) 跟 gitlab-runner免費授權的緣故
成為現在許多企業內部專案管理與自動化建置、測試與部署的首選
以下會以gitlab-ce + gitlab-runner兩個docker image為基底,建置兩個容器服務
並拿Angular、免費網域靜態檔案放置服務Surge當實作目標,走過一遍gitlab CI/CD
整個架構圖如下:
gitlab-ce 為整個專案程式碼管理服務與pipeline主控台
gitlab-runner 則是當pipeline啟動時,會根據gitlab特有的gitlab-ci.yml執行CI/CD
gitlab-ce 每個專案會有一組自己的token,用以註冊gitlab-runner
gitlab-runner註冊時,可以選擇執行方式(executor),我們選用docker
gitlab-runner會另外開幾個container來pull code與執行CI/CD,最後push到surge上
前置作業:
(1). 註冊好surge的帳戶與自定義的網域名稱
(2). 確認docker的記憶體要4gb ram以上 (開發環境為Docker for Mac)
(3). gitlabDocker 目錄,主要放置 docker-compose.yml
(4). testCICD 目錄,主要放置 Angulargitlab-ci.yml
(5). gitlab-ce版本:10.6.1
主要步驟有四:
  1. 撰寫docker-compose file,確認兩個服務之間可以正常連線
  2. 設定User、Group、Project,模擬一般專案合作使用狀況
  3. 設定 gitlab-ce pipeline,註冊runner
  4. Push Code & auto-run pipeline
除了2.以外,會分享一些自己踩到的雷點,希望大家少踩一點~
  1. 撰寫docker-compose file,確認兩個服務之間可以正常連線
    docker-compose.yml
    version: "3.3"
    services:
      gitlab-ce:
        image: gitlab/gitlab-ce:latest
        container_name: 'gitlab-ce-demo'
        ports:
          - '22:22'
          - '80:80'
          - '443:443'
        volumes:
          - gitlab-etc-volume:/etc/gitlab
          - gitlab-varlog-volume:/var/log/gitlab
          - gitlab-varopt-volume:/var/opt/gitlab
        hostname: 'gitlab-ce-demo'
        networks: 
          gitlab-demo-network:
            aliases:
              - gitlab-ce
    
      gitlab-runner:
        image: gitlab/gitlab-runner:latest
        container_name: 'gitlab-runner-demo'
        volumes:
          - /Users/Shared/gitlab-runner/config:/etc/gitlab-runner
          - /var/run/docker.sock:/var/run/docker.sock
        networks: 
          gitlab-demo-network:
    
    networks: 
      gitlab-demo-network:
    
    volumes:
      gitlab-etc-volume:
      gitlab-varlog-volume:
      gitlab-varopt-volume:
    
    1. yaml 解譯檔案時,會因為本身60進位制的設定而對低於60的數字判讀時會有錯誤
      所以ports如果要開59以下的,用quotes(``)包起來才不會有問題
    2. 若要保存gitlab-ce的檔案,建議另外命名一個volume
      掛載外部位置可能會因權限問題而無法正常執行gitlab-ce
      因為gitlab-ce其實在裡頭用不同使用者身份安裝了不少服務,包成一個docker容器
      掛載時可以選擇身份,但由於他本身權限的設定,沒辦法一個身份滿足所有容器內服務
      如果只是備份資料,其實只要命名一個空間,接著再透過備份還原的指令即可保存volumes
    3. hostname設定
      由於gitlab-ce的url 會基於hostname生成,若不指名則會隨機產生一個名稱
      gitlab-runner會基於gitlab-ce給他的hostname連過去
      而不是註冊gitlab-runner時設定的domain name。
      因此,我們必須指名hostname
      然而容器之間的連結是靠container_name與alias設定
      若要指名hostname則另外要對應IP,但是在docker裡容器預設是浮動ip
      因此這裡有個小trick:
      將hostname跟container_name設定一致,就不用hardcode ip跟hostname
      docker-compose up,等一陣子之後到localhost會看到以下畫面,就表示成功了!

      這個密碼是root帳號的密碼
      因為還沒註冊,terminal會看到gitlab-runner有以下錯誤是正常的:
      gitlab-runner-demo |    ERROR: Failed to load config stat /etc/gitlab-runner/config.toml: no such file or directory  builds=0
      
  2. 設定User、Group、Project,模擬一般專案合作使用狀況
    (以下步驟不一一截圖)接著我們,註冊一個lyt與kobayashi的user
    kobayashi是group(teamA)跟project(testCICD)的擁有者
    並將lyt加入teamA群組,lyt的權限為reporter,
    再將lyt加入testCICD專案,lyt的權限為developer
    結果如下:

    左邊是該project(也稱repository)權限為developer,他可以上unprotected branch的code
    右邊是該project的owner,擁有最高權限。比developer多了設定專案的各種參數以外
    亦處理對protected branch的pull request,驗過沒問題就merge
    *關於權限的細部說明可以參考gitlab Q&A
    為了demo CICD方便,省去pull request操作,我們統一用owner kobayashi操作
    也別忘了放上自己的ssh public key (e.g.,id_rsa.pub)
  3. 設定 gitlab-ce pipeline,註冊runner

    由於是跑localhost的docker-compose,加上runner也是用docker去跑
    所以在這有幾個注意事項,依編號說明
    1. 網域名稱填寫 http://gitlab-ce-demo,為runner輪詢的目標之一
    2. 該專案的Token在 CI/CD settings裡,貼上即可
    3. 設定與gitlab-ci.yml裡,對應的tag名稱,這裡寫dev,註冊成功後可在gitlab-ce修改
    4. 如果你希望該pipeline的任何一筆change上了就要跑CI/CD,而不管tag,這裡可以填 true
    5. 我們是用docker,會產生新容器
      為了讓他加入gitlab-ce docker networks裡,在下一步做修改
    6. 打開編輯器後,加上network_mode與links兩行,結果如下
      concurrent = 1
      check_interval = 0
      
      [[runners]]
        name = "Demo CI/CD"
        url = "http://gitlab-ce-demo"
        token = "615f710e4728953ee518102d10fe28"
        executor = "docker"
        [runners.docker]
          tls_verify = false
          image = "trion/ng-cli-karma"
          privileged = false
          disable_cache = false
          volumes = ["/cache"]
          shm_size = 0
          network_mode = "gitlabdocker_gitlab-demo-network"
          links = ["gitlab-ce-demo"]
        [runners.cache]
      
  4. Push Code & auto-run pipeline
    1. 初始化專案,並pull下來
    2. 在testCICD資料夾裡,新增angular專案
    3. npm install surge --save-dev
      如果是第一次註冊沒有帳號、token跟網域需打以下指令:
      ./node_modules/.bin/surge
      註冊完會拿到註冊信箱跟token,記起來
    4. 在gitlab-ce上設定surge所需參數如下 (Secret variables)

      把登入信箱與token放在value裡即可
    5. 撰寫 gitlab-ci.yml
      這是整個CI/CD的重頭戲,push上去就會自動觸發了
      image: trion/ng-cli-karma
      
      cache:
        paths:
          - node_modules/
      
      deploy_stage:
        stage: deploy
        environment: Stage
        tags:
          - dev 
        only:
          - master
        script:
          - rm ./package-lock.json
          - npm install
          - ./node_modules/@angular/cli/bin/ng test --progress false --single-run=true --watch=false
          - ./node_modules/@angular/cli/bin/ng e2e --progress false --watch=false
          - ./node_modules/@angular/cli/bin/ng build --progress false --prod --base-href demo-ci-stage.surge.sh
          - ./node_modules/.bin/surge -p dist/ --domain demo-ci-stage.surge.sh
      
      1. only 要執行pipeline的branch
      2. tags這裡設定的就是要與runner相互對照的
        若沒有對應到runner的tag,也沒有無視tag的runner待命的話會pending住
      3. script就是每一行建置、測試、部署的指令
        surge拿到的domain設置在末兩行 (e.g., demo-ci-stage.surge.sh)
        最後一行就是將檔案放到該網域上
    6. git commit & push後,看gitlab-ce pipeline

      看到pipeline #3即是按照上述步驟剛push上去的狀態
      第一次cancel是因為 gitlab-ci.yml沒有宣告對應的tag而pending,最後取消
      第二次fail是因為沒有安裝surge
    7. 接著可以看看在build的過程

      從gitlab-ce-demo pull master branch後
      會按照script,一行一行執行
      每次build的過程都會被記錄起來,也可以重新build
    8. Build成功後,可以看到http://demo-ci-stage.surge.sh/ 結果如下
    9. 為落實CICD的精神,修改後立刻push,修改如下
    10. 看看pipeline與部署結果

    以上就是整個gitlab-ci + gitlab-runner的實作流程
    希望這篇教學可以讓需要的人少踩一些雷<(_ _)>
References:

留言