摘要:所謂的,正如其名,就是該鏡像的根命令。個人認為,跟第二大不同,在于是對使用者鑒權,而是對目標權限進行鑒權。即使遇到了這樣默認行為是終止進程的信號,也不會直接終止,而會轉發出去。但是,和兩個信號是無法捕獲的,對此也無能為力。
太長不看:如果需要在Dockerfile的ENTRYPONNT中指定運行命令的用戶,用gosu代替sudo可以避免某些信號處理上的邊界條件。不過這些邊界條件比較罕見,就算不用也沒多大關系
docker官方文檔的Dockerfile部分,有一節講的是ENTRYPOINT。在這一節中,提到了如果在啟動腳本中需要指定運行命令的用戶,建議用gosu代替sudo,并給出了一個例子:
#!/bin/bash set -e if [ "$1" = "postgres" ]; then chown -R postgres "$PGDATA" if [ -z "$(ls -A "$PGDATA")" ]; then gosu postgres initdb fi exec gosu postgres "$@" fi exec "$@"
上面的腳本中,docker run指定的命令會以postgres用戶的身份執行。
所謂的ENTRYPOINT,正如其名,就是該鏡像的根命令。默認的ENTRYPOINT為/bin/sh -c,通過docker run或CMD指定的命令會作為ENTRYPOINT的參數執行。舉個例子,docker run ubuntu:latest ls就是執行/bin/sh -c ls。有些時候我們需要指定ENTRYPOINT的值,比如換成自己的包裝腳本。
默認docker中的命令都是以root身份啟動的(因為默認只有root用戶)。不過你也可以通過USER指令設置當前使用的用戶。某些時候,你可能需要在docker build中使用多個用戶,比如上面例子中,安裝依賴需要root,運行程序時使用的是postgres。這時候就需要動態指定一個用戶身份。
docker文檔中建議,如果需要動態指定一個用戶身份,需要使用gosu而非平常的sudo。
然而文檔中并沒有解釋為什么。gosu的項目主頁中也只提到gosu避免了strange and often annoying TTY and signal-forwarding behavior。(然后順便黑了下sudo太過于復雜)。不過gosu的測試用例透露了些蛛絲馬跡,可以看出它認為sudo至少有兩點不好:
sudo會作為被授權的命令的父進程一直存在,直到該命令退出。
sudo模式下的HOME環境變量仍是用sudo者原來的值。
可以實證下這兩個指責:
~ sudo ps -o pid,ppid,cmd PID PPID CMD 12599 4281 sudo ps -o pid,ppid,cmd 12600 12599 ps -o pid,ppid,cmd ~ sudo env | grep HOME HOME=/home/lzx
這兩個現象確實存在,不過會造成什么危害呢?如果真有鬼,夜路走多了自然會碰見。然而平時都是用著sudo,也沒遇到什么事呀。
我們先來看看第二點,sudo模式下HOME環境變量保存不變的事情。
這個事情涉及到sudo的應用場景。sudo用于扮演某個用戶來執行給定的命令,這一點類似于su。個人認為,sudo跟su第二大不同,在于sudo是對使用者鑒權,而su是對目標權限進行鑒權。假定你是sudoer,運行sudo時你要輸入自己的密碼,也即證明自己有扮演的權限;而運行su時,你要輸入的是要扮演的用戶的密碼,也即證明你有扮演的那個用戶的權限。所以sudo會認為,那你使用sudo只是想臨時使用某一身份。既然如此,sudo下HOME環境變量還是原來的樣子,也不是什么bug,而是個feature。如果你不認同這個feature,可以使用sudo -H。
再來看看第一點,sudo作為命令的父進程會一直存在。sudo之所以退而不休,是因為它需要監控命令的輸入輸出。作為一個非常關注安全性的程序,sudo會重置自己的環境變量,盡量以干凈的環境來執行命令。不止如此,它還允許用戶定義安全策略,來處理命令的輸入輸出。不過有種情況下,sudo會直接exec給定的命令。那就是當用戶沒有指定安全策略,且執行的命令不需要占用偽終端的時候。舉個例子,sudo sh -c "sleep 20 &"時,sudo就真的不再作為父進程一直存在了(注意這里我用了個sh來分割整條命令.如果直接輸入sudo sleep 20 &,會被解析成后臺運行sudo sleep 20)。不過這種情況非常特殊,基本上可以忽略。這一點跟上面那條不同,不存在一個改變該默認行為的選項。
看來所謂的“annoying behavior”就是指這個了。不過平時用的時候從沒考慮過這個呀,為什么到了docker里就不建議用呢?
原因在于docker中處理signal的方式。很多程序,比如Apache和Nginx,允許用戶通過發信號的方式來控制程序的生命周期(重啟、關閉、停止,等等)。由于docker把進程封裝了一層,如果想要給這些程序發信號,直接發給docker進程是不行的。那只會影響docker本身的行為。而且這些程序在docker里面運行時,不可能意識到自己在一個獨立的容器里。它們所報告的pid,跟外界的pid是不符合的。
為了跟UNIX的信號機制和諧相處,docker另外提供了發送信號的接口:docker stop和docker kill。docker stop會發兩撥信號,一個是SIGTERM,另一個是SIGKILL。而docker kill則是kill的翻版。這兩個命令有個奇怪的地方,就是它們發送信號,從來都只發給所謂的main process進程,也即ENTRYPOINT進程。如果該進程不會轉發信號(比如默認的/bin/sh -c),目標進程就收不到信號,這個功能便廢了。而當我們用sudo啟動某個命令時,最終收到信號的會是sudo進程,而不是那個命令。
那么sudo是否會轉發信號?答案是,如果可以的話,sudo會盡可能地轉發信號。即使遇到了SIGTERM這樣默認行為是終止進程的信號,sudo也不會直接終止,而會轉發出去。所以盡管多了個sudo攔在路上,大多數情況下,想要發送給目標進程的信號還是能到達的。但是,SIGSTOP和SIGKILL兩個信號是無法捕獲的,sudo對此也無能為力。SIGKILL的話情況還好,因為main process進程(這里的sudo)退出后,整個docker進程都會退出,無意中也達到了一樣的結果。不過SIGSTOP只會讓sudo停下來,結果該停的沒停,不該停的卻停了。
gosu的實現很簡單。它包括以下幾個步驟:
setgroup
setuid
setgid
設置$HOME
exec 目標命令
除了最后關鍵的兩步,其它跟sudo差不多。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/26542.html
摘要:發現問題之后,相應的解決方法也很簡單把當前目錄的擁有者賦值給,再啟動容器就一切正常了。這時我們已經可以知道容器的本地數據卷中文件目錄的權限是和宿主機上一致的,只是在容器和宿主機中可能映射為不同的用戶組名稱。 Volume數據卷是Docker的一個重要概念。數據卷是可供一個或多個容器使用的特殊目錄,可以為容器應用存儲提供有價值的特性: 持久化數據與容器的生命周期解耦:在容器刪除之后數據卷...
摘要:是由一系列命令和參數構成的腳本,這些命令應用于基礎鏡像并最終創建一個新的鏡像。每個中只能有一個,當指定多個時,只有最后一個生效。是改變工作目錄,則是改變之后層的執行以及這類命令的身份。 Dockerfile是由一系列命令和參數構成的腳本,這些命令應用于基礎鏡像并最終創建一個新的鏡像。 常用的選項 例子: FROM node:latest MAINTAINER my_name ADD ....
摘要:比如和指令,鏡像中的文件內容被檢查并且為每個文件計算校驗和。這些文件的最終修改和訪問時間將不被考慮到校驗和內。在查找緩存期間,校驗和將被用于與已存在的鏡像校驗和進行對比。 Docker 可以從 Dockerfile 中讀取指令自動構建鏡像,Dockerfile是一個包含構建指定鏡像所有命令的文本文件。Docker堅持使用特定的格式并且使用特定的命令。你可以在 Dockerfile參考 ...
閱讀 1580·2021-09-26 09:46
閱讀 2665·2021-09-07 09:59
閱讀 2750·2021-09-07 09:59
閱讀 1856·2019-08-30 14:20
閱讀 922·2019-08-26 13:39
閱讀 3174·2019-08-26 12:24
閱讀 771·2019-08-26 11:55
閱讀 1212·2019-08-23 16:49