Tại sao quá trình nền Python của tôi kết thúc khi phiên SSH kết thúc?


19

Tôi có một tập lệnh bash khởi động tập lệnh python3 (hãy gọi nó startup.sh), với dòng khóa:

nohup python3 -u <script> &

Khi tôi sshtrực tiếp và gọi tập lệnh này, tập lệnh python tiếp tục chạy trong nền sau khi tôi thoát. Tuy nhiên, khi tôi chạy nó:

ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"

Quá trình kết thúc ngay sau khi sshchạy xong và đóng phiên.

Sự khác biệt giữa hai là gì?

EDIT: Kịch bản python đang chạy một dịch vụ web thông qua Chai.

EDIT2: Tôi cũng đã thử tạo một tập lệnh init gọi startup.shvà chạy ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>", nhưng có hành vi tương tự.

EDIT3: Có lẽ đó là một cái gì đó khác trong kịch bản. Đây là phần lớn của tập lệnh:

chmod 700 ${key_loc}

echo "INFO: Syncing files."
rsync -azP -e "ssh -i ${key_loc} -o StrictHostKeyChecking=no" ${source_client_loc} ${remote_user}@${remote_hostname}:${destination_client_loc}

echo "INFO: Running startup script."
ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart"

EDIT4: Khi tôi chạy dòng cuối cùng với một giấc ngủ ở cuối:

ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart; sleep 1"

echo "Finished"

Nó không bao giờ đến được echo "Finished"và tôi thấy thông báo máy chủ Chai mà tôi chưa từng thấy trước đây:

Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.

Tôi thấy "Đã hoàn thành" nếu tôi tự thủ công SSH và tự hủy quá trình.

EDIT5: Sử dụng EDIT4, nếu tôi yêu cầu bất kỳ điểm cuối nào, tôi sẽ nhận lại được một trang, nhưng lỗi Chai:

Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.


----------------------------------------
Exception happened during processing of request from ('<IP>', 55104)

Có cách nào chúng ta có thể nhận được nhiều hơn về một mô tả về kịch bản python không? Có lẽ bạn vẫn chỉ đoán được mà không có mã nguồn đầy đủ, nhưng biết thêm về kịch bản python có thể giúp chúng ta đưa ra những phỏng đoán được giáo dục tốt hơn.
Bratchley

Đúng - thêm vào câu hỏi.
neverendingqs

Kịch bản có thể đang làm một cái gì đó sớm mà bằng cách nào đó phụ thuộc vào thiết bị đầu cuối đính kèm hoặc một cái gì đó tương tự và nó có thể là một vấn đề thời gian: nếu phiên kéo dài trong vài giây đầu tiên thì nó hoạt động, nếu không thì không. Tùy chọn tốt nhất của bạn có thể là chạy nó stracenếu bạn đang sử dụng Linux hoặc trussnếu bạn đang chạy Solaris và xem cách thức / lý do tại sao nó chấm dứt. Ví dụ như ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> strace -fo /tmp/debug ./startup.sh.
Celada

Bạn đã thử sử dụng &phần cuối của kịch bản khởi động chưa? Việc thêm &sẽ lấy đi sự phụ thuộc của phiên ssh của bạn từ id của cha mẹ (khi id cha mẹ chết thì con cái của họ cũng vậy). Ngoài ra tôi nghĩ rằng đây là một câu hỏi trùng lặp dựa trên này bài trước. Các bài tôi gửi đến bạn trong câu trước là bản sao của này bài mà có thể cung cấp chi tiết tốt hơn.
Jacob Bryan

Tôi đã thử nohup ./startup.sh &trước đây, nhưng nó có hành vi tương tự. startup.shđã chứa một ngã ba rồi ( nohup python3 -u <script> &), vì vậy tôi khá chắc chắn rằng mình không cần phải rẽ nhánh nữa.
neverendingqs

Câu trả lời:


11

Tôi sẽ ngắt kết nối lệnh khỏi luồng đầu vào / đầu ra tiêu chuẩn và luồng lỗi:

nohup python3 -u <script> </dev/null >/dev/null 2>&1 &  

sshcần một chỉ báo không có thêm đầu ra và nó không yêu cầu thêm đầu vào. Có một cái gì đó khác là đầu vào và chuyển hướng đầu ra có nghĩa là sshcó thể thoát một cách an toàn, vì đầu vào / đầu ra không đến hoặc đi đến thiết bị đầu cuối. Điều này có nghĩa là đầu vào phải đến từ một nơi khác và đầu ra (cả STDOUT và STDERR) sẽ đi đến một nơi khác.

Phần </dev/nullchỉ định /dev/nulllàm đầu vào cho <script>. Tại sao điều đó hữu ích ở đây:

Chuyển hướng / dev / null sang stdin sẽ cung cấp EOF ngay lập tức cho bất kỳ cuộc gọi đọc nào từ quá trình đó. Điều này thường hữu ích để tách một quá trình khỏi một tty (quá trình này được gọi là daemon). Ví dụ: khi bắt đầu một quá trình nền từ xa qua ssh, bạn phải chuyển hướng stdin để ngăn quá trình chờ đầu vào cục bộ. /programming/19955260/what-is-dev-null-in-bash/19955475#19955475

Ngoài ra, chuyển hướng từ một nguồn đầu vào khác phải tương đối an toàn miễn là sshphiên hiện tại không cần phải mở.

Với >/dev/nullphần vỏ chuyển hướng đầu ra tiêu chuẩn thành / dev / null về cơ bản loại bỏ nó. >/path/to/filecũng sẽ làm việc

Phần cuối cùng 2>&1là chuyển hướng STDERR sang STDOUT.

Có ba nguồn đầu vào và đầu ra tiêu chuẩn cho một chương trình. Đầu vào tiêu chuẩn thường xuất phát từ bàn phím nếu đó là chương trình tương tác hoặc từ chương trình khác nếu nó xử lý đầu ra của chương trình khác. Chương trình thường in ra đầu ra tiêu chuẩn, và đôi khi in lỗi tiêu chuẩn. Ba mô tả tập tin này (bạn có thể nghĩ về chúng như các đường ống dữ liệu của Google) thường được gọi là STDIN, STDOUT và STDERR.

Đôi khi chúng không được đặt tên, chúng được đánh số! Các số được tích hợp sẵn cho chúng là 0, 1 và 2, theo thứ tự đó. Theo mặc định, nếu bạn không đặt tên hoặc số một cách rõ ràng, bạn đang nói về STDOUT.

Dựa vào bối cảnh đó, bạn có thể thấy lệnh ở trên đang chuyển hướng đầu ra tiêu chuẩn thành / dev / null, đây là nơi bạn có thể đổ bất cứ thứ gì bạn không muốn (thường được gọi là bit-xô), sau đó chuyển hướng lỗi tiêu chuẩn thành đầu ra tiêu chuẩn ( bạn phải đặt một & trước điểm đến khi bạn làm điều này).

Do đó, lời giải thích ngắn gọn là tất cả đầu ra từ lệnh này sẽ bị đẩy vào một lỗ đen. Đó là một cách tốt để làm cho một chương trình thực sự yên tĩnh!
> / Dev / null 2> & 1 nghĩa là gì? | Xaprb


nohup python3 -u <script> >/dev/null 2>&1 &nohup python3 -u <script> > nohup.out 2>&1 &đã làm việc. Tôi nghĩ rằng nohup tự động chuyển hướng tất cả đầu ra - sự khác biệt là gì?
neverendingqs

@neverendingqs, nohupbạn có phiên bản nào trên máy chủ từ xa? POSIX nohupkhông bắt buộc phải chuyển hướng stdin, điều mà tôi đã bỏ lỡ, nhưng nó vẫn nên chuyển hướng stdoutstderr.
Graeme

Hình như tôi đang làm việc với nohup (GNU coreutils) 8.21.
neverendingqs

@neverendingqs, có nohupin bất kỳ tin nhắn nào nohup: ignoring input and appending output to ‘nohup.out’không?
Graeme

Vâng - đó là thông điệp chính xác.
neverendingqs

3

Nhìn vào man ssh:

 ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
     [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L [bind_address:]port:host:hostport]
     [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
     [-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
     [user@]hostname [command]

Khi bạn chạy, ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"bạn đang chạy shell script startup.sh dưới dạng lệnh ssh.

Từ mô tả:

Nếu lệnh được chỉ định, nó được thực thi trên máy chủ từ xa thay vì vỏ đăng nhập.

Dựa trên điều này, nó sẽ được chạy tập lệnh từ xa.

Sự khác biệt giữa điều đó và chạy nohup python3 -u <script> &trong thiết bị đầu cuối cục bộ của bạn là điều này chạy như một quá trình nền cục bộ trong khi lệnh ssh cố gắng chạy nó như một quá trình nền từ xa.

Nếu bạn có ý định chạy tập lệnh cục bộ thì đừng chạy startup.sh như một phần của lệnh ssh. Bạn có thể thử một cái gì đó nhưssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"

Nếu ý định của bạn là chạy tập lệnh từ xa và bạn muốn quá trình này tiếp tục sau khi phiên ssh của bạn kết thúc, trước tiên bạn phải bắt đầu một screenphiên trên máy chủ từ xa. Sau đó, bạn phải chạy tập lệnh python trong màn hình và nó sẽ tiếp tục chạy sau khi bạn kết thúc phiên ssh của mình.

Xem Hướng dẫn sử dụng màn hình

Mặc dù tôi nghĩ màn hình là lựa chọn tốt nhất của bạn, nhưng nếu bạn phải sử dụng nohup, hãy xem xét cài đặt shopt -s huponexittrên máy chủ từ xa trước khi chạy lệnh nohup. Ngoài ra, bạn có thể sử dụng disown -h [jobID]để đánh dấu quá trình để SIGHUP sẽ không được gửi đến nó. 1

Làm cách nào để tiếp tục chạy công việc sau khi tôi thoát khỏi dấu nhắc shell trong nền?

Tín hiệu SIGHUP (Hangup) được sử dụng bởi hệ thống của bạn khi điều khiển thiết bị đầu cuối hoặc chết trong quá trình kiểm soát. Bạn có thể sử dụng SIGHUP để tải lại các tệp cấu hình và mở / đóng tệp nhật ký. Nói cách khác, nếu bạn đăng xuất khỏi thiết bị đầu cuối của mình, tất cả các công việc đang chạy sẽ bị chấm dứt. Để tránh điều này, bạn có thể chuyển tùy chọn -h để từ chối lệnh. Tùy chọn này đánh dấu mỗi ID công việc để SIGHUP không được gửi đến công việc nếu shell nhận được SIGHUP.

Ngoài ra, hãy xem tóm tắt về cách thức huponexithoạt động khi vỏ được thoát ra, bị giết hoặc rơi. Tôi đoán vấn đề hiện tại của bạn có liên quan đến cách phiên shell kết thúc. 2

  1. Tất cả các tiến trình con, có nền hoặc không có vỏ được mở qua kết nối ssh đều bị hủy bằng SIGHUP khi kết nối ssh chỉ bị đóng nếu tùy chọn huponexit được đặt: chạy shopt huponexit để xem điều này có đúng không.

  2. Nếu huponexit là đúng, thì bạn có thể sử dụng nohup hoặc từ chối để phân tách quá trình khỏi vỏ để nó không bị giết khi bạn thoát. Hoặc, chạy mọi thứ với màn hình.

  3. Nếu huponexit là false, đó là mặc định trên ít nhất một số linux hiện nay, thì các công việc nền sẽ không bị hủy khi đăng xuất bình thường.

  4. Nhưng ngay cả khi huponexit là sai, thì nếu kết nối ssh bị hủy hoặc bị rớt (khác với đăng xuất bình thường), thì các quy trình nền vẫn sẽ bị giết. Điều này có thể tránh được bằng cách từ chối hoặc nohup như trong (2).

Cuối cùng, đây là một số ví dụ về cách sử dụng shopt huponexit. 3

$ shopt -s huponexit; shopt | grep huponexit
huponexit       on
# Background jobs will be terminated with SIGHUP when shell exits

$ shopt -u huponexit; shopt | grep huponexit
huponexit       off
# Background jobs will NOT be terminated with SIGHUP when shell exits

Theo bashtrang man, huponexitchỉ nên ảnh hưởng đến các shell tương tác chứ không ảnh hưởng đến tập lệnh - 'Nếu tùy chọn shell huponexit đã được đặt với shopt, bash sẽ gửi SIGHUP cho tất cả các công việc khi thoát vỏ đăng nhập tương tác.'
Graeme

2

Có lẽ đáng để thử -ntùy chọn khi bắt đầu một ssh? Nó sẽ ngăn chặn sự phụ thuộc quá trình từ xa vào một địa phương stdin, tất nhiên sẽ đóng lại ngay khi ssh sessionkết thúc. Và điều này sẽ gây ra sự chấm dứt giá từ xa bất cứ khi nào nó cố gắng truy cập vào nó stdin.


Đã thử nó không thành công = [.
neverendingqs

2

Tôi nghi ngờ bạn có một điều kiện chủng tộc. Nó sẽ đi một cái gì đó như thế này:

  • Kết nối SSH bắt đầu
  • SSH bắt đầu startup.sh
  • startup.sh bắt đầu một quá trình nền (nohup)
  • startup.sh kết thúc
  • ssh kết thúc và điều này giết chết các tiến trình con (tức là nohup)

Nếu ssh không cắt ngắn mọi thứ, điều sau đây sẽ xảy ra (không chắc về thứ tự của hai thứ này):

  • nohup bắt đầu kịch bản python của bạn
  • nohup ngắt kết nối từ tiến trình cha và thiết bị đầu cuối.

Vì vậy, hai bước quan trọng cuối cùng không xảy ra, bởi vì startup.sh và ssh kết thúc trước khi nohup có thời gian để làm việc đó.

Tôi hy vọng vấn đề của bạn sẽ biến mất nếu bạn đặt một vài giây ngủ vào cuối startup.sh. Tôi không chắc chính xác bạn cần bao nhiêu thời gian. Nếu điều quan trọng là giữ nó ở mức tối thiểu, thì có lẽ bạn có thể nhìn vào một cái gì đó trong Proc để xem khi nào nó an toàn.


Điểm hay, đừng nghĩ rằng cửa sổ cho việc này sẽ rất dài mặc dù - có lẽ chỉ vài mili giây. Bạn có thể kiểm tra /proc/$!/commkhông nohupsử dụng đầu ra của ps -o comm= $!.
Graeme

Điều đó sẽ làm việc cho đăng xuất bình thường, nhưng khi phiên bị hủy hoặc bị giết thì sao? Bạn vẫn không cần phải từ chối công việc sao cho nó hoàn toàn bị phớt lờ?
iyrin

@RyanLoremIpsum: Kịch bản khởi động chỉ cần đợi đủ lâu để quá trình con được tách ra hoàn toàn. Sau đó, không có vấn đề gì xảy ra với phiên ssh. Nếu một cái gì đó khác giết chết phiên ssh của bạn trong cửa sổ ngắn gọn trong khi điều đó xảy ra, thì bạn không thể làm gì nhiều về nó.
mc0e

@Graeme yeah, tôi đoán nó rất nhanh, nhưng tôi không biết đủ về chính xác những gì nohup làm để chắc chắn. Một con trỏ đến một nguồn có thẩm quyền (hoặc ít nhất là có kiến ​​thức và chi tiết) về điều này sẽ hữu ích.
mc0e

Làm thế nào về điều này - lingrok.org/xref/coreutils/src/nohup.c
Graeme

1

Điều này nghe có vẻ giống như một vấn đề với những gì pythonkịch bản hoặc pythonchính nó đang làm. Tất cả những gì nohupthực sự làm (thanh đơn giản hóa chuyển hướng) chỉ là đặt trình xử lý cho HUPtín hiệu thành SIG_IGN(bỏ qua) trước khi chạy chương trình. Không có gì để ngăn chương trình cài đặt lại SIG_DFLhoặc cài đặt trình xử lý riêng của nó một khi nó bắt đầu chạy.

Một điều mà bạn có thể muốn thử là đặt lệnh của bạn trong ngoặc đơn để bạn có được hiệu ứng fork đôi và pythontập lệnh của bạn không còn là con của quá trình shell. Ví dụ:

( nohup python3 -u <script> & )

Một điều khác cũng có thể đáng để thử (nếu bạn đang sử dụng bashchứ không phải vỏ khác) là sử dụng disownnội dung thay thế nohup. Nếu mọi thứ đang hoạt động như tài liệu thì điều này thực sự không có gì khác biệt, nhưng trong một vỏ tương tác, điều này sẽ ngăn HUPtín hiệu truyền đến pythontập lệnh của bạn . Bạn có thể thêm từ chối vào dòng tiếp theo hoặc tương tự như bên dưới (lưu ý thêm một ;sau một &lỗi là bash):

python3 -u <script> </dev/null &>/dev/null & disown

Nếu sự kết hợp ở trên hoặc một số kết hợp của nó không hoạt động thì chắc chắn nơi duy nhất để giải quyết vấn đề là trong pythonchính kịch bản.


Hiệu ứng nĩa đôi có đủ không (dựa trên câu trả lời của @ RyanLoremIpsum)?
neverendingqs

Cả hai đều không giải quyết được vấn đề = [. Nếu đó là vấn đề về Python, bạn có biết nên bắt đầu điều tra ở đâu không (không thể đăng quá nhiều tập lệnh Python ở đây)?
neverendingqs

@neverendingqs, nếu bạn muốn nói là huponexitcông cụ, việc chạy trong một mạng con sẽ có tác dụng tương tự disownnhư quy trình sẽ không được thêm vào danh sách công việc.
Graeme

@neverendingqs, cập nhật câu trả lời của tôi. Quên rằng bạn nên sử dụng chuyển hướng với disown. Đừng hy vọng rằng nó sẽ làm cho nhiều sự khác biệt mặc dù. Tôi nghĩ rằng tốt nhất bạn nên đặt cược là thay đổi pythontập lệnh để nó cho bạn biết lý do tại sao nó lại thoát.
Graeme

Chuyển hướng đầu ra hoạt động ( unix.stackexchange.com/a/176610/52894 ), nhưng tôi không chắc sự khác biệt giữa việc làm rõ ràng và nohuplàm điều đó là gì.
neverendingqs

0

Tôi nghĩ rằng đó là vì công việc gắn liền với phiên. Một khi kết thúc bất kỳ công việc người dùng cũng kết thúc.


2
Nhưng tại sao điều đó khác với việc nhận một thiết bị đầu cuối, gõ và chạy lệnh và thoát? Cả hai phiên đều đóng khi tôi đóng nó.
neverendingqs

Đồng ý, tôi muốn hiểu tại sao điều này không khác gì việc đóng thiết bị đầu cuối của riêng bạn một cách thủ công.
Avindra Goolcharan

0

Nếu nohupcó thể mở tập tin đầu ra của nó, bạn có thể có một đầu mối nohup.out. Có thể pythonkhông có trên đường dẫn khi bạn chạy tập lệnh thông qua ssh.

Tôi sẽ thử tạo một tệp nhật ký cho lệnh. Hãy thử sử dụng:

nohup /usr/bin/python3 -u <script> &>logfile &

Tôi sử dụng sshđể chạy tập lệnh theo cách thủ công, vì vậy tôi giả sử python3 đang ở trong đường dẫn.
neverendingqs

@neverendingqs Logfile có chứa gì không?
BillThor

Không có gì khác thường - khởi động có vẻ bình thường.
neverendingqs
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.