도커 컨테이너에서 호스트에서 셸 스크립트를 실행하는 방법은 무엇입니까?
도커 컨테이너에서 호스트를 제어하는 방법?
예를 들어 호스트 bash 스크립트에 복사된 것을 실행하는 방법은 무엇입니까?
이 대답은 브래드포드 메데이로스의 해결책을 좀 더 상세하게 설명한 것일 뿐인데, 저 역시 최선의 답으로 판명되었습니다. 그래서 그에게 공이 있습니다.
답변에서 그는 할 일(파이프라는 이름)에 대해 설명하지만 정확한 방법은 설명하지 않습니다.
저는 그의 해결책을 읽을 때 파이프라는 이름이 무엇인지 몰랐다는 것을 인정해야 합니다.그래서 구현하는 데 어려움을 겪었지만(실제로는 매우 간단하지만) 성공했습니다.그래서 제 대답의 요점은 실행하기 위해 필요한 명령을 상세히 설명하는 것입니다. 하지만 다시 한번 그에게 공이 있습니다.
1부 - 도커 없이 명명된 파이프 개념 테스트
기본 호스트에서 예를 들어 이름이 지정된 파이프 파일을 넣을 폴더를 선택합니다./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"라고 표시되어야 합니다.
2부 - 파이프를 통해 명령 실행
실행 대신 호스트 컨테이너에서tail -f
입력으로 전송되는 모든 것을 출력하는 명령어를 실행하여 명령어로 실행합니다.
eval "$(cat /path/to/pipe/mypipe)"
그런 다음 다른 터미널에서 다음을 실행해 봅니다.
echo "ls -l" > /path/to/pipe/mypipe
첫번째 터미널로 돌아가시면 결과를 보실 수 있을 것입니다.ls -l
지휘.
파트 3 - 영원히 들을 수 있게 해주세요
여러분은 아마 이전 부분에서, 그 직후에ls -l
출력이 표시되고 명령 수신이 중지됩니다.
대신에eval "$(cat /path/to/pipe/mypipe)"
:,:
while true; do eval "$(cat /path/to/pipe/mypipe)"; done
(당신은 그것을 할 수 없습니다)
이제 무제한의 명령어를 차례로 보낼 수 있게 되었고, 첫 번째 명령어뿐만 아니라 모두 실행됩니다.
Part 4 - 재부팅 시에도 작동 가능
유일한 주의 사항은 호스트를 재부팅해야 하는 경우 "where" 루프가 작동을 중지한다는 것입니다.
재부팅을 처리하기 위해 수행한 작업은 다음과 같습니다.
집어넣어요while true; do eval "$(cat /path/to/pipe/mypipe)"; done
라는 파일로execpipe.sh
와 함께#!/bin/bash
머리말을 쓰다
잊지말구요chmod +x
그것을
실행하여 크론탭에 추가
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)가 출력되어야 합니다.txt.
5부 - 도커와 함께 작동합니다.
저처럼 도커 작성과 도커 파일을 모두 사용하는 경우 다음과 같이 작업합니다.
마이파이프의 상위 폴더를 다음과 같이 마운트하고 싶다고 가정합니다./hostpipe
너의 컨테이너안에
추가할 내용:
VOLUME /hostpipe
마운트 지점을 만들기 위해 도커 파일에
다음을 추가합니다.
volumes:
- /path/to/pipe:/hostpipe
/path/to/pipe를 /hostpipe로 마운트하기 위해 도커 작성 파일에
도커 컨테이너를 다시 시작합니다.
파트 6 - 테스트
도커 컨테이너에 실행합니다.
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 explan하고 여기서는 https://github.com/docker/for-mac/issues/483 ) 작동하지 않습니다. 따라서 Linux에서 파이프에 기록한 내용은 Linux에서만 읽을 수 있고 t에 기록한 내용은 Linux에서 읽을 수 있습니다.Mac OS의 파이프는 Mac OS에서만 읽을 수 있습니다(이 문장은 매우 정확하지 않을 수 있지만 플랫폼 간 문제가 존재한다는 것을 알아야 합니다).
예를 들어 맥 OS 컴퓨터에서 DEV에서 도커 설정을 실행하면 위에서 설명한 대로 명명된 파이프가 작동하지 않습니다.하지만 저는 스테이징과 프로덕션 과정에서 리눅스 호스트와 리눅스 컨테이너를 보유하고 있으며 완벽하게 작동합니다.
파트 7 - 노드의 예.JS컨테이너
노드에서 명령을 보내는 방법은 다음과 같습니다.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);
명명된 파이프를 사용합니다.호스트 OS에서 명령어를 루프하고 읽을 스크립트를 만든 다음 호출합니다.eval
그 위에
도커 컨테이너를 저 명명된 파이프로 읽게 하세요.
파이프에 접근하려면 볼륨을 통해 파이프를 장착해야 합니다.
이는 SSH 메커니즘(또는 유사한 소켓 기반 방법)과 유사하지만 호스트 디바이스로 적절하게 제한하므로 더 나은 방법일 수 있습니다.또한 인증 정보를 전달할 필요가 없습니다.
제 유일한 경고는 당신이 왜 이런 짓을 하는지 조심하라는 것입니다.사용자 입력 등으로 자체 업그레이드할 수 있는 방법을 만들고 싶다면 완전히 해야 할 일이지만 명령을 호출하여 구성 데이터를 가져오는 것은 원하지 않을 것입니다. 적절한 방법은 ars/volume을 도커로 전달하는 것입니다.또한 평가한다는 사실에 주의해야 하므로 허가 모델에 대해 한 번 생각해 보십시오.
스크립트 실행과 같은 다른 답변도 있습니다.전체 시스템 리소스에 액세스할 수 없기 때문에 볼륨이 일반적으로 작동하지는 않지만 사용 용도에 따라 더 적합할 수 있습니다.
제가 사용하는 솔루션은 호스트를 통해 연결하는 것입니다.SSH
다음과 같이 명령을 실행합니다.
ssh -l ${USERNAME} ${HOSTNAME} "${SCRIPT}"
갱신하다
이 답변이 계속해서 표를 얻고 있기 때문에 스크립트를 호출하는 데 사용되는 계정은 권한이 전혀 없는 계정이어야 하며 스크립트를 다음과 같이 실행해야 한다는 점을 상기(그리고 적극 권장)하고 싶습니다.sudo
(이는 가능합니다.sudoers
일).
업데이트: 명명된 파이프
위에서 제안한 솔루션은 도커를 처음 접하는 동안 사용한 솔루션뿐이었습니다.이제 2021년에는 Named Pipes에 대해 이야기하는 답변을 살펴봅니다.이것이 더 나은 해결책인 것 같습니다.
하지만, 그곳에서 보안에 대해서는 아무도 언급하지 않았습니다.파이프를 통해 전송된 명령을 평가할 스크립트(호출하는 스크립트)eval
)을 실제로 사용해서는 안 됩니다.eval
전체 파이프 출력의 경우, 그러나 특정한 경우를 처리하고 전송된 텍스트에 따라 필요한 명령을 호출하려면, 그렇지 않으면 무엇이든 할 수 있는 모든 명령이 파이프를 통해 전송될 수 있습니다.
그것은 정말로 당신이 무엇을 하기 위해 필요로 하는지에 달려있습니다!
예를 들어, bash 스크립트가 출력을 일부 메아리친다면,
docker run --rm -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
다른 가능성은 bash 스크립트가 일부 소프트웨어(예: 스크립트가 도커 컴포지트 설치)를 설치하기를 원할 수 있습니다.당신은 다음과 같은 것을 할 수 있습니다.
docker run --rm -v /usr/bin:/usr/bin --privileged -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
하지만 이 시점에서 컨테이너 내부에서 호스트에 필요한 특정 권한을 허용하기 위해 스크립트가 무엇을 하고 있는지를 자세히 알아야 합니다.
나의 게으름 때문에 나는 여기서 답으로 출판되지 않은 가장 쉬운 해결책을 찾게 되었습니다.
그것은 luc juggery의 위대한 기사를 바탕으로 한 것입니다.
도커 컨테이너 내에서 리눅스 호스트에 전체 셸을 가져오기 위해 필요한 작업은 다음과 같습니다.
docker run --privileged --pid=host -it alpine:3.8 \
nsenter -t 1 -m -u -n -i sh
설명:
--priviled : 컨테이너에 추가 권한을 부여하며, 컨테이너가 호스트(/dev)의 장치에 액세스할 수 있도록 합니다.
--pid=host : 컨테이너가 Docker 호스트(Docker 데몬이 실행 중인 VM)의 프로세스 트리를 사용할 수 있습니다. nsenter 유틸리티: 기존 네임스페이스(컨테이너에 대한 격리를 제공하는 빌딩 블록)에서 프로세스를 실행할 수 있습니다.
nsenter(-t 1 -m -u -n -ish)를 사용하면 PID가 1인 프로세스와 동일한 분리 컨텍스트에서 프로세스 sh를 실행할 수 있습니다.그러면 전체 명령이 VM에 대화형 shshell을 제공합니다.
이 설정은 보안에 중요한 영향을 미치므로 주의사항과 함께 사용해야 합니다(있는 경우).
포트에서 듣기 중인 단순 서버 python 서버를 작성하고(예: 8080), 포트 -p 8080:8080을 컨테이너와 바인딩하고, popen으로 셸 스크립트를 실행하는 python 서버에 요청하기 위해 localhost:8080에 HTTP 요청을 하고, 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()
보안에 대해 걱정하지 않고 OP와 같은 다른 도커 컨테이너 내에서 호스트에서 도커 컨테이너를 시작하려는 경우 호스트에서 실행 중인 도커 서버를 리슨 소켓을 공유하여 도커 컨테이너와 공유할 수 있습니다.
https://docs.docker.com/engine/security/security/ # docker-daemon-attack-surface를 참조하여 이 특정 애플리케이션에 대해 개인적인 위험 허용 범위가 허용되는지 확인하십시오.
시작 명령에 다음 volume args를 추가하여 이 작업을 수행할 수 있습니다.
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
또는 /var/run/ docker를 공유합니다.도커 컴포지트 파일 내에서 다음과 같이 양말을 신습니다.
version: '3'
services:
ci:
command: ...
image: ...
volumes:
- /var/run/docker.sock:/var/run/docker.sock
도커 컨테이너 내에서 도커 시작 명령을 실행하면 호스트에서 실행 중인 도커 서버가 요청을 보고 형제 컨테이너를 프로비저닝합니다.
크레딧: http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
저는 이름붙인 파이프를 이용한 답이 대단하다고 생각했습니다.그런데 실행된 명령어의 출력을 얻을 수 있는 방법이 있는지 궁금합니다.
솔루션은 다음과 같은 두 개의 명명된 파이프를 생성하는 것입니다.
mkfifo /path/to/pipe/exec_in
mkfifo /path/to/pipe/exec_out
그러면 @Vincent가 제안하는 루프를 사용하는 솔루션은 다음과 같습니다.
# on the host
while true; do eval "$(cat exec_in)" > exec_out; done
그런 다음 도커 컨테이너에서 명령을 실행하고 다음을 사용하여 출력을 얻을 수 있습니다.
# on the container
echo "ls -l" > /path/to/pipe/exec_in
cat /path/to/pipe/exec_out
관심 있는 사람이 있다면 컨테이너에서 호스트의 페일오버 IP를 사용하는 것이 필요했습니다. 간단한 루비 방법을 만들었습니다.
def fifo_exec(cmd)
exec_in = '/path/to/pipe/exec_in'
exec_out = '/path/to/pipe/exec_out'
%x[ echo #{cmd} > #{exec_in} ]
%x[ cat #{exec_out} ]
end
# example
fifo_exec "curl https://ip4.seeip.org"
마커스가 상기하듯이, 도커는 기본적으로 프로세스 격리입니다.도커 1.8부터는 호스트와 컨테이너 사이에서 파일을 복사할 수 있습니다. 문서 참조docker cp
https://docs.docker.com/reference/commandline/cp/
파일을 복사하면 로컬에서 실행할 수 있습니다.
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단계: 용기 안에서 아래와 같이 실행합니다.여기서 핵심 부분은 (--네트워크 호스트는 호스트 컨텍스트에서 실행됩니다.)
도커 실행 -i --rm --network host -v /opt/test.sh :/test.sh alpine:3.7sh /test.sh
test.sh 에는 필요한 모든 명령(ifconfig, netstat 등)이 포함되어 있어야 합니다.이제 호스트 컨텍스트 출력을 얻을 수 있습니다.
파이프 개념을 사용할 수 있지만 호스트 및 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
시나리오에서는 컨테이너 내의 호스트(host ip를 통해)에 SSH 로그인만 하면 호스트 시스템에 원하는 작업을 수행할 수 있습니다.
상황에 따라 도움이 되는 자료가 될 수 있습니다.
호스트에서 실행할 수 있는 작업 대기열(Celery)을 사용합니다. Redis(또는 rabbitmq)를 통해 명령/데이터가 전달될 수 있습니다.아래 예제에서 이는 django 애플리케이션(일반적으로 도커화됨)에서 발생합니다.
https://www.codingforentrepreneurs.com/blog/celery-redis-django/
격리의 개념은 애플리케이션/프로세스/컨테이너가 호스트 시스템에 대해 수행할 수 있는 작업을 매우 명확하게 제한할 수 있도록 하는 것입니다.따라서 파일을 복사하고 실행할 수 있다면 전체 개념이 깨질 것입니다.
네, 하지만 필요할 때도 있어요
아뇨, 그렇지도 않고, 도커는 사용하기에 적합하지도 않습니다.수행할 작업은 수행할 작업(예: 호스트 구성 업데이트)에 대한 명확한 인터페이스를 선언하고, 이 작업을 수행하기 위한 최소한의 클라이언트/서버를 작성하는 것입니다.그러나 일반적으로 이것은 별로 바람직하지 않은 것 같습니다.많은 경우, 단순히 자신의 접근 방식을 다시 생각해보고 그러한 요구를 제거해야 합니다.도커는 기본적으로 모든 것이 어떤 프로토콜을 사용하여 도달할 수 있는 서비스였을 때 존재하게 되었습니다.도커 컨테이너가 호스트에서 임의의 것을 실행할 수 있는 권한을 얻는 적절한 사용 사례는 생각나지 않습니다.
언급URL : https://stackoverflow.com/questions/32163955/how-to-run-shell-script-on-host-from-docker-container
'programing' 카테고리의 다른 글
MS SQL 스크립트를 Mysql 및 Oracle로 변환 (0) | 2023.10.17 |
---|---|
C 프로그래밍 언어용 UML (0) | 2023.10.17 |
카르마를 사용한 각도 테스트: "모듈이 정의되지 않았습니다." (0) | 2023.10.17 |
스프링 부트에서 application.properties의 사용자 홈 경로 가져오기 (0) | 2023.10.17 |
URL 인코딩 데이터가 포함된 Spring RestTemplate POST 요청 (0) | 2023.10.12 |