Docker 컨테이너에서 호스트를 제어하는 방법은 무엇입니까?
예를 들어, 호스트 bash 스크립트에 복사를 실행하는 방법은 무엇입니까?
Docker 컨테이너에서 호스트를 제어하는 방법은 무엇입니까?
예를 들어, 호스트 bash 스크립트에 복사를 실행하는 방법은 무엇입니까?
답변:
그것은 정말로 bash 스크립트가 필요한 것에 달려 있습니다!
예를 들어 bash 스크립트가 일부 출력 만 에코하면 다음을 수행 할 수 있습니다.
docker run --rm -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
또 다른 가능성은 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 메커니즘 (또는 유사한 소켓 기반 방법)과 유사하지만 호스트 장치로 적절하게 제한하므로 아마도 더 좋을 것입니다. 또한 인증 정보를 전달할 필요가 없습니다.
나의 유일한 경고는 당신이 이것을하는 이유에 대해 조심하라는 것입니다. 사용자 입력 등으로 자체 업그레이드하는 방법을 만들고 싶다면 완전히해야 할 일이지만, 적절한 방법은 args로 전달하는 것이기 때문에 일부 구성 데이터를 가져 오는 명령을 호출하고 싶지 않을 것입니다. / 볼륨을 도커에 넣습니다. 또한 회피하고 있다는 사실에주의해야하므로 허가 모델에 대해 생각해보십시오.
볼륨에서 스크립트를 실행하는 것과 같은 다른 답변 중 일부는 전체 시스템 리소스에 액세스 할 수 없기 때문에 일반적으로 작동하지 않지만 사용량에 따라 더 적절할 수 있습니다.
보안에 대해 걱정하지 않고 단순히 OP와 같은 다른 도커 컨테이너 내에서 호스트에서 도커 컨테이너를 시작하려는 경우 청취 소켓을 공유하여 호스트에서 실행중인 도커 컨테이너를 도커 컨테이너와 공유 할 수 있습니다.
https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface를 참조 하십시오. 귀하의 개인 위험 허용이 특정 응용 프로그램이 허용하는 경우를 참조하십시오.
시작 명령에 다음 볼륨 인수를 추가하여이를 수행 할 수 있습니다.
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
또는 도커 내에서 /var/run/docker.sock을 공유하여 다음과 같이 파일을 작성합니다.
version: '3'
services:
ci:
command: ...
image: ...
volumes
- /var/run/docker.sock:/var/run/docker.sock
Docker 컨테이너 내에서 docker start 명령을 실행하면 호스트에서 실행중인 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
"hello world"가 표시되어야합니다.
호스트 컨테이너에서 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
(당신은 그것을 할 수 있습니다)
이제 무제한의 명령을 차례로 보낼 수 있으며 첫 번째 명령뿐만 아니라 모두 실행됩니다.
유일한주의 사항은 호스트를 재부팅해야하는 경우 "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
이 될 것입니다.
또 다른 옵션은 다음과 같이 출력을 파일에 저장하도록 스크립트를 수정하는 것입니다.
while true; do eval "$(cat /path/to/pipe/mypipe)" &> /somepath/output.txt; done
이제 실행 ls -l
및 출력 (stdout 및 stderr 모두&>
bash에서 )은 output.txt에 있어야합니다.
나처럼 docker compose와 dockerfile을 모두 사용하는 경우 다음 작업을 수행했습니다.
/hostpipe
컨테이너에서 와 같이 mypipe의 상위 폴더를 마운트한다고 가정 해 보겠습니다.
이거 추가 해봐:
VOLUME /hostpipe
마운트 지점을 만들기 위해 dockerfile에서
그런 다음 이것을 추가하십시오.
volumes:
- /path/to/pipe:/hostpipe
도커에서 / path / to / pipe를 / hostpipe로 마운트하기 위해 파일을 작성하십시오.
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에서만 읽을 수 있고 Mac OS에서 파이프에 쓴 내용은 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)에서 수신 대기하는 간단한 서버 파이썬 서버를 작성하고, -p 8080 : 8080 포트를 컨테이너에 바인딩하고, localhost : 8080에 HTTP 요청을 수행하여 popen으로 쉘 스크립트를 실행하는 파이썬 서버에 요청하고, 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()
내 게으름으로 인해 여기에 답변으로 게시되지 않은 가장 쉬운 해결책을 찾게되었습니다.
그것은 luc juggery 의 위대한 기사 를 기반으로합니다 .
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 마운트 (따라서 컨테이너 내에서 도커 명령을 실행할 수 있음)
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 등)이 포함되어야합니다. 이제 호스트 컨텍스트 출력을 얻을 수 있습니다.
Marcus가 상기시켜 주듯이 도커는 기본적으로 프로세스 격리입니다. docker 1.8부터 호스트와 컨테이너간에 양방향으로 파일을 복사 할 수 있습니다.docker cp
https://docs.docker.com/reference/commandline/cp/
파일이 복사되면 로컬에서 실행할 수 있습니다.
myvalue=$(docker run -it ubuntu echo $PATH)
스크립트 쉘에서 정기적으로 테스트 할 수 있습니다 (물론 $ PATH가 아닌 다른 것을 사용합니다. 단지 예일뿐입니다). 특정 값이면 스크립트를 실행합니다
파이프 개념을 사용할 수 있지만 호스트 및 fswatch 의 파일을 사용 하여 도커 컨테이너의 호스트 시스템에서 스크립트를 실행하는 목표를 달성합니다. 그렇게 (자신의 책임하에 사용) :
#! /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
REPO 나는 '자식 풀'명령 다음에 새로운 고정 표시기 이미지를 생성하고 실행 (물론 오래된 용기를 제거) 적절한 후크를 만들 수 있습니다. 다음 : github에는 마스터에서 푸시 한 후 임의의 엔드 포인트 링크에 POST 요청을 생성 할 수있는 웹 후크가 있습니다. 그래서 나는 그 끝 점이 될 것이고 HOST 머신의 repo A에서만 'git pull'을 실행할 dockerized service B를 만들고 싶지 않습니다 (중요 : 명령 'git pull'은 B 환경이 아니라 B 환경에서 실행되어야합니다. B 내부에서 새 컨테이너 A를 실행할 수 없습니다 ...)