Nhảy tới nội dung

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

Nhân dịp Giáng sinh năm 2022 an lành, không khí mát mẻ, mình bỗng có hứng muốn trải lòng về chuyện nghề, chuyện mình nghĩ sao về cuộc sống của lập trình viên. Sài Gòn giờ này ngoài đường đang kẹt cứng xe cộ vì mọi người đổ ra đường đón Giáng sinh, chắc cũng không nhiều người buồn đọc, nhưng cũng chả sao. Mình chỉ muốn lưu giữ lại cái cảm hứng tuôn trào văn thơi lai láng ở thời điểm hiện tại.

Vì sao mình là lập trình viên mà không phải nghề khác?

Thuở nhỏ, những chiếc máy tính xuất hiện đầu tiên trong xóm mình ở một tiệm game lại là niềm cảm hứng đầu tiên của mình. Với mình đó là thứ gì đó rất mới mẻ và kì diệu, nó sẽ mang lại một tương lai mới. Lúc đó chưa có Internet, cũng chưa có smartphone. Mình có thích chơi game FIFA và mình rất là tò mò làm sao làm được game như vậy. Lúc đó phiên bản FIFA đầu tiên mình chơi là 2002, đồ họa khá hài hước, cầu thủ thì sút bóng bay ra lửa véo véo, cơ mà chơi vui phết. Mình thầm nghĩ trong đầu một ngày nào đó mình sẽ làm được cái game như thế này.

Bây giờ mình đã gần 30 rồi, lại buồn cười hơn là bây giờ mình là Web Developer và mình cũng không còn chơi game FIFA nữa. Cũng dễ hiểu thôi, cũng giống như hồi bé mình ước sau này có thật nhiều tiền để mua bi (cu li) mà bắn và bây giờ thì mình có thể mua được rất nhiều bi nhưng mình không còn chơi bắn bi nữa. Thay vào đó mình đang ngồi viết blog.

Khám phá ra bản thân không có giới hạn

Thực chất lập trình viên theo mình nghĩ nó cũng không hẳn là một cái nghề. Nó chỉ là một phương tiện để bản thân thực hiện quá trình tư duy và khám phá thế giới và cách vận hành của nó. Khi làm lập trình viên, đòi hỏi bản thân phải học hỏi, nghiên cứu liên tục và giải quyết hết vấn đề này đến vấn đề khác. Và nó cứ lặp lại lặp lại, sau một thời gian, bản thân mình đã thay đổi rất nhiều và mỗi thời điểm khác nhau mình lại có bộ kỹ năng khác nhau. Có lúc mình là lập trình viên game, có lúc mình làm Front end, có lúc mình làm Back end, có lúc là Dev Ops. Thậm chí có lúc mình lại là artist, làm marketer ... rồi trader. Mình không biết bạn nghĩ thế nào, nhưng đối với mình, mình không cố định mình là ai và cũng không đặt giới hạn cho bản thân. Vì mình biết nếu mình muốn trở thành như thế nào thì sẽ ra thế đó và đến lúc đủ thời gian thì giới hạn sẽ tự bị xóa bỏ. Thực tế là ai cũng có thể làm lập trình viên chứ không phải là mình học lập trình mới có thể làm lập trình, mình biết có rất nhiều người không phải background lập trình nhưng thực sự rất trâu chó chứ chẳng vừa, đơn giản vì họ thật sự muốn lập trình.

Nghiện cảm giác Ơ rê ka :)))

Thời còn sinh viên, mình rất thích cảm giác này, mỗi lần fix xong một con bug khó sau cả ngày debug, mình cảm thấy mình như một vị thánh tái sinh, I am so powerful. Có những lúc tưởng chừng như hoàn toàn bế tắc trong khi deadline đã dí sát đít, bất ngờ từ trong toilet đang ... thì mình lại nghĩ ra cách giải quyết, thật kì diệu. Bộ não con người thật là khó giải thích.

Cuối cùng cũng xong.

Mình tự nhận mình là một người có sức kiên nhẫn và bền bỉ. Sở thích của mình là build from scratch thành MVP và mình cũng nghĩ đó là sở trường của mình. Mình hiếm khi build một dự án ở mức lưng chừng đèo, tuy nhiên cũng có một vài trường hợp bất khả kháng. Điều thôi thúc của mình đó là phải làm cho xong, phải làm ra được thứ mình dùng được, ít nhất là có giá trị. Mình nghĩ việc mình chơi nhạc cụ và sở thích câu cá lúc nhỏ đã vô tình tập cho mình tính kiên nhẫn và nghiện cảm giác nhận được thành quả. Sau một thời gian nhìn lại có một danh sách dài những thứ mình đã build hoàn chỉnh là một cảm giác rất tuyệt vời. Thật đấy. Mình không nói xạo đâu. Portfolio của mình nè: https://portfolio.dangkhoaicao.me/

Tiền hay giá trị?

Thời gian thấm thoát thôi đưa, cho tới thời điểm hiện tại mình cũng đã 7 mùa khoai sọ làm lập trình viên, mình thấy tiền đến rồi tiền sẽ lại đi như việc FED bơm rồi lại hút tiền, nó sẽ cho mình niềm vui phút chốc rồi cũng sẽ mang đến niềm đau. Tuy nhiên việc đem lại nhiều giá trị hơn bằng cách chia sẻ nhiều hơn, học hỏi nhiều hơn, trau dồi kiến thức nhiều hơn sẽ mang lại niềm vui vững bền hơn. Cảm giác như là bạn tịnh tiến lên cấp giống Siêu Xay Da trong Sôn Gô Ku ấy. Còn tiền thì nó lượn sóng như hình sin, hiện tại mình không có nhiều tiền nhưng mình nghĩ nó có thể đến bất chợt trong tương lai và cũng có thể mất đi nên mình cũng không bận tâm nhiều. Miễn sống no đủ, mạnh khỏe, hạnh phúc là được. Mình không mưu cầu những thứ fancy như nhà cao cửa rộng, xe hơi 8 bánh.

Định viết tiếp mà buồn ngủ ứ chịu được, nên thôi hẹn dịp khác nhé. Vẫn như cũ nếu bạn thấy hay thì mua giúp mình ly cà phê nhé, dạo này buồn ngủ quá mà ko ai mua cà phê cho hết. Buồn nên nói xàm tí cho vui. Chúc giáng sinh vui vẻ.

img alt

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

Hẳn là bạn đã dùng Javascript một thời gian dài, nhưng đã bao giờ bạn dùng đến Proxy trong Javascript hay chưa? Nếu chưa thì bài viết này là dành cho bạn.

Proxy là gì?

Proxy để dễ hiểu thì mình sẽ lấy ví dụ thế này: Tèo với Tí quánh lộn, Tí quánh không lại Tèo thế là Tí nhanh trí chơi chiêu méc với mẹ Tèo rằng Tèo quánh Tí sml. Thế rồi mẹ Tèo xách đầu Tèo về quánh Tèo sml, quánh Tèo xong mẹ Tèo dẫn Tèo qua nhà Tí xin lỗi Tí, (Tí mừng trong bụng cười hí hí). Thế là Tí đã "proxy" đánh Tèo nhờ mẹ Tèo. Đấy là chính là proxy, thay vì tác động trực tiếp tới Tèo, Tí đã thông qua trung gian là mẹ Tèo.

Trong Javascript cơ chế tương tự, thay vì tương tác trực tiếp với object chúng ta sẽ tương tác thông qua Proxy object.

Dùng proxy để làm gì?

Vậy câu hỏi đặt ra là cần proxy để làm gì? tại sao không tương tác trực tiếp luôn mà phải gián tiếp dùng proxy. Để trả lời cho câu hỏi trên mình sẽ đặt một câu hỏi ngược lại cho bạn. Đó là: Làm sao để mình thay đổi xong giá trị của một object trong Javascript thì mình biết nó đã thay đổi và làm một hành động gì đó khi nó thay đổi?

Bạn cứ từ từ đọc qua cách khai báo proxy đã câu trả lời nằm bên dưới.

Tạo proxy như thế nào?

Đầu tiên mình sẽ có một object như thế này:

const person = {
name: "John Doe",
age: 42,
nationality: "American"
};

Để tạo proxy cho object person thì cú pháp như sau:

const person = {
name: "John Doe",
age: 42,
nationality: "American"
};

const personProxy = new Proxy(person, {});

Rất đơn giản, chúng ta tạo một instance mới của class Proxy trong đó argument thứ nhất là target object, argument thứ 2 là một object chứa các handler. Có nhiều kiểu handler nhưng có 2 cái quan trọng là getset. Đặc biệt là set. Và đây là phần quan trọng và hữu dụng của proxy. Dùng proxy mà không có handler thì coi như vứt.

const personProxy = new Proxy(person, {
get: (obj, prop) => {
console.log(`The value of ${prop} is ${obj[prop]}`);
},
set: (obj, prop, value) => {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
obj[prop] = value;
}
});
  • get sẽ invoked khi giá trị của một thuộc tính được truy cập.
  • set sẽ invoked khi giá trị của một thuộc tính bị thay đổi giá trị.
const person = {
name: "John Doe",
age: 42,
nationality: "American"
};

const personProxy = new Proxy(person, {
get: (obj, prop) => {
console.log(`The value of ${prop} is ${obj[prop]}`);
},
set: (obj, prop, value) => {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
obj[prop] = value;
return true;
}
});

console.log(personProxy.name);
personProxy.age = 43;

Như vậy khi console.log(personProxy.name) chúng ta đang access giá trị thuộc tính của personProxy và thế là handler get sẽ chạy đoạn console.log(The value of ${prop} is ${obj[prop]});

Tương tự đối với set, khi ta gán personProxy.age = 13 thì handler set sẽ chạy.

Quay lại câu hỏi lúc nãy là: "Làm sao để mình thay đổi xong giá trị của một object trong Javascript thì mình biết nó đã thay đổi và làm một hành động gì đó khi nó thay đổi? ". Rất nhanh thôi câu trả lời là chúng ta sẽ dùng "set handler". Và từ đó mình có thể chèn bất kì behavior nào chúng ta ví dụ như validation, change detection, re-rendering... bất cứ cái gì cũng được. Đặc biệt hữu dụng cho Reactive Programming.

Mách nhỏ với bạn một package ứng dụng Proxy khá nổi tiếng của VueJS cho reactive programming là @vue/reactivity đấy. Không tin bạn mở code ra mà xem.

Tóm lại Proxy là công cụ built-in mạnh mẽ của Javascript để bạn control một object trong Javascript, bạn hoàn toàn có thể sử dụng nó để viết những code vượt ra khỏi các framework như React, Angular, Vue để có thể tái sử dụng được. Vanilla Javascript muôn năm!

Nếu bạn thấy bài viết hữu ích thì đừng ngần ngại mua giúp mình ly cà phê sữa đá nhé. Thanks.

Bài viết nguồn: https://www.patterns.dev/posts/proxy-pattern/

Official về proxy: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

img alt

· 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

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

Chào các bạn hôm nay mình sẽ hướng dẫn các bạn cách để tạo một docker container cho Single Page Application mà environment có thể truyền vào ở runtime (khi start một container). Cách này có thể áp dụng cho SPA React, Angular hoặc Vue, trong phạm vi bài viết này mình xin lấy React làm ví dụ. Bản thân mình đã từng gặp phải vấn đề này trong quá trình làm việc và cũng đã mất cả ngày trời google, hiểu idea rồi implement. Có thể nó chưa phải là cách tốt nhất nhưng trong trường hợp của mình nó work và giúp cho công việc quản lý biến môi trường nói riêng và DevOps nói chung dễ dàng hơn.

Vấn đề.

Trong một số trường hợp ứng dụng web SPA (gọi nôm na là con Front end) của mình phải đóng gói theo dạng container (Docker) để phục vụ cho nhu cầu cloud hóa giúp dễ dàng cài đặt và triển khai hơn. Điều này kéo theo việc set up biến môi trường phải khác đi một chút. Thông thường biến môi trường sẽ được inject vào trong source ở build time bằng các plugin của webpack như DotEnv hoặc WebpackDefinePlugin và mỗi lần muốn cập nhật biến môi trường lại phải build lại image. Điều này rất mất thời gian và lại khó quản lý vì mình ko thể biết được image nào đang chứa những environment nào dẫn đến rất dễ nhầm lẫn và nhập nhằng về version. Vậy thì làm thế nào? Đừng lo, giải pháp ngay bên dưới này đây, chỉ cần nhìn xuống là thấy.

Cách giải quyết

Trước hết mình sẽ nói về ý tưởng. Bạn đang không muốn build lại image mỗi lần cập nhật biến môi trường, vậy thì đơn giản thôi, mình sẽ không inject biến môi trường ở build time nữa. Hợp lý chưa? ^^ quá là hợp lý luôn. Thay vào đó chúng ta sẽ cần làm được các công việc sau:

  • Tạo được một file env-config.js ở runtime khi start docker container
  • Load file này vào khi initial-load và inject environment vào application
  • Sử dụng các environment đó.

Tạo file env-config.js ở runtime

Bạn hãy xem qua file docker-compose.yml này

version: "3.2"

services:
my-spa-example:
image: my-spa-example
ports:
- "5000:80"
environment:
- "API_URL=production.example.com"

Khi chúng ta run container này với lệnh docker-compose up -d environment sẽ được truyền vào như trên theo mô tả của file docker-compose. Biến API_URL sẽ nhận giá trị production.example.com. Mục tiêu của chúng ta là tạo được một file env-config có nội dung như thế này mỗi lần container khởi chạy thành công.

window.env = {
API_URL: "production.example.com",
}

Vậy làm sao ta có thể làm được điều này? Chúng ta cần có một bash script để làm điều đó. Bash script này có nhiệm vụ là lấy environment để ghi thành file. Nội dung bash script đó như sau:

#!/bin/bash

# Recreate config file
rm -rf ./env-config.js
touch ./env-config.js

# Add assignment
echo "window._env_ = {" >> ./env-config.js

# Read each line in .env file
# Each line represents key=value pairs
while read -r line || [[ -n "$line" ]];
do
# Split env variables by character `=`
if printf '%s\n' "$line" | grep -q -e '='; then
varname=$(printf '%s\n' "$line" | sed -e 's/=.*//')
varvalue=$(printf '%s\n' "$line" | sed -e 's/^[^=]*=//')
fi

# Read value of current variable if exists as Environment variable
value=$(printf '%s\n' "${!varname}")
# Otherwise use value from .env file
[[ -z $value ]] && value=${varvalue}

# Append configuration property to JS file
echo " $varname: \"$value\"," >> ./env-config.js
done < .env

echo "}" >> ./env-config.js

Mình không rành lắm về các lệnh của Linux nhưng đại ý cái script này là nó sẽ tìm đọc một file môi trường .env do chúng ta định nghĩa các biến môi trường (kiểu như template file), scan để lấy danh sách các biến đó ra, sau đó nó tìm trong global environment nếu có biến nào tồn tại thì ưu tiên lấy giá trị của biến trong global còn không thì lấy cái giá trị định trong file .env. Ví dụ ở đây chúng ta có biến global là API_URL có giá trị production.example.com thì nó sẽ ưu tiên lấy giá trị đó mà điền vào. Thế thì câu hỏi đặt ra là cái bash script này chạy ở đâu? Câu trả lời sẽ có sau khi bạn đọc Dockerfile này.

# => Build container
FROM node:alpine as builder
WORKDIR /app
COPY package.json .
COPY yarn.lock .
RUN yarn
COPY . .
RUN yarn build

# => Run container
FROM nginx:1.15.2-alpine


# Nginx config
RUN rm -rf /etc/nginx/conf.d
COPY conf /etc/nginx

# Static build
COPY --from=builder /app/build /usr/share/nginx/html/

# Default port exposure
EXPOSE 80

# Copy .env file and shell script to container
WORKDIR /usr/share/nginx/html
COPY ./env.sh .
COPY .env .

# Add bash
RUN apk add --no-cache bash

# Make our shell script executable
RUN chmod +x env.sh

# Start Nginx server
CMD ["/bin/bash", "-c", "/usr/share/nginx/html/env.sh && nginx -g \"daemon off;\""]

Đây là Dockerfile mô tả image mà chúng ta sẽ build. Đối với SPA ta sẽ áp dụng kiểu 2 stage build, với bước 1 là build static assets (html/css/js...) , bước 2 copy cái đống asset đó qua một con web server (nginx) để serve, với cách làm này thì Docker Image build ra có size nhỏ và chỉ chứa những thứ cần thiết.

Và điểm nhấn ở đây nằm ở dòng này

# Start Nginx server
CMD ["/bin/bash", "-c", "/usr/share/nginx/html/env.sh && nginx -g \"daemon off;\""]

Dòng này giúp con container của chúng ta sẽ run cái script lúc nãy khi khởi chạy và tạo ra file env-config.js như chúng ta mong muốn. Rồi bây giờ chúng ta sẽ tìm hiểu cách load và sử dụng file này như thế nào.

Load file env-config.js

Công việc bây giờ trở nên đơn giản hơn. Chúng ta chỉ cần thêm dòng này vào thẻ <head> của file index.html

<script src="./env-config.js"></script>

Sử dụng các environment

Bây giờ mình tạo một thẻ p như thế này:

<p>API_URL: {window.env.API_URL}</p>

Các bạn sẽ thấy giá trị từ docker-compose.yml đã được hiển thị lên đây, giờ tha hồ mà change environment nhé. Trên đây là những công việc cơ bản để có thể set up runtime environment cho SPA. Ngoài ra có một số lưu ý như sau, mình muốn chia sẻ thêm.

Một số lưu ý

  • Nhớ chỉnh sửa file .gitignore nhé
# Temporary env files
/public/env-config.js
env-config.js
  • Để tránh mismatch giữa development và production bạn nên set up thêm command line cho dev server thế này nhé. Dưới đây là ví dụ dùng boilerplate create-react-app, nếu bạn tự set up build tool thì chế cháo lại nhé.
  "scripts": {
"dev": "chmod +x ./env.sh && ./env.sh && cp env-config.js ./public/ && react-scripts start",
"test": "react-scripts test",
"eject": "react-scripts eject",
"build": "react-scripts build'"
},
  • Chúng ta đang dùng image nginx để serve đống static assets này, và chúng ta có thể config như sau:
# Create directory for Nginx configuration / Tạo Nginx config
mkdir -p conf/conf.d
touch conf/conf.d/default.conf conf/conf.d/gzip.conf

Nội dung conf/conf.d/default.conf.

server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
expires -1; # Set it to different value depending on your standard requirements
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

Nội dung conf/conf.d/gzip.conf

gzip on;
gzip_http_version 1.0;
gzip_comp_level 5; # 1-9
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;

# MIME-types
gzip_types
application/atom+xml
application/javascript
application/json
application/rss+xml
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/svg+xml
image/x-icon
text/css
text/plain
text/x-component;

Hãy đọc lại file Dockerfile ở trên một lần nữa và chú ý dòng này

# Nginx config
RUN rm -rf /etc/nginx/conf.d
COPY conf /etc/nginx

Tổng kết.

Thế là xong xuôi rồi đó, mình đã áp dụng và thành công chúc các bạn cũng áp dụng thành công nhé, nó cũng có một số trường hợp không mong muốn nhưng không sao từ từ rồi các bạn có thể chế cháo và hoàn thiện thêm, nếu có góp ý gì thì cứ liên hệ với mình nhé. Chào tạm biệt và hẹn gặp lại. Dưới đây là bài viết nguồn mình lượm của một thánh ở trên Internet:

https://www.freecodecamp.org/news/how-to-implement-runtime-environment-variables-with-create-react-app-docker-and-nginx-7f9d42a91d70/

See you next time!

Nếu bạn thấy hay thì ủng hộ mình ly cà phê nhé.

img alt