Nhảy tới nội dung

Dockerize NodeJS App và Deploy với GitLab CI/CD như thế nào?

· Một phút để đọc

Ở phần trước mình đã hướng dẫn các bạn cách Dockerize một ứng dụng SPA với runtime environment . Hôm nay mình sẽ tiếp tục hướng dẫn các bạn dockerize ứng dụng nhưng thay vì là frontend app thì sẽ là backend app, đồng thời auto deploy nó với gitlab CI/CD pipeline luôn. Các bạn sẽ dễ dàng quản lý và cứ merge vào là tự deploy luôn, không phải lằng nhằng setup gì cả. Hy vọng bài viết sẽ giúp ích được cho các bạn.

Các công việc cần thực hiện

Để làm được như vậy chúng ta cần thực hiện các công việc sau:

  1. Tạo một con Node JS app đơn giản để làm REST API và đóng Docker Image cho nó
  2. Chuẩn bị một con server Ubuntu có cài docker (1) và có thể SSH vào được (2), hiện mình đang mua một con Server nhỏ của Digital Ocean để dùng, cũng khá ổn. Bạn có thể chọn con server nào cũng được miễn đáp ứng (1) và (2)
  3. Config trên Gitlab để chạy được CI/CD pipeline cho project. Từ đó có thể tự động build và deploy mỗi lần code mới được đẩy lên.

img alt

Bước 1. Đóng Docker Image con Node JS App

Bạn có thể dùng Express JS để tạo một con server nhỏ.

import express from 'express';

const app = express();
const PORT = process.env.PORT || 8000;

app.get('/', (req, res) => {
res.send('Express REST API!');
});

app.listen(PORT, () => {
console.log(`⚡️[server]: Server is running at http://localhost:${PORT}`);
});

Sau đó đóng Docker Image cho nó bằng file Dockerfile như sau

FROM node:16 AS BUILD_IMAGE

WORKDIR /usr/src/app

COPY package.json .
COPY yarn.lock .

RUN yarn install

COPY . .

RUN yarn build

FROM node:16-alpine

WORKDIR /usr/src/app

COPY --from=BUILD_IMAGE /usr/src/app/package.json ./package.json
COPY --from=BUILD_IMAGE /usr/src/app/dist ./dist
COPY --from=BUILD_IMAGE /usr/src/app/node_modules ./node_modules

EXPOSE 8000

CMD yarn start:prod

Đây là bước đơn giản và nhẹ nhàng nhất nên mình không muốn đi sâu vào chi tiết.

Bước 2. Chuẩn bị một con Server Ubuntu

Ở bước này chúng ta sẽ chuẩn bị một con server có cài docker và có thể ssh vào được. Muốn vậy ta cần:

  • Tạo một Linux User và add user này vào group docker
  • Tạo SSH key cho User trên

Tạo user mới như sau. Ở đây mình sẽ tạo user có tên là deployer

sudo adduser deployer
sudo usermod -aG docker deployer

Sau đó tạo ssh key cho user này. Key này sẽ được thêm vào gitlab để từ phía Gitlab có thể ssh vào.

su deployer
ssh-keygen -b 4096
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

Bước 3. Config Gitlab CI/CD

Bây giờ đảm bảo rằng source code của chúng ta đang được host thành một project trên gitlab

Truy cập vào phần Settings > CI/CD > Variables.

Tại đây chúng ta cần tạo 3 biến môi trường:

  • STAGE_SERVER_IP: địa chỉ IP của con server Ubuntu
  • STAGE_SERVER_USER: Tên user chúng ta tạo khi nãy.
  • STAGE_ID_RSA: Cái này là SSH Private key của user chúng ta tạo trước đó. Nội dung nằm trong file ~/.ssh/id_rsa. Chúng ta sẽ copy nội dung của file này và enter vào biến này. Lưu ý là phải chọn type là File thay vì Variable như 2 biến trên và nhớ thêm dòng mới sau -----END OPENSSH PRIVATE KEY----- ở cuối.

img alt

Xong bước này, bước cuối cùng là add file gitlab-ci.yaml vào root của project với nội dung như sau:

stages:
- build
- deploy-stage

variables:
VARIABLE_DATA: Gitlab-CI-YAML

build:
stage: build
image: docker:18-git
services:
- docker:18-dind
script:
# get node app version from package.json for use in tagging docker image
- apk update && apk add jq
- export VERSION=`jq -r ".version" < ./package.json`
# login to docker
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
# build and tag docker image
- docker build -t $CI_REGISTRY_IMAGE:$VERSION -t $CI_REGISTRY_IMAGE:latest .
# publish finished image
- docker push $CI_REGISTRY_IMAGE:$VERSION
- docker push $CI_REGISTRY_IMAGE:latest

deploy-stage:
stage: deploy-stage
image: alpine:latest
script:
- chmod og= $STAGE_ID_RSA
- apk update && apk add openssh-client
# stop existing docker container & remove images
- ssh -i $STAGE_ID_RSA -o StrictHostKeyChecking=no $STAGE_SERVER_USER@$STAGE_SERVER_IP "docker stop $CI_PROJECT_NAME || true"
- ssh -i $STAGE_ID_RSA -o StrictHostKeyChecking=no $STAGE_SERVER_USER@$STAGE_SERVER_IP "docker rm $CI_PROJECT_NAME || true"
- ssh -i $STAGE_ID_RSA -o StrictHostKeyChecking=no $STAGE_SERVER_USER@$STAGE_SERVER_IP "docker image rm $CI_REGISTRY_IMAGE:latest || true"
# pull new container
- ssh -i $STAGE_ID_RSA -o StrictHostKeyChecking=no $STAGE_SERVER_USER@$STAGE_SERVER_IP "docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY"
- ssh -i $STAGE_ID_RSA -o StrictHostKeyChecking=no $STAGE_SERVER_USER@$STAGE_SERVER_IP "docker pull $CI_REGISTRY_IMAGE:latest"
# run new container
- ssh -i $STAGE_ID_RSA -o StrictHostKeyChecking=no $STAGE_SERVER_USER@$STAGE_SERVER_IP "docker run -d --restart unless-stopped --name $CI_PROJECT_NAME -p 8882:3000 -e "variableData=Docker-Run-Command" $CI_REGISTRY_IMAGE:latest"

Đại ý nội dung file này sẽ gồm 2 bước.

Bước 1. Build docker image và push cái image đó lên gitlab registry

Bước 2. Deploy. Gitlab runner sẽ ssh vào con server của chúng ta, xóa các image cũ, pull image mới về rồi khởi chạy 1 container mới.

Thế là mọi thứ đã xong xuôi rồi. Thấy tick xanh thế này là pipeline đã chạy thành công.

img alt

Chúc các bạn set up thành công. Bài viết nguồn: https://taylor.callsen.me/how-to-dockerize-a-nodejs-app-and-deploy-it-using-gitlab-ci/

Nếu bạn thấy bài viết này hữu ích, mua giúp mình một ly cà phê nhé. Cảm ơn.

img alt