Covid-19 住院患者即時動態(下)
DevOps 的實踐是一種組織文化與哲學的轉變,其中對內外在環境的迅速應變也是一個非常重要的議題,在 Covid-19 疫情嚴峻之際,筆者特別以「Covid-19 住院患者即時動態」為例,以兩上、下期來介紹,醫院是如何用運 DevOps 工具快速解決管理及臨床人員的痛點。
文/鄭重男
Rials 像網頁系統開發者的魔法手套,除了本身具備的能力(Ruby)以外,更可以依需求加載數以萬計的寶石(RubyGems)來強化其他能力(開發的速度),由 RubyGems 官方網站(https://rubygems.org)可觀察到,下載次數超過一億次的寶石(Gems)竟多達 200 個之多,由此可見,在 Ruby 或 Rails 的開發領域中,有效學習及使用(收集)寶石,是 Rails(魔法手套)能力極致發揮的關鍵及優勢之一。
[ 推薦閱讀:Covid-19 住院患者即時動態(上)]
Rails 框架
上一期文章中我們提到了如何使用 Rails 來建立最小可用的專案,也約略提及開發前期部分常用的方法及 Git 的概念,在筆者還沒有接觸 Rails 框架開發系統前,曾經以為大家說的「用框架開發系統很快」就是建立(Create)一個專案(如,部落格系統,rails new blog)後,「部落格系統」就寫好了,如果真的連商業邏輯都不用寫就建好一個完整部落格系統,那就像不久前流行的一句話:「將來 AI 會取代(XXX)工作」一樣,系統開發者也許有可能也淪為 AI 眾多犧牲者之一吧!(透過深度學習的 AI,或許真的有機會走到這一步,目前已知 Github 的 Copilot 也開始提供部分類似的開發功能,如:主動提供下一行程式碼建議,或是提供樣板程式碼等),然而,在真正動手開發專案後,才知道原來心裡想的,其實不是那麼一回事。
Rails 框架,基本上是讓我們可以用一些命令語法(Linux-like 或 Microsoft-Windows-Subsystem-Linux,Command)簡單且快速地將 Rails 基本架構預先產生出來,開發人員便可在此架構上撰寫商業邏輯、前端介面(UI/UX)及資料表的設計。
Rails Scaffolding
回到開發最小可用產品這個主題,此專案一開始只有一個資料表,我們可依據已規劃好的資料表名稱(pcr)、欄位名稱及屬性,於建立 Rails 專案後,再於專案目錄內執行下列語法(如圖一所示)產生核酸檢驗的鷹架(scaffold)及資料遷移檔。
這一段語法的意思是要讓 rails 產生一個名為 pcr 的鷹架(scaffold),成功執行後,Rails 便會以專案預設的模板(templates)建立一個(新增、讀取、更新、刪除,CRUD)的 MVC 架構。
Model
產生出來 model migration 檔,如下:
class CreatePcrs < ActiveRecord::Migration[7.0]
def change
create_table :pcrs do |t|
t.string :ca_inpseq
t.string :ca_medno
t.string :mh_name
t.string :ca_bedno
t.string :ca_vsdoccd
t.string :em_empname
t.string :mh_idno
t.string :ca_inpdate
t.string :lbh_culldate
t.string :lbd_repdate
t.string :lbh_diacode
t.string :lbd_state
t.float :positive_days
t.timestamps
end
end
end
只要執行 rails db:migrate 之後,系統便會自動建立資料表及新增所有欄位及屬性,如(圖二)所示。
如果你的系統只是簡單的讓使用者透過網頁操作此資料表的增、讀、修、刪動作,到這裡就可以收工了。或許你會納悶為何都沒有寫任何程式碼(難道是 NoCode?),也沒有碰到資料庫的(Structured Query Language,SQL)?其實不是,而是當我們用 Rails 產生鷹架(scaffold)時,裡面的模板機制會自動將相關的程式檔案通通產生出來,於是就讓人產生沒有寫程式碼的錯覺,其實這一切都是 Rails 的陰謀,筆者相信,有不少人就是被這些看似甜蜜的語法糖給騙進來的。
話說回來,一個系統的開發,不會只用固定的幾個欄位或一張資料表就足夠的,在迭代過程中,一定會碰到欄位修改及異動,當你想在資料表新增一個 boolean 欄位時,Rails 也提供了另一個指令代碼來產生對應的遷移檔,如:
rails g migration add_is_special_ward_to_pcr is_special_ward:boolean
若過程沒有任何錯誤,執行 rails db:migrate 之後,就會發現新的欄位已經被正確的產生出來了。
之前我們就曾經提過,在這種開發架構下的資料庫或資料表的遷移,也繼承 Rails 「約定優於設定原則」,所以,在處理資料庫 schema 的成本相對是較低的,無論原來的資料庫是在哪個遷移版次,下次執行遷移時,都會自動將沒執行過的遷移檔補足,因此,幾乎不會出現資料庫欄位不一致的問題。
提示:在產生任何遷移檔後,記得一定要執行 rails db:migrate 以執行資料庫的異動更新,否則執行網頁時便會出現錯誤。
[ 加入 CIO Taiwan 官方 LINE 與 Facebook ,與全球CIO同步獲取精華見解 ]
另外,專案中,我們還需要將原來存在於醫療資訊系統(HIS)資料庫的病患及檢驗相關資料,定時寫入在 Postgresql 的資料表(pcrs),因此,在擷取 HIS 資料時,常見會有兩種做法,一種是在原來的 HIS 端撰寫一個對應的 API,以 JOSN 或其他格式進行交換,另一種則是直接用 Rails 建立另一個資料庫連結,用 ORM 方式擷取 HIS 資料表內容,由於是在醫院內部開發且尚在資安可控的範疇,通常就會以後者的開發模式優先。
為了讓 Rails 可以順利連結兩個不同的資料庫,必須在 app/database.yml 資料庫設定檔中,增加一個 HIS 資料庫設定,便於連結 HIS 的資料庫,由於 Rails 預設的資料庫是 Postgresql ,所以,我們要增加一個處理 Oracle 資料庫連接的寶石(Gem)來處理並增加開發效率,如圖三所示。
Views & Controllers
使用 Scaffold 建立程式時,除了產生 model 檔案外,也會同時建立使用者瀏覽器的顯示頁面 views 以及讓我們用來撰寫商業邏輯的 controller,也就是 MVC 架構。
由於一開始的使用者故事定義中,是設定每十分鐘擷取一次 HIS 的資料並寫入 Postgresql 資料庫,想當然爾,勢必要有一個排程工作來完成這個事情。
jobs 與 sidekiq 排程
● Job 工作
為了解決定時擷取HIS資料的問題,我們可以利用 Rails 的指令來產生一個工作(job),如:
rails g job get_his_pcr_data
執行後,Rails 便會在專案 jobs 目錄內產生一個名為 get_his_pcr_data_job.rb 的檔案,如:
# app/jobs/get_his_pcr_job.rb
class GetHisPcrDataJob < ActiveJob::Base
queue_as :default
def perform(*args)
#
# 從這裏開始寫 HIS 擷取相關資料並存入 postgresql 的 pcrs 資料表
#
end
end
● Sidekiq 定時排程
sidekiq 是 Rails 專案中,常被拿來用於特定時間執行某一個事件的寶石(gem),它可以定時執行一次工作,因此,我們可以將上述的 job 直接設定給 sidekiq 執行,如:
# config/schedule.yml
job1:
cron: "*/10 * * * *"
class: "GetHisPcrDataJob"
queue: default
args: []
config/schedule.yml
job1:
cron: “*/10 * * * *”
class: “GetHisPcrDataJob”
queue: default
args: []
設定完成後,系統便會在每十分鐘執行一次 GetHisPcrDataJob 裡面的程式碼,至此,我們算是初步完成第一個版本的使用者故事核心邏輯了。
Deploy 發佈程序
最後一個階段,也就是發佈生產環境階段,由於我們希望系統未來可以進入 CI/CD pipeline 或最終以 kubernetes 來運行,因此用 docker 來建立專案的 image 是必需要的動作。
Dockerfile
docker image 在 DevOps 中是一個非常的重要角色,因此,我們必須先撰寫 Dockerfile 來產生專案的 image,產生出來的 image,會包含容器啟動時的作業系統、Rails 執行環境及其他網路設定等,礙於篇幅所限,有興趣的讀者,可自行到網路搜尋 rails Dockerfile 相關的範例檔案。
如果你目前尚無完整的 CI/CD 環境,可以把程式碼(含 Dockerfile)推到雲端的儲存庫中,如 Github 或 Gitlab 之類的服務,體驗一下 CI/CD pipeline 的流程及運作模式。如果你的環境暫時無法使用完整的 CI/CD 程序,也可以像筆者一樣,採用半自動的方式啟動 Rails 專案。
首先要用 docker 命令來建立專案的影像檔(image),如下:
docker build -t covid-19 .
一旦編譯完成,就可以用 docker images 來檢視編譯出來影像檔,如下:
covid-19 latest 914b540a5a18 11
days ago 1.84GB
docker-compose.yml
若決定要以 docker-compose 啟動 Rails 專案,那麼就要先建立 docker-compose.yml 檔案,如:
version: "3.9"
services:
db:
image: postgres
volumes:
- ./tmp/db:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: db_password
app:
image: covid-19
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p
3000 -b '0.0.0.0'"
volumes:
- .:/myapp
ports:
- "3000:3000"
depends_on:
- db
redis:
image: 'redis:alpine'
restart: always
sidekiq:
image: covid-19
environment:
-
DATABASE_URL=postgres://db_user:db_password@db/e_paper_card_production
- REDIS_URL_SIDEKIQ=redis://redis:6379
- RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
depends_on:
- app
- db
- redis
command: bundle exec sidekiq
撰寫完成後便可以利用下列指令啟動相關的服務:
docker-compose up -d
一旦啟動成功,使用者就可以由主機上的 3000 port 登入專案了,如:http://192.168.1.2:3000。
開發與測試環境(Development & Test)
就是開發者用來撰寫程式碼的環境(含除錯模式、單元及系統測試)等,通常是自己用的本機電腦,筆者所知的 Rails 開發者中,似乎也都以 MacOS 居多,不是因為蘋果電腦比較好,而又是另一種不成文的約定或習慣。一般來說,如果你周圍開發圈多數人採用的開發電腦,會直接影響新進入者的採用意向,為的只是避開一些可能連老鳥都沒碰過的坑。
預備環境 (Staging)
就算是同樣的環境,有時因為資料的差異或因為版本發佈太過於頻繁,可能會直接影響正式生產環境的正常運作,因此,若有個 Staging 預備環境作為緩衝或預先驗證是否衍生其他問題後,再發佈到正式生產環境以避免不必要的服務中斷。
生產環境 (Production)
經過前述幾個階段環境測試無誤後,就可以發佈到正式的生產環境,以 DevOps 來說,就是將這些過程以自動化的方式運作,盡可能減少人為介入,也是我們說的持續發佈(CI/CD)的最後一哩路了。不過,在實務上,如果組織尚不具備完全自動化的持續交付環境時,仍可依組織的資源及環境做適度調整。
(本文授權非營利轉載,請註明出處:CIO Taiwan)