1. 安裝 docker
1
2
3
4
5
6
7
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
  1. 安裝 docker-compose
1
2
3
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
  1. 設定 n8ndocker-compose.yml
1
2
sudo mkdir -p /srv/n8n
sudo vim /srv/n8n/docker-compose.yml

/srv/n8n/docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
services:
n8n:
image: n8nio/n8n:latest
restart: unless-stopped
environment:
# Host config
- N8N_HOST=n8n_server_dns_or_ip
- N8N_PORT=5678
- N8N_PROTOCOL=http
- WEBHOOK_URL=https://n8n_server_dns_or_ip
# Basic Auth
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=ReplaceWithStrongPassword
# 自動修正設定檔權限
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
ports:
- "127.0.0.1:5678:5678"
volumes:
- n8n-data:/home/node/.n8n # 使用 Docker volume

volumes:
n8n-data:
  1. 佈署 n8n
1
docker-compose up -d
  1. 設定 nginx
1
sudo vim /etc/nginx/sites-available/n8n.conf

/etc/nginx/sites-available/n8n.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 80;
server_name n8n_server_dns_or_ip;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl;
server_name n8n_server_dns_or_ip;

ssl_certificate /etc/ssl/n8n/n8n.crt;
ssl_certificate_key /etc/ssl/n8n/n8n.key;

location / {
proxy_pass http://127.0.0.1:5678;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
  1. 產生自簽名証書
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sudo mkdir -p /etc/ssl/n8n
cd /etc/ssl/n8n

sudo openssl genrsa -out n8n.key 4096

cat > san.cnf <<'EOF'
[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_ca]
subjectAltName = @alt_names
[alt_names]
IP.1 = 10.0.0.10
EOF

sudo openssl req -new -sha256 -key n8n.key -out n8n.csr -config san.cnf -subj "/CN=10.0.0.10"
sudo openssl x509 -req -in n8n.csr -signkey n8n.key -out n8n.crt -days 3650 -extensions v3_ca -extfile san.cnf
  1. 啟動 nginx
1
2
3
sudo ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
  1. 複製 n8n 自簽名証書到 gitlab server
1
2
3
4
sudo scp n8n.crt user_name@gitlab_server_dns_or_ip:/usr/local/share/ca-certificates/
sudo update-ca-certificates
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart
  1. gitlab 設定 webhook
  • 打開 GitLab 專案 → Settings → Webhooks
  • URL 填 n8n webhook
  • 觸發事件只選 Pipeline events
  • 保存 webhook
  1. Teams 設定 workflow
  • 選範本 收到 webhook 要求時發佈在頻道中
  • 選擇要發佈的頻道
  • 複製 URL
  1. 建立 n8n webhook
  • 連上 n8n server https://n8n_server_dns_or_ip
  • 打開 n8n Web UI
  • 建立 新 Workflow
  • 建立 Webhook Node
  1. HTTP method: POST
  2. Path: gitlab-cicd
  3. Authentication: None
  4. Respond: onReceived
  • 建立 HTTP Request Node
  1. Method: POST
  2. URL: 剛才 teams 產生的 workflow url
  3. Authentication: None
  4. Enable Send Headers
  5. Specify Headers: Using JSON
1
2
3
{
"Content-Type": "application/json"
}
  1. Enable Send Body
  2. Body Content Type: JSON
  3. Specify Body: Using JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{
"type": "message",
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": "GitLab Pipeline Result"
},
{
"type": "FactSet",
"facts": [
{
"title": "Project:",
"value": "={{$json["project"]["name"]}}"
},
{
"title": "Branch/Tag:",
"value": "={{$json["object_attributes"]["ref"]}}"
},
{
"title": "Status:",
"value": "={{$json["object_attributes"]["status"]}}"
},
{
"title": "Pipeline URL:",
"value": "={{$json["object_attributes"]["url"]}}"
}
]
}
]
}
}
]
}
  • Enable workhook to active
  1. gitlab webhook 按 Test 觸發 pipeline event

  1. 安裝 k3sNode1
1
2
3
curl -sfL https://get.k3s.io | sh -
sudo k3s kubectl get nodes
sudo cat /var/lib/rancher/k3s/server/node-token
  1. 安裝 k3s-agentNode2
1
curl -sfL https://get.k3s.io | K3S_URL=https://10.0.0.1:6443 K3S_TOKEN=<TOKEN> sh -
  1. 安裝 HelmNode1
1
2
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
  1. 加入 Gitlab Helm repo 在 Node1
1
2
helm repo add gitlab https://charts.gitlab.io/
helm repo update
  1. 建立命名空間在 Node1
1
sudo k3s kubectl create namespace gitlab
  1. 建立設定檔 values.yamlNode1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mkdir -p gitlab

vim values.yaml
# values.yaml
global:
hosts:
domain: gitlab.local
https: true
edition: ce
ingress:
configureCertmanager: false

nginx-ingress:
enabled: true

gitlab-runner:
install: false

  1. 安裝 GitLab Server
1
helm upgrade --install gitlab gitlab/gitlab -n gitlab -f values.yaml
  1. 等待 Pod 啟動
1
sudo k3s kubectl get pods -n gitlab
  1. 取得 root 密碼
1
sudo k3s kubectl get secret gitlab-gitlab-initial-root-password -n gitlab -ojsonpath='{.data.password}' | base64 -d
  1. 確認 k3s 狀態
1
sudo k3s kubectl get nodes -o wide
  1. 讓 Helm 用對的 kubeconfig
1
2
3
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
  1. 修改 ~/.kube/config127.0.0.1 改成 Node1 IP

  2. 確認 kubectl正常

1
2
3
export KUBECONFIG="$HOME/.kube/config"
kubectl config get-contexts
kubectl get ns
  • (Optional) 如何刪除 Pod
1
2
3
4
5
kubectl -n gitlab delete pod -l app=registry
kubectl -n gitlab delete pod -l app=webservice
kubectl -n gitlab delete pod -l app=sidekiq

kubectl -n gitlab get pods
  • (Optional) Node hostname 不能一樣
  1. 加入 Node1 IP在PC上
1
2
3
echo "192.168.1.10 gitlab.gitlab.local registry.gitlab.local minio.gitlab.local kas.gitlab.local" | sudo tee -a /etc/hosts

curl -k -I -H "Host: gitlab.gitlab.local" https://<任一節點IP>:30721/
  1. 啟用 ServiceLB 做到可以直接連線 80/443 在 Node1
1
sudo systemctl cat k3s | grep -i servicelb -n || true

用 systemd drop-in 覆蓋 ExecStart

1
2
3
4
5
sudo systemctl edit k3s
# 進入編輯器後貼上(這會清掉舊的 ExecStart,請保留你需要的其他參數)
[Service]
ExecStart=
ExecStart=/usr/local/bin/k3s server --write-kubeconfig-mode 644

套用並重啟:

1
2
sudo systemctl daemon-reload
sudo systemctl restart k3s

把 GitLab 的 Ingress Service 切成 LoadBalancer

1
2
3
4
5
6
7
helm upgrade --install gitlab gitlab/gitlab -n gitlab -f values.yaml \
--set nginx-ingress.controller.service.type=LoadBalancer \
--set nginx-ingress.controller.service.ports.gitlab-shell=2222 \
--set global.shell.port=2222 \
--set nginx-ingress.controller.service.nodePorts.http=null \
--set nginx-ingress.controller.service.nodePorts.https=null \
--set nginx-ingress.controller.service.nodePorts.gitlab-shell=null

幾秒後查看 EXTERNAL-IP(會是某個節點的實體 IP,或兩個節點都列出)

1
kubectl -n gitlab get svc gitlab-nginx-ingress-controller

在你的用戶端把 DNS/hosts 指到這個 EXTERNAL-IP

1
echo "<EXTERNAL-IP> gitlab.gitlab.local registry.gitlab.local minio.gitlab.local kas.gitlab.local" | sudo tee -a /etc/hosts

測試 Gitlab

1
2
3
curl -I -H "Host: gitlab.gitlab.local" http://<EXTERNAL-IP>/

curl -k -I -H "Host: gitlab.gitlab.local" https://<EXTERNAL-IP>/

測試 SSH

1
ssh -p 2222 git@gitlab.gitlab.local

First Step

Buy a Raspberry Pi

Actually the link explains everything. I will note that something important and explain how to figure out some tricky problems.

Please following the steps.

Install GCC 7

1
2
3
4
5
6
7
git clone https://bitbucket.org/sol_prog/raspberry-pi-gcc-binary.git
cd raspberry-pi-gcc-binary
tar xf gcc-7.2.0.tar.bz2
sudo mv gcc-7.2.0 /usr/local
export PATH=/usr/local/gcc-7.2.0/bin:$PATH
echo 'export PATH=/usr/local/gcc-7.2.0/bin:$PATH' >> .bashrc
source .bashrc

Install Node

1
2
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

Install Avahi and other Dependencies

1
sudo apt-get install libavahi-compat-libdnssd-dev

Install Homebridge

1
sudo npm install -g homebridge

Use the following commaned if you got the permission problem.

1
gyp WARN EACCES user "root" does not have permission to access the dev dir "/root/.node-gyp/5.5.0"
1
2
sudo npm install -g --unsafe-perm homebridge
homebridge

Figure out some problems

You got the error message if you runs the homebridge command. I have a tricky solution. Please following the steps.

1
2
3
sudo rm -rf /usr/lib/node_modules/
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

Set up the configure and name /home/pi/.homebridge/config.json

1
2
3
4
5
6
7
8
{
"bridge": {
"name": "Homebridge",
"username": "FF:FF:FF:FF:FF:FF", // MAC address of Ethernet on Raspberry
"port": 51826,
"pin": "111-11-111" // Set up what you want to
},
}

Set up boot up

  • To save the following words and to name homebridge into /etc/default
1
2
3
4
5
6
7
# Defaults / Configuration options for homebridge
# The following settings tells homebridge where to find the config.json file and where to persist the data (i.e. pairing and others)
HOMEBRIDGE_OPTS=-U /var/lib/homebridge

# If you uncomment the following line, homebridge will log more
# You can display this via systemd's journalctl: journalctl -f -u homebridge
# DEBUG=*
  • To save the following words and to name homebridge.service into /etc/systemd/system/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Unit]
Description=Node.js HomeKit Server
After=syslog.target network-online.target

[Service]
Type=simple
User=homebridge
EnvironmentFile=/etc/default/homebridge
# Adapt this to your specific setup (could be /usr/bin/homebridge)
# See comments below for more information
ExecStart=/usr/local/bin/homebridge $HOMEBRIDGE_OPTS
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target
  • Configuration
1
2
3
4
5
6
7
8
sudo useradd --system homebridge
sudo mkdir /var/lib/homebridge
sudo cp /home/pi/.homebridge/config.json /var/lib/homebridge/config.json
sudo chown -R homebridge:homebridge /var/lib/homebridge/
sudo systemctl daemon-reload
sudo systemctl enable homebridge
sudo systemctl start homebridge
journalctl -u homebridge

Open your Homekit on your iPhone or iPad

  • Import the homebridge into your Homekit

Plugins

  • homebridge-server
    MUST install the plugin that will helpful to install other plugins and management your homebridge
1
sudo npm install homebridge-server@latest -g

Add this snippet to your config.json:

1
2
3
4
5
6
7
{
"platform": "Server",
"port" : 8765,
"name" : "Homebridge Server",
"log" : "/var/log/homebridge-server.log",
"restart" : ""
}

Restart your service and open your browser http://raspberry-ip:8765

1
sudo systemctl restart homebridge

How to remote control the Homekit

Please set up Home Hub in your iPad

Enjoy your Smart Home

it’s a simple way to install vnc server. Don’t use default vnc server (Realvnc)
that is not compactable on mac since it need to activate license, however, I
haven’t a monitor to display.

1
$ sudo apt-get install tightvncserver

Now you can use build-in vnc client on mac.

  • open Finder and press ⌘+k
  • In the Server Address enter vnc:// followed by your computer name or IP address.

First, I got a problem that the taskbar disappeared when I remoted to vnc server.
I have had a tricky solution. to remove lxpanel and the taskbar is back.

1
rm -rf ~/.config/lxpanel/

When I used cmocka to write the unit test, the compiler alerts a error cast from pointer to integer of different size [-Werror=pointer-to-int-cast]. I have tried to write a unit code for running arm 32-bit and x64 machine however it was not working since I wrote the following code that was only working properly on x64.

1
assert_false(list_find(list, 11));

Let’s figure out the problem since the void pointer.

First try, I rewrite the code on arm 32-bit that convert the void pointer to integer. So that it was working on my raspberry. However it was not working on x64 machine. WTF.

1
assert_false((int)list_find(list, 11));

Since the size of void pointer on x64 is 64-bit and the size of integer is 32-bit. Now the compiler alerts me another error message. How to figure out the problem. Let’s use a type intptr_t. The magic that converts to suitable size on the platform. If you compile the code on x64, it will convert to unsigned long that is 64-bit. Otherwise it converts to int.

1
assert_false((intptr_t)list_find(list, 11));

To compile bluez on the raspberry that got the following message when to install libreadline-dev.

1
2
3
4
5
Setting up install-info (4.13a.dfsg.1-5ubuntu1) ...
dpkg: error processing install-info (--configure):
subprocess installed post-installation script returned error exit status 127
Errors were encountered while processing:
install-info

It’s a locale problem. You can modify the /etc/environment to correct locale. My solution modify /etc/environment to en_US.UTF-8 and use raspi-config to install en_US.UTF-8. One more thing, don’t forget to run locale-gen to generate locale profile. Reboot your system. All problem solved.

You got the ERROR: polls..AppleDouble (unittest.loader._FailedTest) when you run the unit test on Django. It means OSX create the .AppleDouble folders in your Django project.

$ find . -name .AppleDouble
./.vscode/.AppleDouble
./.AppleDouble
./__pycache__/.AppleDouble
./polls/templates/.AppleDouble
./polls/templates/polls/.AppleDouble
./polls/.AppleDouble
./polls/__pycache__/.AppleDouble
./mysite/.AppleDouble
./mysite/__pycache__/.AppleDouble

Delete all of .AppleDouble and the problems solved.

$ find . -name .AppleDouble | xargs rm -rf

Updated:
Best solution. You can disable to create .AppleDouble on OSX.

$ defaults write com.apple.desktopservices DSDontWriteNetworkStores true

Check out the reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <unistd.h>
int Modulus(int iN, int iMod) {
int iQ = (iN/iMod);
return iN - (iQ*iMod);
}
char GetChar(int iGenerator, char cBase, int iRange) {
return (cBase + Modulus(iGenerator, iRange));
}
int main() {
char caRow[80];
int j = 7;
int k = 2;
int l = 5;
int m = 1;
while (1) {
int i = 0;
while (i < 80) {
if (caRow[i] != ' ') {
caRow[i] = GetChar(j + i*i, 33, 30);
}
// colour output -- green
printf("\033[32m%c", caRow[i]);
++i;
}
j = (j + 31);
k = (k + 17);
l = (l + 47);
m = (m + 67);
caRow[Modulus(j, 80)] = '-';
caRow[Modulus(k, 80)] = ' ';
caRow[Modulus(l, 80)] = '-';
caRow[Modulus(m, 80)] = ' ';
usleep(10000);
}
return 0;
}

有個傢伙回答的不錯,可以參考 知乎

大學在恐龍書學過很多,什麼哲學家問題,但由於工作多年,一直不需要太複雜的處理這問題,所以也沒真心比較差異,好像都可以用,但看來還是有些差異。

然後在查資料時,也看到一篇 文章,裡面提到 Multi-thread 避免使用 semaphore,這其實也是合理。semaphore 可以處理 Multi-process 問題,這功能還是比較強大的。

No shit, just following the code. OSX based on POSIX that it can’t use sem_init to initial the semaphore and it can’t use sem_destroy to destroy the semaphore, too.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
static sem_t *s;
int a, b, c;
void getA() {
sleep(10);
a = 3;
printf("a: %d\r\n", a);
sem_post(s);
pthread_exit("Thread is done");
}
void getB() {
sleep(5);
b = 10;
printf("b: %d\r\n", b);
sem_post(s);
pthread_exit("Thread is done");
}
void getC() {
sem_wait(s);
sem_wait(s);
c = a+b;
printf("c: %d\r\n", c);
sleep(10);
pthread_exit("Thread is done");
}
int main(int argc, char *argv[]) {
pthread_t tid1, tid2, tid3;
sem_unlink("s");
s = sem_open("s", O_CREAT, S_IRUSR | S_IWUSR, 0);
if(pthread_create(&tid1, NULL, (void*)getA, NULL)) {
}
if(pthread_create(&tid2, NULL, (void*)getB, NULL)) {
}
if(pthread_create(&tid3, NULL, (void*)getC, NULL)) {
}
pthread_join(tid3, NULL);
sem_close(s);
sem_unlink("s");
return 0;
}
0%