Eric Guo's blog.cloud-mes.com

Hoping writing JS, Ruby & Rails and Go article, but fallback to DevOps note

My 2024 Monthly Subscription Review List

Permalink

Like the before 2022 / 2023 year, here is my current subscription, the first number is RMB per month.

  1. (68) iCloud 2T
  2. (17) Apple Music family plan
  3. (26) Dragonruby Pro (annual 42 USD)
  4. (32) bandwagon host (month 33 USD, shared)
  5. (6) Adblock Pro (annual 70 RMB)
  6. (32) Rubymine (annual 53 USD)
  7. (6) blog domain (annual 10 USD)
  8. (1.5) 香哈菜谱 (annual 18 RMB)
  9. (49) AWS hosting (3 Year 206 USD, month 1.35 USD)
  10. (16.8) Meituan biking
  11. (43) Google Workspace Business Starter (month 6 USD)
  12. (115) Cursor.sh AI editor. (yearly 192 USD)
  13. (8.2) Ivory for Mastondon. (yearly 98 RMB)
  14. (8) IndieWeb.Social Backer. (monthly 1.5 SGD)
  15. (31) Sublime Text and Merge (3 yearly, 152 AUD)
  16. (0) Margin Note Max (free for 6 month)
  17. (21) Surge (Est 2 years, 513 RMB)

So totally 480 RMB per month have to pay, in previous year is 419 RMB, so 14% higher compare with year 2023.

Review of the Management Transformation of HuaWei

Permalink

其实这本书可以吐槽的地方非常多,看了几页后,我就有了两个槽点:为啥书名是《华为管理变革》,而不是《华为变革》呢?看书的目录,国际化,研发,供应链,财经,组织,人力各个方面都说到了,为啥要单单强调管理变革呢?还有全书真正和华为密切相关的内容实在不多,绝大多数的内容,将华为改成小米也一样成立,那么这本书的独特性在哪里呢?抱着这两个疑问,又因为这本是直属领导要求的读书会交流书目,我开始了精读。

关于作者

年轻的时候,觉得一本书最重要的是内容,常常跳过序,现在年纪大了,觉得序和作者是最重要的,序代表的写作的出发点,作者的背景决定了书的方向和内容,《华为管理变革》的第一作者是吴晓波,第二作者是Johann Peter Murmann,成书于 2017 年,是一个国际化合作和充满希望的年代。

关于序

本书有两个序,序 2 更值得一度,提到了中澳合作,最优秀的中国企业融入全球化,还有根本的原因,恐惧。序 2 也回答了本文一开始的疑问,因为是管理学院的人书写的,所以必须加上管理两个字。

第一章,概述

概述的作者是老外,所以更精读一下,也不知道这 36 页到底给了老外多少钱,华为以客户为第一,通用电器为股东利益为第一次,西南航空以员工满意为第一优先目标,从 2024 年的角度看,这三者显然都不太成功。文中还提到了华为基本法,但是华为基本法从 1998 年后就没有发布过更新版本。任也从曾经的削足适履学习 IBM 到不再遵循基本法,可见华为管理变革的目标就是变革,华为本身是没有目标的,如果活下去不能算作目标的话。

这一章最大的看点还是2007年到2005年任的地位的空前提高,这显然离不开 IPD,让我想起了任的学习毛泽东思想的标兵身份,可见一个组织内的权利收拢必须有外部威胁,也需要内部流程变革,如果是收拢权利的话,这个案例真的值得学习,可惜只能从字里行间读,实在是不直白。

第七章,研发管理变革

华为主要分三个阶段:

  1. 第一个阶段是生产直接研发,体系崩溃的原因是想做的产品太多,做不过来,质量无法保证,救火不断,甚至损害了企业声誉
  2. 第二个阶段是成立的中央测试部,并且将测试的重要性放到非常高,实际上为了确保测试的重要性,甚至为了中央测试而成立了中央研究部,这样具体研发的级别就低于测试,体系奔溃的原因还是想做的产品太多,以至于中央研究部和测试部成了瓶颈,而且估计是中央研究部的待遇高,进入又必须有成功的项目经验,导致想做的产品更多了,所以崩溃没商量。
  3. 第三个阶段就是 IPD,学 IBM 阶段,其实本质上就是把中央研究部打散,所有的人都围着项目做,而且由于拉上了市场部,第一线销售,可以有效防止承诺功能过多而做不出来,再加上项目的人数总体总是小于华为的总人数,所以不太容易奔溃。缺点也不是没有,就是研发人员的大大增加,直接拉到了70000人,研发效率在我看来是存疑的。

总结

时间关系,就看了两章,总的结论就是华为是一个挺典型的国内企业案例,啥都想做,也没啥目标,后面的国际化全球化其实还是卖货,估计西班牙用了华为很多产品,所以必须建立一个研究中心,总之这些套路也是微软玩下来的,大家都差不多。

一句话总结全文就是,既要还要也要,唯一不变的就是变化,能赚到钱就能请管理学院的人写书,实打实的表示羡慕。

Install dify.ai at Rocky Linux 9.3 From Source Code

Permalink

Setup dify user account

adduser dify
gpasswd -a dify wheel
cd /etc/sudoers.d
# you can later change more limit like "dify ALL=(ALL) NOPASSWD:/usr/bin/systemctl"
echo "dify ALL=(ALL) NOPASSWD:ALL" > 96-dify-user
sudo su - dify
mkdir .ssh
chmod 700 .ssh
vi .ssh/authorized_keys
chmod 600 .ssh/authorized_keys

Install local python 3.10 for dify

sudo dnf install tar curl gcc openssl-devel bzip2-devel libffi-devel zlib-devel wget make -y
wget https://www.python.org/ftp/python/3.10.13/Python-3.10.13.tar.xz
tar -xf Python-3.10.13.tar.xz
cd Python-3.10.13
./configure --enable-optimizations
make -j 2
sudo make altinstall

Create a Virtual Environment in Python

Copy from guide

python3.10 -m venv dify_env
source dify_env/bin/activate # also add in .bash_profile

Install basic environment

following guide

cd dify/api
cp .env.example .env
openssl rand -base64 42
sed -i 's/SECRET_KEY=.*/SECRET_KEY=<your_value>/' .env
pip install -r requirements.txt
# if meet https://github.com/langgenius/dify/pull/707
pip install -r requirements.txt --upgrade --force-reinstall

Create postgresql db user

sudo su - postgres
createuser dify
psql
ALTER ROLE dify LOGIN;
CREATE DATABASE dify_prod WITH ENCODING='UTF8' OWNER=dify;
logout
flask db upgrade

Install ffmpeg as dify require it

following guide

sudo dnf install epel-release
sudo dnf config-manager --set-enabled crb
sudo dnf install --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm -y
sudo dnf install --nogpgcheck https://mirrors.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-$(rpm -E %rhel).noarch.rpm -y
sudo dnf install ffmpeg ffmpeg-devel

Deploy Rails 7.1 and AnyCable 1.4 With RPC Services in Rocky Linux 9

Permalink

I’m using an brand new Rocky Linux 9 server, to deploy the pgac_demo project.

Install htop and atop

dnf -y install epel-release
dnf makecache
dnf -y install htop

If htop not found

dnf repolist
vi /etc/yum.repos.d/epel.repo # In the [epel] section, change enabled=0 to enabled=1
dnf -y install htop atop

Install nginx and certbot

dnf install nginx
dnf install certbot python3-certbot-nginx

Setup pgac user account

adduser pgac
gpasswd -a pgac wheel
cd /etc/sudoers.d
# you can later change more limit like "pgac ALL=(ALL) NOPASSWD:/usr/bin/systemctl"
echo "pgac ALL=(ALL) NOPASSWD:ALL" > 97-pgac-user
sudo su - pgac
mkdir .ssh
chmod 700 .ssh
vi .ssh/authorized_keys
chmod 600 .ssh/authorized_keys

Install ruby dep

sudo dnf -y install git make gcc curl openssl-devel zlib-devel libffi-devel readline-devel sqlite-devel
sudo dnf --enablerepo=crb -y install libyaml-devel gdbm-devel
sudo yum -y install gcc-c++ # required by grpc
sudo yum -y install rust # if need YJIT
sudo yum -y install git-lfs # if any repository using LFS to store picture

Install rbenv and ruby-build

cd # as a pgac
git clone https://github.com/rbenv/rbenv .rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
~/.rbenv/bin/rbenv init # also edit ~/.bash_profile
# As an rbenv plugin
mkdir -p "$(rbenv root)"/plugins
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
git clone https://github.com/andorchen/rbenv-china-mirror.git "$(rbenv root)"/plugins/rbenv-china-mirror

Install Ruby 3.2.3

rbenv install -l
rbenv install 3.2.3
rbenv global 3.2.3
rbenv shell 3.2.3
echo "gem: --no-document" > ~/.gemrc

create deploy folder

cd /var/www
sudo mkdir pgac
sudo chown pgac:pgac pgac/

The extra file for capistrano

The puma file which will lauch puma via system service.

/var/www/pgac/shared/puma.rb
#!/usr/bin/env puma
directory '/var/www/pgac/current'
rackup "/var/www/pgac/current/config.ru"
environment 'production'
tag ''
pidfile "/var/www/pgac/shared/tmp/pids/puma.pid"
state_path "/var/www/pgac/shared/tmp/pids/puma.state"
stdout_redirect '/var/www/pgac/shared/log/puma_access.log', '/var/www/pgac/shared/log/puma_error.log', true
threads 0,16
bind 'unix:///var/www/pgac/shared/tmp/sockets/puma.sock'
workers 0
restart_command 'bundle exec puma'
prune_bundler
on_restart do
puts 'Refreshing Gemfile'
ENV["BUNDLE_GEMFILE"] = ""
end

The systemd service file.

/etc/systemd/system/puma_pgac.service
# This file tells systemd how to run Puma as a 24/7 long-running daemon.
#
# Customize this file based on your bundler location, app directory, etc.
# Customize and copy this into /usr/lib/systemd/system (CentOS) or /lib/systemd/system (Ubuntu).
# Then run:
# - systemctl enable puma_pgac
# - systemctl {start,stop,restart} puma_pgac
#
#
# Use `journalctl -u puma_pgac -rn 100` to view the last 100 lines of log output.
#
[Unit]
Description=Puma HTTP Server for pgac (production)
After=syslog.target network.target
[Service]
Type=simple
WatchdogSec=10
User=pgac
Environment=CABLE_URL=wss://pgac.redwoodjs.cn:3334/cable
WorkingDirectory=/var/www/pgac/current
ExecStart=/home/pgac/.rbenv/bin/rbenv exec bundle exec --keep-file-descriptors puma -C /var/www/pgac/shared/puma.rb
ExecReload=/bin/kill -USR1 $MAINPID
# if we crash, restart
RestartSec=10
Restart=on-failure
StandardOutput=append:/var/www/pgac/shared/log/puma.log
StandardError=append:/var/www/pgac/shared/log/puma.log
SyslogIdentifier=puma_pgac
[Install]
WantedBy=multi-user.target

The nginx conf files.

/etc/nginx/conf.d/pgac.conf
upstream puma_pgac_production {
server unix:/var/www/pgac/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
if ($host = pgac.redwoodjs.cn) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name pgac.redwoodjs.cn;
return 301 https://$host$1$request_uri;
}
server {
listen 443 ssl http2; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/pgac.redwoodjs.cn/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/pgac.redwoodjs.cn/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
server_name pgac.redwoodjs.cn;
root /var/www/pgac/current/public;
try_files $uri/index.html $uri @puma_pgac_production;
client_max_body_size 4G;
keepalive_timeout 10;
error_page 500 502 504 /500.html;
error_page 503 @503;
location @puma_pgac_production {
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_redirect off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://puma_pgac_production;
# limit_req zone=one;
access_log /var/www/pgac/shared/log/nginx.access.log;
error_log /var/www/pgac/shared/log/nginx.error.log;
}
location ~ (/manifest.json|/favicon.ico|/*.txt|/*.png) {
access_log off;
expires 3d;
gzip_static on;
add_header Cache-Control public;
}
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
location = /50x.html {
root html;
}
location = /404.html {
root html;
}
location @503 {
error_page 405 = /system/maintenance.html;
if (-f $document_root/system/maintenance.html) {
rewrite ^(.*)$ /system/maintenance.html break;
}
rewrite ^(.*)$ /503.html break;
}
if ($request_method !~ ^(GET|HEAD|PUT|PATCH|POST|DELETE|OPTIONS)$ ){
return 405;
}
if (-f $document_root/system/maintenance.html) {
return 503;
}
}

The systemd files for AnyCable Go/RPC service

Large following the AnyCable deployment document

Using redis as broadcast_adapter

/etc/systemd/system/pgac-anycable-rpc.service
[Unit]
Description=AnyCable gRPC Server for pgac
After=syslog.target network.target
[Service]
Type=simple
Environment=RAILS_ENV=production
Environment=ANYCABLE_REDIS_URL=redis://localhost:6379/5
Environment=ANYCABLE_REDIS_CHANNEL=__anycable__
WorkingDirectory=/var/www/pgac/current
ExecStart=/home/pgac/.rbenv/bin/rbenv exec bundle exec anycable
ExecStop=/bin/kill -TERM $MAINPID
User=pgac
RestartSec=9
Restart=on-failure
[Install]
WantedBy=multi-user.target

The anycable-go can download from release page

/etc/systemd/system/pgac-anycable-go.service
[Unit]
Description=anycable-go for pgac
After=syslog.target network.target
[Service]
Type=simple
Environment=ANYCABLE_HOST=pgac.redwoodjs.cn
Environment=ANYCABLE_PORT=3334
Environment=ANYCABLE_PATH=/cable
Environment=ANYCABLE_REDIS_URL=redis://localhost:6379/5
Environment=ANYCABLE_REDIS_CHANNEL=__anycable__
Environment=ANYCABLE_METRICS_HTTP=/metrics
WorkingDirectory=/var/www/pgac/current
ExecStart=/usr/local/bin/anycable-go --ssl_cert=/etc/letsencrypt/live/pgac.redwoodjs.cn/cert.pem --ssl_key=/etc/letsencrypt/live/pgac.redwoodjs.cn/privkey.pem
ExecStop=/bin/kill -TERM $MAINPID
User=pgac
LimitNOFILE=16384 # increase open files limit (see OS Tuning guide)
Restart=on-failure
[Install]
WantedBy=multi-user.target

You may need to fix access control for user pgac for letsencrypt key files.

Using http as broadcast_adapter

/etc/systemd/system/pgac-anycable-rpc.service
[Unit]
Description=AnyCable gRPC Server for pgac
After=syslog.target network.target
[Service]
Type=simple
Environment=RAILS_ENV=production
WorkingDirectory=/var/www/pgac/current
ExecStart=/home/pgac/.rbenv/bin/rbenv exec bundle exec anycable
ExecStop=/bin/kill -TERM $MAINPID
User=pgac
RestartSec=9
Restart=on-failure
[Install]
WantedBy=multi-user.target
/etc/systemd/system/pgac-anycable-go.service
[Unit]
Description=anycable-go for pgac
After=syslog.target network.target
[Service]
Type=simple
Environment=ANYCABLE_HOST=pgac.redwoodjs.cn
Environment=ANYCABLE_PORT=3334
Environment=ANYCABLE_PATH=/cable
Environment=ANYCABLE_METRICS_HTTP=/metrics
WorkingDirectory=/var/www/pgac/current
ExecStart=/usr/local/bin/anycable-go --ssl_cert=/etc/letsencrypt/live/pgac.redwoodjs.cn/cert.pem --ssl_key=/etc/letsencrypt/live/pgac.redwoodjs.cn/privkey.pem --broadcast_adapter=http --rpc_impl=grpc
ExecStop=/bin/kill -TERM $MAINPID
User=pgac
LimitNOFILE=16384 # increase open files limit (see OS Tuning guide)
Restart=on-failure
[Install]
WantedBy=multi-user.target

Convert Angular Project Module Manager From Npm to Yarn

Permalink

Rails’ default package manager for JS is yarn, and I like yarn because the package installation process on the China network is much more stable, and Capistrano’s support for yarn is also great.

However, Angular projects usually use npm as the default project manager. I used to believe it’s not changeable because every time I tried to switch, I failed. But yesterday, I found the correct way to do it, and here is how.

Run it in npm

Make sure the Angular project works normally in npm.

npm install
npm update
npm run start
npm run build

Import installed node modules in yarn.

This can be done via the import feature

rm package-lock.json # optional, but need it if yarn import failed
yarn import

Sync back to npm from yarn.lock

This is required to ensure Angular works after the yarn import. syncyarnlock is a tool that syncs yarn.lock versions into an existing package.json file.

npm -g install syncyarnlock
syncyarnlock -s -k
npm update
npm run start
npm run build

Run yarn update

After running yarn update, compare the yarn.lock file, add missing dependencies and devDependencies after the yarn update. I guess this is the key step to migrate from npm to yarn because npm dependencies are nested but yarn’s are flat.

yarn update
# check yarn.lock and add removed lines of the package to package.json

Now yarn works

yarn install
yarn update
yarn run start
yarn run build

You can keep package-lock.json or delete it, but your Angular project works with yarn now!

Install Gitea 1.21.3 From Source Code Into Rocky Linux 9

Permalink

Install dependency

Install htop and atop

sudo dnf update
sudo dnf install epel-release
sudo dnf install htop
sudo dnf install atop

Node.js v18

Using nodesource distribution

curl -fsSL https://rpm.nodesource.com/setup_18.x | bash -
dnf install -y nodejs
dnf groupinstall 'Development Tools'

Install yarn

curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
sudo yum install yarn

Go 1.20

Rocky Linux 9 including golang 1.20.

dnf install golang

Compile

Largely following official guide

Disable SELinux

vi /etc/selinux/config

Clone and build

git clone https://git.thape.com.cn/go-gitea/gitea.git
git checkout v1.21.3
npm config set registry https://registry.npmmirror.com/ --global
npm -g install webpack
TAGS="bindata" make build

Install Gitea

Install dependency

yum install redis
yum install memcached

Create git user

groupadd --system git
adduser \
--system \
--shell /bin/bash \
--comment 'Git Version Control' \
--gid git \
--home-dir /home/git \
--create-home \
git

Create required directory structure

Don’t forget change back to more strict setting.

mkdir -p /var/lib/gitea/{custom,data,log}
chown -R git:git /var/lib/gitea/
chmod -R 750 /var/lib/gitea/
mkdir /etc/gitea
chown root:git /etc/gitea
chmod 770 /etc/gitea

Create DB and DB user

sudo su - postgres
createuser gitea_t --pwprompt
psql
ALTER ROLE gitea_t LOGIN;
CREATE DATABASE gitea_staging WITH ENCODING='UTF8' OWNER=gitea_t;
logout
vi /var/lib/pgsql/16/data/pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
host gitea_staging gitea_t 0.0.0.0/0 scram-sha-256

Reload conf without restart DB

sudo su - postgres
/usr/pgsql-16/bin/pg_ctl reload

Open port to access and configure

sudo firewall-cmd --zone=public --add-port=3000/tcp
sudo firewall-cmd --zone=public --add-port=3000/tcp --permanent
sudo firewall-cmd --reload
sudo su - git
cd /var/lib/gitea/
gitea web -c /etc/gitea/app.ini

And open your server like http://172.16.1.47:3000 to configure

Binding to 443 and using https

Should following document, but if found cmd/web.go:353:listen() [E] Failed to start server: listen tcp 0.0.0.0:443: bind: permission denied error try below:

sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/gitea

Create DB ReadOnly User

createuser gitea_t_read --pwprompt
psql
ALTER ROLE gitea_t_read LOGIN;
GRANT USAGE ON SCHEMA public TO gitea_t_read;
SELECT 'GRANT SELECT ON ' || tablename || ' TO gitea_t_read;'
FROM pg_catalog.pg_tables
WHERE schemaname = 'public' AND tableowner = 'gitea_t';

Upgrade Gitea

cd /usr/local/bin/
wget https://github.com/go-gitea/gitea/releases/download/v1.21.8/gitea-1.21.8-linux-amd64.xz
xz -d gitea-1.21.8-linux-amd64.xz
systemctl stop gitea
systemctl status gitea
mv gitea-1.21.8-linux-amd64 gitea
chmod +x gitea
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/gitea
systemctl start gitea

Deploy Rails 7.1 and AnyCable 1.4 With RPC Services in the Existing CentOS 7

Permalink

I’m using the same server as post mention, to deploy the pgac_demo project.

Setup pgac user account

adduser pgac
gpasswd -a pgac wheel
cd /etc/sudoers.d
echo "pgac ALL=(ALL) NOPASSWD:ALL" > 97-pgac-user
sudo su - pgac
mkdir .ssh
chmod 700 .ssh
vi .ssh/authorized_keys
chmod 600 .ssh/authorized_keys

Install rbenv and ruby-build

cd # as a pgac
git clone https://github.com/rbenv/rbenv .rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
~/.rbenv/bin/rbenv init # also edit ~/.bash_profile
# As an rbenv plugin
mkdir -p "$(rbenv root)"/plugins
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
git clone https://github.com/andorchen/rbenv-china-mirror.git "$(rbenv root)"/plugins/rbenv-china-mirror

Install Ruby 3.2.2

scl enable devtoolset-7 bash
rbenv install -l
rbenv install 3.2.2
rbenv global 3.2.2
rbenv shell 3.2.2
echo "gem: --no-document" > ~/.gemrc

create deploy folder

cd /var/www
sudo mkdir pgac
sudo chown pgac:pgac pgac/

The extra file for capistrano

The puma file which will lauch puma via system service.

/var/www/pgac/shared/puma.rb
#!/usr/bin/env puma
directory '/var/www/pgac/current'
rackup "/var/www/pgac/current/config.ru"
environment 'production'
tag ''
pidfile "/var/www/pgac/shared/tmp/pids/puma.pid"
state_path "/var/www/pgac/shared/tmp/pids/puma.state"
stdout_redirect '/var/www/pgac/shared/log/puma_access.log', '/var/www/pgac/shared/log/puma_error.log', true
threads 0,16
bind 'unix:///var/www/pgac/shared/tmp/sockets/puma.sock'
workers 0
restart_command 'bundle exec puma'
prune_bundler
on_restart do
puts 'Refreshing Gemfile'
ENV["BUNDLE_GEMFILE"] = ""
end

The systemd service file.

/etc/systemd/system/puma_pgac.service
# This file tells systemd how to run Puma as a 24/7 long-running daemon.
#
# Customize this file based on your bundler location, app directory, etc.
# Customize and copy this into /usr/lib/systemd/system (CentOS) or /lib/systemd/system (Ubuntu).
# Then run:
# - systemctl enable puma_pgac
# - systemctl {start,stop,restart} puma_pgac
#
#
# Use `journalctl -u puma_pgac -rn 100` to view the last 100 lines of log output.
#
[Unit]
Description=Puma HTTP Server for pgac (production)
After=syslog.target network.target
[Service]
Type=simple
WatchdogSec=10
User=pgac
Environment=CABLE_URL=wss://pgac.redwoodjs.cn:3334/cable
WorkingDirectory=/var/www/pgac/current
ExecStart=/home/pgac/.rbenv/bin/rbenv exec bundle exec --keep-file-descriptors puma -C /var/www/pgac/shared/puma.rb
ExecReload=/bin/kill -USR1 $MAINPID
# if we crash, restart
RestartSec=10
Restart=on-failure
StandardOutput=append:/var/www/pgac/shared/log/puma.log
StandardError=append:/var/www/pgac/shared/log/puma.log
SyslogIdentifier=puma_pgac
[Install]
WantedBy=multi-user.target

The nginx conf files.

/etc/nginx/conf.d/pgac.conf
upstream puma_pgac_production {
server unix:/var/www/pgac/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
if ($host = pgac.redwoodjs.cn) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name pgac.redwoodjs.cn;
return 301 https://$host$1$request_uri;
}
server {
listen 443 ssl http2; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/pgac.redwoodjs.cn/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/pgac.redwoodjs.cn/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
server_name pgac.redwoodjs.cn;
root /var/www/pgac/current/public;
try_files $uri/index.html $uri @puma_pgac_production;
client_max_body_size 4G;
keepalive_timeout 10;
error_page 500 502 504 /500.html;
error_page 503 @503;
location @puma_pgac_production {
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_redirect off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://puma_pgac_production;
# limit_req zone=one;
access_log /var/www/pgac/shared/log/nginx.access.log;
error_log /var/www/pgac/shared/log/nginx.error.log;
}
location ~ (/manifest.json|/favicon.ico|/*.txt|/*.png) {
access_log off;
expires 3d;
gzip_static on;
add_header Cache-Control public;
}
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
location = /50x.html {
root html;
}
location = /404.html {
root html;
}
location @503 {
error_page 405 = /system/maintenance.html;
if (-f $document_root/system/maintenance.html) {
rewrite ^(.*)$ /system/maintenance.html break;
}
rewrite ^(.*)$ /503.html break;
}
if ($request_method !~ ^(GET|HEAD|PUT|PATCH|POST|DELETE|OPTIONS)$ ){
return 405;
}
if (-f $document_root/system/maintenance.html) {
return 503;
}
}

The systemd files for AnyCable Go/RPC service

Large following the AnyCable deployment document

Using redis as broadcast_adapter

/etc/systemd/system/pgac-anycable-rpc.service
[Unit]
Description=AnyCable gRPC Server for pgac
After=syslog.target network.target
[Service]
Type=simple
Environment=RAILS_ENV=production
Environment=ANYCABLE_REDIS_URL=redis://localhost:6379/5
Environment=ANYCABLE_REDIS_CHANNEL=__anycable__
WorkingDirectory=/var/www/pgac/current
ExecStart=/home/pgac/.rbenv/bin/rbenv exec bundle exec anycable
ExecStop=/bin/kill -TERM $MAINPID
User=pgac
RestartSec=9
Restart=on-failure
[Install]
WantedBy=multi-user.target

The anycable-go can download from release page

/etc/systemd/system/pgac-anycable-go.service
[Unit]
Description=anycable-go for pgac
After=syslog.target network.target
[Service]
Type=simple
Environment=ANYCABLE_HOST=pgac.redwoodjs.cn
Environment=ANYCABLE_PORT=3334
Environment=ANYCABLE_PATH=/cable
Environment=ANYCABLE_REDIS_URL=redis://localhost:6379/5
Environment=ANYCABLE_REDIS_CHANNEL=__anycable__
Environment=ANYCABLE_METRICS_HTTP=/metrics
WorkingDirectory=/var/www/pgac/current
ExecStart=/usr/local/bin/anycable-go --ssl_cert=/etc/letsencrypt/live/pgac.redwoodjs.cn/cert.pem --ssl_key=/etc/letsencrypt/live/pgac.redwoodjs.cn/privkey.pem
ExecStop=/bin/kill -TERM $MAINPID
User=pgac
LimitNOFILE=16384 # increase open files limit (see OS Tuning guide)
Restart=on-failure
[Install]
WantedBy=multi-user.target

Using http as broadcast_adapter

/etc/systemd/system/pgac-anycable-rpc.service
[Unit]
Description=AnyCable gRPC Server for pgac
After=syslog.target network.target
[Service]
Type=simple
Environment=RAILS_ENV=production
WorkingDirectory=/var/www/pgac/current
ExecStart=/home/pgac/.rbenv/bin/rbenv exec bundle exec anycable
ExecStop=/bin/kill -TERM $MAINPID
User=pgac
RestartSec=9
Restart=on-failure
[Install]
WantedBy=multi-user.target
/etc/systemd/system/pgac-anycable-go.service
[Unit]
Description=anycable-go for pgac
After=syslog.target network.target
[Service]
Type=simple
Environment=ANYCABLE_HOST=pgac.redwoodjs.cn
Environment=ANYCABLE_PORT=3334
Environment=ANYCABLE_PATH=/cable
Environment=ANYCABLE_METRICS_HTTP=/metrics
WorkingDirectory=/var/www/pgac/current
ExecStart=/usr/local/bin/anycable-go --ssl_cert=/etc/letsencrypt/live/pgac.redwoodjs.cn/cert.pem --ssl_key=/etc/letsencrypt/live/pgac.redwoodjs.cn/privkey.pem --broadcast_adapter=http --rpc_impl=grpc
ExecStop=/bin/kill -TERM $MAINPID
User=pgac
LimitNOFILE=16384 # increase open files limit (see OS Tuning guide)
Restart=on-failure
[Install]
WantedBy=multi-user.target

Install Unleash Feature Flag Management OSS Software on Rocky Linux

Permalink

Unleash only depend on postgresql and recommand installation method including Docker/Docker-compose and pure Node.js. I using pm2 to do the daemon job and using a postgresql server seperatedly.

Install htop and atop

sudo dnf update
sudo dnf install epel-release
sudo dnf install htop
sudo dnf install atop

Setup unleash user account

adduser unleash
sudo su - unleash
mkdir .ssh
chmod 700 .ssh
vi .ssh/authorized_keys # and paste your public key
chmod 600 .ssh/authorized_keys

Install nginx

sudo dnf install nginx

Install node.js v18

Using nodesource distribution

curl -fsSL https://rpm.nodesource.com/setup_18.x | bash -
yum install -y nodejs
yum groupinstall 'Development Tools'

Install yarn

curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
sudo yum install yarn

Disable firewall

firewall-cmd --zone=public --add-port=4242/tcp
firewall-cmd --permanent --zone=public --add-port=4242/tcp
firewall-cmd --reload
systemctl restart firewalld

Install Unleash

sudo su - unleash
git clone https://git.thape.com.cn/Eric-Guo/unleash.git
yarn
yarn run build

Run Unleash

node eric_server.js
eric_server
const unleash = require('./unleash/dist/lib/server-impl');
unleash
.start({
db: {
ssl: false,
host: '172.17.1.66',
port: 5432,
database: 'unleash_prod',
user: 'unleash',
password: 'password',
},
server: {
port: 4242,
unleashUrl: 'https://unleash.thape.com.cn',
baseUriPath: '/'
},
ui: { flags: { P: true } },
versionCheck: {
enable: false,
},
})
.then((unleash) => {
console.log(
`Unleash started on http://localhost:${unleash.app.get('port')}`,
);
});

Nginx conf

upstream unleash_proxy {
server 127.0.0.1:4242;
}
server {
listen 80;
server_name unleash.thape.com.cn;
return 301 https://unleash.thape.com.cn$request_uri;
}
server {
listen 443 ssl;
server_name unleash.thape.com.cn;
ssl_certificate /etc/ssl/cert/STAR_thape_com_cn_integrated.crt;
ssl_certificate_key /etc/ssl/private/STAR_thape_com_cn.key;
client_max_body_size 10M;
location / {
proxy_pass http://unleash_proxy;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
access_log /var/www/unleash/shared/log/nginx.access.log;
error_log /var/www/unleash/shared/log/nginx.error.log;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}