0%

緣由

因為某天需要使用 ssh tunnel 打到 aws 服務的內部去,但因為需要使用 pem 憑證當作連線身份驗證,所以查了一下指令該如何下

操作

使用如下指令應該可以輕鬆完成

1
sshuttle -r <hostname> --dns 0/0 --ssh-cmd 'ssh -i <filename>.pem'

緣由

就是需要判斷某字串是否是數字,一開始用了 isnumeric() 發現中招了,因為國字 “五” 也被判斷成數字,想要來解決一下這問題

過程

一查之下才發現 python 這個內建也太強大,以下是網路查詢到的解釋:
isnumeric() 方法檢測字符串是否只由數字組成,數字可以是: Unicode 數字,全角數字(雙字節),羅馬數字,漢字數字。
指數類似 ² 與分數類似 ½ 也屬於數字。

解決

實際上應該使用 string.isdigit() 可以用來檢查所有字元是否都是數字的情況
string 底下還有其他的 function 可以利用也順便列一下
string.isalnum() 所有字元都是數字或者字母
string.isalpha() 所有字元都是字母
string.isspace() 所有字元都是空白字元、t、n、r

string.islower() 所有字元都是小寫
string.isupper() 所有字元都是大寫
string.istitle() 所有單詞都是首字母大寫,像標題
string.isspace() 所有字元都是空白字符、\t、\n、\r

另外 isdecimal() 是用來判斷是否是十進位的數字

緣起

最近一年零零星星斷斷續續的有在看些工作的機會,想說趁著這個機會稍微檢視一下經歷和自我介紹的部分。

研究所

研究所時期因為一些陰錯陽差,本來想念的學校沒拿到,又放棄了一些國立大學,最後因為地利的關係去了某私立前段,入了當時出初茅廬的一位現國內 AI 領域大咖老師門下;除了初期草創了實驗室,掌管實驗室所有大小事,同時倚靠著中研院的資源主要做過生物醫學類蛋白質交互作用、自然語言處理、跨語檢索等不同領域。

在碩二下學期一般菸酒生準備衝刺畢業的前夕,我拿到了一個代表學校去韓國交流一個月的機會(但是是在暑假),我毅然決然就決定回來再來忙畢業的事情(就算到現在回想起來,我也沒後悔過,而韓國相關的事情又是另一段故事惹)。

回國後因為有要事要在 12月前往美國,於是在滿短的時間內一陣能量爆發就完成研究也結束口試了。

畢業後的路

回台灣差不多是一月底的事情,處理完學校的事情之後開始投入找研發替代役的準備,因為時間已經有點晚了,很多比較早開始的公司都結束了;但前前後後還是面了十多間,大約拿到了一半的 offer,屬意其中一間筆電代工大廠,當時他們想做智慧電視吧。

然後當時真的太天真,在講好口頭協議之後,我就推掉了其他全部,直到某天接到一通電話,是打來道歉的,說他們拿不到名額,所以很抱歉同學你要被婊囉…,頓時我陷入一個進退兩難也沒有其他機會可以面試,也沒有其他地方可以去,一個很想死差點要被逼去當兵的狀態。

又直到註冊前一週,信箱來了一封信,某間公司某主管問我找到了沒,他們的人落跑了(關於這個機會其實有個插曲可以說,是研究所其他系的某同學有天傳訊息給我,說有個公司在找人,那主管認識我老闆叫我去投看看,我寫信去問,但是他們說已經收滿了),於似乎我就在很快速的流程之下,三天內決定了之後三年落腳的地方。

研發替代役

關於這個單位呢,我只能說是屬於一個法人公司,網路資料裡面都會說是個涼爽的地方,但我待的部門算是公司的戰鬥單位,號稱整個公司大約兩千人裡面少數真的可以打的單位,但會選擇這邊一方面是領域和學校是有延伸,二方面是我們部門和我們中研院的體系有產學合作,不單只是沒得選擇了而已。

由於四月到新訓入伍還有一段時間,我就提前進公司想說可以提早適應,這段時間我平均一週去個兩到三天,想說可以先打打雜練練功,殊不知這是一個小崩潰的開始。

一進去被要求寫 java,同時開始獨立開發一個新的專案,但在此時 java 我一行也沒寫過?喔可能在學校寫過一點 jsp 但那真的很淺,但死拼活拼也差不多在一兩個月內完成了一套雛形,沒想到業務、小主管就開始帶著我出去找客戶兜售這個產品了,我也是差不多醉了…

但這樣兜售兜售也是前前後後合作了不少間廠商,所以很多面試官都會問說你有獨立從無到有開發過一個產品的經驗嗎?我說打從頭開始就是,常常直接被給一個不屑的眼神…

這專案持續了大概三年左右,技轉金額累計大約有 1200w+,也讓我幾乎以一個核心專案就待到可以退伍了

後替代役時期

結束了這三年雖然沒有達成當初設定的目標,但是卻也達成了許多當初沒想過的成就,有很大一部分的替代役會考慮在這個時機點跳槽,而我當時因為業務穩定且也是單位主力栽培的對象就也暫時打算邊走邊看,但我主要負責的案子也因為公司政策不打算繼續發展(法人單位的悲哀)而慢慢走向後期,部門大約在這個時期到達如日中天的巔峰時期大約維持了兩年左右;在這個時期部門的政策將核心的研發方向從數位學習轉向了 AI,也因此在此時期做了很多不同類型的專案,但因為在這塊並沒有長期的將基礎打好,業務那邊也沒有兜售成功,大多數都只做了幾個月就被丟棄。

在這段期間其實也有面了幾間公司和被人拉去做些創業專案題目,因為想去外商和創業一直是我想做的事,但最後都因為各種原因而不了了之,值得一提的大概是某跨國企業很想找我去當系統分析師,但考量到還不想太早完全脫離寫 code 生涯同時公司太遠上班時間又太早這件事,就沒有接受。

這段時間大概持續兩年多大概是我在該單位最迷惘的時候,我覺得我的進步停滯了,這邊好像沒什麼可以學了,同時因為政策等等因素一直導致都在用錯誤的方法做錯的事,顯得自己很智障的感覺,我不知道是不是所有公司都這樣,但至少在這邊或甚至說我合作過的外頭的工程師,大多都會用很多不正規的方法來做事,導致要跟其他人和做的時候事情會事倍功半,這個現象在往後出去面試的時候曾經被不少工程師取笑「什麼叫做正確的方法做事,有人不是這樣做事嗎?」,但就我的經驗來說亂搞的人就是居多。

救火隊時期

在這個時期差不多在公司了待了五六年了,credit 也累積到了一定,但當然就更想轉職了,但在這個時間點隨著組織結構變動,我算是有了自己建軍的機會,lead 一個大約 3~5人的小團隊,於是我終於可以有一定的權限決定處理問題的方式(不管需求是合理的還是蠢的,至少希望是用合理的方法)。

但我們這團隊在短時間內變成了所有大團隊裡面最急、最難、最賽、最新、沒人會、最不合理的需求,都是丟過來我們這邊,反正我們這邊都有辦法解決,常常有今天開需求明天要、後天要之類的狀況,於是我們這團隊拿到工作時間極度彈性權利,主管也不會管我們幾點上下班,這點對我們而言還算不錯,公司沒重大的事的話,可以中午在家吃完飯下午再進辦公室或是直接在家上班了(我們應該算是台灣 WFH 先驅之一),但忙起來的時候也是很誇張,一週工作個一百小時,連續幾個禮拜睡公司等等。

由於團隊成員組成正確、做事方法正確、工作方式自由這些因素,我又對這份工作有了動力;但此時是公司開始走下坡的開始,所以預算慢慢開始緊縮,加薪什麼的也開始變少,但最糟糕的是因為這樣公司慢慢變成一攤死水,人員只出不進,這就慢慢種下往後失敗的種子。

後救火隊時期

大約又過了一兩年,差不多滿七年或八年之後某年,我們的主管被拔擢上去當了大老闆的副手,升上去之後突然一個性情大變,時常丟出很多荒誕怪奇的想法以及行事作風,也常常很大聲的去對外承諾做不到的事情(也許就是所謂「將帥無能,累死三軍」),整個部門的方向又從 AI 相關轉去 FinTech,儘管實際上不是真正的 FinTech 但要建構整個平台也是要花不少心力,為了要結盟更多的廠商我們也花了非常非常非常(真的很多要講三次)多的時間,幫這堆廠商做了很多本來是他們應該要做的事情,而且這個業務量非常大也繁瑣,簡單說事情多又很蠢,但又沒辦法,不時也是要大加班。

但這時期我們老闆不時被人問起我們為什麼這麼常加班,他總笑著跟他們說:「哦,他們習慣晚上工作!」,我其實一直不懂他說這什麼鬼話,也許是怕被人家說他是個害人加班的主管吧?但就是你三天兩頭今天開需求後天就要啊…

反正後來花了幾個月總算也是把那個平台的雛形給弄的差不多了,然後那個案子因為牽扯到某開幕記者會,後來是順利完成,完成後還有慶功宴,慶功宴沒有列到我們團隊,對外也完全沒有提到這系統是誰做的,也完全沒人問,彷彿是在空氣間自己生出來的,在這個時間點我其實覺得這群人有那麼一點點可笑。

然後沒多久又有新的規定頒佈了,開始要嚴格要求上下班時間,交代了一位同仁早上九點半要進行早點名,沒到沒請假的都算曠職,這一個舉動基本上徹底惹毛我們,擺明了就是針對我們團隊來的,從此開始我們團隊所剩的三人,一個開始找工作,另外兩個開始每天早上請假一到兩小時,另外拒絕所有的加班,反正我們一年 30天假,一天請一小時的話可以請個 240天…

而在這個年初,前一年度的考績也出來了,我本人前面六年每年度都是 Top 10%,總算今年掉下去了,其實這也是做得很明顯了,不離不棄被當北七的常見案例,大約在五六月的時候,找了一陣子工作的那位拿到了國內某知名外商的 offer 準備轉職,因為我們三人的工作負擔很重的關係,因此提早了先知會了主管,希望盡快找人來做交接,需要的時間很長。

而到了七月,是我們發前一年 bonus 的時候,這位拿到了一個很微妙的數字就是 0,會用微妙來形容是因為幾個原因,首先這是前一年的業績獎金,那當然以前一年的貢獻來評估,如果主管因為私心給的少,或許也可以以貢獻度低來做合理解釋,給了一個 0就如同挖了一個坑給自己;於是過了幾天這位就直接去找主管本人丟了個直球「請問我去年的貢獻如何,為什麼我是 0?」這主管也妙了,情急之下給了個天才般的答案「哦,因為我們七月的獎金是預先發的,其實是當年度 7~12月的獎金,跟 1月的農曆年獎金一樣,那是 1~6月的獎金,那你八月之後就不在了,當然就是 0!」

因為這全程有錄音,因此這主管也就幫他老闆捅出個大簍子,事後直接去找人資單位來解釋「預發獎金」這個行為,後來人資單位直接下來介入,最後大老闆出來處理這件事,後來當然也是補了些錢息事寧人;但這事件也是徹底加深了剩下兩位要離開的決心。

我們剩下的兩位是部門上系統的核心,從各項服務、軟硬體架構、對內對外各項系統幾乎都掌握在我們手上,要完全交接的話我們估計大約要五位 JR. 或是 三位 SR.,於是我們大約間隔一週先後提了離職,小主管希望我們撐久點給他點時間找人,這點忙本來也就在我們計算之中所以他待了兩個月、我待了三個月才離開。(儘管給了這麼長時間,最終還是沒有找到新人,所以只有把一些短期看起來比較重要的、還有用到的進行簡單交接,以及留下些文件好讓他們能有點眉目)

以為開了大絕可以一路爽到離職日嗎?事情當然沒有這麼順利,FinTech 的案子還有些功能和服務要和外部廠商對接,然後又沒人搞得動,所以又呼叫 119…,搞到最後我離職前最後個週末的六日都還在加班整天搞到一肚子不爽,所以一直到離職日也幾乎沒閒過一天。(到此結束這邊約莫八年半的經歷)

休息期

被操了好陣子的我們也是想先休息一陣再看看下一步,但雖說是休息其實也不是真的沒事幹,除了專心去照顧投資的事業之外,另一邊去玩了點音樂,其實每天也是頗忙碌的,不時因為一些活動要跟媒體記者交涉、跟網紅網美打交道,到處經營一些人際關係,還要照顧員工的心理以及工作狀態其實也是要花非常多時間的。

重出江湖

日子過了大概持續了十個月到了隔年九月,經過朋友的介紹認識了某類似人力仲介手上有個案子,某小有名氣的國內電商想要開發個新功能,去聊了兩次之後覺得可以打個工賺點錢補貼一下,於是我們老同事的核心兩位以及又拉了一位前端一起進去幫忙。

誰知道一進去就是一陣莫名,開發新功能為何要交接這麼多東西,原來他們技術部門全部要離職(唯一留下的一位前端在不久後也被火掉),結果我們就不小心掉入這個屎坑,花了兩週就開始接手這個電商的所有系統。(原有的技術頭還私下叫我們趕快閃超級賽XD)

後來待了一陣子之後才了解到這地方最大的問題是政治問題,但我們一直維持著 Full-time remote 的工作模式倒也互不影響,每週花兩個下午去公司開會其餘時間大約是 24hr on call,以壓力來說其實很大,每天都是超過百萬的營業額,一有什麼閃失就會電話響,而舊有系統裡面有極多沒修好的漏洞,會造成訂單漏單以及送貨延遲等諸多問題,但身為專業救火隊員這點火還是可以撐的下來。

直到某天發現人力仲介那邊和公司資方對於薪水認知有些問題,我們當然不可能白做工就斷然決定離開,前後待了大約五個月左右的地方,也大致摸了一輪電商的營運模式以及所需工作,也算是有點收穫,但薪水的恩怨情仇又是另外一段故事。

等待機會

離開了電商之後,直接就遇到了歷史級恐怖的 2020,外面的工作開始緊縮,雖然也是有些小新創想要趁著這時間起步,但這年我們主要也以幾個外包案為主,並沒有拿到太多機會,在這年我不時會透過一些獵頭朋友去找些公司聊聊天,前後大概也接觸了七八間公司,就我的感覺現在外面的工作機會說是要找 Sr. 其實都是想要 Super Jr.,想要找開發能力高的黑手,一個人可以當兩三個用,而不是來幫忙解決一些新手菜鳥解決不了的問題,大多數面試自介完就是開始考實作或是刷題,基本上並不在乎你之前有過什麼樣的經歷或是你的經驗,說好聽是只要有實力人人平等,說難聽點大家都不缺高端人才,因為高端位置大多都被卡住了,或是都選擇先升本土栽培的員工;而現在最缺的就是可以快速動手開發的即戰力,整體來說國內的資訊業環境真的不太好,可能只有外商會真正比較尊重 Sr. 的職缺吧。

給後進一點建議

我喜歡那個理念「不是你不好,是你還沒遇到適合你可以看見你的好的機會」,的確每個人因為背景、成長過程不同都會有不一樣的特質,這世界總會有些機會就是絕佳的適合你,也總有主管完美的被你打中,在世界看的不夠多的時候還沒遇到罷了,還是隨時要保持信心與好奇心,持續學習,多看些機會總會有適合自己的。

結論

不要委屈自己,我們在做任何選擇的時候都是有得有失的,失去了這邊也會得到其他的,所以不要害怕做決定,每個人都會因為選了不同道路變成不同特質的人,誰也沒辦法取代誰。

緣由

因為專案太多了,想簡單製作 CI/CD 的機制,至少把 git push 後的 auto deploy 建置起來,個人之前還有些 gitlab-ci 的經驗,於是就看上了最近兩年比較紅的 drone ci,過程中遇到了不少問題,官方文件也不盡詳細,最後順利解決了在這邊記錄分享一下。

過程

官網上面或是網路上面有一堆教學文件應該大家都看過不少,我直接提供我這邊遇到的狀況,因為我想要在 git push 之後讓平台直接透過 ssh 工具去進行 auto deploy,同時又想要使用一些 docker image 工具,但官網上面是分開去撰寫的,並沒有特別寫說並用的時候怎麼處理。

我在了解過 drone 運作原理之後摸索出解法,其實很簡單,就是同時起 ssh 和 docker 兩種類型的 runner,然後串在同一包就可以了,下面直接分享我最後的 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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
version: '3'
services:
drone-server:
image: drone/drone:1
ports:
- 80
volumes:
- ./:/data
restart: always
environment:
- DRONE_SERVER_HOST=${DRONE_SERVER_HOST}
- DRONE_SERVER_PROTO=${DRONE_SERVER_PROTO}
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}

# GitHub Config
- DRONE_GITHUB_SERVER=https://github.com
- DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT_ID}
- DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_CLIENT_SECRET}

- DRONE_LOGS_DEBUG=true
- DRONE_LOGS_PRETTY=true
- DRONE_LOGS_COLOR=true

- VIRTUAL_HOST=<your_domain>
- VIRTUAL_PORT=80
- LETSENCRYPT_HOST=<your_domain>
- LETSENCRYPT_EMAIL=<your_mail>
networks:
- public

# runner for docker version
drone-runner-docker:
image: drone/drone-runner-docker:1
restart: always
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_RPC_HOST=${DRONE_RPC_HOST}
- DRONE_RPC_PROTO=${DRONE_RPC_PROTO}
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_RUNNER_CAPACITY=1
networks:
- public
drone-runner-ssh:
image: drone/drone-runner-ssh:1
restart: always
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_RPC_HOST=${DRONE_RPC_HOST}
- DRONE_RPC_PROTO=${DRONE_RPC_PROTO}
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_RUNNER_CAPACITY=1
networks:
- public

networks:
public:

緣由

因為使用 docker 加上套用了 CI/CD 之後呢,用來 build 的中繼 container 還有舊版本的 images 會佔掉很多空間,所以想來做一下每天自動清除這些垃圾的機制

操作

其實很簡單,只要在 cron job 裡面加入清除指令就行了

1
2
3
4
5
6
7
$ crontab -e

加入一條這個 #每天凌晨三點清除一次
0 3 * * * /usr/bin/docker system prune -f

#記得要重新讀取設定
$ sudo service cron reload

就這樣打完收工

緣由

由於想要在 container 裡面測試此 container 與某服務連線有通,但通常 container 裡面的軟體都極度精簡,所以來記錄一下在 alpine 的容器裡面該怎麽操作

操作

首先進入到容器內

1
2
3
$ docker exec -it <CONTAINERID> sh
or
$ docker-compose exec <containerName> sh

進入到容器內後開始進行安裝和使用

1
2
3
$ apk update
$ apk add busybox-extras
$ busybox-extras telnet localhost 5432

收工

緣由

前幾天 Linode 的 vps 忽然無法寫入硬碟,重開機之後直接找不到硬碟,出現 segmentation fault 之類的錯誤,於是開始了救援的任務。

嘗試步驟

一開始想說使用自動備份的 image 進行 restore 之後可能可以恢復正常,所以就先進行 restore,等了一陣後來開機後出現找不到硬碟的狀況,訊息如下:

1
ALERT!  UUID=<UUID> does not exist. Dropping to !

看到這訊息多半是因為是 VM 的關係,硬碟的區塊改變了 uuid 也跟著變了,所以 /etc/fstab 裡面記錄的還是原來的 uuid 造成 mount 不上去的關係
查了一下之後找到了這篇 點我,剩下就照抄他的吧

1
2
3
4
5
6
7
8
9
10
mount -o exec,barrier=0 /dev/sda
cd /media/sda
mount -t proc proc proc/
mount -t sysfs sys sys/
mount -o bind /dev dev/
mount -t devpts pts dev/pts/
chroot /media/sda /bin/bash
mdadm --detail --scan >> /etc/mdadm.conf
update-initramfs -u
update-grub

其中 mdadm 那行卡到,因為沒安裝過這個套件,所以多加了下面一個指令

1
apt install mdadm

操作完重開機就沒問題了!

緣由

docker 在運作的時候會記錄每一筆 stdout 還有 stderr,寫在一個 log file 裡面,通常是長這樣
/var/lib/docker/containers/[container-id]/[container-id]-json.log
然後久了之後,這檔案會超級大,到某一天發現硬碟快爆了的時候你就會發現

所以可以透過官方給的文件做一些 rotation 的設定
https://docs.docker.com/config/containers/logging/configure/

實際操作

如果要手動刪除那個檔案可以用這個指令

1
$ sudo truncate -s 0 <logfile>

建立一個 /etc/docker/daemon.json
內容用以下即可

1
2
3
4
5
6
7
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "10"
}
}

重新吃一下設定檔還有重新啟動 docker service

1
2
$ systemctl daemon-reload
$ systemctl restart docker

這樣應該就行了

Installation

1
2
3
4
curl https://sh.rustup.rs -sSf | sh

sudo vim /private/etc/paths
// add $HOME/.cargo/bin to a new line

Test

1
2
3
cargo
rustc
rustup

Installation

1
2
3
sudo add-apt-repository ppa:wireguard/wireguard
sudo apt-get update
sudo apt-get install wireguard

Configuration

  1. 確認 kernel 版本非客制
    1
    2
    uname --kernel-release
    // check your kernel name without linode

如果執行完上面指令看到有其他非正規字眼像是 xxxxxx-linde152 之類的,你必須去 linode boot settings 的後台設定裡面去把改成 GRUB 2,不然會造成 wireguard 無法確認 kernel 版本會安裝執行失敗
參考這邊

改完重開之後會像再執行一次會變成像這樣 4.15.0-54-generic

  1. 按照正常安裝流程
  2. Key Generation
1
wg genkey | tee privatekey | wg pubkey > publickey
  1. 新建 server 端設定檔
    在這個位置建立一個檔案 /etc/wireguard/wg0.conf
1
2
3
4
5
6
7
8
9
10
11
12
[Interface]
Address = 10.66.66.1/24 // 通道連上之後的內網 ipv4 ip
Address = fd42:42:42::1/64 // 通道連上之後的內網 ipv6 ip
SaveConfig = true
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820 // vpn listen port
PrivateKey = <server private key> // 填上前一個步驟產生的 private key

[Peer] // 設定可以連入的 client 資訊
PublicKey = <client public key>
AllowedIPs = 10.66.66.2/32, fd42:42:42::2/128 // client 會取到的 ip
  1. client 端設定檔
1
2
3
4
5
6
7
8
9
10
[Interface]
PrivateKey = <client private key>
Address = 10.66.66.2/24, fd42:42:42::2/64
DNS = 8.8.8.8, 1.1.1.1

[Peer]
PublicKey = <server public key>
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = <server ip>:<listenPort> // xxx.xxx.xxx.xxx:51820
PersistentKeepalive = 25
  1. Set Up Firewall Rules
1
2
3
4
5
sudo ufw allow 22/tcp
sudo ufw allow 51820/udp
sudo ufw enable

sudo ufw status verbose // for test
  1. Start the Wireguard Service
1
2
3
wg-quick up wg0
sudo systemctl enable wg-quick@wg0 //設定開機自動執行
sudo wg show