มาทำ CI/CD ผ่าน Gitlab เข้า EC2 กัน

รูป Copy มาจาก Google อีกที

CI/CD คืออะไรคือกระบวนการตั้งแต่เราเริ่มวางแผน จนไปถึง Deploy ขึ้น Server

Continuous Integration (CI) มาจาก “Continuous Integration” ซึ่งกระบวนการนี้ประกอบด้วยขั้นตอนต่างๆ ได้แก่ Plan, Code, Build, และ Test เพื่อกระชับกระบวนการ
- การวางแผน (Plan): ในขั้นตอนนี้จะวางแผนโครงการและกำหนดเครื่องมือที่ใช้รวมถึงสิ่งที่จะทำในแต่ละ Sprint
- การเขียนโค้ด (Code): เมื่อแผนเสร็จสิ้นแล้วจะเริ่มเขียนโค้ด
- การสร้าง (Build): หลังจากนั้นจะสร้าง Build จากโค้ด
- การทดสอบ (Test): กระบวนการทดสอบโดยอัตโนมัติเพื่อตรวจสอบความถูกต้องและประสิทธิภาพของโค้ด รวมถึงการรันการทดสอบต่างๆ

Continuous Deployment (CD) มาจาก “Continuous Delivery” (Manual) หรือ “Continuous Deployment” (Auto) ซึ่งกระบวนการประกอบด้วย Release, Deploy, Operate, และ Monitor
- การปล่อย (Release): เป็นขั้นตอนที่ทำให้ซอฟต์แวร์พร้อมที่จะถูกนำไปใช้งาน ซึ่งอาจรวมถึงการเตรียมการอัพเดตหรือการปล่อยเวอร์ชันใหม่ของซอฟต์แวร์
- การนำไปใช้งาน (Deploy): ในขั้นตอนนี้ ซอฟต์แวร์จะถูกนำไปใช้งานในสภาพแวดล้อมการทำงาน โดยมีกระบวนการอัตโนมัติในการติดตั้งและกำหนดค่าซอฟต์แวร์
- การดำเนินการ (Operate): เป็นขั้นตอนที่ซอฟต์แวร์ถูกใช้งานจริง ในขณะนี้ต้องดูแลรักษาและบำรุงรักษาซอฟต์แวร์เพื่อให้มันทำงานอย่างเสถียรและปลอดภัย
- การตรวจสอบ (Monitor): การตรวจสอบและติดตามการทำงานของซอฟต์แวร์ในสภาพแวดล้อมการทำงานจริง

ซึ่งกระบวนการจริงๆอาจจะมีมากหรือน้อยกว่านี้แล้วแต่องกรค์ที่เราอยู่

สิ่งที่ต้องเตรียมสำหรับบทความนี้

  1. Account AWS
  2. Server EC2 (Ubuntu ≤ 20.04)
  3. Domain(บทความใช้ Cloudflare จัดการ DNS)
  4. Gitlab
  5. Nodejs
  6. Dockerfile
  7. Docker-Compose
  8. .Gitlab-CI.yml

มาเริ่มกันเลยดีกว่าครับโดยบทความจะหยิบ CI/CD มาทำแค่
3 ขั้นตอน Code->Build->Deploy

สร้าง EC2 มา 1 ตัวแนะนำ UBUNTU ไม่เกิน 20.04
หลังจากนั้นก็เข้ามาที่ EC2 ตัวนั้นเพื่อจะ Connect โดยผมจะ SSH เข้าไป
โดยจะไปที่ SSH Client แล้ว Copy ตรง Example
จากนั้นนำมาวางบน Terminal ก็จะสามารถเข้า EC2 ที่เราสร้างได้
Copy ไปวางบน Server เราได้เลยเป็นการติดตั้ง Docker บน Server เรา

มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน

  1. sudo apt update: คำสั่งนี้ใช้ในการอัปเดตรายการแพคเกจของระบบปฏิบัติการ Ubuntu ให้เป็นรายการล่าสุดก่อนที่จะดำเนินการติดตั้งแพคเกจอื่น ๆ
  2. sudo apt install -y apt-transport-https ca-certificates curl software-properties-common: คำสั่งนี้ใช้ในการติดตั้งแพคเกจที่จำเป็นสำหรับการดาวน์โหลดและติดตั้ง Docker และความจำเป็นในการสื่อสารที่ปลอดภัย
  3. curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -: คำสั่งนี้ใช้ในการดาวน์โหลดและเพิ่มคีย์ GPG ของ Docker เพื่อยืนยันแพคเกจที่มาจาก Docker Repository
  4. sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable": คำสั่งนี้เพิ่ม Docker Repository ลงในรายการแหล่งที่มาของแพคเกจ
  5. sudo apt update: อัปเดตรายการแพคเกจอีกครั้งเพื่อรับข้อมูลล่าสุดจาก Docker Repository
  6. apt-cache policy docker-ce: คำสั่งนี้ใช้เพื่อแสดงแพคเกจ docker-ce ในรายการแพคเกจ
  7. sudo apt install -y docker-ce: คำสั่งนี้ใช้ในการติดตั้ง Docker Community Edition (CE)
  8. sudo usermod -aG docker ${USER}: คำสั่งนี้เพิ่มผู้ใช้ปัจจุบันเข้าสู่กลุ่ม docker เพื่อให้เขาสามารถเรียกใช้ Docker ได้โดยไม่ต้องใช้สิทธิ์ root
  9. exit: คำสั่งนี้จะออกจากเซสชันของผู้ใช้ที่รันคำสั่งนี้ เมื่อทุกอย่างเสร็จสิ้น
ก็จะทำการติดตั้งทรัพยากรณ์ต่างๆตาม Script ที่เรานำมาวาง
หลังจากนั้นจะมา Run Script เกี่ยวกับ Docker Compose

มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน

  1. sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-uname -s-uname -m -o /usr/local/bin/docker-compose: คำสั่งนี้ใช้ในการดาวน์โหลดและติดตั้ง Docker Compose โดยใช้ curl ในการดาวน์โหลดไฟล์ที่เหมาะสมสำหรับระบบปฏิบัติการที่กำลังใช้งานอยู่
  2. sudo chmod +x /usr/local/bin/docker-compose: คำสั่งนี้ให้สิทธิ์การเข้าถึงแก้ไขไฟล์ Docker Compose ที่ถูกดาวน์โหลด
  3. docker-compose --version: คำสั่งนี้ใช้ในการตรวจสอบเวอร์ชันของ Docker Compose หลังจากที่ได้ติดตั้งเสร็จสิ้น
  4. sudo apt-get install docker-compose-plugin: คำสั่งนี้พยายามติดตั้งแพคเกจ docker-compose-plugin โดยใช้ apt-get
ก็จะทำการติดตั้งทรัพยากรณ์ต่างๆตาม Script ที่เรานำมาวาง
คำสั่งสำหรับสร้าง Network และ Nginx Proxy

มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน

  1. docker network create sandbox_network: คำสั่งนี้ใช้ในการสร้าง Docker Network ชื่อ sandbox_network เพื่อให้คอนเทนเนอร์ที่ต้องการใช้งานอยู่ในระบบเครือข่ายเดียวกัน
  2. docker run -d --name reverse-proxy --restart=always -p 80:80 -v /etc/nginx/certs -v /etc/nginx/vhost.d -v /usr/share/nginx/html -v /var/run/docker.sock:/tmp/docker.sock:ro --net sandbox_network riomus/nginx-proxy: คำสั่งนี้ใช้ในการเริ่มต้นการทำงานของคอนเทนเนอร์ Nginx Proxy ในพอร์ต 80 โดยใช้รายการโฟลเดอร์ที่ระบุเป็นพารามิเตอร์ -v และเชื่อมต่อเข้ากับ Docker Socket เพื่อเข้าถึงข้อมูลของคอนเทนเนอร์อื่น ๆ และเชื่อมต่อกับเครือข่าย sandbox_network
เสร็จแล้วลองเช็คด้วย docker ps ดู

ตอนนี้ก็เสร็จสิ้นแล้วสำหรับการติดตั้งทรัพยากรณ์บน Server ของเรา

จากนั้นมา Set Security
Set Inbound เปิด Port 80 เพื่อให้ภายนอกเข้ามาถึง Nginx Proxy ในพอร์ต 80
จากนั้นมาทำ Domain เพื่อ Map กับ EC2 จากภาพจะเห็นได้ว่าผม Map ไป 2 ตัว โดย test-node จะชี้ไปที่ ip ของ EC2 ส่วน test-node-api จะชี้ไป ที่ test-node ที่ทำแบบนี้ก็เพื่อเราจะได้สร้างหลาย Domain ใน 1 EC2 โดยในรูปจะใช้ Cloudflare จัดการ DNS

จากนั้นมาถึงขั้นลง Coding กัน

mkdir test-node
cd test-node
npm init
npm i express
code .
จะได้หน้าตาออกมาประมาณนี้

สร้าง File index.js

Copy Code ไปไว้ใน File index.js
จากนั้นลอง run : node index.js ถ้าได้แบบในรูปเป็นอันเสร็จสิ้น
ลองเอา URL ไป Run ที่ Browser ดูเป็นอันเสร็จเรียบร้อย

ขั้นตอนเตรียม Code เสร็จแล้วต่อไปมาเตรียมพวก Docker,Docker Compose กันต่อ

Copy Code ไปไว้ใน Dockerfile
จะได้หน้าตา Dockerfile แบบนี้ถือว่าจัดเตรียม Dockerfile เรียบร้อยแล้ว
จากนั้นสร้าง Project บน Gitlab แล้วทำการ Clone แล้วเอา Project ที่เราสร้างไปไว้ใน Gitlab
จากนั้นสร้าง File docker-compose.yml
Copy และนำไปวางใน File docker-compose.yml และเปลี่ยนเป็น gitlab และ domain ตัวเองได้เลย

มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน

  1. version: '3.3': ระบุเวอร์ชันของ Docker Compose ที่ใช้ในไฟล์
  2. services:: เริ่มต้นกำหนดการตั้งค่าของคอนเทนเนอร์ในส่วนนี้
  3. node-test-api:: ชื่อของ service หรือคอนเทนเนอร์ที่จะรัน
  4. container_name: node-test-api: ชื่อของ container ที่จะถูกสร้างขึ้นเมื่อคอนเทนเนอร์ถูกเริ่มต้น
  5. image: registry.gitlab.com/dodohandsome/test-node:latest: กำหนด image ที่จะใช้ในการสร้าง container โดยใช้ image ที่อยู่ใน GitLab Container Registry
  6. restart: unless-stopped: กำหนดว่า container จะถูกรีสตาร์ตใหม่เมื่อเกิดข้อผิดพลาดหรือหยุดทำงาน ยกเว้นกรณีที่ถูกหยุดโดยผู้ใช้
  7. volumes:: กำหนดการแชร์ข้อมูลระหว่างโฟลเดอร์ในเครื่อง host และเครื่อง guest (container)
  8. - .env:/app/.env: กำหนดว่าจะแชร์ไฟล์ .env จากโฟลเดอร์ในเครื่อง host ไปยังโฟลเดอร์ /app/.env ใน container
  9. environment:: กำหนดตัวแปรแวดล้อมใน container
  10. - NODE_ENV=development: กำหนดค่าตัวแปรแวดล้อม NODE_ENV เป็น development
  11. - VIRTUAL_PORT=8080: กำหนดค่าตัวแปรแวดล้อม VIRTUAL_PORT เป็น 8080
  12. - VIRTUAL_HOST=test-node-api.yourplaceth.com: กำหนดค่าตัวแปรแวดล้อม VIRTUAL_HOST เป็น test-node-api.yourplaceth.com
  13. - LETSENCRYPT_HOST=test-node-api.yourplaceth.com: กำหนดค่าตัวแปรแวดล้อม LETSENCRYPT_HOST เป็น test-node-api.yourplaceth.com
  14. - LETSENCRYPT_EMAIL=thamrong.chaiwong@gmail.com: กำหนดค่าตัวแปรแวดล้อม LETSENCRYPT_EMAIL เป็น thamrong.chaiwong@gmail.com
  15. logging:: กำหนดการตั้งค่า logging ของ container
  16. driver: "json-file": กำหนดใช้งานไดรเวอร์การเก็บ log ประเภท "json-file"
  17. options:: กำหนดตัวเลือกเพิ่มเติมสำหรับการตั้งค่า logging
  18. max-file: "3": กำหนดจำนวนไฟล์ log สูงสุดที่เก็บไว้
  19. max-size: "500m": กำหนดขนาดไฟล์ log สูงสุดที่เก็บไว้
  20. networks:: กำหนดการตั้งค่าเครือข่ายในส่วนนี้
  21. default:: ชื่อของเครือข่าย
  22. external:: กำหนดว่าเครือข่ายนี้เป็นเครือข่ายภายนอก
  23. name: sandbox_network: กำหนดชื่อของเครือข่ายภายนอกที่จะถูกเชื่อมต่อกับ container

หลังจากนั้นกลับมาที่ Gitlab และ Project ที่เราเตรียมไว้ไปที่ Settings->Variables->Add Variable

จะทำการเพิ่ม Key: EC2_DEV , Value : ubuntu@ec9–99–999–999–9.ap-southeast-1.compute.amazonaws.com โดยที่จะ Value จะเป็น Ubuntu ตามด้วย Public IPv4 DNS ของ EC2
จากนั้นเพิ่ม Key: SSH_PRIVATE_KEY_DEV , Value : Key.pem ตอนสร้าง EC2 เพื่อที่จะให้ตัว Gitlab ssh เข้า EC2 เรา
จะได้หน้าตาออกมาแบบนี้
หลังจากนั้นสร้าง File .gitlab-ci.yml ขึ้นมาแล้ว Copy Script ข้างต้นไปวางโดยจะมีแค่ Step Build->Deploy

มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน

  1. image: docker:latest: กำหนดให้ CI/CD runner ใช้ image docker:latest เป็นพื้นฐาน
  2. services:: กำหนด services ที่จะใช้ในการรันงานใน pipeline
  3. - docker:dind: กำหนดให้มี service docker:dind ที่เป็น Docker-in-Docker เพื่อให้สามารถรัน Docker commands ใน runner ได้
  4. stages:: กำหนด stages หรือขั้นตอนของ pipeline ที่จะถูกดำเนินการ
  5. - build: กำหนดขั้นตอน build
  6. - deploy: กำหนดขั้นตอน deploy
  7. build-job-dev:: กำหนด job ใน stage build ชื่อ build-job-dev
  8. stage: build: ระบุว่า job นี้อยู่ใน stage build
  9. before_script:: คำสั่งที่จะถูกรันก่อนเริ่ม script ใน job
  10. - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY: เข้าสู่ระบบ Docker Registry ของ GitLab โดยใช้ข้อมูลเข้าสู่ระบบจาก environment variables
  11. script:: คำสั่งที่จะถูกรันใน job
  12. - docker build --tag $CI_REGISTRY_IMAGE:latest .: สร้าง Docker image จาก Dockerfile ในโฟลเดอร์ปัจจุบันและให้ image ชื่อเป็น $CI_REGISTRY_IMAGE:latest
  13. - docker push $CI_REGISTRY_IMAGE:latest: อัปโหลด Docker image ที่สร้างขึ้นไปยัง Docker Registry ของ GitLab
  14. only:: กำหนดว่า job นี้จะถูกเรียกเฉพาะเมื่อเป็น branch develop
  15. - develop: ระบุชื่อ branch ที่จะทำให้ job นี้ถูกเรียก
  16. deploy-dev:: กำหนด job ใน stage deploy ชื่อ deploy-dev
  17. stage: deploy: ระบุว่า job นี้อยู่ใน stage deploy
  18. image: alpine: กำหนดให้ job นี้ใช้ image alpine เป็นพื้นฐาน
  19. variables:: กำหนดตัวแปรใน job
  20. DEPLOY_PATH: /home/ubuntu/test-node-api: กำหนดค่าตัวแปร DEPLOY_PATH เป็นเส้นทางที่จะใช้ในการ deploy
  21. EC2: $EC2_DEV: กำหนดค่าตัวแปร EC2 เป็นที่อยู่ของ EC2 instance ที่จะ deploy ไป
  22. SSH_PRIVATE_KEY: $SSH_PRIVATE_KEY_DEV: กำหนดค่าตัวแปร SSH_PRIVATE_KEY เป็นคีย์ SSH ส่วนตัวของเครื่อง server
  23. before_script:: คำสั่งที่จะถูกรันก่อนเริ่ม script ใน job
  24. - apk add openssh-client: ติดตั้ง openssh-client เพื่อใช้ในการเชื่อมต่อ SSH
  25. - eval $(ssh-agent -s): เริ่มใช้ ssh-agent ใน background
  26. - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -: เพิ่มคีย์ SSH ส่วนตัวในตัวแปร $SSH_PRIVATE_KEY ลงใน ssh-agent
  27. - mkdir -p ~/.ssh: สร้างโฟลเดอร์ ~/.ssh สำหรับเก็บคีย์ SSH
  28. - chmod 700 ~/.ssh: กำหนดสิทธิ์เป็นแบบเป็นเจ้าของเท่านั้นสำหรับโฟลเดอร์ ~/.ssh
  29. script:: คำสั่งที่จะถูกรันใน job
  30. - scp -o StrictHostKeyChecking=no ./docker-compose.yml $EC2:$DEPLOY_PATH: คัดลอกไฟล์ docker-compose.yml ไปยังเครื่อง EC2 ที่ $EC2 ในเส้นทาง $DEPLOY_PATH
  31. - ssh -o StrictHostKeyChecking=no $EC2 "cd $DEPLOY_PATH && docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && docker-compose down --rmi all && docker-compose up -d": เชื่อมต่อ SSH ไปยังเครื่อง EC2 และรันคำสั่งในการดำเนิน deploy ที่ระบุ
  32. only:: กำหนดว่า job นี้จะถูกเรียกเฉพาะเมื่อเป็น branch develop
  33. - develop: ระบุชื่อ branch ที่จะทำให้ job นี้ถูกเรียก

กระบวนการนี้เป็นเป็นการทำ CI/CD สำหรับ Project NodeJS ด้วยการสร้าง Docker image, อัปโหลด image ไปยัง GitLab Container Registry และทำการ deploy ไปยัง EC2 instance

หน้าตา Folder ทั้งหมดจะมีประมาณนี้หลังเราจัดเตรียมหมดแล้วลอง Push ขึ้น Gitlab กัน
git checkout -b develop

จากนั้นจะทำการสร้าง branch develop ขึ้นมาเพราะเราจะให้ .gitlab-ci.yml run เฉพาะ branch develop

git add .
git commit -m "test deploy by ci cd"
git push origin develop

จากนั้น push code ไปที่ gitlab branch develop

จากนั้นไปดูที่ Pipeline บน Gitlab จะมีแค่ 2 Stage Build->Deploy
จากนั้นลองเข้า URL ที่เราทำการ Map กับ API ก็จะขึ้น 1.0.1
จากนั้นจะเปลี่ยนจาก 1.0.1->1.0.2 แล้วทำการ Push ขึ้น Git อีกครั้ง
ก็จะเห็น Pipline ที่เรา Push เข้า Gitlab และรอ Stages ทำงานเสร็จทั้งหมด
หลังจากเสร็จแล้วลอง Refresh ดูก็จะเห็นว่า API เราได้เปลี่ยนจาก 1.0.1 -> 1.0.2 เรียบร้อยแล้ว

จบไปแล้วนะครับกับบทความการทำ CI/CD Gitlab จะเห็นว่าจากบทความยกมาแค่ส่วนหนึ่งของการทำ CI/CD เท่านั้น ซึ่งเนื้อหาไม่ได้ยากและก็ไม่ได้ง่าย ลองฝึกฝนกันดูนะครับ

( CR. ธำรงค์ ไชยวงค์ )

--

--

No responses yet