Dockerコンテナからホストを制御する方法は?
たとえば、コピーされたホストbashスクリプトを実行する方法は?
Dockerコンテナからホストを制御する方法は?
たとえば、コピーされたホストbashスクリプトを実行する方法は?
回答:
それは本当にあなたがそのbashスクリプトが何をする必要があるかに依存します!
たとえば、bashスクリプトが出力をエコーするだけの場合は、次のようにすることができます。
docker run --rm -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
もう1つの可能性は、bashスクリプトでソフトウェアをインストールすることです。たとえば、docker-composeをインストールするスクリプトです。あなたは次のようなことをすることができます
docker run --rm -v /usr/bin:/usr/bin --privileged -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
しかし、この時点で、コンテナー内からホストに必要な特定のアクセス許可を許可するために、スクリプトが何を実行しているかを詳しく知る必要があります。
docker run --rm -v $(pwd)/mybashscript.sh:/work/mybashscript.sh ubuntu /work/mybashscript.sh
/usr/bin
をコンテナに公開します。どちらの場合も、コンテナはホストシステムへのフルアクセスを持っていません。私は間違っているかもしれませんが、それは悪い質問に対する悪い答えのようです。
私が使用する解決策は、ホストに接続しSSH
て次のようなコマンドを実行することです。
ssh -l ${USERNAME} ${HOSTNAME} "${SCRIPT}"
この回答は票を取得し続けるように、私は、スクリプトを呼び出すために使用されているアカウントが、まったく権限を持つアカウントであるが、唯一のようにそのスクリプトを実行する必要があることを、思い出させる(と非常にお勧め)したいと思いますsudo
(それが可能sudoers
ファイルから実行)。
ssh
と見つかりません。他に何か提案はありますか?
apt update && apt install openssh-client
。
名前付きパイプを使用しました。ホストOSで、コマンドをループして読み取るスクリプトを作成し、その上でevalを呼び出します。
Dockerコンテナにその名前付きパイプを読み取らせます。
パイプにアクセスできるようにするには、ボリュームを介してパイプをマウントする必要があります。
これはSSHメカニズム(または同様のソケットベースの方法)に似ていますが、ホストデバイスに適切に制限されます。これはおそらくより良い方法です。さらに、認証情報を渡す必要はありません。
私の唯一の警告は、なぜあなたがこれをしているのかについて注意することです。ユーザー入力などで自己アップグレードするメソッドを作成したい場合は、完全に何かを行う必要がありますが、コマンドを呼び出して構成データを取得することはおそらく望ましくありません。適切な方法は、それを引数として渡すことです。 / volumeをdockerに入れます。また、回避しているという事実にも注意してください。許可モデルについて考えてみてください。
スクリプトの実行など、ボリュームの下でのその他の回答の一部は、システムリソース全体にアクセスできないため、一般的には機能しませんが、使用状況によってはより適切な場合があります。
セキュリティについて心配する必要がなく、OPなどの別のDockerコンテナー内からホスト上でDockerコンテナーを起動することを検討している場合は、ホストで実行されているDockerサーバーをそのリスンソケットを共有することでDockerコンテナーと共有できます。
https://docs.docker.com/engine/security/security/#docker-daemon-attack-surfaceを参照してくださいし、あなたの個人的なリスク許容度は、この特定のアプリケーションのためにこれを可能にするかどうかを確認します。
これを行うには、次のボリューム引数を開始コマンドに追加します
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
または、次のようにdockercomposeファイル内で/var/run/docker.sockを共有します。
version: '3'
services:
ci:
command: ...
image: ...
volumes
- /var/run/docker.sock:/var/run/docker.sock
Dockerコンテナ内でdockerstartコマンドを実行すると、ホストで実行されているDockerサーバーがリクエストを確認し、兄弟コンテナをプロビジョニングします。
クレジット:http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
/usr/bin/docker:/usr/bin/docker
)。
この回答は、Bradford Medeirosのソリューションのより詳細なバージョンであり、私にとっても最良の回答であることが判明したため、彼の功績が認められます。
彼の答えでは、彼は何をすべきか(名前付きパイプ)を説明していますが、それをどのように行うかは正確ではありません。
彼の解決策を読んだとき、名前付きパイプが何であるかを知らなかったことを認めなければなりません。それで、実装するのに苦労しましたが(実際には本当に簡単ですが)、成功したので、どのように実装したかを説明することで喜んでお手伝いします。したがって、私の答えのポイントは、それを機能させるために実行する必要のあるコマンドを詳しく説明することですが、繰り返しになりますが、彼の功績が認められます。
メインホストで、たとえば名前付きパイプファイルを配置するフォルダー/path/to/pipe/
と、たとえばパイプ名を選択して、次のコマンドmypipe
を実行します。
mkfifo /path/to/pipe/mypipe
パイプが作成されます。タイプ
ls -l /path/to/pipe/mypipe
そして、アクセス権が「p」で始まることを確認します。
prw-r--r-- 1 root root 0 mypipe
今すぐ実行:
tail -f /path/to/pipe/mypipe
端末は現在、このパイプにデータが送信されるのを待っています
次に、別のターミナルウィンドウを開きます。
そして、実行します:
echo "hello world" > /path/to/pipe/mypipe
最初の端末(のある端末)を確認するとtail -f
、「helloworld」と表示されます。
ホストコンテナで、tail -f
入力として送信されたものを出力するだけで実行する代わりに、コマンドとして実行する次のコマンドを実行します。
eval "$(cat /path/to/pipe/mypipe)"
次に、他の端末から実行してみてください。
echo "ls -l" > /path/to/pipe/mypipe
最初のターミナルに戻ると、結果が表示されます。 ls -l
コマンドの。
前の部分で、直後に気づいたかもしれません ls -l
出力が表示された、コマンドのリッスンを停止して。
の代わりにeval "$(cat /path/to/pipe/mypipe)"
、次を実行します。
while true; do eval "$(cat /path/to/pipe/mypipe)"; done
(あなたはそれをnohupすることができます)
これで、無制限の数のコマンドを次々に送信できます。最初のコマンドだけでなく、すべてのコマンドが実行されます。
唯一の注意点は、ホストを再起動する必要がある場合、「while」ループが機能しなくなることです。
再起動を処理するために、ここで私が行ったことは次のとおりです。
置くwhile true; do eval "$(cat /path/to/pipe/mypipe)"; done
というファイルにexecpipe.sh
して#!/bin/bash
、ヘッダ
chmod +x
それを忘れないでください
を実行してcrontabに追加します
crontab -e
そして追加
@reboot /path/to/execpipe.sh
この時点で、テストします。サーバーを再起動し、サーバーがバックアップされたら、いくつかのコマンドをパイプにエコーして、実行されているかどうかを確認します。もちろん、コマンドの出力を見ることができls -l
ないので、役に立ちませんtouch somefile
が、役に立ちます。
もう1つのオプションは、スクリプトを変更して、次のような出力をファイルに入れることです。
while true; do eval "$(cat /path/to/pipe/mypipe)" &> /somepath/output.txt; done
これで実行できls -l
、出力(&>
bashで使用するstdoutとstderrの両方)がoutput.txtにあるはずです。
私のようにdockercomposeとdockerfileの両方を使用している場合は、次のようにします。
/hostpipe
コンテナのようにmypipeの親フォルダをマウントするとします。
これを追加:
VOLUME /hostpipe
マウントポイントを作成するためにdockerfileで
次に、これを追加します。
volumes:
- /path/to/pipe:/hostpipe
/ path / to / pipeを/ hostpipeとしてマウントするために、dockercomposeファイルで
Dockerコンテナを再起動します。
Dockerコンテナに実行します。
docker exec -it <container> bash
マウントフォルダに移動し、パイプが表示されることを確認します。
cd /hostpipe && ls -l
次に、コンテナ内からコマンドを実行してみます。
echo "touch this_file_was_created_on_main_host_from_a_container.txt" > /hostpipe/mypipe
そしてそれはうまくいくはずです!
警告:OSX(Mac OS)ホストとLinuxコンテナーがある場合、それは機能しません(説明はhttps://stackoverflow.com/a/43474708/10018801で、問題はhttps://github.com/dockerです) / for-mac / issues / 483)パイプの実装が同じではないため、Linuxからパイプに書き込んだものはLinuxでのみ読み取ることができ、MacOSからパイプに書き込んだものはMac OS(この文はあまり正確ではないかもしれませんが、クロスプラットフォームの問題が存在することに注意してください)。
たとえば、Mac OSコンピューターからDEVでDockerセットアップを実行すると、上記で説明した名前付きパイプが機能しません。しかし、ステージングと本番環境では、LinuxホストとLinuxコンテナーがあり、完全に機能します。
ノードjsコンテナからメインホストにコマンドを送信して出力を取得する方法は次のとおりです。
const pipePath = "/hostpipe/mypipe"
const outputPath = "/hostpipe/output.txt"
const commandToRun = "pwd && ls-l"
console.log("delete previous output")
if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath)
console.log("writing to pipe...")
const wstream = fs.createWriteStream(pipePath)
wstream.write(commandToRun)
wstream.close()
console.log("waiting for output.txt...") //there are better ways to do that than setInterval
let timeout = 10000 //stop waiting after 10 seconds (something might be wrong)
const timeoutStart = Date.now()
const myLoop = setInterval(function () {
if (Date.now() - timeoutStart > timeout) {
clearInterval(myLoop);
console.log("timed out")
} else {
//if output.txt exists, read it
if (fs.existsSync(outputPath)) {
clearInterval(myLoop);
const data = fs.readFileSync(outputPath).toString()
if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath) //delete the output file
console.log(data) //log the output of the command
}
}
}, 300);
ポート(たとえば8080)をリッスンする単純なサーバーpythonサーバーを作成し、ポート-p 8080:8080をコンテナーにバインドし、localhost:8080にHTTPリクエストを送信して、popenでシェルスクリプトを実行しているpythonサーバーに要求するか、curlを実行するか、 HTTPリクエストを作成するコードを書くcurl-d '{"foo": "bar"}' localhost:8080
#!/usr/bin/python
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
import subprocess
import json
PORT_NUMBER = 8080
# This class will handles any incoming request from
# the browser
class myHandler(BaseHTTPRequestHandler):
def do_POST(self):
content_len = int(self.headers.getheader('content-length'))
post_body = self.rfile.read(content_len)
self.send_response(200)
self.end_headers()
data = json.loads(post_body)
# Use the post data
cmd = "your shell cmd"
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
p_status = p.wait()
(output, err) = p.communicate()
print "Command output : ", output
print "Command exit status/return code : ", p_status
self.wfile.write(cmd + "\n")
return
try:
# Create a web server and define the handler to manage the
# incoming request
server = HTTPServer(('', PORT_NUMBER), myHandler)
print 'Started httpserver on port ' , PORT_NUMBER
# Wait forever for incoming http requests
server.serve_forever()
except KeyboardInterrupt:
print '^C received, shutting down the web server'
server.socket.close()
私の怠惰は、ここで答えとして公開されていない最も簡単な解決策を見つけることにつながりました。
lucjuggeryによる素晴らしい記事に基づいています。
Dockerコンテナ内からLinuxホストへの完全なシェルを取得するために必要なことは次のとおりです。
docker run --privileged --pid=host -it alpine:3.8 \
nsenter -t 1 -m -u -n -i sh
説明:
--privileged:コンテナーに追加のアクセス許可を付与し、コンテナーがホスト(/ dev)のデバイスにアクセスできるようにします。
--pid = host:コンテナーがDockerホスト(Dockerデーモンが実行されているVM)のプロセスツリーを使用できるようにしますnsenterユーティリティ:既存の名前空間(コンテナーを分離するビルディングブロック)でプロセスを実行できるようにします
nsenter(-t 1 -m -u -n -i sh)を使用すると、PID 1のプロセスと同じ分離コンテキストでプロセスshを実行できます。コマンド全体で、VMにインタラクティブなshシェルが提供されます。
この設定はセキュリティに大きな影響を与えるため、注意して使用する必要があります(ある場合)。
docker run --detach-keys="ctrl-p" -it -v /:/mnt/rootdir --name testing busybox
# chroot /mnt/rootdir
#
私には簡単なアプローチがあります。
ステップ1:/var/run/docker.sock:/var/run/docker.sockをマウントします(これにより、コンテナー内でdockerコマンドを実行できるようになります)
ステップ2:コンテナ内で以下を実行します。ここで重要な部分は(--network hostこれはホストコンテキストから実行されるため)
docker run -i --rm --network host -v /opt/test.sh:/test.sh alpine:3.7 sh /test.sh
test.shには、必要なコマンド(ifconfig、netstatなど)が含まれている必要があります。これで、ホストコンテキストの出力を取得できるようになります。
マーカスが思い出させるように、dockerは基本的にプロセスの分離です。docker 1.8以降、ホストとコンテナ間でファイルを双方向にコピーできます。のドキュメントを参照してください。docker cp
https://docs.docker.com/reference/commandline/cp/
ファイルがコピーされると、ローカルで実行できます
myvalue=$(docker run -it ubuntu echo $PATH)
して、スクリプトシェルで定期的にテストできます(もちろん、$ PATH以外のものを使用しますが、これは単なる例です)。は特定の値であり、スクリプトを起動します
パイプの概念を使用できますが、ホスト上のファイルとfswatchを使用して、Dockerコンテナーからホストマシン上でスクリプトを実行するという目標を達成します。そのように(あなた自身の責任で使用してください):
#! /bin/bash
touch .command_pipe
chmod +x .command_pipe
# Use fswatch to execute a command on the host machine and log result
fswatch -o --event Updated .command_pipe | \
xargs -n1 -I "{}" .command_pipe >> .command_pipe_log &
docker run -it --rm \
--name alpine \
-w /home/test \
-v $PWD/.command_pipe:/dev/command_pipe \
alpine:3.7 sh
rm -rf .command_pipe
kill %1
この例では、コンテナ内で次のようにコマンドを/ dev / command_pipeに送信します。
/home/test # echo 'docker network create test2.network.com' > /dev/command_pipe
ホストで、ネットワークが作成されたかどうかを確認できます。
$ docker network ls | grep test2
8e029ec83afe test2.network.com bridge local
user2915097の 応答を拡張するには:
分離の考え方は、アプリケーション/プロセス/コンテナー(これに対する角度が何であれ)がホストシステムに対して実行できることを非常に明確に制限できるようにすることです。したがって、ファイルをコピーして実行できると、概念全体が実際に壊れてしまいます。
はい。しかし、それは時々必要です。
いいえ。そうではありません。または、Dockerを使用するのは適切ではありません。あなたがすべきこと(例えば、ホストの設定を更新)あなたがやりたいことのための明確なインターフェースを宣言し、行いませんし、最小限のクライアント/サーバを書くで正確に、よりその、何も。ただし、一般的に、これはあまり望ましくないようです。多くの場合、あなたは単にあなたのアプローチを再考し、その必要性を根絶するべきです。Dockerは、基本的にすべてが何らかのプロトコルを使用して到達可能なサービスであったときに誕生しました。ホスト上で任意のものを実行する権限を取得するDockerコンテナーの適切なユースケースは考えられません。
A
(githubのsrc)があります。ではA
レポ私は「gitのプル」コマンドの後に新しいドッキングウィンドウのイメージを作成し、それらを実行します(もちろん、古いコンテナを除く)適切なフックを作成します。次へ:githubには、マスターをプッシュした後に任意のエンドポイントリンクへのPOSTリクエストを作成できるWebフックがあります。そのため、そのエンドポイントになり、HOSTマシンのリポジトリAでのみ「gitpull」を実行するドッキングされたサービスBを作成しません(重要:コマンド「gitpull」はHOST環境で実行する必要があります-B環境ではなくB B内で新しいコンテナAを実行できません...)