Ở 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:
- Tạo một con Node JS app đơn giản để làm REST API và đóng Docker Image cho nó
- 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)
- 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.
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 UbuntuSTAGE_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.
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.
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.