我正在設定一個 docker 容器來作為 Ocaml 的簡單環境,因為我不想在兩臺計算機上管理兩個 OPAM 工具鏈。(Windows 桌面,Linux 筆記本電腦)我的目標是將容器加載到 docker-compose 上的 bash 命令提示符中,并準備好運行 ocaml,為此我需要輸入 bash,然后運行 ??eval $(opam env) 在啟動時。這是我當前的 docker 檔案:
FROM ocaml/opam:alpine-3.12
# Create folder and assign owner
USER root
RUN mkdir /code
WORKDIR /code
RUN chown opam:opam /code
USER opam
# Install ocaml
RUN opam init
RUN opam switch create 4.11.1
RUN opam install dune
# bash env
CMD [ "/bin/bash" ]
ENTRYPOINT [ "eval", "\$(opam env)" ]
構建并嘗試運行它會給我錯誤:
sh: $(opam env): unknown operand
ERROR: 2
我嘗試制作一個 run.sh 腳本,但遇到了一些可能比這更難除錯的 chmod/permission 問題。我該怎么做才能在 bash 中打開這個容器,然后運行 ??eval $(opam env) 命令?我不想用命令列引數來做這件事,我想在 dockerfile 或 docker-compose 檔案中做這一切
uj5u.com熱心網友回復:
訣竅是使用opam exec1作為入口點,例如,
ENTRYPOINT ["opam", "exec", "--"]
然后,您可以直接從已安裝的交換機運行命令,也可以啟動互動式 shell,run -it --rm <cont> sh您將完全激活交換機,例如,
$ docker run -it --rm binaryanalysisplatform/bap:latest sh
$ which ocaml
/home/opam/.opam/4.09/bin/ocaml
順便說一句,既然我們在談論 docker 和 OCaml,讓我分享一些更多的技巧。首先,您可以查看我們在 BAP 中收集的dockerfile以獲得一些靈感。我想分享的另一個重要技巧是使用多階段構建來縮小影像的大小,這是一個Dockerfile 示例。在我們的例子中,它使我們從 7.5 Gb 減少到只有 750 Mb,同時仍然保留了運行和構建 OCaml 程式的能力。
另一個注意事項 :) 您還應該在單個 RUN 條目中運行您的安裝,否則您的層最終會出現分歧,并且您會收到奇怪的丟失包錯誤。基本上,這是您要查找的 Dockerfile,
FROM ocaml/opam2:alpine
WORKDIR /home/opam
RUN opam switch 4.11.1 \
&& eval "$(opam env)" \
&& opam remote set-url default https://opam.ocaml.org \
&& opam update \
&& opam install dune \
&& opam clean -acrs
ENTRYPOINT ["opam", "exec", "--"]
1)或者opam config exec,即,ENTRYPOINT ["opam", "config", "exec", "--"]對于舊版本的 opam。
uj5u.com熱心網友回復:
沒有辦法告訴 Docker 在主容器行程啟動后做某事,或者向主容器行程發送輸入。
您可以做的是撰寫一個包裝腳本,該腳本進行一些初始設定,然后運行主容器行程。由于該eval命令只會設定環境變數,因此這些變數將傳遞到主 shell。
#!/bin/sh
# entrypoint.sh
# Set up the version-manager environment
eval $(opam env)
# Run the main container command
exec "$@"
在 Dockerfile 中,將此腳本設為ENTRYPOINT:
COPY entrypoint.sh /usr/local/bin
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["/bin/bash"]
也可以將此設定放入 shell 點檔案中,并bash -l作為主容器命令運行以強制它讀取點檔案。但是,該$HOME目錄通常在 Docker 中沒有明確定義,因此您可能需要設定該變數。如果您擴展此設定以運行完整的應用程式,入口點包裝方法也將在那里,但該序列可能根本不會讀取 shell 點檔案。
您展示的內容看起來像是一個非常簡單的安裝順序,我可能不會更改它,但請注意,在 Docker 中使用版本管理器很復雜。特別是每個 DockerfileRUN命令都有一個新的 shell 環境,并且該eval命令不會“粘住”。我通常建議選擇工具鏈的特定版本并直接安裝它,也許在/usr/local沒有版本管理器的情況下,但這種方法將比你目前使用的方法復雜得多。對于更主流的語言,您通常也可以使用例如預構建的node:16.13影像。
你得到的錯誤是什么?For ENTRYPOINTand CMD(and also RUN) Docker 有兩種形式。如果某個東西是 JSON 陣列,那么 Docker 將命令作為一系列單詞運行,陣列中的一個單詞轉換為命令中的一個單詞,并且沒有額外的解釋或轉義。如果它不是 JSON 陣列——即使它主要是 JSON 陣列,但有錯字——Docker 會將其解釋為 shell 命令并使用sh -c. Docker 將此規則分別應用于ENTRYPOINTand CMD,然后將它們組合成一個命令。
In particular in your ENTRYPOINT line, RFC 8259 §7 defines the valid character escapes in JSON, so \n is a newline and so on, but \$ is not one of those. That makes the embedded string invalid, and therefore the ENTRYPOINT line isn't valid, and Docker runs it via a shell. The single main container command is then
sh -c '[ "eval", "\$(opam env)" ]' '/bin/bash'
which runs the shell command [, as in if [ "$1" = yes ]; then ...; fi. That command doesn't understand the $(...) string as an argument, which is the error you're getting.
The JSON array already has escaped the things that need to be escaped, so it looks like you could get around this immediate error by removing the erroneous backslash
ENTRYPOINT ["eval", "$(opam env)"] # won't actually work
Docker will run this as-is, combining it with the CMD, and you get
'eval' '$(opam env)' '/bin/bash'
But eval isn't a "real" command – there is no /bin/eval binary – and Docker will pass on the literal string $(opam env) without interpreting it at all. That's also not what you want.
In principle it's possible to do this without writing a script, but you lose a lot of flexibility. For example, consider
# no ENTRYPOINT; shell-form CMD
CMD eval $(opam env) && exec /bin/bash
Again, though, if you replace this CMD with anything else you won't have done the initial setup step.
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/423117.html
標籤:
