Deploy ứng dụng Docker, NodeJS, Mongo, Redis
Chào mừng các bạn đã quay trở lại với series Học Docker và CICD của mình 👋👋
Ở bài trước chúng ta đã cùng nhau tìm hiểu về cách chạy container với non-root user. Từ bài này chúng ta sẽ bắt đầu deploy các ứng dụng Docker chạy trên server thật, cuối cùng là dùng CICD để auto deploy và chúng ta kết thúc series này nhé
Ở bài này chúng ta sẽ cùng nhau deploy ứng dụng Docker, NodeJS, MongoDB, Redis trên server thật, cùng với đó là setup domain, HTTPS nhé. Cuối bài ta sẽ cùng so sánh với cách deploy kiểu truyền thống ngày xưa khi không có Docker sẽ như thế nào nhé
Điều kiện tiên quyết
Nghe vẫn nom như đám học sinh cấp 3
Vì bài này ta deploy ở trên server thật nên đương nhiên điều kiện cần có là các bạn có 1 VPS của riêng mình, nhớ là VPS chứ không phải Hosting nhé. Nên chọn Ubuntu nhé các bạn (Centos, Redhat cũng không sao).
Setup
Đầu tiên các bạn clone source code của mình ở đây (nhánh master
). Bài này chúng ta chỉ quan tâm tới folder deploy-docker-node
nhé.
Tổng quan project:
- Về chức năng thì project này vẫn giống các project NodeJS ở các bài trước gồm có: login, logout, thêm sản phẩm, lưu user session,...
- Tất cả các container đều được chạy với non-root user
Tiếp theo các bạn copy folder deploy-docker-node
này ra 1 nơi riêng biệt nào đó để lát nữa ta push lên Repository nhé
Sau đó các bạn tạo 1 repository trên Gitlab với tên là deploy-docker-node
để tẹo nữa chạy ở local ngon nghẻ xong ta sẽ push code lên repository, rồi lên server pull về và build image nhé.
Chạy ở local
Như thường lệ, việc đầu tiên ta cần làm là build Docker image và chạy thử ở local trước để đảm bảo mọi thứ vẫn ổn định trước khi ta lên server nhé.
Bởi vì tất cả các container của ta đều chạy với non-root user nên trước khi chạy ta cần xác định UserID + Group ID của user ở môi trường gốc của ta là gì nhé (nếu các bạn không hiểu tại sạo lại làm thế thì đọc lại bài Chạy ứng dụng container bằng non-root user của mình nhé).
Ở môi trường gốc các bạn chạy command sau:
id -u
----> 501
id -g
----> 20
Như các bạn thấy User ID ở môi trường gốc của mình là 501
và Group ID là 20
.
Chú ý quan trọng rằng: nếu UserID + Group ID của các bạn là 1000:1000
thì ở Dockerfile bên dưới có chút khác, vì trùng với user node
trong container NodeJS ở bài trước mình đã nói rất kĩ rồi nhé.
Sau đó các bạn mở Dockerfile
và ta cùng sửa lại phần user như sau nhé:
FROM node:12.18-alpine
WORKDIR /app
RUN npm install -g pm2
COPY ["package.json", "package-lock.json*", "./"]
RUN npm install --production --silent
COPY . .
# ----- Nếu UID:GID là 1000:1000
# USER node
# RUN chown -R node:node /app
# ----- Nếu không thì
RUN addgroup -g 20 appgroup
RUN adduser -D -u 501 appuser -G appgroup
RUN chown -R appuser:appgroup /app
USER appuser
# ---------------------------------------
CMD ["pm2-runtime", "ecosystem.config.js", "--env", "production"]
Ở trên UID:GID
của mình là 501:20
nhé.
Sau đó ta tiến hành build image nhé:
docker build -t learning-docker:deploy-node .
BÙMMMM , Lỗi @@:
Lỗi báo group với ID là 20
được được sử dụng mất rồi. Khả năng là Group mà ta đang tạo với ID 20 đã bị trùng với 1 group nào đó có sẵn trong container.
Các bạn sửa lại Dockerfile thêm vào cho mình 1 dòng sau để liệt kê ra các group có sẵn trong container nhé:
FROM node:12.18-alpine
WORKDIR /app
RUN npm install -g pm2
COPY ["package.json", "package-lock.json*", "./"]
RUN npm install --production --silent
COPY . .
# ----- Nếu UID:GID là 1000:1000
# USER node
# RUN chown -R node:node /app
# ----- Nếu không thì
RUN cat /etc/group # +++++++++++++++++ THÊM VÀO DÒNG NÀY
RUN addgroup -g 20 appgroup
RUN adduser -D -u 501 appuser -G appgroup
RUN chown -R appuser:appgroup /app
USER appuser
# ---------------------------------------
CMD ["pm2-runtime", "ecosystem.config.js", "--env", "production"]
Sau đó ta tiến hành build lại image:
docker build -t learning-docker:deploy-node .
Xem ở cửa sổ Terminal các bạn sẽ thấy in ra như sau:
....
dialout:x:20:root ->>>>>>>>>>>>>> Chú ý dòng này
....
Ồ vậy là GroupID 20 đã bị trùng với 1 group tên là dialout
trong container, vậy thì ở Dockerfile thay vì tạo group mới ta đơn giản là dùng luôn group này, các bạn sửa lại Dockerfile như sau nhé:
FROM node:12.18-alpine
WORKDIR /app
RUN npm install -g pm2
COPY ["package.json", "package-lock.json*", "./"]
RUN npm install --production --silent
COPY . .
RUN adduser -D -u 501 appuser -G dialout
RUN chown -R appuser:dialout /app
USER appuser
CMD ["pm2-runtime", "ecosystem.config.js", "--env", "production"]
Sau đó ta tiến hành build lại image và sẽ không thấy bị lỗi nữa nhé:
docker build -t learning-docker:deploy-node .
Tiếp theo ta cập nhật lại file docker-compose.yml
để chạy MongoDB
và Redis
với cùng UID:GID
như ở môi trường gốc nhé:
...
db:
image: mongo
volumes:
- .docker/data/db:/data/db
restart: unless-stopped
user: "501:20"
redis:
image: redis:5-alpine
volumes:
- .docker/data/redis:/data
restart: unless-stopped
user: "501:20"
Cuối cùng là ta tiến hành chạy project lên nhé:
docker-compose up -d
Sau đó ta mở trình duyệt ở localhost:3000
thấy như sau là cuộc đời tươi sáng rồi nhé :
Các bạn thử tạo account, login và thêm mới thử vài sản phẩm xem sao nhé.
Note cho bạn nào đang dùng Windows: các bạn xem lại phần chú ý lúc mount volume cho MongoDB mình đã nói ở bài Dockerize ứng dụng NodeJS, Mongo rồi nhé
Vậy là đã ổn rồi đó, các bạn ta tiến hành push code lên Gitlab repository nhé:
git init
git add .
git commit -m "first commit"
git remote add origin https://gitlab.com/maitrungduc1410/deploy-docker-node.git
git push -u origin master
Note: Ở trên các bạn thay tên account của các bạn vào nhé (như của mình là maitrungduc1410
)
Âu cây tiếp theo ta sẽ bước tới công đoạn deploy trên server nhé
Deploy
Đầu tiên các bạn nhớ SSH vào server nhé.
Điểm hay ho và cũng là thứ tuyệt vời nhất khi làm việc với ứng dụng Docker đó là việc ít (hay thậm chí là không) bị ảnh hưởng bởi môi trường ngoài, và 1 khi ta đã Dockerize thành công ở local thì lên server deploy sẽ rất đơn giản. Thứ ta cần chỉ là Docker
và Docker Compose
(tất nhiên ).
Bắt đầu thôi nào
Đầu tiên các bạn kiểm tra xem trên server đã có Docker và Docker compose chưa nhé:
docker --version
docker-compose --version
Nếu chưa có các bạn cài đặt bằng hướng dẫn ở đây(Docker) và ở đây(Docker compose) nhé
Sau đó các bạn chuyển tới 1 folder bất kì trước khi bắt đầu nhé, ở đây mình chọn /var/www/html
cd /var/www/html
Sau đó ta clone source code từ Gitlab về nhé:
git clone https://gitlab.com/maitrungduc1410/deploy-docker-node
Note: ở trên các bạn thay tên account Gitlab của các bạn vào nhé.
Nếu được hỏi username + pass thì các bạn nhập email + pass tài khoản Gitlab.
Và cũng giống như ở local, trước khi chạy ta cần check xem User ID và Groupd ID của user ở môi trường gốc của chúng ta là gì nhé:
id -u
--->> 1000
id -g
--->> 1000
Ở trên mình có UID:GID
là 1000:1000
(yeah, lát nữa viết Dockerfile cho nhàn )
Các bạn sửa lại Dockerfile với nội dung như sau nhé:
FROM node:12.18-alpine
WORKDIR /app
RUN npm install -g pm2
COPY ["package.json", "package-lock.json*", "./"]
RUN npm install --production --silent
COPY . .
RUN chown -R node:node /app
USER node
CMD ["pm2-runtime", "ecosystem.config.js", "--env", "production"]
Note quan trọng: vì ở trên UID:GID của mình là 1000:1000 nên mình dùng luôn user node
, còn không thì các bạn lại làm như ở local vừa nãy nhé.
Tiếp theo ta cần update lại user cho MongoDB và Redis trong docker-compose.yml
nữa:
db:
image: mongo
volumes:
- .docker/data/db:/data/db
restart: unless-stopped
user: "1000:1000"
redis:
image: redis:5-alpine
volumes:
- .docker/data/redis:/data
restart: unless-stopped
user: "1000:1000"
Ổn rồi đó ta tiến hành build image nhé:
docker build -t learning-docker:deploy-node .
Sau khi build xong thì ta chạy project lên thôi:
docker-compose up -d
Đợi tầm 1 phút để MongoDB khởi động hoàn toàn, ta mở trình duyệt ở địa chỉ <server_IP>:3000
nhé.
Và..............BÙM...
Sao vậy nhỉ??
Thử kiểm tra xem container đã chạy chưa nhé:
docker-compose ps
Name Command State Ports
--------------------------------------------------------------------------------------------
deploy-docker-node_app_1 docker-entrypoint.sh pm2-r ... Up 0.0.0.0:3000->3000/tcp
deploy-docker-node_db_1 docker-entrypoint.sh mongod Up 27017/tcp
deploy-docker-node_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
Ổn rồi mà nhỉ
Vấn đề là với các VPS ta mua, thì thường ban đầu sẽ không có port nào được mở để cho traffic từ thế giới bên ngoài đi vào, mà ta phải tự tay mở. Có 2 cách: 1 là ta dùng UFW
(Firewall) như 1 số tutorial có làm, nhưng cách này không hay, nếu các bạn mua VPS ở các nhà cung cấp lớn (Google, AWS, Azure, Digital Ocean,...) thì họ cho phép ta có thể mở port trên giao diện web quản lý riêng. Mình thấy điều này tốt hơn, ta chỉ cần chỉ định port nào cần mở, việc còn lại thì nhà cung cấp họ lo, cũng đỡ lo các vấn đề bảo mật hơn. Dùng UFW
không đọc kĩ hướng dẫn lúc enable
lên khéo lại bị terminate và không SSH lại được
Ví dụ như mình dùng Azure thì giao diện nom như sau:
Nhưng nếu nơi các bạn mua VPS không hỗ trợ việc này hoặc bạn cứ muốn mở bằng "UFW" thì ta chạy command sau nhé:
sudo ufw allow 3000
Và ngay sau khi ta mở port để traffic bên ngoài có thể đi vào server, quay trở lại trình duyệt F5 ta sẽ thấy kết quả:
Sau đó ta thử tạo tài khoản, login và thêm mới vài sản phẩm sẽ thấy mọi thứ hoạt động trơn tru như local
Quá đơn giản khi deploy ứng dụng Container với Docker phải không nào
HTTPS
Trỏ Domain về Server
Khi ta chạy project ở production thường ta sẽ dùng domain để trông cho chuyên nghiệp, có thương hiệu hơn mà còn có những lợi ích to lớn như sau:
- Dùng domain lấy HTTPS rất dễ
- Không phải mở port vô tội vạ khi số project trên server của ta tăng lên nhiều, khi đó mỗi project ta phải chạy ở port khác nhau.
- Dùng domain ta chỉ cần mở duy nhất 2 port: 80 cho HTTP và 443 cho HTTPS, dù số lượng project có nhiều thế nào.
Ở đây mình không khuyến khích các bạn dùng Self-signed certificate để lấy HTTPS cho IP nhé, vì gần như chẳng bao giờ ta dùng cái đó ở production vì các trình duyệt bây giờ đã không cho phép Self-Signed Certificate và sẽ hiển thị màn hình đỏ lòm.
Vậy nên điều kiện cần có để lấy HTTPS ở bài này là các bạn cần phải có 1 tên miền (domain name) nhé. Mình khuyến khích các bạn mua ở những nhà cung cấp lớn như Goddady, chọn 1 tên cùi cùi để học tập (loanh quanh 100K VND là cùng )
Sau khi các bạn có domain thì các bạn vào trang quản trị của domain đó, sửa lại địa chỉ IP của bản ghi A
mà được tạo sẵn để trỏ về server IP của chúng ta nhé:
Ngay sau đó các bạn quay trở lại trình duyệt truy cập lại app của chúng ta bằng domain xem sao nhé, ví dụ như của mình ở đây là xoixeotv.com:3000
Âu cây vậy là giờ app của chúng ta đã có domain rồi, việc tiếp theo là lấy HTTPS nữa thôi
Note quan trọng: Ở trên các bạn thấy mình có tạo 1 bản ghi CNAME
với name
là www
và value
trỏ về @
tức là trỏ về cùng IP với bản ghi A
để lát nữa ta có thể truy cập theo cả 2 kiểu xoixeotv.com
hoặc www.xoixeotv.com
nhé. Bước này rất quan trọng nhé, nếu bạn quên thì tí nữa command certbot lấy https sẽ lỗi như sau:
Cách lấy HTTPS
Ở production, người ta thường dùng 1 webserver đứng trước app của chúng ta đóng vai trò vừa là tầng bảo mật, load balancing, caching hoặc proxy cho request nhận từ phía user giúp tăng hiệu suất cho ứng dụng của chúng ta.
Và 1 trong những webserver mình thấy người ta dùng nhiều nhất bây giờ là Nginx. Ở bài này ta sẽ dùng nó nhé. Cùng với đó, để lấy chứng chỉ HTTPS free thì ta dùng Certbot nhé.
Từ đây ta có luồng xử lý như sau:
Nom cũng như deploy kiểu truyền thống trước giờ vẫn làm ấy nhỉ
Vậy vấn đề ở đây là: thế Nginx kia ta đặt ở môi trường ngoài hay môi trường bên trong Docker?
Ô hay đang làm Docker, muốn giữ môi trường ngoài "nguyên trinh" mà, tất nhiên đặt Nginx chạy trong container luôn chứ, vậy nên chi tiết design nom sẽ như sau:
Với cách design bên trên, khi ta có nhiều project thì trông sẽ như sau:
Và với mỗi project thường ta sẽ đặt chúng ở những nơi riêng biệt:
- 1 folder cho project NodeJS + MongoDB + Redis
- Project Laravel + MySQL + Redis đặt ở folder khác
- ... N project khác
Còn riêng container Nginx + Certbot ta sẽ đặt ở 1 nơi khác vì đây là thứ dùng chung cho tất cả project. Và mình nhận thấy là việc chạy Nginx + Certbot trong Docker container này có 1 số nhược điểm như sau:
- Việc lấy HTTPS trong container hơi lằng nhằng hơn 1 chút khi phải map volume, chạy script bằng tay. Các bạn có thể xem ở đây
- Trong trường hợp ta có 1 số app chạy trong Docker, 1 số app khác lại làm theo kiểu truyền thống không có Docker (cài trực tiếp vào môi trường gốc), và khi lấy HTTPS cho chúng sẽ "hơi bị đau đầu"
- Cùng với đó mình hay dùng Wildcard Certificate (dạng kiểu
*.example.com
) thì hơi khó lấy hơn
Do vậy sau 1 thời gian làm mình đã chuyển qua cách khác, đó là đưa Nginx + Certbot ra môi trường ngoài, làm nhiệm vụ chuyên trách như 1 tầng bảo mật chứ không chạy trong container nữa. Design mới nom sẽ như sau:
Và ở bài này mình sẽ hướng dẫn các bạn làm theo kiểu design này nhé. Môi trường ngoài "mất trinh" chút không sao các bạn nhé :joy.
Mục đích chính nhất khi mình lựa chọn cách tiếp cận này là vì ta có thể lấy HTTPS rất dễ, dù chúng có chạy trong Docker hay được deploy theo kiểu truyền thống
Các bạn hoàn toàn có thể chạy cả Nginx + Certbot trong container cũng được nhé, những cái nhược điểm bên trên có thể chỉ với mình, với các bạn lại không sao. Các bạn thử xem và comment cho mình nhé
Bắt đầu thôi nào
Đầu tiên ở môi trường gốc các bạn cài Nginx:
sudo apt update
sudo apt install nginx
chú ý rằng ở trên mình dùng
apt
chỉ có ở trên Ubuntu/Linix Mint/Debian, các bạn nào dùng Centos/Redhat Fedora/Alpine... thì phải thay đổi đi nhé
Sau khi cài xong các bạn kiểm tra xem Nginx chạy chưa nhé:
sudo service nginx status
Sau đó ta sẽ cần mở port 80 cho HTTP và 443 HTTPS cho traffic bên ngoài truy cập vào được nhé. Chú ý rằng dù ta chỉ mong muốn user truy cập bằng HTTPS nhưng port 80 vẫn cần được mở vì khi user gõ trực tiếp vào trình duyệt http://...
(không có S) mà port 80 ta không được mở là user sẽ không truy cập được đâu nhé. Và để mở port thì các bạn lại dùng 1 trong 2 cách: qua trang quản trị nhà cung cấp VPS (nên dùng) hoặc dùng UFW. Nếu dùng UFW thì các bạn làm như sau:
sudo ufw allow 'Nginx Full'
Đến đây các bạn quay lại trình duyệt truy cập ở địa chỉ domain của các bạn (cổng 80) thấy như sau là oke nhé:
Tiếp theo trước khi đến phần quan trọng là lấy HTTPS thì với các bạn không dùng Ubuntu thì xem cho mình phần bên dưới nhé
Oke rồi thì tiếp là ta cài Certbot:
sudo apt update
sudo apt install snapd
sudo snap install --classic certbot
chú ý rằng mình đang dùng Ubuntu (Debian cũng vậy), bạn nào dùng các bản distro khác (Redhat Fedora/Centos/Alpine...) thì check docs của snapd nhé
Nếu quá trình cài
certbot
bị lỗi thì các bạn xem thêm ở đây nhé
Tiếp theo các bạn xoá file cấu hình cho domain mặc định của Nginx đi nhé.
sudo rm -rf /etc/nginx/sites-available/default
sudo rm -rf /etc/nginx/sites-enabled/default
Sau đó ta tạo 1 file cấu hình cho domain của chúng ta như sau (nhớ đổi tên thành tên domain của các bạn cho phù hợp nhé):
sudo touch /etc/nginx/sites-available/xoixeotv.com
Nội dung thì các bạn để như sau nhé:
server {
listen [::]:80;
listen 80;
# allow upload file with size upto 500MB
client_max_body_size 500M;
server_name xoixeotv.com www.xoixeotv.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header x-forwarded-for $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
Ở trên các bạn thấy rằng ta sẽ proxy request vào cổng 3000
nơi mà app NodeJS của chúng ta đang chạy, nhớ là cổng 3000 là cổng mà ta đã map port nhé.
Sau đó các bạn lưu lại, tiếp theo ta cần tạo symbolic link tới folder /etc/nginx/sites-enabled
nhé:
sudo ln -s /etc/nginx/sites-available/xoixeotv.com /etc/nginx/sites-enabled/
Note: các bạn thay tên domain của các bạn vào cho phù hợp nhé.
Sau đó ta kiểm tra xem cấu hình Nginx đã đúng hay chưa bằng command:
sudo nginx -t
->>>>>
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Nếu cấu hình đã đúng ta tiến hành khởi động lại Nginx:
sudo service nginx restart
Ngay sau đó ta quay lại trình duyệt truy cập ở địa chỉ http://<tên miền>
(không cần nói cổng bao nhiêu nữa nhé):
Cuối cùng ta lấy HTTPS với 1 command đơn giản như sau:
sudo certbot --nginx -d xoixeotv.com -d www.xoixeotv.com
Khi được hỏi có Redirect HTTP to HTTPS
hay không thì các bạn chọn option 2 (YES), để chuyển hướng toàn bộ HTTP sang HTTPS nhé.
Các bạn để ý rằng khi lấy chứng chỉ HTTPS ta lấy cho đồng thời 2
xoixeotv.com
vàwww.xoixeotv.com
Cuối cùng là quay lại trình duyệt F5 và xem kết quả nào:
Vậy là chúng ta đã hoàn thành việc lấy HTTPS, các bạn có thể đóng lại cổng 3000, không cho traffic từ thế giới ngoài gọi vào vì giờ ta dùng domain truy cập qua cổng 80 và 443 rồi nhé. Các bạn có thể đóng cổng thông qua giao diện Web của nhà cung cấp như mình đã nói ở trên hoặc bằng UFW
như sau:
sudo ufw delete 3000
Nếu không phải là Ubuntu
Một số provider như kiểu AWS EC2 thì họ không dùng Ubuntu mà dùng các bản phân phối khác của Linux kiểu Centos hay Redhat,..., nên sẽ hơi khác một chút xíu xiu ở đoạn cấu hình Nginx.
Để check hệ điều hành hiện tại là gì các bạn có thể chạy command sau:
cat /etc/os-release
Đầu tiên ta kiểm tra xem có 2 folder này sau khi cài nginx
không nhé (ta cd
vào là biết):
cd /etc/nginx/sites-available
cd /etc/nginx/sites-enabled
Nếu không có thì các bạn tạo chúng nhé:
sudo mkdir -p /etc/nginx/sites-available
sudo mkdir -p /etc/nginx/sites-enabled
Tiếp theo ta kiểm tra file cấu hình tổng của nginx đã include
folder /etc/nginx/sites-enabled
vào hay chưa nhé:
cat /etc/nginx/nginx.conf
Check block http
xem có đoạn sau không nhé:
http {
...
include /etc/nginx/conf.d/*.conf;
# dòng này quan trọng
include /etc/nginx/sites-enabled/*;
}
Nếu chưa có thì các bạn thêm vào cho mình là được nhé (dùng vi
, vim
hoặc nano
).
So sánh với cách deploy truyền thống
Ta cùng hồi ức lại cách deploy ngày xưa nhé:
- Đầu tiên ta cần cài NodeJS
- Sau đó ta cần cài MongoDB, setup authentication
- Cài Redis
Cứ mỗi khi deploy trên 1 server mới ta đều phải làm lại những bước như vậy, nếu các bạn có xem bài Deploy ứng dụng chat realtime Laravel của mình sẽ thấy rằng ta phải cài mệt nghỉ mới đủ để chạy project lên.
Cùng với đó 1 điều vô cùng đáng sợ đó là khi ta có nhiều project và ta cần update, ví dụ update MySQL chẳng hạn, không may xảy ra lỗi và DB "tắt điện", BÙM, tất cả project xong phim .
Thứ nữa là với những project cần cài nhiều thứ, như project realtime chat kia của mình chẳng hạn, việc nhớ cài tất cả mọi thứ lắm lúc là 1 điều khó khăn, đặc biệt những hôm nào cãi nhau với người yêu
Ngoài ra còn N + 1 nỗi sợ nữa
Khi các bạn xem lại phần deploy ở bài này, ta thấy rằng rất nhanh đơn giản và tiện lợi, thích thì update phiên bản bằng tên image mới, thích thì xoá, thay mới, vô cùng dễ. Điều mà nếu ta deploy theo kiểu truyền thống khó mà có được. Và đó cũng là điều tuyệt vời nhất mình muốn truyền tải với các bạn trong series này.
Đóng máy
Qua bài này ta đã hoàn thành deploy hoàn chỉnh 1 ứng dụng NodeJS cùng với đó là lấy HTTPS xịn xò cho app của chúng ta.
Cảm nhận được sự đơn giản khi deploy ứng dụng Docker so với kiểu deploy truyền thống. Bằng cách chạy app trong Docker container, ta đã có thể tự tin hơn trong việc deploy, thêm sửa xoá, update thành phần bất kì mà không còn những lỗi lo "em xoá đi rồi không cài lại được" hay "thôi đừng update, đang chạy ngon update lên chết cả đấy"
Mong rằng các bạn sẽ áp dụng thật tốt vào công việc của riêng mình.
Cám ơn các bạn đã theo dõi, hẹn gặp lại các bạn vào những bài sau ^^
All rights reserved