มาทำ CI/CD ผ่าน Gitlab เข้า EC2 กัน
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): การตรวจสอบและติดตามการทำงานของซอฟต์แวร์ในสภาพแวดล้อมการทำงานจริง
ซึ่งกระบวนการจริงๆอาจจะมีมากหรือน้อยกว่านี้แล้วแต่องกรค์ที่เราอยู่
สิ่งที่ต้องเตรียมสำหรับบทความนี้
- Account AWS
- Server EC2 (Ubuntu ≤ 20.04)
- Domain(บทความใช้ Cloudflare จัดการ DNS)
- Gitlab
- Nodejs
- Dockerfile
- Docker-Compose
- .Gitlab-CI.yml
มาเริ่มกันเลยดีกว่าครับโดยบทความจะหยิบ CI/CD มาทำแค่
3 ขั้นตอน Code->Build->Deploy
มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน
sudo apt update
: คำสั่งนี้ใช้ในการอัปเดตรายการแพคเกจของระบบปฏิบัติการ Ubuntu ให้เป็นรายการล่าสุดก่อนที่จะดำเนินการติดตั้งแพคเกจอื่น ๆsudo apt install -y apt-transport-https ca-certificates curl software-properties-common
: คำสั่งนี้ใช้ในการติดตั้งแพคเกจที่จำเป็นสำหรับการดาวน์โหลดและติดตั้ง Docker และความจำเป็นในการสื่อสารที่ปลอดภัยcurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
: คำสั่งนี้ใช้ในการดาวน์โหลดและเพิ่มคีย์ GPG ของ Docker เพื่อยืนยันแพคเกจที่มาจาก Docker Repositorysudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
: คำสั่งนี้เพิ่ม Docker Repository ลงในรายการแหล่งที่มาของแพคเกจsudo apt update
: อัปเดตรายการแพคเกจอีกครั้งเพื่อรับข้อมูลล่าสุดจาก Docker Repositoryapt-cache policy docker-ce
: คำสั่งนี้ใช้เพื่อแสดงแพคเกจ docker-ce ในรายการแพคเกจsudo apt install -y docker-ce
: คำสั่งนี้ใช้ในการติดตั้ง Docker Community Edition (CE)sudo usermod -aG docker ${USER}
: คำสั่งนี้เพิ่มผู้ใช้ปัจจุบันเข้าสู่กลุ่ม docker เพื่อให้เขาสามารถเรียกใช้ Docker ได้โดยไม่ต้องใช้สิทธิ์ rootexit
: คำสั่งนี้จะออกจากเซสชันของผู้ใช้ที่รันคำสั่งนี้ เมื่อทุกอย่างเสร็จสิ้น
มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน
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
ในการดาวน์โหลดไฟล์ที่เหมาะสมสำหรับระบบปฏิบัติการที่กำลังใช้งานอยู่sudo chmod +x /usr/local/bin/docker-compose
: คำสั่งนี้ให้สิทธิ์การเข้าถึงแก้ไขไฟล์ Docker Compose ที่ถูกดาวน์โหลดdocker-compose --version
: คำสั่งนี้ใช้ในการตรวจสอบเวอร์ชันของ Docker Compose หลังจากที่ได้ติดตั้งเสร็จสิ้นsudo apt-get install docker-compose-plugin
: คำสั่งนี้พยายามติดตั้งแพคเกจdocker-compose-plugin
โดยใช้apt-get
มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน
docker network create sandbox_network
: คำสั่งนี้ใช้ในการสร้าง Docker Network ชื่อsandbox_network
เพื่อให้คอนเทนเนอร์ที่ต้องการใช้งานอยู่ในระบบเครือข่ายเดียวกัน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
ตอนนี้ก็เสร็จสิ้นแล้วสำหรับการติดตั้งทรัพยากรณ์บน Server ของเรา
จากนั้นมาถึงขั้นลง Coding กัน
mkdir test-node
cd test-node
npm init
npm i express
code .
สร้าง File index.js
ขั้นตอนเตรียม Code เสร็จแล้วต่อไปมาเตรียมพวก Docker,Docker Compose กันต่อ
มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน
version: '3.3'
: ระบุเวอร์ชันของ Docker Compose ที่ใช้ในไฟล์services:
: เริ่มต้นกำหนดการตั้งค่าของคอนเทนเนอร์ในส่วนนี้node-test-api:
: ชื่อของ service หรือคอนเทนเนอร์ที่จะรันcontainer_name: node-test-api
: ชื่อของ container ที่จะถูกสร้างขึ้นเมื่อคอนเทนเนอร์ถูกเริ่มต้นimage: registry.gitlab.com/dodohandsome/test-node:latest
: กำหนด image ที่จะใช้ในการสร้าง container โดยใช้ image ที่อยู่ใน GitLab Container Registryrestart: unless-stopped
: กำหนดว่า container จะถูกรีสตาร์ตใหม่เมื่อเกิดข้อผิดพลาดหรือหยุดทำงาน ยกเว้นกรณีที่ถูกหยุดโดยผู้ใช้volumes:
: กำหนดการแชร์ข้อมูลระหว่างโฟลเดอร์ในเครื่อง host และเครื่อง guest (container)- .env:/app/.env
: กำหนดว่าจะแชร์ไฟล์.env
จากโฟลเดอร์ในเครื่อง host ไปยังโฟลเดอร์/app/.env
ใน containerenvironment:
: กำหนดตัวแปรแวดล้อมใน container- NODE_ENV=development
: กำหนดค่าตัวแปรแวดล้อมNODE_ENV
เป็นdevelopment
- VIRTUAL_PORT=8080
: กำหนดค่าตัวแปรแวดล้อมVIRTUAL_PORT
เป็น8080
- VIRTUAL_HOST=test-node-api.yourplaceth.com
: กำหนดค่าตัวแปรแวดล้อมVIRTUAL_HOST
เป็นtest-node-api.yourplaceth.com
- LETSENCRYPT_HOST=test-node-api.yourplaceth.com
: กำหนดค่าตัวแปรแวดล้อมLETSENCRYPT_HOST
เป็นtest-node-api.yourplaceth.com
- LETSENCRYPT_EMAIL=thamrong.chaiwong@gmail.com
: กำหนดค่าตัวแปรแวดล้อมLETSENCRYPT_EMAIL
เป็นthamrong.chaiwong@gmail.com
logging:
: กำหนดการตั้งค่า logging ของ containerdriver: "json-file"
: กำหนดใช้งานไดรเวอร์การเก็บ log ประเภท "json-file"options:
: กำหนดตัวเลือกเพิ่มเติมสำหรับการตั้งค่า loggingmax-file: "3"
: กำหนดจำนวนไฟล์ log สูงสุดที่เก็บไว้max-size: "500m"
: กำหนดขนาดไฟล์ log สูงสุดที่เก็บไว้networks:
: กำหนดการตั้งค่าเครือข่ายในส่วนนี้default:
: ชื่อของเครือข่ายexternal:
: กำหนดว่าเครือข่ายนี้เป็นเครือข่ายภายนอกname: sandbox_network
: กำหนดชื่อของเครือข่ายภายนอกที่จะถูกเชื่อมต่อกับ container
หลังจากนั้นกลับมาที่ Gitlab และ Project ที่เราเตรียมไว้ไปที่ Settings->Variables->Add Variable
มาอธิบายแต่ละคำสั่งแต่ละคำสั่งกัน
image: docker:latest
: กำหนดให้ CI/CD runner ใช้ imagedocker:latest
เป็นพื้นฐานservices:
: กำหนด services ที่จะใช้ในการรันงานใน pipeline- docker:dind
: กำหนดให้มี servicedocker:dind
ที่เป็น Docker-in-Docker เพื่อให้สามารถรัน Docker commands ใน runner ได้stages:
: กำหนด stages หรือขั้นตอนของ pipeline ที่จะถูกดำเนินการ- build
: กำหนดขั้นตอน build- deploy
: กำหนดขั้นตอน deploybuild-job-dev:
: กำหนด job ใน stagebuild
ชื่อbuild-job-dev
stage: build
: ระบุว่า job นี้อยู่ใน stagebuild
before_script:
: คำสั่งที่จะถูกรันก่อนเริ่ม script ใน job- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
: เข้าสู่ระบบ Docker Registry ของ GitLab โดยใช้ข้อมูลเข้าสู่ระบบจาก environment variablesscript:
: คำสั่งที่จะถูกรันใน job- docker build --tag $CI_REGISTRY_IMAGE:latest .
: สร้าง Docker image จาก Dockerfile ในโฟลเดอร์ปัจจุบันและให้ image ชื่อเป็น$CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
: อัปโหลด Docker image ที่สร้างขึ้นไปยัง Docker Registry ของ GitLabonly:
: กำหนดว่า job นี้จะถูกเรียกเฉพาะเมื่อเป็น branchdevelop
- develop
: ระบุชื่อ branch ที่จะทำให้ job นี้ถูกเรียกdeploy-dev:
: กำหนด job ใน stagedeploy
ชื่อdeploy-dev
stage: deploy
: ระบุว่า job นี้อยู่ใน stagedeploy
image: alpine
: กำหนดให้ job นี้ใช้ imagealpine
เป็นพื้นฐานvariables:
: กำหนดตัวแปรใน jobDEPLOY_PATH: /home/ubuntu/test-node-api
: กำหนดค่าตัวแปรDEPLOY_PATH
เป็นเส้นทางที่จะใช้ในการ deployEC2: $EC2_DEV
: กำหนดค่าตัวแปรEC2
เป็นที่อยู่ของ EC2 instance ที่จะ deploy ไปSSH_PRIVATE_KEY: $SSH_PRIVATE_KEY_DEV
: กำหนดค่าตัวแปรSSH_PRIVATE_KEY
เป็นคีย์ SSH ส่วนตัวของเครื่อง serverbefore_script:
: คำสั่งที่จะถูกรันก่อนเริ่ม script ใน job- apk add openssh-client
: ติดตั้ง openssh-client เพื่อใช้ในการเชื่อมต่อ SSH- eval $(ssh-agent -s)
: เริ่มใช้ ssh-agent ใน background- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
: เพิ่มคีย์ SSH ส่วนตัวในตัวแปร$SSH_PRIVATE_KEY
ลงใน ssh-agent- mkdir -p ~/.ssh
: สร้างโฟลเดอร์~/.ssh
สำหรับเก็บคีย์ SSH- chmod 700 ~/.ssh
: กำหนดสิทธิ์เป็นแบบเป็นเจ้าของเท่านั้นสำหรับโฟลเดอร์~/.ssh
script:
: คำสั่งที่จะถูกรันใน job- scp -o StrictHostKeyChecking=no ./docker-compose.yml $EC2:$DEPLOY_PATH
: คัดลอกไฟล์docker-compose.yml
ไปยังเครื่อง EC2 ที่$EC2
ในเส้นทาง$DEPLOY_PATH
- 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 ที่ระบุonly:
: กำหนดว่า job นี้จะถูกเรียกเฉพาะเมื่อเป็น branchdevelop
- develop
: ระบุชื่อ branch ที่จะทำให้ job นี้ถูกเรียก
กระบวนการนี้เป็นเป็นการทำ CI/CD สำหรับ Project NodeJS ด้วยการสร้าง Docker image, อัปโหลด image ไปยัง GitLab Container Registry และทำการ deploy ไปยัง EC2 instance
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
จบไปแล้วนะครับกับบทความการทำ CI/CD Gitlab จะเห็นว่าจากบทความยกมาแค่ส่วนหนึ่งของการทำ CI/CD เท่านั้น ซึ่งเนื้อหาไม่ได้ยากและก็ไม่ได้ง่าย ลองฝึกฝนกันดูนะครับ
( CR. ธำรงค์ ไชยวงค์ )