Flux architecture trong Reactjs

(Đợt này dịch Anh Cô Vy cũng căng, các trường mầm non đều nghỉ nên cả ngày toàn dành thời gian chơi với con nên mãi không viết được blog nào. )

Từ khi vào đời bằng nghề code thì hầu hết thời gian mình lăn lộn trong rừng backend, devops, app, … cũng không mấy khi làm dự án frontend.

Backend – Frontend
From: https://www.reddit.com/r/ProgrammerHumor/comments/84mzyg/frontend_vs_backend_20/

Lâu lâu thì cũng có nhảy vào fix bug nhưng hầu hết là xử lý logic hoặc task đơn giản. Gần đây, mình tham gia một dự án Web chatbot với Rasa Backend (để make color cho CV full-stack engineer) từ khâu thiết kế code base, … Nên muốn chia sẻ đôi chút về cấu trúc code.

Những kỹ thuật đã áp dụng

Dự án lần này mình làm dự án về kỹ thuật thì có một số thứ
– React js
– Typescript
– MobX
– Webpack
– Socket.io
– Unittest (Jest)

Architecture

Mục đích đặt ra
– Cấu trúc đơn giản dễ hiểu do hệ thống cũng khá đơn giản
– Hạn chế tối đa lỗi, bug
– Đảm bảo code format, code style cho dự án (tận dụng sức mạnh của typescript, unittest)

Sử dụng Flux architecture

Flux Architecture
From: https://medium.com/@me_76676/hand-tooled-flux-creating-a-tiny-flux-architecture-in-a-handful-of-lines-d3ae27865e2e

Data flow

Dataflow
  • Repository: phần phụ trách trao đổi dữ liệu từ server thông qua API hoặc Websocket. Các thao tác lấy và cập nhật dữ liệu thực hiện qua lớp này. Bên mình dùng Restful nên thường đặt tên theo resource ví dụ như: UserRepository gọi các API liên quan đến user, ApplicationRepository phụ trách các API liên quan đến application
  • Actions: action là phần quan trọng phụ trách các tác vụ sau
    • Thao tác Repository để lấy và cập nhật dữ liệu từ backend
    • Nhận các event từ view
    • Cập nhật store = trạng thái application
  • Stores: Quản lý trạng thái application (MobX)
  • Views: Là phần phụ trách UI, nhận các thao tác của người dùng, truyền các event vào actions. View sẽ giám sát sự thay đổi của store để cập nhật các component của mình

Một nguyên tắc quan trọng ở đây là trạng thái của application – store chỉ được thay đổi thông qua actions, views sẽ giám sát thực thay đổi của store để cập nhật lại. Views không thao tác trực tiếp đến store.

Kết luận

  • Cũng khá thú vị khi nhìn phần code base mình viết ra được thay đổi, trưởng thành theo dự án.
  • Có background backend thì phần thiết kế gọi API restful hoặc build môi trường dev ở local, API nhàn hơn. Cần thiết tự sửa API, tự sửa DB theo nhu cầu

PS, Việt Nam cố lên, thế giới cố lên!

Additional, Other Kubernetes related topics is here. https://tuantranf.me/tag/kubernetes/

Synchronizing files between local machine and Kubernetes Persistent Volume

How to rsync files to a Kubernetes Persistent Volume (PV)?

I need to rsync a recursive directory tree to a specific PV in a Kubernetes cluster.
Unfortunately, kubectl cp command does not support rsync behavior at the moment. So we have to do a simple hacking to do it.
rsync behavior to copy files from pod to pod, perhaps for kubectl cp · Issue #551 · kubernetes/kubectl · GitHub

TLDR;

We can use a rsync script to rsync files to a Kubernetes pod which mount specific PV as a volume and rsync is executable.

The same idea can be found here
rsync files to a kubernetes pod – Server Fault

Let’s say we have a recursive folder sample-dir and a PV named sample-pv which Persistent Volume Claim (PVC) named sample-pvc

Prepare working pod

The working pod has to (1) mount PVC sample-pvc as a volume; (2) has rsync is executable.

We will create a YAML file for working pod which mounts PVC sample-pvc as a volume. And for (2), we will use a rsync executable Docker image eeacms/rsync.

working-pod.yml

apiVersion: v1
kind: Pod
metadata:
    name: working-pod
spec:
    volumes:
    - name: sample-volume
      persistentVolumeClaim:
        claimName: sample-pvc # mount pvc as volume
    containers:
    - name: operator
      image: eeacms/rsync # rsync executable Docker image
      command: ['sh', '-c', 'sleep 3600'] # sleep 3600 secs for files copy
      volumeMounts:
      - mountPath: /var/files # mount volume to path /var/files
        name: sample-volume

Prepare rsync bash script

We will create a bash script name rsync-helper.sh for execute rsync on working pod.

#!/bin/bash
pod=$1;shift;kubectl exec -i $pod -- "$@"

Make sure rsync-helper.sh is executable.

$ chmod +x ./rsync-helper.sh

Deploy working pod to Kubernetes cluster

In next step, we will deploy working pod to Kubernetes cluster.

$ kubectl apply -f ./working-pod.yml

Execute rsyn command on remote working pod

After status of wroking-pod pod become running, we will execute rsync-helper.sh script on it remotely.

$ rsync -av --delete-after --progress -e './rsync-helper.sh' sample-dir working-pod:/var/files/

building file list ...
 0 files...
19 files to consider
sample-dir/
sample-dir/new-file
           6 100%    0.00kB/s    0:00:00
           6 100%    0.00kB/s    0:00:00 (xfer#1, to-check=16/19)
sample-dir/updated
           5 100%    4.88kB/s    0:00:00
           5 100%    4.88kB/s    0:00:00 (xfer#2, to-check=15/19)
deleting sample_dir/deleted

sent 689 bytes  received 70 bytes  1518.00 bytes/sec
total size is 98957  speedup is 130.38

Note, you can add –dryrun option for debuging

$ rsync -av --dry-run --delete-after --progress -e './rsync-helper.sh' sample_dir working-pod:/var/files/

Teardown

We will delete working pod by bellow command

$ kubectl delete --grace-period=1 -f ./working-pod.yml

Conclusion

In this blog, we have a work around solution for synchronizing files and directories between local machine and PV on Kubernetes cluster.

Goodluck, happy Hacking!

Additional, Other Kubernetes related topics is here. https://tuantranf.me/tag/kubernetes/

Kubernetes ConfigMap recursive directory issues

A workaround solution for using Kubernetes Configmap to map a recursive directory.

First, about ConfigMap recursive directory issues, you can find from below topics

or

However, unfortunately, it isn’t possible to build a Kubernetes ConfigMap from a directory, recursively [1] currently.

For example,

conf/
└── integrator
    └── conf
        ├── axis2
        │   └── axis2.xml
        ├── carbon.xml
        ├── datasources
        │   └── master-datasources.xml
        ├── registry.xml
        └── user-mgt.xml

Therefore, we will use a work-around to solve this issue.

Prepare a Helm template

First, let prepare a Helm template for Kubernetes deployment with configmap.yaml for ConfigMap and deployment.yaml for Deployment

$ tree -L 2 helm_templates/your-template
helm_templates/your-template
├── Chart.yaml
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── configmap.yaml  <--- ConfigMap
│   ├── deployment.yaml <--- Deployment
└── values.yaml

For instance, Configmap yaml for you Helm template

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ template "your-template.fullname" . }}-config
data:
# mapping configMaps from values.yml to ConfigMap
{{- range $key, $value := .Values.configMaps }}
  {{ $key }}: |-
{{ $value.content | indent 4 }}
{{- end }}

After that, a Deployment.yaml which mount all configMap data to volume using subPath. Please note that we using path properties for mount path setting.

spec:
      volumes:
        - name: {{ template "your-template.fullname" . }}-config
          configMap: { name: {{ template "your-template.fullname" . }}-config }
      containers:
        - volumeMounts:
            {{- range $key, $value := .Values.configMaps }}
            - name: {{ template "your-template.fullname" $ }}-config
              # note that we using path properties for mount path setting
              mountPath: /config/{{ $value.path }}
              subPath: {{ $key }}
            {{- end }}

Generate your Helm values.yaml by Python code

After that, we will prepare a simple Python code to read recursive directory and pass file’s content to values.yaml. For loading recursive directory from config_dir.

def get_config_maps(config_dir) -> dict:
         config_maps = {}
         filenames = [y for x in os.walk(config_dir) for y in glob.glob(os.path.join(x[0], '*')) if os.path.isfile(y)]

         for filename in filenames:
             # remove parent dir path
             relative_path = filename.replace(config_dir + os.path.sep, "")
             # replace path separator by "__" to prevent Kubernetes syntax error
             # currently file path doesn't validate '[-._a-zA-Z0-9]+'
             key = relative_path.replace(os.path.sep, "__")
             with open(filename, 'r') as file:
                 config_maps[key] = {
                     "path": relative_path, # a path that will be used for mount ConfigMap to Pod's volume
                     "content": file.read()
                 }
         return config_maps

The next step is, saving file’s content to values.yaml

 # load value template
 confif_dir = 'path/to/your/conf'
 with open('helm_templates/your-template/values.yaml', 'r') as file:
      deploy_values = yaml.safe_load(file)
      deploy_values['configMaps'] = get_config_maps(config_dir)

Deploy using your Helm template

helm install [your-helm-template-path] -f values.yaml

In conclusion, we can build a ConfigMap from a recursive directory by using a simple Python code with Helm template. Good luck & happy Kubernetes! :beer:

Additional, Other Kubernetes related topics is here. https://tuantranf.me/tag/kubernetes/

Original work is here https://gist.github.com/tuantranf/1faf61d0864353ed617af1f90f3ee453