目錄
CMD
s6-overlay 是一組易於安裝(只需提取一兩個 tarball!)的腳本和實用程序,允許您使用現有的 Docker 映像,同時使用 s6 作為容器的 pid 1 和服務的進程管理程序。
建立以下 Dockerfile 並嘗試一下:
# Use your favorite image
FROM ubuntu
ARG S6_OVERLAY_VERSION=3.2.0.2
RUN apt-get update && apt-get install -y nginx xz-utils
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
CMD ["/usr/sbin/nginx"]
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
ENTRYPOINT ["/init"]
docker-host $ docker build -t demo .
docker-host $ docker run --name s6demo -d -p 80:80 demo
docker-host $ docker top s6demo acxf
PID TTY STAT TIME COMMAND
11735 ? Ss 0:00 _ s6-svscan
11772 ? S 0:00 _ s6-supervise
11773 ? Ss 0:00 | _ s6-linux-init-s
11771 ? Ss 0:00 _ rc.init
11812 ? S 0:00 | _ nginx
11814 ? S 0:00 | _ nginx
11816 ? S 0:00 | _ nginx
11813 ? S 0:00 | _ nginx
11815 ? S 0:00 | _ nginx
11779 ? S 0:00 _ s6-supervise
11785 ? Ss 0:00 | _ s6-ipcserverd
11778 ? S 0:00 _ s6-supervise
docker-host $ curl --head http://127.0.0.1/
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 17 Jan 2022 13:33:58 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Mon, 17 Jan 2022 13:32:11 GMT
Connection: keep-alive
ETag: "61e56fdb-264"
Accept-Ranges: bytes
如果您要從先前版本的 s6-overlay ( v2 ) 遷移到新版本 ( v3 ),您可能需要對您的服務或使用 s6-overlay 的方式進行一些更改,以便一切順利進行。本文檔試圖準確說明 v3 的工作原理,但我們有一個單獨的頁面列出了主要差異以及您可能會注意到的內容。如果您遇到這種情況,請閱讀本文!
此專案有以下目標:
cont-init.d
)、終結 ( cont-finish.d
) 等任務以及他們自己的服務以及它們之間的依賴關係PID 1
功能s6
和s6-portable-utils
中包含一整套實用程式。它們包括方便且可組合的實用程序,使我們的生活變得更加輕鬆。logutil-service
進行開箱即用的日誌輪換,該服務在背景使用s6-log
。USER
指令的一些支持,以特定使用者身分運行整個進程樹。不相容所有功能,詳細資訊請參閱註釋部分。 經常重複的 Docker 口頭禪之一是“每個容器一個進程”,但我們不同意。在容器中運行多個進程本身並沒有什麼壞處。更抽象的「每個容器做一件事」是我們的政策——容器應該做一件事,例如「運行聊天服務」或「運行 gitlab」。這可能涉及多個進程,這很好。
映像作者迴避進程管理器的另一個原因是他們相信進程管理器必須重新啟動失敗的服務,這意味著 Docker 容器永遠不會死。
這確實有效地破壞了 Docker 生態系統——大多數映像運行一個進程,當出現錯誤時該進程就會退出。透過出錯退出,您允許系統管理員按照他們喜歡的方式處理故障。如果您的圖像永遠不會退出,您現在需要一些錯誤恢復和失敗通知的替代方法。
我們的政策是,如果「事物」失敗,那麼容器也應該失敗。我們透過確定哪些進程可以重新啟動以及哪些進程應該關閉容器來做到這一點。例如,如果cron
或syslog
失敗,您的容器很可能會重新啟動它而不會產生任何不良影響,但如果ejabberd
失敗,容器應該退出,以便系統管理員可以採取行動。
我們對「Docker Way」的解釋是:
我們的 init 系統就是為了做到這一點而設計的。您的映像將像其他 Docker 映像一樣運行,並適合現有的映像生態系統。
有關停止“事物”的詳細信息,請參閱“用法”部分下的“編寫可選的完成腳本”。
我們的覆蓋初始化是經過適當定制的,可以在容器化環境中正確運作。本節簡要介紹了各個階段的工作原理,但如果您想了解完整的 init 系統應如何運作,您可以閱讀這篇文章:如何將 s6-svscan 作為進程 1 運行
/etc/cont-init.d
中包含的舊版 oneshot 使用者腳本。/etc/s6-overlay/s6-rc.d
中聲明的用戶 s6-rc 服務,遵循依賴關係/etc/services.d
) 複製到臨時目錄並讓 s6 啟動(並監督)它們。/etc/cont-finish.d
中包含的任何終止腳本。TERM
訊號。無論如何,不應該有任何剩餘的進程。KILL
訊號。然後容器退出。 s6-overlay 作為一組 tarball 提供,您可以將其提取到圖像上。您需要的 tarball 是您使用的映像的函數;大多數人都需要前兩個,其他的是您可以在方便時使用的額外功能。
s6-overlay-noarch.tar.xz
:此 tarball 包含實現覆蓋的腳本。我們稱之為“noarch”,因為它是獨立於體系結構的:它只包含腳本和其他文字檔案。每個想要運行 s6-overlay 的人都需要提取這個 tarball。s6-overlay-x86_64.tar.xz
:將x86_64
替換為您的系統架構。該 tarball 包含 s6 生態系統中所有必需的二進位文件,所有文件都是靜態連結的,並且不影響圖像的二進位。除非您確定您的映像已附帶提供覆蓋中使用的二進位檔案的所有軟體包,否則您需要提取此 tarball。s6-overlay-symlinks-noarch.tar.xz
:此 tarball 包含 s6-overlay 腳本的符號鏈接,因此可以透過/usr/bin
存取它們。通常不需要,所有腳本都可以透過 PATH 環境變數訪問,但如果您有包含 shebang 的舊使用者腳本,例如#!/usr/bin/with-contenv
,安裝這些符號連結將使它們正常運作。s6-overlay-symlinks-arch.tar.xz
:此 tarball 包含指向第二個 tarball 提供的 s6 生態系統中的二進位檔案的符號鏈接,以便透過/usr/bin
訪問它們。通常不需要,但如果您有包含 shebangs 的舊使用者腳本,例如#!/usr/bin/execlineb
,安裝這些符號連結將使它們正常運作。syslogd-overlay-noarch.tar.xz
:此 tarball 包含syslogd
服務的定義。如果您執行的守護程式無法登入 stderr 以利用 s6 日誌記錄基礎設施,但對舊syslog()
機制的使用進行了硬編碼,則可以提取此 tarball,並且您的容器將執行syslogd
守護程式的輕量級級模擬,因此您的系統日誌將被捕獲並儲存到磁碟上。若要安裝這些 tarball,請在 Dockerfile 中新增與要安裝的功能相對應的行。例如,大多數人會使用以下內容:
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
確保在提取時保留檔案權限(即使用-p
選項來tar
。)
該專案作為一組標準 .tar.xz 檔案分發,您可以將其提取到映像的根目錄中。 (您需要tar
的 xz-utils 包才能理解.tar.xz
檔案;它在每個發行版中都可用,但並不總是在預設容器映像中,因此您可能需要apt install xz-utils
或apk add xz
,或等價,然後才能擴充檔案。
然後,將ENTRYPOINT
設為/init
。
現在,我們建議使用 Docker 的ADD
指令,而不是在RUN
指令中執行wget
或curl
- 當您使用ADD
時,Docker 能夠處理 https URL,而您的基礎映像可能無法使用 https,甚至可能沒有wget
或curl
已安裝。
從那裡,您有幾個選擇:
CMD
方式執行程式。CMD
使用CMD
是利用疊加層的便捷方法。您的CMD
可以在建置時在 Dockerfile 中給出,也可以在執行時間在命令列中給出,無論哪種方式都可以。它將在s6設定的環境中作為正常進程運行;當它失敗或退出時,容器將乾淨地關閉並退出。您可以透過這種方式執行互動式程式:只有 CMD 會收到您的互動式命令,支援進程將不受影響。
例如:
FROM busybox
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
ENTRYPOINT ["/init"]
docker-host $ docker build -t s6demo .
docker-host $ docker run -ti s6demo /bin/sh
/package/admin/s6-overlay/libexec/preinit: notice: /var/run is not a symlink to /run, fixing it
s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service legacy-services: starting
s6-rc: info: service legacy-services successfully started
/ # ps
PID USER TIME COMMAND
1 root 0:00 /package/admin/s6/command/s6-svscan -d4 -- /run/service
17 root 0:00 {rc.init} /bin/sh -e /run/s6/basedir/scripts/rc.init top /bin/sh
18 root 0:00 s6-supervise s6-linux-init-shutdownd
20 root 0:00 /package/admin/s6-linux-init/command/s6-linux-init-shutdownd -c /run/s6/basedir -g 3000 -C -B
24 root 0:00 s6-supervise s6rc-fdholder
25 root 0:00 s6-supervise s6rc-oneshot-runner
31 root 0:00 /package/admin/s6/command/s6-ipcserverd -1 -- /package/admin/s6/command/s6-ipcserver-access -v0 -E -l0 -i data/rules -- /packa
58 root 0:00 /bin/sh
66 root 0:00 ps
/ # exit
s6-rc: info: service legacy-services: stopping
s6-rc: info: service legacy-services successfully stopped
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped
docker-host $
使用具有 s6-overlay 的容器的另一種方法是對您的服務進行監督。您可以監督任意數量的服務;通常它們只是作為 CMD 運行的主守護程序的支援服務,但如果這就是您想要的,那麼沒有什麼可以阻止您擁有一個空的 CMD 並將主守護程序也作為受監督的服務運行。在這種情況下,守護程式每當退出時都會被 s6 重新啟動;只有當您透過docker stop
命令或在容器內部使用/run/s6/basedir/bin/halt
命令告訴容器這樣做時,容器才會停止。
有兩種方式提供監督服務。仍然支援的舊方法是建立“純 s6”服務目錄。在/etc/services.d
中建立以您的服務名稱命名的目錄,並將可執行run
檔放入其中;這是您將在其中放置長期進程執行的檔案。有關服務目錄監管的詳細信息,以及如何配置 s6 處理守護程序的方式,您可以查看 servicedir 文件。一個簡單的例子如下:
/etc/services.d/myapp/run
:
#!/command/execlineb -P
nginx -g "daemon off;"
新的方法是在/etc/s6-overlay/s6-rc.d
目錄下建立一個 s6-rc來源定義目錄,並將該目錄的名稱加入到user
包中,即建立一個同名的空檔案在/etc/s6-overlay/s6-rc.d/user/contents.d
目錄中。本頁描述了來源定義目錄的格式。請注意,您可以定義longruns ,即將由 s6 監督的守護進程,就像使用/etc/services.d
方法一樣,但也可以定義oneshots ,即將運行一次並退出的程序。您的主要服務可能是長期運行的,而不是一次性的:您可能需要一個守護程序來保留。
這種新格式的優點是它允許您定義服務之間的依賴關係:如果B依賴A ,那麼A將首先啟動,然後當A準備好時B將啟動,當容器被告知退出時, B將停止首先,然後A 。如果您有一個複雜的架構,其中各種流程相互依賴,或者您必須以精確的順序混合單次運行和長期運行,那麼這可能適合您。
上面的例子可以這樣重寫:
/etc/s6-overlay/s6-rc.d/myapp/type
:
longrun
/etc/s6-overlay/s6-rc.d/myapp/run
:
#!/command/execlineb -P
nginx -g "daemon off;"
/etc/s6-overlay/s6-rc.d/user/contents.d/myapp
:空檔。 (這會將myapp
新增至 s6-rc 將在容器啟動時啟動的服務集中。)
/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base
:空檔案。 (這告訴 s6-rc 僅在所有基本服務準備就緒時才啟動myapp
:它可以防止競爭條件。)
我們鼓勵您切換到新格式,但如果您不需要它的好處,您可以堅持使用/etc/services.d
中的常規服務目錄,它也能正常工作。
如果您將主服務作為 CMD 運行,則無需執行任何操作:當您的 CMD 退出時,或當您執行docker stop
時,容器將自然退出,並使用與您的服務相同的退出程式碼。 (但請注意,在docker stop
情況下,您的服務將收到SIGTERM,在這種情況下,退出代碼將完全取決於您的服務如何處理它- 它可能會捕獲它並退出0,捕獲它並退出其他內容,或不捕獲它並讓 shell 退出它自己的程式碼 - 通常為 130。
但是,如果您將主服務作為受監督服務運行,則情況有所不同,並且當您向容器發送docker stop
命令時,您需要告訴容器使用什麼代碼退出。為此,您需要編寫一個finish
腳本:
/etc/services.d
中的舊服務,則需要一個可執行的/etc/services.d/myapp/finish
腳本。/etc/s6-overlay/s6-rc.d/myapp/finish
檔案(該檔案可能可執行,也可能無法執行)。該finish
腳本將在您的服務退出時運行,並將採用兩個參數:
在finish
腳本中,您需要將所需的容器退出程式碼寫入/run/s6-linux-init-container-results/exitcode
檔案 - 就是這樣。
例如,上面myapp
服務的finish
腳本可能如下所示:
#! /bin/sh
if test " $1 " -eq 256 ; then
e= $(( 128 + $2 ))
else
e= " $1 "
fi
echo " $e " > /run/s6-linux-init-container-results/exitcode
當您向容器發送docker stop
命令時, myapp
服務將被終止,並且該腳本將運行;它會將myapp
的退出代碼(如果myapp
捕獲 TERM 信號)或 130(如果myapp
沒有捕獲 TERM 信號)寫入特殊的/run/s6-linux-init-container-results/exitcode
文件,該文件將在容器關閉過程結束時由s6-overlay 讀取,您的容器將以該值退出。
本節介紹 v3之前的s6-overlay 版本的功能。 fix-attrs 在 v3 中仍然受支持,但已被棄用,原因有幾個:其中之一是,當可以靜態完成時,動態更改所有權通常不是一個好的策略。另一個原因是它不適用於 USER 容器。我們現在建議您在執行容器之前處理離線掛載的主機的所有權和權限,而不是修復屬性。當您擁有所有需要的資訊時,這應該在您的 Dockerfile 中完成。
也就是說,這是我們為以前的版本編寫的內容,今天仍然適用(但請停止依賴它):
有時,在繼續之前修復所有權和權限很有趣,因為例如,您已經在容器內安裝/映射了主機資料夾。我們的覆寫提供了一種使用/etc/fix-attrs.d
中的檔案來解決此問題的方法。這是修復屬性檔案後面的模式格式:
path recurse account fmode dmode
path
:檔案或目錄路徑。recurse
:(設定為true
或false
)如果找到資料夾,則遞歸遍歷其中包含的所有檔案和資料夾。account
:目標帳戶。如果找不到帳戶,可以預設使用後備uid:gid
。例如, nobody,32768:32768
會先嘗試使用nobody
帳戶,然後回退到uid 32768
。例如,如果daemon
帳戶是UID=2
和GID=2
,則account
欄位的可能值如下:daemon: UID=2 GID=2
daemon,3:4: UID=2 GID=2
2:2,3:4: UID=2 GID=2
daemon:11111,3:4: UID=2 GID=11111
11111:daemon,3:4: UID=11111 GID=2
daemon:daemon,3:4: UID=2 GID=2
daemon:unexisting,3:4: UID=2 GID=4
unexisting:daemon,3:4: UID=3 GID=2
11111:11111,3:4: UID=11111 GID=11111
fmode
:目標檔案模式。例如, 0644
。dmode
:目標目錄/資料夾模式。例如, 0755
。這裡有一些工作範例:
/etc/fix-attrs.d/01-mysql-data-dir
:
/var/lib/mysql true mysql 0600 0700
/etc/fix-attrs.d/02-mysql-log-dirs
:
/var/log/mysql-error-logs true nobody,32768:32768 0644 2700
/var/log/mysql-general-logs true nobody,32768:32768 0644 2700
/var/log/mysql-slow-query-logs true nobody,32768:32768 0644 2700
這是舊的做法:
修復屬性後(透過/etc/fix-attrs.d/
)和啟動使用者提供的服務之前(透過 s6-rc 或/etc/services.d
),我們的覆蓋層將執行/etc/cont-init.d
/etc/cont-init.d/02-confd-onetime
:
#!/command/execlineb -P
with-contenv
s6-envuidgid nginx
multisubstitute
{
import -u -D0 UID
import -u -D0 GID
import -u CONFD_PREFIX
define CONFD_CHECK_CMD "/usr/sbin/nginx -t -c {{ .src }}"
}
confd --onetime --prefix="${CONFD_PREFIX}" --tmpl-uid="${UID}" --tmpl-gid="${GID}" --tmpl-src="/etc/nginx/nginx.conf.tmpl" --tmpl-dest="/etc/nginx/nginx.conf" --tmpl-check-cmd="${CONFD_CHECK_CMD}" etcd
這種方式還是支持的。然而,現在有一種更通用和更有效的方法來做到這一點:透過在/etc/s6-overlay/s6-rc.d
中新增服務定義目錄,將一次性初始化和完成任務編寫為s6-rc服務,使它們成為一部分user
捆綁包(因此它們實際上在容器啟動時啟動),並使它們依賴base
捆綁包(因此它們僅在base
之後啟動)。
有關 s6-rc 的所有資訊都可以在這裡找到。
容器啟動時,按以下順序執行操作:
/etc/fix-attrs.d
中的檔案執行屬性修復。/etc/cont-init.d
中的一次性初始化腳本依序執行。user
包中的服務由 s6-rc 依照依賴項定義的順序啟動。服務可以是一次性(初始化任務)或長期運行(將在容器的整個生命週期中運行的守護程序)。如果服務依賴base
,則保證它們在此時啟動,而不是更早;如果不這樣做,它們可能會更早開始,這可能會導致競爭條件 - 因此建議始終使它們依賴base
。/etc/services.d
中的長期服務。user2
捆綁包中具有正確相依性的服務將會啟動。 (大多數人不需要使用它;如果您不確定,請堅持user
包。)當容器停止時,無論是因為管理員發送停止命令還是因為 CMD 退出,操作都會以相反的順序執行:
user2
捆綁包中具有正確依賴關係的服務將停止。/etc/services.d
中的長期運行服務已停止。down
腳本;這就是 s6-rc 執行終結任務的方式。/etc/cont-finish.d
中的一次性完成腳本按順序運行。 user2
捆綁包的目的是允許其中宣告的使用者服務在/etc/services.d
服務之後啟動;但為了做到這一點, user2
中的每個服務都需要聲明對legacy-services
依賴。換句話說,要讓foobar
服務延遲啟動,您需要:
/etc/s6-overlay/s6-rc.d/foobar
中定義它。/etc/s6-overlay/s6-rc.d/foobar/dependencies.d/legacy-services
文件/etc/s6-overlay/s6-rc.d/user2/contents.d/foobar
檔案。這將確保foobar
將在/etc/services.d
中的所有內容之後啟動。
預設情況下,在/etc/services.d
中建立的服務將自動重新啟動。如果某個服務應該關閉容器,您可能應該將其作為 CMD 運行;但如果您希望將其作為受監督的服務運行,那麼您需要編寫一個finish
腳本,該腳本將在服務關閉時運行;要使容器停止,必須呼叫/run/s6/basedir/bin/halt
指令。這是一個完成腳本範例:
/etc/services.d/myapp/finish
:
#!/command/execlineb -S0
foreground { redirfd -w 1 /run/s6-linux-init-container-results/exitcode echo 0 }
/run/s6/basedir/bin/halt
腳本的第一行將0
寫入/run/s6-linux-init-container-results/exitcode
檔案。第二行停止容器。當您透過從容器內部執行的/run/s6/basedir/bin/halt
命令停止容器時,將讀取/run/s6-linux-init-container-results/exitcode
並將其內容用作退出程式碼啟動容器的docker run
指令。如果檔案不存在,或容器因docker stop
或其他原因而停止,則退出程式碼預設為 0。
可以在完成腳本中執行更進階的操作。例如,下面的腳本僅在服務非零退出時關閉服務:
/etc/services.d/myapp/finish
:
#!/command/execlineb -S1
if { eltest ${1} -ne 0 -a ${1} -ne 256 }
/run/s6/basedir/bin/halt
請注意,一般來說,結束腳本只能用於守護程序終止後的本機清理。如果某個服務非常重要,以至於容器在死亡時需要停止,我們強烈建議將其作為 CMD 運行。
每個服務都可以有其專用的記錄器。記錄器是一個 s6 服務,它會自動從服務的標準輸出中讀取數據,並將數據記錄到您想要的位置的自動旋轉檔案中。請注意,守護程式通常會記錄到 stderr,而不是 stdout,因此您應該在 shell 中使用exec 2>&1
或在 execline 中使用fdmove -c 2 1
啟動服務的執行腳本,以便擷取stderr 。
s6-overlay 提供了一個名為logutil-service
實用程序,它是s6-log
程序的包裝器。該助手執行以下操作:
S6_LOGGING_SCRIPT
中所包含的日誌記錄腳本nobody
使用者(如果不存在則預設為65534:65534
)然後 s6-log 將永遠運行,從您的服務讀取資料並將其寫入您為logutil-service
指定的目錄。
請注意:
s6-setuidgid
切換用戶nobody
用戶寫入nobody
使用者可寫入父資料夾。您可以在cont-init.d
腳本中或作為 s6-rc oneshots 建立日誌資料夾。以下是一個以舊方式實作的記錄服務myapp
的範例:
/etc/cont-init.d/myapp-log-prepare
:
#! /bin/sh -e
mkdir -p /var/log/myapp
chown nobody:nogroup /var/log/myapp
chmod 02755 /var/log/myapp
/etc/services.d/myapp/run
:
#! /bin/sh
exec 2>&1
exec mydaemon-in-the-foreground-and-logging-to-stderr
/etc/services.d/myapp/log/run
:
#! /bin/sh
exec logutil-service /var/log/myapp
這是在 s6-rc 中實作的相同服務 myapp。
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/dependencies.d/base
:空文件
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/type
:
oneshot
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/up
:
if { mkdir -p /var/log/myapp }
if { chown nobody:nogroup /var/log/myapp }
chmod 02755 /var/log/myapp
(詳細部分開始。)
因此, up
和down
檔案很特別:它們不是 shell 腳本,而是由 execlineb 解釋的單一命令列。您不必擔心 execline;您應該只記住up
檔案包含單一命令列。因此,如果您需要包含多個指令的腳本,請按以下步驟操作:
up
檔中呼叫該腳本。以下是通常為myapp-log-prepare
編寫up
檔案的方法:
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/up
:
/etc/s6-overlay/scripts/myapp-log-prepare
/etc/s6-overlay/scripts/myapp-log-prepare
:(需可執行)
#! /bin/sh -e
mkdir -p /var/log/myapp
chown nobody:nogroup /var/log/myapp
chmod 02755 /var/log/myapp
實際腳本的位置是任意的,它只需要與您在up
檔案中寫入的內容相符即可。
但在這裡,碰巧腳本足夠簡單,可以完全放入up
檔案中,而不會使其變得太複雜或太難以理解。因此,我們選擇將其作為範例,以表明如果您願意的話,您可以對up
執行更多操作。您可以在此處閱讀 execline 語言的完整文件。
(詳細部分結束後,再次點擊上面的三角形即可折疊。)
/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base
:空文件
/etc/s6-overlay/s6-rc.d/myapp-log/dependencies.d/myapp-log-prepare
:空文件
/etc/s6-overlay/s6-rc.d/myapp/type
:
longrun
/etc/s6-overlay/s6-rc.d/myapp/run
:
#! /bin/sh
exec 2>&1
exec mydaemon-in-the-foreground-and-logging-to-stderr
/etc/s6-overlay/s6-rc.d/myapp/producer-for
for :
myapp-log
/etc/s6-overlay/s6-rc.d/myapp-log/type
:
longrun
/etc/s6-overlay/s6-rc.d/myapp-log/run
:
#! /bin/sh
exec logutil-service /var/log/myapp
/etc/s6-overlay/s6-rc.d/myapp-log/consumer-for
:
myapp
/etc/s6-overlay/s6-rc.d/myapp-log/pipeline-name
:
myapp-pipeline
/etc/s6-overlay/s6-rc.d/user/contents.d/myapp-pipeline
:空文件
有很多文件!這一切的涵義總結如下:
myapp | myapp-log
管道被賦予一個名稱myapp-pipeline
,並且該名稱被聲明為user
包的一部分,因此它將在容器啟動時啟動。myapp-log-prepare
、 myapp-log
和myapp
都依賴base
包,這意味著它們只有在系統實際準備好啟動它們時才會啟動。它確實完成了與/etc/cont-init.d
加/etc/services.d
方法相同的事情,但它的底層更乾淨,並且可以處理更複雜的依賴關係圖,因此只要有機會,我們建議您您熟悉聲明服務和記錄器的s6-rc 方式。服務定義目錄的完整語法,包括聲明您的服務是長期運行還是一次性服務、聲明管道、添加特定於服務的超時(如果需要)等,可以在此處找到。
當執行服務時,無論是服務還是記錄器,一個好的做法是在執行之前放棄權限。 s6
已經包含了可以執行這些操作的實用程式:
在execline
中:
#!/command/execlineb -P
s6-setuidgid daemon
myservice
在sh
中:
#! /bin/sh
exec s6-setuidgid daemon myservice
如果您想了解有關這些實用程式的更多信息,請查看: s6-setuidgid
、 s6-envuidgid
和s6-applyuidgid
。
如果您希望自訂腳本具有可用的容器環境:您可以使用with-contenv
幫助程序,它將所有這些都推送到您的執行環境中,例如:
/etc/cont-init.d/01-contenv-example
:
#! /command/with-contenv sh
env
該腳本將輸出容器環境的內容。
Docker 的最新版本允許使用唯讀根檔案系統運行容器。如果您的容器處於這種情況,您應該設定S6_READ_ONLY_ROOT=1
來通知 s6-overlay 它不應嘗試寫入某些區域 - 相反,它將複製到安裝在/run
上的 tmpfs 中。
請注意,s6-overlay 假設:
/run
存在並且可寫入。如果不是,它將嘗試在那裡掛載 tmpfs。/var/run
是/run
的符號鏈接,用於與先前的版本相容。如果不是,它就會變成這樣。一般來說,您的預設 docker 設定應該已經在/run
中提供了合適的 tmpfs 。
透過向執行上下文提供一組已經預先定義的環境變量,可以以某種方式調整 s6-overlay 的行為:
PATH
(預設 = /command:/usr/bin:/bin
):這是容器中所有服務(包括 CMD)的預設 PATH。如果您有很多服務依賴儲存在另一個目錄(例如/usr/sbin
中的二進位文件,請設定此變數。請注意,如果/command
、 /usr/bin
和/bin
尚未位於您提供的路徑中,則它們將始終新增至該路徑。S6_KEEP_ENV
(預設= 0):如果設置,則環境不會重置,整個監督樹會看到原始的環境變數集。它將with-contenv
切換為 nop。S6_LOGGING
(預設 = 0):0
:將所有內容輸出到 stdout/stderr。1
:使用內部catch-all
記錄器並保留其上的所有內容,它位於/var/log/s6-uncaught-logs
中。作為CMD
運作的任何內容仍會輸出到 stdout/stderr。2
:使用內部catch-all
記錄器並保留其上的所有內容,包括CMD
的輸出。絕對沒有任何內容寫入 stdout/stderr。S6_CATCHALL_USER
(預設 = root):如果設置,且S6_LOGGING
為 1 或 2,則全能記錄器將以該使用者身分執行,該使用者必須在映像的/etc/passwd
中定義。每一點特權分離都對安全性有幫助。S6_BEHAVIOUR_IF_STAGE2_FAILS
(預設值 = 0):確定如果服務腳本之一失敗,容器應執行的操作。這包括:fix-attrs
中出現任何問題/etc/cont-init.d
或新式 s6-rc oneshot 失敗/etc/services.d
或新式 s6-rc longrun 被標記為等待就緒通知,並且未能在分配的時間內就緒(請參閱下方的S6_CMD_WAIT_FOR_SERVICES_MAXTIME
)。 S6_BEHAVIOUR_IF_STAGE2_FAILS
的有效值如下:0
:即使腳本失敗,也以靜默方式繼續。1
:繼續,但會發出惱人的錯誤訊息警告。2
:停止容器。S6_KILL_FINISH_MAXTIME
(預設 = 5000):系統在關閉時應等待/etc/cont-finish.d
中的腳本自然完成的時間(以毫秒為單位)。在此持續時間之後,將向腳本發送 SIGKILL。請記住, /etc/cont.finish.d
中的腳本是按順序運行的,並且關閉序列可能會為每個腳本等待S6_KILL_FINISH_MAXTIME
毫秒。S6_SERVICES_READYTIME
(預設值= 50):對於在/etc/services.d
中聲明的服務,在服務啟動的時刻和可以測試其就緒狀態的時刻之間存在不可避免的競爭條件。為了避免這種競爭,我們在測試準備情況之前會睡一會兒,預設為 50 毫秒。如果您的電腦速度緩慢或非常繁忙,您可能會收到類似s6-svwait: fatal: unable to s6_svstatus_read: No such file or directory
錯誤。在這種情況下,您應該透過在S6_SERVICES_READYTIME
變數中聲明它(以毫秒為單位)來增加睡眠時間。請注意,它僅涉及/etc/services.d
; s6-rc 不受競爭條件的影響。S6_SERVICES_GRACETIME
(預設 = 3000):在關閉時, s6
應等待多長時間(以毫秒為單位),以便/etc/services.d
中聲明的服務終止,然後再繼續其餘的關閉操作。S6_KILL_GRACETIME
(預設 = 3000):在關閉過程結束時,當所有進程都收到 TERM 訊號時, s6
應等待多長時間(以毫秒為單位),以便它們在發送KILL
訊號以確保它們已死亡之前死亡。S6_LOGGING_SCRIPT
(預設=“n20 s1000000 T”):此環境決定記錄什麼以及如何記錄,預設每行都會以 ISO8601 開頭,噹噹前日誌檔案達到 1mb 時進行輪換,並最多歸檔 20 個檔案。S6_CMD_ARG0
(預設 = 未設定):此環境變數的值將會加到 docker 傳遞的任何CMD
參數之前。如果您要將現有映像遷移到 s6-overlay 並希望將其作為直接替換,請使用它:將此變數設為先前使用的 ENTRYPOINT 的值將幫助您進行轉換。S6_CMD_USE_TERMINAL
(預設 = 0):如果您的 CMD 需要終端進行輸出(通常當您使用docker run -it
執行容器時),並且您已將S6_LOGGING
設為非零值,請將此值設為1 。這個設定會讓你的CMD實際輸出到你的終端;缺點是它的輸出不會被記錄。預設情況下(當此變數為0或未設定時),當S6_LOGGING
非零時,會記錄 CMD 的 stdout 和 stderr,這表示即使您在互動式終端中執行它們,它們也會進入管道。S6_FIX_ATTRS_HIDDEN
(預設 = 0):控制fix-attrs.d
腳本處理檔案和目錄的方式。0
:排除隱藏檔案和目錄。1
:處理所有文件和目錄。S6_CMD_WAIT_FOR_SERVICES
(預設 = 0):預設情況下,當容器啟動時, /etc/services.d
中的服務將啟動,並且執行將繼續啟動user2
捆綁包和 CMD(如果定義了其中任何一個)。但是,如果S6_CMD_WAIT_FOR_SERVICES
不為零,則容器啟動序列將等待,直到/etc/services.d
中的服務準備就緒,然後再繼續序列的其餘部分。請注意,只有當/etc/services.d
中的服務向 s6 通知其準備就緒時,這才有意義。S6_CMD_WAIT_FOR_SERVICES_MAXTIME
(預設 = 0,即無限):在繼續執行 CMD 之前服務可以啟動的最長時間(以毫秒為單位)。如果您的服務可能會無限期地阻塞,並且您希望容器在給定時間後一切都未正常運行時發生故障,請將此變數設為正值。請注意,此值還包括設定舊容器初始化 ( /etc/cont-init.d
) 和服務 ( /etc/services.d
) 的時間,因此在計算合適的值時請考慮這一點。在s6-overlay 3.1.6.2 之前的版本中,預設值為5000(五秒),但它導致的不必要的容器故障比它解決的問題還要多,所以現在預設沒有超時:s6-overlay將等待啟動所有服務所必需的。S6_READ_ONLY_ROOT
(預設 = 0):當在根檔案系統為唯讀的容器中執行時,將此 env 設定為1以通知 init 階段 2 它應該在先前將使用者提供的初始化腳本從/etc
複製到/run/s6/etc
它嘗試更改權限等。S6_SYNC_DISKS
(預設 = 0):將此環境設為1以通知 init 階段 3 它應該在停止容器之前嘗試同步檔案系統。注意:這可能會同步主機上的所有檔案系統。S6_STAGE2_HOOK
(預設 = 無):如果此變數存在,則其內容將被解釋為 shell 摘錄,該摘錄將在服務啟動之前的早期階段 2 中運行。例如,這可以用於在編譯和運行之前在運行時動態修補服務資料庫。錯誤的值可能會阻止您的容器運行或危及您的安全,因此僅在您確切知道自己在做什麼時才使用此值。如有疑問,請將此變數保留為未定義。S6_VERBOSITY
(預設 = 2):控制 s6-rc 以及可能的其他工具在容器啟動和停止時的詳細程度。預設值 2 通常很詳細:它將列出服務啟動和停止操作。您可以透過減少此數字來使容器更安靜:1 將僅列印警告和錯誤,0 將僅列印錯誤。您還可以通過將此數字增加到 5 來使容器更加詳細,即打印跟踪和調試信息,但輸出很快就會變得非常嘈雜,大多數人不需要這個。S6_CMD_RECEIVE_SIGNALS
(預設= 0):決定傳送到容器的訊號是否應該傳送到容器的pid 1或CMD。預設情況下,當您執行例如docker stop
時,將向容器的 pid 1 發送 TERM 訊號,這將觸發完整的容器關閉序列 - 但如果存在 CMD,它將是最後被殺死的進程之一,僅當其他一切都停止並且容器即將退出時。如果此變數為 1 或更大,則訊號將從 pid 1 轉移到 CMD,這表示docker stop
將向 CMD 發送 SIGTERM,並且容器僅在 CMD 死亡時才會觸發其關閉程序。注意,只有SIGTERM、SIGQUIT、SIGINT、SIGUSR1、SIGUSR2、SIGPWR和SIGWINCH被轉移;其他訊號要么被忽略,要么無法轉移,並且必須由 pid 1 處理。變數;但這應該沒問題,因為在這種情況下,您已經有了停止 CMD 的互動式方式。如果容器中執行的軟體需要 syslog,請擷取syslogd-overlay-noarch.tar.xz
tarball:這將為您提供一個小型 syslogd 模擬。日誌可以在/var/log/syslogd
的各個子目錄下找到,例如訊息可以在/var/log/syslogd/messages/
目錄中找到,最新的日誌可以在/var/log/syslogd/messages/current