CI/CD

CI/CD .... ủa từ đâu ra vậy ?

Detect Bug Earlier

The Old Story

  • Thuở xa xưa developer rất ít khi merge code, 1 người sẽ giữa code hiện tại của mình cho đến khi code xong 1 tính năng rồi mới merge và push lên Github
  • Giả sử chúng ta có 1 team với 2 developer A và B. Một ngày đẹp trời nó team này nhân 1 dự án lớn toàn tính năng khủng. Thế là sau khi nhận dự án A và B bắt đầu chia nhau ra code dần các tính năng
  • Vì là tính năng khủng nên cả A và B đều mất khoảng tg rất lâu mới có thể hoàn thành (2 tháng), tuy nhiên vì trước h ko code thói quen thường xuyên push và merge code nên phải 2 tháng sau khi quá dev tính năng kết thúc A và B mới có thể ngồi lại cùng nhau để merge code
  • Tuy nhiên quá trình merge code này đã trở nên cực kì phức tạp vì lượng code mà 2 bên thay đổi quá lớn. Giới dev thường hay đồn là Merge Hell :)))

First Approach

  • Để giái quyết vấn đề trên. Team của A và B đã quyết định lập ra 1 khế ước là họ cần push code và merge code mỗi ngày. Trước khi ai leave office thì đều phải push code của ngày hôm đó lên
  • Cách giải quyết trên tạm ổn, họ đã không còn gặp Merge Hell nữa, tuy nhiên lúc này lại có 1 vấn đề khác xảy ra
  • 1 ngày đẹp trời B đang dev, nhưng vì vợ B có việc gấp và cần B phải về. Do đang dev nên B đã push code vội trước khi leave office mà quên xoá bug đang tồn động trong code lên
  • Đang tiếc thay bug này không dễ phát hiện, điều đó khiến A pull code của B nhưng không phát hiện bug và bị bug kiến code trở nên sai hướng và mất rất nhiều thời gian để về đúng hướng trở lại

Final Approach

  • Như thế ta có thể trong TH trên ta cần 1 lớp hàng rào, khi B push code lên, code của B cần được tự động test để loại bỏ bug trước khi được merge vào source code cho toàn team sử dụng
  • Để làm được như vậy ta cần 1 server luôn lăng nghe sự thay đổi khi có developer nào đó push hoặc commit code ⇒ Người ta gọi server đó là CI Server

Deploy Easier

  • Anh A là devops của 1 công ty X, vì là startup nên quá trình deploy và build cũng khá dễ
  • Anh A chỉ cần chờ code được merge từ team Dev và deploy nó lên trên mt production
  • Không lâu sau đó, công ty X ngày càng phát triển và họ cần có 1 đội ngũ QA để test ứng dụng trước khi release
  • Lúc này công việc của anh A vẫn chưa khó khắn lắm thay vì deploy ngay lên MT production thì h anh A sẽ deploy lên MT QA trước để Tester test
  • Một lần nữa cty X lại phát triển, nhiều cty thứ 3 muốn hợp tác với cty X.
  • Lúc này để thuận tiện, với mỗi cty thứ 3 anh A phải cung cấp 1 mt test riêng. Và khi có cập nhật về code, anh A sẽ phải deploy trên từng mt đó
  • Sẽ ra sao nếu có 100 cty thứ 3 muốn hợp tác với cty X. Anh A sẽ phải deploy lên 100 MT ⇒ Chính vì khó khăn này anh A cần có 1 cách nào đó giúp anh A có thể xây dựng kịch bản và tự động hoá quá trình deploy ⇒ Các tool CI/CD sẽ giúp anh A làm việc này

CI/CD ra đời đã thay đổi thế nào trong giới công nghệ ?

Kể từ khi các qui trình CI / CD ra đời cũng là lúc các team Dev nhận ra rằng họ cần một người có thể giúp họ không chỉ triển khai qui trình này, mà còn có thể update và maintain nó khi cần thiết và từ đó 1 vị trí mới trong các team lập trình được vinh danh ⇒ Shout out for DevOps developer.

Và cũng kể từ khi CI / CD ra đời một số miếng bánh dần hé lộ ra để các công ty công nghệ như Github / Gitlab / CircleCI nhảy vào với tư tưởng là chúng tôi có giúp bạn có được 1 qui trình CI /CD chỉ trong vài click chuột

Và cũng chính vì lý do đó từ đây chúng ta có 2 trường phái sử dụng CI / CD rõ rệt

  • Tự build 1 flow CI / CD (CI / CD with Jenkins)
  • Sử dụng các dịch vụ có sẵn của Github / Gitlab

CI/CD in Jenkins vs CI/CD in Github

CI/CD in Jenkins

Overview

  • Jenkins là 1 open source giúp chúng ta build 1 quy trình CI / CD

Advantage

  • Vì là open source nên chúng ta ko phải trả phí cho việc sử dụng Jenkins
  • Vì tự tay chúng ta build CI/CD nên Jenkins cho chúng ta có thể custom được tất qui trình CI/CD theo ý mình
  • CI Server có thể lắng nghe từ nhiều nguồn source code khác nhau Github / Gitlab / Bitbucket

Disadvantage

  • Khó cho người mới bắt đầu vì qui trình config phức tạp
  • Chúng ta không phải trả chi phí cho việc sử dụng Jenkins, tuy nhiên chúng ta phải trả tiền để thuê server build code của chúng ta và chạy test

Dành cho ai

  • Các công ty, doanh nghiệp muốn làm chủ 100% qui trình CI / CD riêng
  • Có đội ngũ Devops dày dặn kinh nghiệm để develop và maintain 1 qui trình CI / CD

CI/CD in Github (GIthub Action)

Overview

  • Github Action là 1 dịch vụ của Github, giúp chúng ta có ngay được 1 flow CI/CD chỉ từ 1 file config và không cần phải làm thêm bất kì thứ gì cả (thuê server, maintain server ...)
  • Tuy nhiên chúng ta sẽ trả phí cho Github Action thời gian chúng ta dành để build project
  • Cấu hình server mạnh sẽ giúp chúng ta build nhanh hơn vì thế mức giá trên 1 phút build cũng sẽ cao hơn

Advantage

  • Ko phải config nhiều, chỉ cần code 1 file là xong
  • Tính tiền theo phút ⇒ pay as you go, thậm chí có free version, phù cho người mới học về Devops ⇒ ko phải đau đầu thuê 1 server

Disadvantage

  • Phụ thuộc vào Github, source code trước h lưu ở Gitlab hay Bitbucket ko chơi được với em này
  • Vì server build code được cung cấp bởi Github nên khó custom, không phù hợp với 1 số dự án lớn
  • Free version đồng nghĩa với máy yếu và thời gian build lâu hơn

Example

Scenario 1

  • Một công ty công nghệ đang phát triển 1 codebase rất là lớn
    • Mỗi lần build toàn bộ cái web này phải tốn hơn 50 server
    • Mỗi lần test công ty này cần hơn 100 server để tạo ra 1 triệu user ảo nhầm giả lập lại trường hợp website của họ khi có 1 triệu user vào cùng 1 lúc sẽ handle thế nào
    • Khi fail thì gửi mail đến các phòng bạn liên quan
💡
Rõ ràng với nhu cầu này chúng ta không thể dùng Github Action được vì số lượng server cần build và test quá lớn, đồng thời qui trình CI/CD ngày cũng cần 1 độ custom cao nên Jenkins sẽ là lựa chọn thích hợp cho công ty trên

Scenario 2

  • Một công ty startup ko có tiền thuê Devops, tuy nhiên CEO vẫn muốn công ty ko bị lệ thuộc vào Devops mà cần phải đưa ra sản phầm càng nhanh càng tốt
  • Vì là startup nên codebase cũng nhỏ ko cần quá nhiều resource để build
  • Vì là startup nên test chưa phải là mục tiêu cao nhất, cái quan trọng là có thể mang được idea đến cho user
💡
Rõ ràng với trường hợp này, Github Actions là 1 vũ khí cần thiết hơn bao giờ hết

CI/CD Github Hand-on Actions

Requirement

  • Môi trường NodeJS → để code 1 simple web
  • Tài khoản Herouku dùng để deploy
  • Tài khoản Github để chạy Github Actions

B1) Khởi tạo project Nodejs đơn giản

  • Để khởi tạo project Nodejs các bạn gõ theo các lệnh dưới đây
    yarn init // Khởi tạo 1 project Nodejs -> sau khi chạy xong dòng này chúng ta 
    sẽ có được 1 file package.json đơn giản
          
    yarn add express // Install web framework để build simple web với Nodejs
          
    yarn add mocha supertest // Install 2 testing tool là mocha và supertest để 
    thực hiện phase test 
          
    yarn add nodemon // Install package này để thực hiện việc chạy live test server
  • Sau khi đã khởi tạo và cài đặt các thư viện cần thiết xong ta tiến thành tạo file index.js với nội dung như sau
    const express = require("express");
          const port = process.env.PORT || 8000;
            const app = express();
          
          app.get("/", (req, res) => {
              res.send("Welcome to CI/CD Exercise of CS101");
          })
          
          app.listen(port, () => {
              console.log("Server is listening");
          });
          module.exports = app;
  • Từ đoạn code trên ta có thể thấy ta đang tạo 1 web với configuration sau đây
    • Web được chạy ở port mặc định là 8000
    • Trang index của web trả về 1 đoạn text là Welcome to CI/CD Exercise of CS101
  • Sau đó ta thêm các script cần thiết để có khởi động web lên (Dưới đây là file package.json sau khi ta đã thêm script)
    {
              "name": "github-actions",
              "version": "1.0.0",
              "main": "index.js",
              "license": "MIT",
              "scripts": { // Added script here
                  "test": "mocha ./test/* --exit", // script used for testing phase
                  "start": "node index.js", // script used for start in production 
                  environment
                  "dev": "nodemon index.js" // script used for start in dev 
                  environment
              },
              "dependencies": {
                  "express": "^4.17.1",
                  "mocha": "^9.1.3",
                  "nodemon": "^2.0.14",
                  "supertest": "^6.1.6"
              }
          }
  • Cuối cùng ta gõ lệnh yarn dev để tiến hành chạy web
    yarn dev
  • Sau khi chạy xong ta sẽ có được hình như dưới đây
  • Và bây giờ ta tiến hành đoạn code để test app của chúng ta
    const request = require("supertest");
          const app = require("../index");
          
          describe("GET /", () => {
            it("responds with Welcome to CI/CD Exercise of CS101!", (done) => {
              request(app).get("/").expect("Welcome to CI/CD Exercise of CS101", 
              done);
            });
          });
  • Bạn cũng có thể chạy yarn test để test thử app của chúng ta. Nếu thành công sẽ hiện ra như hình dưới đây
    yarn test
  • Ngoài ra bạn có thể thêm .gitignore để ignore đi một số folder nặng nhưng lại không cần thiết. Bạn cũng có thể sử dụng file code .gitignore như ở dưới đây
    node_modules/
          yarn.lock

B2) Tạo Github Actions đầu tiên

  • Tạo 1 Github Repo. Sau khi tạo xong ta sẽ thấy trang dưới đây (Nếu ko có README)
  • Gõ các lệnh sau để push code lên Github
  • Sau khi push code thành công repo của chúng ta sẽ trông giống như vậy
  • Và bây giờ chúng ta hãy cùng workflow đầu tiên trong Github Actions. Để tạo được workflow đầu tiên ta cần tạo 1 folder có tên là .github và tạo thêm 1 folder workflows bên trong folder .github
  • Sau đó ta tạo 1 file tên là app.yml chứa toàn bộ thông tin config của workflow và copy thông tin config ở dưới đây
    name: Node.js CI
          
          on: // Đầu tiên chúng ta cần setup workflow listener cho workflows, 
          khi nào thì workflows bắt đầu chạy
              push: // Ở đây ta đang setup cho workflow sẽ chạy khi chúng ta push 
              code lên branch main
                  branches: [main]
          
          jobs: // Sau khi đã setup xong workflow listener, ta tiến hành setup 
          các job cho workflows
              build: // Trong workflow đơn giản này ta chỉ có 1 job duy nhất tên là 
              build. 1 job trong workflows sẽ bao gồm nhiều steps
          
                  runs-on: ubuntu-latest // Đầu tiên ta cần chọn môi trường để chạy 
                  job - Ở đây chúng ta đang setup workflow sẽ chạy trên máy ảo có 
                  HĐH ubuntu-latest cho job này
          
                  steps: // Dưới đây là các step ta sẽ có trong job
                      - uses: actions/checkout@v2 // 1. Checkout code mới nhất từ 
                        github repo
                      - name: Use Node.js // 2. Setup nodejs lên trên máy ảo dùng 
                        để chạy job
                        uses: actions/setup-node@v2
                        with:
                            node-version: '14.x'
                      - run: npm install // 3. Tiến hành install các library từ node
                      - run: npm run build --if-present // 4. Build web của chúng ta
                      - run: npm test // 5. Tiến hành chạy web được build
  • Sau khi đã tạo xong file config cho workflow ta tiến hành push chúng lên để github hiểu nó với các lệnh dưới đây
    git add .
          git commit -m "add workflow"
          git push origin main
  • Sau đó ta thay đổi nội dung file README.md (hoặc bất cứ 1 file nào trong src code) để tiến hành commit và push 1 lần nữa cho việc test workflow
    git add .
          git commit -m "test workflow"
          git push origin main
  • Ta có thể kiểm tra workflow đã đươc test thành công chưa bằng cách vào mục Actions của Github và ta sẽ thấy kết quả của các lần test sẽ hiện ra như dưới đây

B3) Setup với Heroku và thêm deployment cho workflow

  • Đến phần này chúng ta đã hoàn tất build thành công workflow cho bản thân mình. Tuy nhiên hiện tại workflow chỉ thực hiện đúng 1 job là build web. Nếu ta muốn deploy web thì cần phải làm gì ?
  • Ở phần này mình sẽ hướng dẫn các bạn config 1 job deploy trong Github Actions, cụ thể hơn chúng ta sẽ tiến hành deploy web lên Heroku
  • Để tạo Heroku đầu tiên ta cần truy cập vào https://www.heroku.com/. Sau đó chọn Signup và hoàn thành form
  • Sau khi tạo xong Heroku ta sẽ vào được trang chủ với giao diện như dưới đây
  • Sau khi đã tạo được tài khoản Heroku ta tiến hành thêm job deploy vào workflow
    name: Node.js CI
          
          on:
            push:
              branches: [main]
          
          jobs:
            build:
              runs-on: ubuntu-latest
          
              steps:
                - uses: actions/checkout@v2
                - name: Use Node.js
                  uses: actions/setup-node@v2
                  with:
                    node-version: "14.x"
                - run: npm install
                - run: npm run build --if-present
                - run: npm test
          
          // Adding job below here
            deploy:
              needs: build
              runs-on: ubuntu-latest
              steps:
                - uses: actions/checkout@v2
                - uses: akhileshns/heroku-deploy@v3.12.12
                  with: // Để deploy lên heroku ta cần 3 thành phần sau
                    heroku_api_key: ${{secrets.HEROKU_API_KEY}} /* Ở đây chúng ta 
                    không muốn lộ HEROKU_API_KEY ở trong code nên ta sẽ dùng Github 
                    Secrets Key để giúp chúng ta giấu đi thông tin về API Key */
                    heroku_app_name: "gdscxsab-cicd-lab01"
                    heroku_email: "gdscxsab@gmail.com"
  • Để lấy được HEROKU_API_KEY ta cần trang settings của Account
  • Sau đó ta vào trang Settings của Github Repo và chọn Secrets
  • Tiến hành thêm 1 secret với name là HEROKU_API_KEY và content là API Key của chúng ta
  • Xong sau đó chúng ta sẽ tiến hành push code và test thử job deploy này