美思 [技術雜談] 以 Docker 容器編譯並執行 Swift 程式

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

雖然 Swift 支援的系統比先前多,但很多 GNU/Linux 發行版都沒有官方的 Swift 開發環境。由於編譯 Swift 的過程相當複雜,官方的說明文件也不友善,自行編譯幾乎不可行。

為了要在非官方支援的 GNU/Linux 發行版上編譯和執行 Swift 程式,使用 Docker 是相對簡單且可行的方式。本文介紹使用 Docker 建置 Swift 開發環境的過程。

從命令列直接執行 Docker 容器

在動工前,本節先簡要地說明在命令列上直接執行 Docker 容器的方式。

使用 docker run 指令即可直接執行 Docker 容器,其虛擬指令如下:

$ sudo docker run image command

在這個虛擬指令中,image 是系統上已安裝的 Docker 映像檔,command 是要在 Docker 容器中執行的 GNU/Linux 指令。

docker run 指令只能執行單行 GNU/Linux 指令。因應這項限制,我們改用 /bin/sh -c "..." 的方式來執行指令:

$ sudo docker run image /bin/sh -c "..."

把實際要執行的指令以字串的型態寫在 "..." 內即可。這時候就可以寫入多行指令,指令間以 ; 隔開即可。

此外,我們要將程式碼拷貝到 Docker 容器中,該容器中的 Swift 編譯器才能讀得到程式碼。處理方式是將本地端的特定資料夾掛載 (mounting) 到 Docker 映像檔中:

$ sudo docker run -w /app -v `pwd`:/app run image /bin/sh -c "..."

-w 參數是在 Docker 容器中新增工作目錄。-v 參數用來指定要掛載的目錄。-v 參數的格式是 src:dest,其中 src 是本地端的目錄,而 dest 是 Docker 容器內相對應的目錄。

在這裡用 pwd(1) 指令動態地將當前工作目錄掛載到 Docker 容器中,不論工作目錄在那裡,都不需要另外指定程式碼的位置。

前置作業

要先安裝 Docker 才能進行後續的步驟。Docker 是蠻知名的軟體,大部分 GNU/Linux 發行版都有預編好的執行檔。以 openSUSE 為例:

$ sudo zypper install docker

安裝好 Docker 後,要啟動 Docker 服務:

$ sudo systemctl start docker

如果很常用 Docker 的話,可自行將 Docker 服務加入開機啟動項目中。

下載 Swift 官方 Docker 映像檔

使用以下指令下載 Swift 官方映像檔:

$ sudo docker pull swift:5.3

由於此官方映像檔已有完整的 Swift 編譯器,不需要在 Docker 映像檔中再額外安裝其他軟體。

將執行 Docker 的指令包成 Shell 命令稿

執行 Docker 容器的指令比較複雜,每次都手動輸入不太經濟,所以我們將其包成 shell 命令稿 $HOME/bin/swift

#!/bin/sh

input=$1

if ! [ -f $input ];
then
    sudo docker run --rm --cap-drop=all swift:5.3 /bin/sh -c "swift $@"
    exit $?
fi

sudo docker run --rm -w /app --cap-drop=all -v `pwd`:/app swift:5.3 /bin/sh -c \
"cp /app/$input /tmp; cd /tmp; swift $@"

這個指令會把程式碼傳入 Docker 容器中,並以該容器的 swift(1) 指令直接編譯及執行程式碼。

由於 swift(1) 在編譯及執行 Swift 程式的過程中會產生一些暫存檔,在 /tmp 目錄執行指令就不需要考慮權限的問題,是最簡單的方式。

將該命令稿加上可執行權限:

$ chmod +x ~/bin/swift

寫一個簡單的 Swift 程式來測試一下開發環境:

print("Hello World")

的確可順利地編譯和執行 Swift 程式:

$ swift hello.swift
Hello World

執行 Docker 容器時省略輸入密碼的過程

由於 Docker 需要 root 權限,使用 sudo(1) 輸入 Docker 指令時系統會多次詢問密碼。為了簡化輸入密碼的過程,可設置 sudo(1) 的權限:

$ sudo visudo

加入以下這行:

user ALL=(ALL) NOPASSWD: /usr/bin/docker

請將 user 換成實際的系統使用者帳號。我們只要快速地執行 Docker 容器,所以不開放其他的指令。

注意事項

本文所提供的 shell 命令稿以內部使用為主。由於此命令稿未考慮使用 Docker 的安全事項,勿將此命令稿用於對外公開的服務程式。

本方案的限制

本文所使用的 shell 命令稿只能執行單一 Swift 程式碼,而且該 Swift 程式也無法讀入外部檔案。如果需要其他的應用情境,請自行修改此命令稿。

對於有多個檔案的 Swift 專案來說,另外寫 Dockerfile 是比較好的選擇。限於篇幅,不在本文說明撰寫 Dockerfile 的方式。

關於作者

身為資訊領域碩士,美思認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

美思喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,美思將所學寫成文章,放在這個網站上和大家分享。