αž€αžΆαžšαžŠαžΆαž€αŸ‹αž–αž„αŸ’αžšαžΆαž™αž–αžŽαŸŒαžαŸ€αžœαž”αŸƒαžαž„αž“αŸ…αž”αŸ’αžšαžΆαž€αŸ‹αžˆαŸ’αž“αž½αž›αž’αž”αŸ’αž”αž”αžšαž˜αžΆ

αž“αŸ…αž€αŸ’αž“αž»αž„αž’αžαŸ’αžαž”αž‘αž“αŸαŸ‡αž™αžΎαž„αž”αŸ’αžšαžΎ bash, ssh, docker ΠΈ nginx αž™αžΎαž„β€‹αž“αžΉαž„β€‹αžšαŸ€αž”αž…αŸ†β€‹αž”αŸ’αž›αž„αŸ‹β€‹αž€αž˜αŸ’αž˜αžœαž·αž’αžΈβ€‹αž”αžŽαŸ’αžŠαžΆαž‰β€‹αžŠαŸ‚αž›β€‹αž‚αŸ’αž˜αžΆαž“β€‹αžαŸ’αž“αŸαžšαŸ” αž€αžΆαžšαžŠαžΆαž€αŸ‹αž–αž„αŸ’αžšαžΆαž™αž–αžŽαŸŒαžαŸ€αžœαž”αŸƒαžαž„ αž‚αžΊαž‡αžΆαž”αž…αŸ’αž…αŸαž€αž‘αŸαžŸαžŠαŸ‚αž›αž’αž“αž»αž‰αŸ’αž‰αžΆαžαž±αŸ’αž™αž’αŸ’αž“αž€αž’αŸ’αžœαžΎαž”αž…αŸ’αž…αž»αž”αŸ’αž”αž“αŸ’αž“αž—αžΆαž–αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαž—αŸ’αž›αžΆαž˜αŸ—αžŠαŸ„αž™αž˜αž·αž“αž”αžŠαž·αžŸαŸαž’αžŸαŸ†αžŽαžΎαžαŸ‚αž˜αž½αž™αŸ” αžœαžΆαž‚αžΊαž‡αžΆαž™αž»αž‘αŸ’αž’αžŸαžΆαžŸαŸ’αžšαŸ’αžαž˜αž½αž™αž€αŸ’αž“αž»αž„αž…αŸ†αž“αŸ„αž˜αž™αž»αž‘αŸ’αž’αžŸαžΆαžŸαŸ’αžαŸ’αžšαžŠαžΆαž€αŸ‹αž±αŸ’αž™αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž‚αŸ’αž˜αžΆαž“αž–αŸαž›αž‘αŸ†αž“αŸαžš αž αžΎαž™αžŸαŸαž€αŸ’αžαž·αžŸαž˜αž”αŸ†αž•αž»αžαžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαžŠαŸ‚αž›αž˜αžΆαž“αž§αž‘αžΆαž αžšαžŽαŸαž˜αž½αž™ αž”αŸ‰αž»αž“αŸ’αžαŸ‚αžŸαž˜αžαŸ’αžαž—αžΆαž–αž€αŸ’αž“αž»αž„αž€αžΆαžšαž•αŸ’αž‘αž»αž€αžœαžαŸ’αžαž»αž‘αžΈαž–αžΈαžšαžŠαŸ‚αž›αžαŸ’αžšαŸ€αž˜αžšαž½αž…αž‡αžΆαžŸαŸ’αžšαŸαž…αžŠαžΎαž˜αŸ’αž”αžΈαžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž“αŸ…αž€αŸ’αž”αŸ‚αžšαž“αŸ„αŸ‡αŸ”

αž…αžΌαžšαž“αž·αž™αžΆαž™αžαžΆαž’αŸ’αž“αž€αž˜αžΆαž“αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαž”αžŽαŸ’αžαžΆαž‰αžŠαŸ‚αž›αž’αžαž·αžαž·αž‡αž“αž‡αžΆαž…αŸ’αžšαžΎαž“αž€αŸ†αž–αž»αž„αž’αŸ’αžœαžΎαž€αžΆαžšαž™αŸ‰αžΆαž„αžŸαž€αž˜αŸ’αž˜ αž αžΎαž™αžœαžΆαž–αž·αžαž‡αžΆαž‚αŸ’αž˜αžΆαž“αž•αŸ’αž›αžΌαžœαžŸαž˜αŸ’αžšαžΆαž”αŸ‹αžœαžΆαžŠαŸαž€αž–αžΈαžšαž”αžΈαžœαž·αž“αžΆαž‘αžΈαž“αŸ„αŸ‡αž‘αŸαŸ” αž αžΎαž™β€‹αž’αŸ’αž“αž€β€‹αž–αž·αžβ€‹αž‡αžΆβ€‹αžαŸ’αžšαžΌαžœβ€‹αžŠαžΆαž€αŸ‹β€‹αž…αŸαž‰β€‹αž€αžΆαžšβ€‹αž’αžΆαž”αŸ‹αžŠαŸαžβ€‹αž”αžŽαŸ’αžŽαžΆαž›αŸαž™ αž€αžΆαžšβ€‹αž‡αž½αžŸαž‡αž»αž›β€‹αž”αž‰αŸ’αž αžΆ αž¬β€‹αž˜αž»αžαž„αžΆαžšβ€‹αž–αž·αžŸαŸαžŸβ€‹αžαŸ’αž˜αžΈαŸ” αž€αŸ’αž“αž»αž„αžŸαŸ’αžαžΆαž“αž—αžΆαž–αž’αž˜αŸ’αž˜αžαžΆ αž’αŸ’αž“αž€αž“αžΉαž„αžαŸ’αžšαžΌαžœαž”αž‰αŸ’αžˆαž”αŸ‹αž€αž˜αŸ’αž˜αžœαž·αž’αžΈ αž‡αŸ†αž“αž½αžŸαžœαžΆ αž αžΎαž™αž…αžΆαž”αŸ‹αž•αŸ’αžαžΎαž˜αžœαžΆαž˜αŸ’αžαž„αž‘αŸ€αžαŸ” αž€αŸ’αž“αž»αž„αž€αžšαžŽαžΈ docker αžŠαŸ†αž”αžΌαž„αž’αŸ’αž“αž€αž’αžΆαž…αž‡αŸ†αž“αž½αžŸαžœαžΆ αž”αž“αŸ’αž‘αžΆαž”αŸ‹αž˜αž€αž…αžΆαž”αŸ‹αž•αŸ’αžαžΎαž˜αžœαžΆαž‘αžΎαž„αžœαž·αž‰ αž”αŸ‰αž»αž“αŸ’αžαŸ‚αžœαžΆαž“αžΉαž„αž“αŸ…αžαŸ‚αž˜αžΆαž“αžšαž™αŸˆαž–αŸαž›αžŠαŸ‚αž›αžŸαŸ†αžŽαžΎαž‘αŸ…αž€αžΆαž“αŸ‹αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαž“αžΉαž„αž˜αž·αž“αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž‘αŸ αž–αŸ’αžšαŸ„αŸ‡αž‡αžΆαž’αž˜αŸ’αž˜αžαžΆαž€αž˜αŸ’αž˜αžœαž·αž’αžΈαžαŸ’αžšαžΌαžœαž…αŸ†αžŽαžΆαž™αž–αŸαž›αžαŸ’αž›αŸ‡αžŠαžΎαž˜αŸ’αž”αžΈαžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαžŠαŸ†αž”αžΌαž„αŸ” αž…αž»αŸ‡β€‹αž”αžΎβ€‹αžœαžΆβ€‹αž…αžΆαž”αŸ‹β€‹αž•αŸ’αžŠαžΎαž˜ αž”αŸ‰αž»αž“αŸ’αžαŸ‚β€‹αžœαžΆβ€‹αž˜αž·αž“β€‹αž’αžΆαž…β€‹αž”αŸ’αžšαžΎβ€‹αž”αžΆαž“? αž“αŸαŸ‡β€‹αž‡αžΆβ€‹αž”αž‰αŸ’αž αžΆ αžŸαžΌαž˜β€‹αžŠαŸ„αŸ‡αžŸαŸ’αžšαžΆαž™β€‹αžœαžΆβ€‹αžŠαŸ„αž™β€‹αž˜αž’αŸ’αž™αŸ„αž”αžΆαž™β€‹αžαž·αž…αžαž½αž…β€‹αž”αŸ†αž•αž»αž αž“αž·αž„β€‹αž†αžΎαžαž†αžΆαž™β€‹αžαžΆαž˜β€‹αžŠαŸ‚αž›β€‹αž’αžΆαž…β€‹αž’αŸ’αžœαžΎβ€‹αž‘αŸ…β€‹αž”αžΆαž“αŸ”

αž€αžΆαžšαž”αžŠαž·αžŸαŸαž’αŸ– αž’αžαŸ’αžαž”αž‘αž—αžΆαž‚αž…αŸ’αžšαžΎαž“αžαŸ’αžšαžΌαžœαž”αžΆαž“αž”αž„αŸ’αž αžΆαž‰αž‡αžΆαž‘αž˜αŸ’αžšαž„αŸ‹αž–αž·αžŸαŸ„αž’αž“αŸ - αž‡αžΆαž‘αž˜αŸ’αžšαž„αŸ‹αž“αŸƒαž€αžΆαžšαžαžαžŸαž˜αŸαž™αž€αž»αž„αžŸαžΌαž›αŸ” αžŸαž„αŸ’αžƒαžΉαž˜αžαžΆαž“αŸαŸ‡αž“αžΉαž„αž˜αž·αž“αž–αž·αž”αžΆαž€αž™αž›αŸ‹αž–αŸαž€αž‘αŸ αž αžΎαž™αž›αŸαžαž€αžΌαžŠαž“αžΉαž„αž…αž„αž€αŸ’αžšαž„αžŠαŸ„αž™αžαŸ’αž›αž½αž“αžœαžΆαž‚αŸ’αžšαž”αŸ‹αž‚αŸ’αžšαžΆαž“αŸ‹αŸ” αžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž”αžšαž·αž™αžΆαž€αžΆαžŸ αžŸαžΌαž˜αžŸαŸ’αžšαž˜αŸƒαžαžΆ αž‘αžΆαŸ†αž„αž“αŸαŸ‡αž˜αž·αž“αž˜αŸ‚αž“αž‚αŸ’αžšαžΆαž“αŸ‹αžαŸ‚αž‡αžΆαž’αžαŸ’αžαž”αž‘αž€αžΌαžŠαž”αŸ‰αž»αžŽαŸ’αžŽαŸ„αŸ‡αž‘αŸ αž”αŸ‰αž»αž“αŸ’αžαŸ‚αž‡αžΆαž€αŸ’αžšαžŠαžΆαžŸαž–αžΈαž‘αžΌαžšαž›αŸαž "αžŠαŸ‚αž€"αŸ”

αž€αžΆαžšαžŠαžΆαž€αŸ‹αž–αž„αŸ’αžšαžΆαž™αž–αžŽαŸŒαžαŸ€αžœαž”αŸƒαžαž„αž“αŸ…αž”αŸ’αžšαžΆαž€αŸ‹αžˆαŸ’αž“αž½αž›αž’αž”αŸ’αž”αž”αžšαž˜αžΆ

αž”αž…αŸ’αž…αŸαž€αž‘αŸαžŸαž‚αž½αžšαž±αŸ’αž™αž…αžΆαž”αŸ‹αž’αžΆαžšαž˜αŸ’αž˜αžŽαŸαžŠαŸ‚αž›αž–αž·αž”αžΆαž€αžŸαž˜αŸ’αžšαžΆαž”αŸ‹ Google αžŠαŸ„αž™αž‚αŸ’αžšαžΆαž“αŸ‹αžαŸ‚αž’αžΆαž“αž€αžΌαžŠαžαŸ’αžšαžΌαžœαž”αžΆαž“αž–αž·αž–αžŽαŸŒαž“αžΆαž“αŸ…αžŠαžΎαž˜αž•αŸ’αž“αŸ‚αž€αž“αžΈαž˜αž½αž™αŸ—αŸ” αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž˜αžΆαž“αž’αŸ’αžœαžΈαž•αŸ’αžŸαŸαž„αž‘αŸ€αžαž˜αž·αž“αž…αŸ’αž”αžΆαžŸαŸ‹αž›αžΆαžŸαŸ‹, google αžœαžΆαž αžΎαž™αž–αž·αž“αž·αžαŸ’αž™αž˜αžΎαž›αžœαžΆαž…αŸαž‰αŸ” αž–αž“αŸ’αž™αž›αŸ‹αžŸαŸ‚αž› (αž‡αžΆαžŸαŸ†αžŽαžΆαž„αž›αŸ’αž’ αžœαžΆαžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž˜αŸ’αžαž„αž‘αŸ€αž αžŠαŸ„αž™αžŸαžΆαžšαžαŸ‚αž€αžΆαžšαž”αž·αž‘αž‘αžΌαžšαž›αŸαž)αŸ” αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž’αŸ’αž“αž€αž˜αž·αž“αž’αžΆαž… Google αž’αŸ’αžœαžΈαž”αžΆαž“αž‘αŸ αžŸαžΌαž˜αžŸαž½αžšαž“αŸ…αž€αŸ’αž“αž»αž„αž˜αžαž·αž™αŸ„αž”αž›αŸ‹αŸ” αžαŸ’αž‰αž»αŸ†αž“αžΉαž„αžšαžΈαž€αžšαžΆαž™αž€αŸ’αž“αž»αž„αž€αžΆαžšαž”αž“αŸ’αžαŸ‚αž˜αž‘αŸ…αž•αŸ’αž“αŸ‚αž€αžŠαŸ‚αž›αžαŸ’αžšαžΌαžœαž‚αŸ’αž“αžΆ "αž”αž…αŸ’αž…αŸαž€αž‘αŸαžŸαž‚αž½αžšαž±αŸ’αž™αž…αžΆαž”αŸ‹αž’αžΆαžšαž˜αŸ’αž˜αžŽαŸ" αŸ”

αžαŸ„αŸ‡αž…αžΆαž”αŸ‹αž•αŸ’αžαžΎαž˜αŸ”

$ mkdir blue-green-deployment && cd $_

RЎRΞΌSΠ‚RΠ†RΡ‘SΠƒ

αž…αžΌαžšαž™αžΎαž„αž”αž„αŸ’αž€αžΎαžαžŸαŸαžœαžΆαž€αž˜αŸ’αž˜αž–αž·αžŸαŸ„αž’αž“αŸ αž αžΎαž™αžŠαžΆαž€αŸ‹αžœαžΆαž“αŸ…αž€αŸ’αž“αž»αž„αž’αž»αž„αž˜αž½αž™αŸ”

αž”αž…αŸ’αž…αŸαž€αž‘αŸαžŸαž‚αž½αžšαž±αŸ’αž™αž…αžΆαž”αŸ‹αž’αžΆαžšαž˜αŸ’αž˜αžŽαŸ

  • cat << EOF > file-name (αž―αž€αžŸαžΆαžšαž“αŸ…αž‘αžΈαž“αŸαŸ‡ + αž€αžΆαžšαž”αž‰αŸ’αž‡αžΌαž“αž”αž“αŸ’αž I/O) αž‚αžΊαž‡αžΆαžœαž·αž’αžΈαž˜αž½αž™αžŠαžΎαž˜αŸ’αž”αžΈαž”αž„αŸ’αž€αžΎαžαž―αž€αžŸαžΆαžšαž–αž αž»αž”αž“αŸ’αž‘αžΆαžαŸ‹αžŠαŸ„αž™αž”αŸ’αžšαžΎαž–αžΆαž€αŸ’αž™αž”αž‰αŸ’αž‡αžΆαž˜αž½αž™αŸ” αž’αŸ’αžœαžΈαž‚αŸ’αžšαž”αŸ‹αž™αŸ‰αžΆαž„αžŠαŸ‚αž› bash αž’αžΆαž“αž–αžΈ /dev/stdin αž”αž“αŸ’αž‘αžΆαž”αŸ‹αž–αžΈαž”αž“αŸ’αž‘αžΆαžαŸ‹αž“αŸαŸ‡ αž“αž·αž„αž˜αž»αž“αž”αž“αŸ’αž‘αžΆαžαŸ‹ EOF αž“αžΉαž„αžαŸ’αžšαžΌαžœαž”αžΆαž“αž€αžαŸ‹αžαŸ’αžšαžΆαž“αŸ…αž€αŸ’αž“αž»αž„ file-name.
  • wget -qO- URL (αž–αž“αŸ’αž™αž›αŸ‹αžŸαŸ‚αž›) β€” αž”αž‰αŸ’αž…αŸαž‰αž―αž€αžŸαžΆαžšαžŠαŸ‚αž›αž”αžΆαž“αž‘αž‘αž½αž›αžαžΆαž˜αžšαž™αŸˆ HTTP αž‘αŸ… /dev/stdout (αž’αžΆαžŽαžΆαž‘αžΌαž€ curl URL).

αž”αŸ„αŸ‡αž–αž»αž˜αŸ’αž–

αž‡αžΆαž–αž·αžŸαŸαžŸ αžαŸ’αž‰αž»αŸ†αž‘αž˜αŸ’αž›αžΆαž™αž–αŸαžαŸŒαž˜αžΆαž“αžŸαž„αŸ’αžαŸαž” αžŠαžΎαž˜αŸ’αž”αžΈαž”αžΎαž€αž€αžΆαžšαž”αž“αŸ’αž›αž·αž…αžŸαž˜αŸ’αžšαžΆαž”αŸ‹ Python αŸ” αž“αŸ…αž…αž»αž„αž”αž‰αŸ’αž…αž”αŸ‹αž“αžΉαž„αž˜αžΆαž“αž”αŸ†αžŽαŸ‚αž€αž˜αž½αž™αž‘αŸ€αžαžŠαžΌαž…αž“αŸαŸ‡αŸ” αž–αž·αž…αžΆαžšαžŽαžΆαžαžΆαž“αŸ…αž€αž“αŸ’αž›αŸ‚αž„αž‘αžΆαŸ†αž„αž“αŸαŸ‡ αž€αŸ’αžšαžŠαžΆαžŸαžαŸ’αžšαžΌαžœαž”αžΆαž“αž€αžΆαžαŸ‹αžŠαžΎαž˜αŸ’αž”αžΈαž•αŸ’αž‰αžΎαž‘αŸ…αž•αŸ’αž“αŸ‚αž€αž”αž“αŸ’αž›αž·αž… (αžŠαŸ‚αž›αž›αŸαžαž€αžΌαžŠαžαŸ’αžšαžΌαžœαž”αžΆαž“αž›αžΆαž”αž–αžŽαŸŒαžŠαŸ„αž™αžŠαŸƒαž‡αžΆαž˜αž½αž™αž§αž”αž€αžšαžŽαŸαž”αž“αŸ’αž›αž·αž…) αž αžΎαž™αž”αž“αŸ’αž‘αžΆαž”αŸ‹αž˜αž€αž”αŸ†αžŽαŸ‚αž€αž‘αžΆαŸ†αž„αž“αŸαŸ‡αžαŸ’αžšαžΌαžœαž”αžΆαž“αžŸαŸ’αž’αž·αžαž‡αžΆαž”αŸ‹αŸ”

$ cat << EOF > uptimer.py
from http.server import BaseHTTPRequestHandler, HTTPServer
from time import monotonic

app_version = 1
app_name = f'Uptimer v{app_version}.0'
loading_seconds = 15 - app_version * 5

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            try:
                t = monotonic() - server_start
                if t < loading_seconds:
                    self.send_error(503)
                else:
                    self.send_response(200)
                    self.send_header('Content-Type', 'text/html')
                    self.end_headers()
                    response = f'<h2>{app_name} is running for {t:3.1f} seconds.</h2>n'
                    self.wfile.write(response.encode('utf-8'))
            except Exception:
                self.send_error(500)
        else:
            self.send_error(404)

httpd = HTTPServer(('', 8080), Handler)
server_start = monotonic()
print(f'{app_name} (loads in {loading_seconds} sec.) started.')
httpd.serve_forever()
EOF

$ cat << EOF > Dockerfile
FROM python:alpine
EXPOSE 8080
COPY uptimer.py app.py
CMD [ "python", "-u", "./app.py" ]
EOF

$ docker build --tag uptimer .
Sending build context to Docker daemon  39.42kB
Step 1/4 : FROM python:alpine
 ---> 8ecf5a48c789
Step 2/4 : EXPOSE 8080
 ---> Using cache
 ---> cf92d174c9d3
Step 3/4 : COPY uptimer.py app.py
 ---> a7fbb33d6b7e
Step 4/4 : CMD [ "python", "-u", "./app.py" ]
 ---> Running in 1906b4bd9fdf
Removing intermediate container 1906b4bd9fdf
 ---> c1655b996fe8
Successfully built c1655b996fe8
Successfully tagged uptimer:latest

$ docker run --rm --detach --name uptimer --publish 8080:8080 uptimer
8f88c944b8bf78974a5727070a94c76aa0b9bb2b3ecf6324b784e782614b2fbf

$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                    NAMES
8f88c944b8bf        uptimer             "python -u ./app.py"   3 seconds ago       Up 5 seconds        0.0.0.0:8080->8080/tcp   uptimer

$ docker logs uptimer
Uptimer v1.0 (loads in 10 sec.) started.

$ wget -qSO- http://localhost:8080
  HTTP/1.0 503 Service Unavailable
  Server: BaseHTTP/0.6 Python/3.8.3
  Date: Sat, 22 Aug 2020 19:52:40 GMT
  Connection: close
  Content-Type: text/html;charset=utf-8
  Content-Length: 484

$ wget -qSO- http://localhost:8080
  HTTP/1.0 200 OK
  Server: BaseHTTP/0.6 Python/3.8.3
  Date: Sat, 22 Aug 2020 19:52:45 GMT
  Content-Type: text/html
<h2>Uptimer v1.0 is running for 15.4 seconds.</h2>

$ docker rm --force uptimer
uptimer

αž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸ

αžŠαžΎαž˜αŸ’αž”αžΈαž±αŸ’αž™αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαžšαž”αžŸαŸ‹αž™αžΎαž„αž’αžΆαž…αž•αŸ’αž›αžΆαžŸαŸ‹αž”αŸ’αžαžΌαžšαžŠαŸ„αž™αž˜αž·αž“αž˜αžΆαž“αž“αžšαžŽαžΆαž€αžαŸ‹αžŸαž˜αŸ’αž‚αžΆαž›αŸ‹ αžœαžΆαž…αžΆαŸ†αž”αžΆαž…αŸ‹αžαžΆαž˜αžΆαž“αž’αž„αŸ’αž‚αž—αžΆαž–αž•αŸ’αžŸαŸαž„αž‘αŸ€αžαž“αŸ…αž–αžΈαž˜αž»αžαžœαžΆαžŠαŸ‚αž›αž“αžΉαž„αž›αžΆαž€αŸ‹αž€αžΆαžšαž‡αŸ†αž“αž½αžŸαžšαž”αžŸαŸ‹αžœαžΆαŸ” αžœαžΆαž’αžΆαž…αž‡αžΆ web server nginx Π² αžšαž”αŸ€αž”αž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸ. αž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸαžαŸ’αžšαžΌαžœαž”αžΆαž“αž”αž„αŸ’αž€αžΎαžαž‘αžΎαž„αžšαžœαžΆαž„αž’αžαž·αžαž·αž‡αž“ αž“αž·αž„αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαŸ” αžœαžΆαž‘αž‘αž½αž›αž™αž€αžŸαŸ†αžŽαžΎαž–αžΈαž’αžαž·αžαž·αž‡αž“ αž“αž·αž„αž”αž‰αŸ’αž‡αžΌαž“αž–αž½αž€αž‚αŸαž‘αŸ…αž€αžΆαž“αŸ‹αž€αž˜αŸ’αž˜αžœαž·αž’αžΈ αž“αž·αž„αž”αž‰αŸ’αž‡αžΌαž“αž€αžΆαžšαž†αŸ’αž›αžΎαž™αžαž”αžšαž”αžŸαŸ‹αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαž‘αŸ…αž’αžαž·αžαž·αž‡αž“αŸ”

αž€αž˜αŸ’αž˜αžœαž·αž’αžΈ αž“αž·αž„αž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸαž’αžΆαž…αžαŸ’αžšαžΌαžœαž”αžΆαž“αž—αŸ’αž‡αžΆαž”αŸ‹αž“αŸ…αžαžΆαž„αž€αŸ’αž“αž»αž„ docker αžŠαŸ„αž™αž”αŸ’αžšαžΎ αž”αžŽαŸ’αžαžΆαž‰ docker. αžŠαžΌαž…αŸ’αž“αŸαŸ‡ αž€αž»αž„αžαžΊαž“αŸαžšαžŠαŸ‚αž›αž˜αžΆαž“αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαž˜αž·αž“αž…αžΆαŸ†αž”αžΆαž…αŸ‹αž”αž‰αŸ’αž‡αžΌαž“αž…αŸ’αžšαž€αž“αŸ…αž›αžΎαž”αŸ’αžšαž–αŸαž“αŸ’αž’αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž‘αŸ αžœαžΆαž’αž“αž»αž‰αŸ’αž‰αžΆαžαž±αŸ’αž™αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαžŠαžΆαž…αŸ‹αž†αŸ’αž„αžΆαž™αž–αžΈαž€αžΆαžšαž‚αŸ†αžšαžΆαž˜αž€αŸ†αž αŸ‚αž„αž–αžΈαžαžΆαž„αž€αŸ’αžšαŸ…αŸ”

αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸαžšαžŸαŸ‹αž“αŸ…αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž•αŸ’αžŸαŸαž„αž‘αŸ€αž αž’αŸ’αž“αž€αž“αžΉαž„αžαŸ’αžšαžΌαžœαž”αŸ„αŸ‡αž”αž„αŸ‹αž…αŸ„αž›αž”αžŽαŸ’αžαžΆαž‰ docker αž αžΎαž™αž—αŸ’αž‡αžΆαž”αŸ‹αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαž‘αŸ…αž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸαžαžΆαž˜αžšαž™αŸˆαž”αžŽαŸ’αžαžΆαž‰αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“ αžŠαŸ„αž™αž”αž‰αŸ’αž‡αžΌαž“αž”αž“αŸ’αžαž…αŸ’αžšαž€αŸ” αž€αž˜αŸ’αž˜αžœαž·αž’αžΈ αž”αŸ‰αžΆαžšαŸ‰αžΆαž˜αŸ‰αŸ‚αžαŸ’αžš --publishαžŠαžΌαž…αž–αŸαž›αž…αžΆαž”αŸ‹αž•αŸ’αžαžΎαž˜αžŠαŸ†αž”αžΌαž„ αž“αž·αž„αžŠαžΌαž…αž‚αŸ’αž“αžΆαž“αžΉαž„αž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸαžŠαŸ‚αžšαŸ”

αž™αžΎαž„αž“αžΉαž„αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸαž“αŸ…αž›αžΎαž…αŸ’αžšαž€ 80 αž–αžΈαž–αŸ’αžšαŸ„αŸ‡αž“αŸαŸ‡αž–αž·αžαž‡αžΆαž’αž„αŸ’αž‚αž—αžΆαž–αžŠαŸ‚αž›αž‚αž½αžšαžŸαŸ’αžαžΆαž”αŸ‹αž”αžŽαŸ’αžαžΆαž‰αžαžΆαž„αž€αŸ’αžšαŸ…αŸ” αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž…αŸ’αžšαž€ 80 αž‡αžΆαž”αŸ‹αžšαžœαž›αŸ‹αž“αŸ…αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αžŸαžΆαž€αž›αŸ’αž”αž„αžšαž”αžŸαŸ‹αž’αŸ’αž“αž€ αžŸαžΌαž˜αž”αŸ’αžαžΌαžšαž”αŸ‰αžΆαžšαŸ‰αžΆαž˜αŸ‰αŸ‚αžαŸ’αžš --publish 80:80 αž“αŸ…αž›αžΎ --publish ANY_FREE_PORT:80.

αž”αž…αŸ’αž…αŸαž€αž‘αŸαžŸαž‚αž½αžšαž±αŸ’αž™αž…αžΆαž”αŸ‹αž’αžΆαžšαž˜αŸ’αž˜αžŽαŸ

αž”αŸ„αŸ‡αž–αž»αž˜αŸ’αž–

$ docker network create web-gateway
5dba128fb3b255b02ac012ded1906b7b4970b728fb7db3dbbeccc9a77a5dd7bd

$ docker run --detach --rm --name uptimer --network web-gateway uptimer
a1105f1b583dead9415e99864718cc807cc1db1c763870f40ea38bc026e2d67f

$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer:8080
<h2>Uptimer v1.0 is running for 11.5 seconds.</h2>

$ docker run --detach --publish 80:80 --network web-gateway --name reverse-proxy nginx:alpine
80695a822c19051260c66bf60605dcb4ea66802c754037704968bc42527bf120

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                NAMES
80695a822c19        nginx:alpine        "/docker-entrypoint.…"   27 seconds ago       Up 25 seconds       0.0.0.0:80->80/tcp   reverse-proxy
a1105f1b583d        uptimer             "python -u ./app.py"     About a minute ago   Up About a minute   8080/tcp             uptimer

$ cat << EOF > uptimer.conf
server {
    listen 80;
    location / {
        proxy_pass http://uptimer:8080;
    }
}
EOF

$ docker cp ./uptimer.conf reverse-proxy:/etc/nginx/conf.d/default.conf

$ docker exec reverse-proxy nginx -s reload
2020/06/23 20:51:03 [notice] 31#31: signal process started

$ wget -qSO- http://localhost
  HTTP/1.1 200 OK
  Server: nginx/1.19.0
  Date: Sat, 22 Aug 2020 19:56:24 GMT
  Content-Type: text/html
  Transfer-Encoding: chunked
  Connection: keep-alive
<h2>Uptimer v1.0 is running for 104.1 seconds.</h2>

αž€αžΆαžšαžŠαžΆαž€αŸ‹αž–αž„αŸ’αžšαžΆαž™αž‚αŸ’αž˜αžΆαž“αžαŸ’αž“αŸαžš

αžŸαžΌαž˜αžŠαžΆαž€αŸ‹αž…αŸαž‰αž€αŸ†αžŽαŸ‚αžαŸ’αž˜αžΈαž“αŸƒαž€αž˜αŸ’αž˜αžœαž·αž’αžΈ (αž‡αžΆαž˜αž½αž™αž€αžΆαžšαž‡αŸ†αžšαž»αž‰αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž…αžΆαž”αŸ‹αž•αŸ’αžαžΎαž˜αž–αžΈαžšαžŠαž„) αž αžΎαž™αž–αŸ’αž™αžΆαž™αžΆαž˜αžŠαžΆαž€αŸ‹αž±αŸ’αž™αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαžœαžΆαž™αŸ‰αžΆαž„αžšαž›αžΌαž“αŸ”

αž”αž…αŸ’αž…αŸαž€αž‘αŸαžŸαž‚αž½αžšαž±αŸ’αž™αž…αžΆαž”αŸ‹αž’αžΆαžšαž˜αŸ’αž˜αžŽαŸ

  • echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' - αžŸαžšαžŸαŸαžšαž’αžαŸ’αžαž”αž‘ my text αž‘αŸ…αž€αžΆαž“αŸ‹β€‹αž―αž€αžŸαžΆαžš /my-file.txt αž“αŸ…αžαžΆαž„αž€αŸ’αž“αž»αž„αž’αž»αž„ my-container.
  • cat > /my-file.txt - αžŸαžšαžŸαŸαžšαž˜αžΆαžαž·αž€αžΆαž“αŸƒαž€αžΆαžšαž”αž‰αŸ’αž…αžΌαž›αžŸαŸ’αžαž„αŸ‹αžŠαžΆαžšαž‘αŸ…αž―αž€αžŸαžΆαžš /dev/stdin.

αž”αŸ„αŸ‡αž–αž»αž˜αŸ’αž–

$ sed -i "s/app_version = 1/app_version = 2/" uptimer.py

$ docker build --tag uptimer .
Sending build context to Docker daemon  39.94kB
Step 1/4 : FROM python:alpine
 ---> 8ecf5a48c789
Step 2/4 : EXPOSE 8080
 ---> Using cache
 ---> cf92d174c9d3
Step 3/4 : COPY uptimer.py app.py
 ---> 3eca6a51cb2d
Step 4/4 : CMD [ "python", "-u", "./app.py" ]
 ---> Running in 8f13c6d3d9e7
Removing intermediate container 8f13c6d3d9e7
 ---> 1d56897841ec
Successfully built 1d56897841ec
Successfully tagged uptimer:latest

$ docker run --detach --rm --name uptimer_BLUE --network web-gateway uptimer
96932d4ca97a25b1b42d1b5f0ede993b43f95fac3c064262c5c527e16c119e02

$ docker logs uptimer_BLUE
Uptimer v2.0 (loads in 5 sec.) started.

$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer_BLUE:8080
<h2>Uptimer v2.0 is running for 23.9 seconds.</h2>

$ sed s/uptimer/uptimer_BLUE/ uptimer.conf | docker exec --interactive reverse-proxy sh -c 'cat > /etc/nginx/conf.d/default.conf'

$ docker exec reverse-proxy cat /etc/nginx/conf.d/default.conf
server {
    listen 80;
    location / {
        proxy_pass http://uptimer_BLUE:8080;
    }
}

$ docker exec reverse-proxy nginx -s reload
2020/06/25 21:22:23 [notice] 68#68: signal process started

$ wget -qO- http://localhost
<h2>Uptimer v2.0 is running for 63.4 seconds.</h2>

$ docker rm -f uptimer
uptimer

$ wget -qO- http://localhost
<h2>Uptimer v2.0 is running for 84.8 seconds.</h2>

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                NAMES
96932d4ca97a        uptimer             "python -u ./app.py"     About a minute ago   Up About a minute   8080/tcp             uptimer_BLUE
80695a822c19        nginx:alpine        "/docker-entrypoint.…"   8 minutes ago        Up 8 minutes        0.0.0.0:80->80/tcp   reverse-proxy

αž“αŸ…αžŠαŸ†αžŽαžΆαž€αŸ‹αž€αžΆαž›αž“αŸαŸ‡ αžšαžΌαž”αž—αžΆαž–αžαŸ’αžšαžΌαžœαž”αžΆαž“αž”αž„αŸ’αž€αžΎαžαž‘αžΎαž„αžŠαŸ„αž™αž•αŸ’αž‘αžΆαž›αŸ‹αž“αŸ…αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸ αžŠαŸ‚αž›αžαž˜αŸ’αžšαžΌαžœαž±αŸ’αž™αž”αŸ’αžšαž—αž–αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαž“αŸ…αž‘αžΈαž“αŸ„αŸ‡ αž αžΎαž™αž•αŸ’αž‘αž»αž€αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸαž‡αžΆαž˜αž½αž™αž“αžΉαž„αž€αžΆαžšαž„αžΆαžšαžŠαŸ‚αž›αž˜αž·αž“αž…αžΆαŸ†αž”αžΆαž…αŸ‹αŸ” αž‡αŸ†αž αžΆαž“αž”αž“αŸ’αž‘αžΆαž”αŸ‹αž‚αžΊαžαŸ’αžšαžΌαžœαž”αŸ‚αž„αž…αŸ‚αž€αž€αžΆαžšαž•αŸ’αž‚αž»αŸ†αžšαžΌαž”αž—αžΆαž–αž‘αŸ…αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αžŠαžΆαž…αŸ‹αžŠαŸ„αž™αž‘αŸ‚αž€ (αž§αž‘αžΆαž αžšαžŽαŸαž‘αŸ…αž”αŸ’αžšαž–αŸαž“αŸ’αž’ CI) αž αžΎαž™αž”αž“αŸ’αž‘αžΆαž”αŸ‹αž˜αž€αž•αŸ’αž‘αŸαžšαžœαžΆαž‘αŸ…αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸαŸ”

αž€αžΆαžšαž•αŸ’αž‘αŸαžšαžšαžΌαž”αž—αžΆαž–

αž‡αžΆαž’αž€αž»αžŸαž› αžœαžΆαž˜αž·αž“αžŸαž˜αž αŸαžαž»αž•αž›αž€αŸ’αž“αž»αž„αž€αžΆαžšαž•αŸ’αž‘αŸαžšαžšαžΌαž”αž—αžΆαž–αž–αžΈ localhost αž‘αŸ… localhost αž‘αŸ αžŠαžΌαž…αŸ’αž“αŸαŸ‡αž•αŸ’αž“αŸ‚αž€αž“αŸαŸ‡αž’αžΆαž…αžšαž»αž€αžšαž€αž”αžΆαž“αž›αž»αŸ‡αžαŸ’αžšαžΆαžαŸ‚αž’αŸ’αž“αž€αž˜αžΆαž“αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž–αžΈαžšαž‡αžΆαž˜αž½αž™ Docker αž“αŸ…αž“αžΉαž„αžŠαŸƒαŸ” αž™αŸ‰αžΆαž„αž αŸ„αž…αžŽαžΆαžŸαŸ‹αžœαžΆαž˜αžΎαž›αž‘αŸ…αžŠαžΌαž…αž“αŸαŸ‡αŸ–

$ ssh production-server docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

$ docker image save uptimer | ssh production-server 'docker image load'
Loaded image: uptimer:latest

$ ssh production-server docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
uptimer             latest              1d56897841ec        5 minutes ago       78.9MB

αž€αŸ’αžšαž»αž˜αž€αžΆαžšαž„αžΆαžš docker save αžšαž€αŸ’αžŸαžΆαž‘αž»αž€αž‘αž·αž“αŸ’αž“αž“αŸαž™αžšαžΌαž”αž—αžΆαž–αž“αŸ…αž€αŸ’αž“αž»αž„αž”αŸαžŽαŸ’αžŽαžŸαžΆαžš .tar αž˜αžΆαž“αž“αŸαž™αžαžΆαžœαžΆαž˜αžΆαž“αž‘αž˜αŸ’αž„αž“αŸ‹αž”αŸ’αžšαž αŸ‚αž› 1.5 αžŠαž„αž…αŸ’αžšαžΎαž“αž‡αžΆαž„αž‘αž˜αŸ’αž„αž“αŸ‹αžšαž”αžŸαŸ‹αžœαžΆαž€αŸ’αž“αž»αž„αž‘αž˜αŸ’αžšαž„αŸ‹αžŠαŸ‚αž›αž”αžΆαž“αž”αž„αŸ’αž αžΆαž”αŸ‹αŸ” αžŠαžΌαž…αŸ’αž“αŸαŸ‡β€‹αžŸαžΌαž˜β€‹αž’αŸ’αžšαž„αž½αž“β€‹αž€αŸ’αž“αž»αž„β€‹αž“αžΆαž˜β€‹αž“αŸƒβ€‹αž€αžΆαžšβ€‹αžŸαž“αŸ’αžŸαŸ†β€‹αž–αŸαž›β€‹αžœαŸαž›αžΆ αž“αž·αž„β€‹αž…αžšαžΆαž…αžšαžŽαŸαŸ–

$ docker image save uptimer | gzip | ssh production-server 'zcat | docker image load'
Loaded image: uptimer:latest

αž’αŸ’αž“αž€αž€αŸαž’αžΆαž…αžαŸ’αžšαž½αžαž–αž·αž“αž·αžαŸ’αž™αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž‘αžΆαž‰αž™αž€ (αž‘αŸ„αŸ‡αž”αžΈαž‡αžΆαžœαžΆαž‘αžΆαž˜αž‘αžΆαžšαž§αž”αž€αžšαžŽαŸαž”αŸ’αžšαžΎαž”αŸ’αžšαžΆαžŸαŸ‹αž—αžΆαž‚αžΈαž‘αžΈαž”αžΈαž€αŸαžŠαŸ„αž™)αŸ–

$ docker image save uptimer | gzip | pv | ssh production-server 'zcat | docker image load'
25,7MiB 0:01:01 [ 425KiB/s] [                   <=>    ]
Loaded image: uptimer:latest

αž‚αž“αŸ’αž›αžΉαŸ‡αŸ– αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž’αŸ’αž“αž€αžαŸ’αžšαžΌαžœαž€αžΆαžšαž”αŸ‰αžΆαžšαŸ‰αžΆαž˜αŸ‰αŸ‚αžαŸ’αžšαž‡αžΆαž…αŸ’αžšαžΎαž“αžŠαžΎαž˜αŸ’αž”αžΈαž—αŸ’αž‡αžΆαž”αŸ‹αž‘αŸ…αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸαžαžΆαž˜αžšαž™αŸˆ SSH αž’αŸ’αž“αž€αž”αŸ’αžšαž αŸ‚αž›αž‡αžΆαž˜αž·αž“αž”αŸ’αžšαžΎαž―αž€αžŸαžΆαžšαž‘αŸ ~/.ssh/config.

αž•αŸ’αž‘αŸαžšαžšαžΌαž”αž—αžΆαž–αžαžΆαž˜αžšαž™αŸˆ docker image save/load - αž“αŸαŸ‡αž‚αžΊαž‡αžΆαžœαž·αž’αžΈαžŸαžΆαžŸαŸ’αžšαŸ’αžαžαž·αž…αžαž½αž…αž”αŸ†αž•αž»αž αž”αŸ‰αž»αž“αŸ’αžαŸ‚αž˜αž·αž“αž˜αŸ‚αž“αžαŸ‚αž˜αž½αž™αž‘αŸαŸ” αž˜αžΆαž“αž•αŸ’αžŸαŸαž„αž‘αŸ€αžαŸ–

  1. αž€αžΆαžšαž…αž»αŸ‡αž”αž‰αŸ’αž‡αžΈαž€αž»αž„αžαžΊαž“αŸαžš (αžŸαŸ’αžαž„αŸ‹αžŠαžΆαžšαž§αžŸαŸ’αžŸαžΆαž αž€αž˜αŸ’αž˜) αŸ”
  2. αž—αŸ’αž‡αžΆαž”αŸ‹αž‘αŸ…αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸ docker daemon αž–αžΈαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž•αŸ’αžŸαŸαž„αž‘αŸ€αžαŸ–
    1. αž”αžšαž·αžŸαŸ’αžαžΆαž“αž”αŸ’αžšαŸ‚αž”αŸ’αžšαž½αž› DOCKER_HOST.
    2. αž‡αž˜αŸ’αžšαžΎαžŸαž”αž“αŸ’αž‘αžΆαžαŸ‹αž–αžΆαž€αŸ’αž™αž”αž‰αŸ’αž‡αžΆ -H ឬ --host αž§αž”αž€αžšαžŽαŸ docker-compose.
    3. docker context

αžœαž·αž’αžΈαžŸαžΆαžŸαŸ’αžšαŸ’αžαž‘αžΈαž–αžΈαžš (αž‡αžΆαž˜αž½αž™αž“αžΉαž„αž‡αž˜αŸ’αžšαžΎαžŸαž”αžΈαžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž€αžΆαžšαž’αž“αž»αžœαžαŸ’αžαžšαž”αžŸαŸ‹αžœαžΆ) αžαŸ’αžšαžΌαžœαž”αžΆαž“αž–αž·αž–αžŽαŸŒαž“αžΆαž™αŸ‰αžΆαž„αž›αŸ’αž’αž“αŸ…αž€αŸ’αž“αž»αž„αž’αžαŸ’αžαž”αž‘ αžšαž”αŸ€αž”αžŠαžΆαž€αŸ‹αž–αž„αŸ’αžšαžΆαž™αž“αŸ…αž›αžΎ Docker hosts αž–αžΈαž…αž˜αŸ’αž„αžΆαž™αž‡αžΆαž˜αž½αž™ docker-compose.

deploy.sh

αž₯αž‘αžΌαžœαž“αŸαŸ‡ αž…αžΌαžšαž™αžΎαž„αž”αŸ’αžšαž˜αžΌαž›αž’αŸ’αžœαžΈαž‚αŸ’αžšαž”αŸ‹αž™αŸ‰αžΆαž„αžŠαŸ‚αž›αž™αžΎαž„αž”αžΆαž“αž’αŸ’αžœαžΎαžŠαŸ„αž™αžŠαŸƒαž‘αŸ…αž€αŸ’αž“αž»αž„αžŸαŸ’αž‚αŸ’αžšαžΈαž”αžαŸ‚αž˜αž½αž™αŸ” αž…αžΌαžšαž…αžΆαž”αŸ‹αž•αŸ’αžαžΎαž˜αž‡αžΆαž˜αž½αž™αž“αžΉαž„αž˜αž»αžαž„αžΆαžšαž€αž˜αŸ’αžšαž·αžαž€αŸ†αž–αžΌαž› αž αžΎαž™αž”αž“αŸ’αž‘αžΆαž”αŸ‹αž˜αž€αž˜αžΎαž›αž˜αž»αžαž„αžΆαžšαž•αŸ’αžŸαŸαž„αž‘αŸ€αžαžŠαŸ‚αž›αž”αžΆαž“αž”αŸ’αžšαžΎαž“αŸ…αž€αŸ’αž“αž»αž„αžœαžΆαŸ”

αž”αž…αŸ’αž…αŸαž€αž‘αŸαžŸαž‚αž½αžšαž±αŸ’αž™αž…αžΆαž”αŸ‹αž’αžΆαžšαž˜αŸ’αž˜αžŽαŸ

  • ${parameter?err_msg} - αž˜αž½αž™αž“αŸƒαž’αž€αŸ’αžαžšαžΆαžœαž·αžšαž»αž‘αŸ’αž’αžœαŸαž‘αž˜αž“αŸ’αž bash (aka αž€αžΆαžšαž‡αŸ†αž“αž½αžŸαž”αŸ‰αžΆαžšαŸ‰αžΆαž˜αŸ‰αŸ‚αžαŸ’αžš) αž”αŸ’αžšαžŸαž·αž“αž”αžΎ parameter αž˜αž·αž“αž”αžΆαž“αž”αž‰αŸ’αž‡αžΆαž€αŸ‹, αž‘αž·αž“αŸ’αž“αž•αž› err_msg αž αžΎαž™αž…αŸαž‰αžŠαŸ„αž™αž›αŸαžαž€αžΌαžŠ 1 αŸ”
  • docker --log-driver journald - αžαžΆαž˜αž›αŸ†αž“αžΆαŸ†αžŠαžΎαž˜ αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαž”αž‰αŸ’αž‡αžΆαž€αžΆαžšαž€αžαŸ‹αžαŸ’αžšαžΆ docker αž‚αžΊαž‡αžΆαž―αž€αžŸαžΆαžšαž’αžαŸ’αžαž”αž‘αžŠαŸ„αž™αž‚αŸ’αž˜αžΆαž“αž€αžΆαžšαž”αž„αŸ’αžœαž·αž›αžŽαžΆαž˜αž½αž™αž‘αžΎαž™αŸ” αž‡αžΆαž˜αž½αž™αž“αžΉαž„αžœαž·αž’αžΈαžŸαžΆαžŸαŸ’αžšαŸ’αžαž“αŸαŸ‡ αž€αŸ†αžŽαžαŸ‹αž αŸαžαž»αž’αžΆαž…αž”αŸ†αž–αŸαž‰αžαžΆαžŸαž‘αžΆαŸ†αž„αž˜αžΌαž›αž”αžΆαž“αž™αŸ‰αžΆαž„αž†αžΆαž”αŸ‹αžšαž αŸαžŸ αžŠαžΌαž…αŸ’αž“αŸαŸ‡αžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž”αžšαž·αž™αžΆαž€αžΆαžŸαž•αž›αž·αžαž€αž˜αŸ’αž˜ αž…αžΆαŸ†αž”αžΆαž…αŸ‹αžαŸ’αžšαžΌαžœαž•αŸ’αž›αžΆαžŸαŸ‹αž”αŸ’αžαžΌαžšαž€αž˜αŸ’αž˜αžœαž·αž’αžΈαž”αž‰αŸ’αž‡αžΆαž±αŸ’αž™αž€αžΆαž“αŸ‹αžαŸ‚αž†αŸ’αž›αžΆαžαžœαŸƒαŸ”

αžŸαŸ’αž‚αŸ’αžšαžΈαž”αžŠαžΆαž€αŸ‹αž±αŸ’αž™αž”αŸ’αžšαžΎαž”αŸ’αžšαžΆαžŸαŸ‹

deploy() {
    local usage_msg="Usage: ${FUNCNAME[0]} image_name"
    local image_name=${1?$usage_msg}

    ensure-reverse-proxy || return 2
    if get-active-slot $image_name
    then
        local OLD=${image_name}_BLUE
        local new_slot=GREEN
    else
        local OLD=${image_name}_GREEN
        local new_slot=BLUE
    fi
    local NEW=${image_name}_${new_slot}
    echo "Deploying '$NEW' in place of '$OLD'..."
    docker run 
        --detach 
        --restart always 
        --log-driver journald 
        --name $NEW 
        --network web-gateway 
        $image_name || return 3
    echo "Container started. Checking health..."
    for i in {1..20}
    do
        sleep 1
        if get-service-status $image_name $new_slot
        then
            echo "New '$NEW' service seems OK. Switching heads..."
            sleep 2  # Ensure service is ready
            set-active-slot $image_name $new_slot || return 4
            echo "'$NEW' service is live!"
            sleep 2  # Ensure all requests were processed
            echo "Killing '$OLD'..."
            docker rm -f $OLD
            docker image prune -f
            echo "Deployment successful!"
            return 0
        fi
        echo "New '$NEW' service is not ready yet. Waiting ($i)..."
    done
    echo "New '$NEW' service did not raise, killing it. Failed to deploy T_T"
    docker rm -f $NEW
    return 5
}

αž›αž€αŸ’αžαžŽαŸˆαž–αž·αžŸαŸαžŸαžŠαŸ‚αž›αž”αžΆαž“αž”αŸ’αžšαžΎαŸ–

  • ensure-reverse-proxy - αžαŸ’αžšαžΌαžœαž”αŸ’αžšαžΆαž€αžŠαžαžΆαž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸαž€αŸ†αž–αž»αž„αžŠαŸ†αžŽαžΎαžšαž€αžΆαžš (αž˜αžΆαž“αž”αŸ’αžšαž™αŸ„αž‡αž“αŸαžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž€αžΆαžšαžŠαžΆαž€αŸ‹αž–αž„αŸ’αžšαžΆαž™αžŠαŸ†αž”αžΌαž„)
  • get-active-slot service_name - αž€αŸ†αžŽαžαŸ‹αžαžΆαžαžΎαžšαž“αŸ’αž’αžŠαŸ„αžαž˜αž½αž™αžŽαžΆαžŠαŸ‚αž›αžŸαž€αž˜αŸ’αž˜αžŸαž˜αŸ’αžšαžΆαž”αŸ‹αžŸαŸαžœαžΆαž€αž˜αŸ’αž˜αžŠαŸ‚αž›αž”αžΆαž“αž•αŸ’αžαž›αŸ‹αž±αŸ’αž™ (BLUE ឬ GREEN)
  • get-service-status service_name deployment_slot - αž€αŸ†αžŽαžαŸ‹αžαžΆαžαžΎαžŸαŸαžœαžΆαž€αž˜αŸ’αž˜αž“αŸαŸ‡αžšαž½αž…αžšαžΆαž›αŸ‹αžŠαžΎαž˜αŸ’αž”αžΈαžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαžŸαŸ†αžŽαžΎαž…αžΌαž›αž¬αž’αžαŸ‹
  • set-active-slot service_name deployment_slot - αž•αŸ’αž›αžΆαžŸαŸ‹αž”αŸ’αžαžΌαžšαž€αžΆαžšαž€αŸ†αžŽαžαŸ‹αžšαž…αž“αžΆαžŸαž˜αŸ’αž–αŸαž“αŸ’αž’ nginx αž“αŸ…αž€αŸ’αž“αž»αž„αž’αž»αž„αž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸ

αž“αŸ…β€‹αž€αŸ’αž“αž»αž„β€‹αž›αŸ†αžŠαžΆαž”αŸ‹:

ensure-reverse-proxy() {
    is-container-up reverse-proxy && return 0
    echo "Deploying reverse-proxy..."
    docker network create web-gateway
    docker run 
        --detach 
        --restart always 
        --log-driver journald 
        --name reverse-proxy 
        --network web-gateway 
        --publish 80:80 
        nginx:alpine || return 1
    docker exec --interactive reverse-proxy sh -c "> /etc/nginx/conf.d/default.conf"
    docker exec reverse-proxy nginx -s reload
}

is-container-up() {
    local container=${1?"Usage: ${FUNCNAME[0]} container_name"}

    [ -n "$(docker ps -f name=${container} -q)" ]
    return $?
}

get-active-slot() {
    local service=${1?"Usage: ${FUNCNAME[0]} service_name"}

    if is-container-up ${service}_BLUE && is-container-up ${service}_GREEN; then
        echo "Collision detected! Stopping ${service}_GREEN..."
        docker rm -f ${service}_GREEN
        return 0  # BLUE
    fi
    if is-container-up ${service}_BLUE && ! is-container-up ${service}_GREEN; then
        return 0  # BLUE
    fi
    if ! is-container-up ${service}_BLUE; then
        return 1  # GREEN
    fi
}

get-service-status() {
    local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
    local service=${1?usage_msg}
    local slot=${2?$usage_msg}

    case $service in
        # Add specific healthcheck paths for your services here
        *) local health_check_port_path=":8080/" ;;
    esac
    local health_check_address="http://${service}_${slot}${health_check_port_path}"
    echo "Requesting '$health_check_address' within the 'web-gateway' docker network:"
    docker run --rm --network web-gateway alpine 
        wget --timeout=1 --quiet --server-response $health_check_address
    return $?
}

set-active-slot() {
    local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
    local service=${1?$usage_msg}
    local slot=${2?$usage_msg}
    [ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1

    get-nginx-config $service $slot | docker exec --interactive reverse-proxy sh -c "cat > /etc/nginx/conf.d/$service.conf"
    docker exec reverse-proxy nginx -t || return 2
    docker exec reverse-proxy nginx -s reload
}

αž˜αž»αžαž„αžΆαžš get-active-slot αž‘αžΆαž˜αž‘αžΆαžšαž€αžΆαžšαž–αž“αŸ’αž™αž›αŸ‹αžαž·αž…αžαž½αž…αŸ–

αž αŸαžαž»αž’αŸ’αžœαžΈαž”αžΆαž“αž‡αžΆαžœαžΆαžαŸ’αžšαž‘αž”αŸ‹αž›αŸαž αž αžΎαž™αž˜αž·αž“αž…αŸαž‰αžαŸ’αžŸαŸ‚?

αž‘αŸ„αŸ‡αž™αŸ‰αžΆαž„αžŽαžΆαž€αŸαžŠαŸ„αž™ αž“αŸ…αž€αŸ’αž“αž»αž„αž˜αž»αžαž„αžΆαžšαž αŸ…αž‘αžΌαžšαžŸαž–αŸ’αž‘ αž™αžΎαž„αž–αž·αž“αž·αžαŸ’αž™αž˜αžΎαž›αž›αž‘αŸ’αž’αž•αž›αž“αŸƒαž€αžΆαžšαž„αžΆαžšαžšαž”αžŸαŸ‹αžœαžΆ αž αžΎαž™αž€αžΆαžšαž–αž·αž“αž·αžαŸ’αž™αž˜αžΎαž›αž›αŸαžαž€αžΌαžŠαž…αŸαž‰αžŠαŸ„αž™αž”αŸ’αžšαžΎ bash αž‚αžΊαž„αžΆαž™αžŸαŸ’αžšαž½αž›αž‡αžΆαž„αž€αžΆαžšαž–αž·αž“αž·αžαŸ’αž™αž˜αžΎαž›αžαŸ’αžŸαŸ‚αž’αž€αŸ’αžŸαžšαŸ” αž›αžΎαžŸαž–αžΈαž“αŸαŸ‡αž‘αŸ€αžαž€αžΆαžšαž‘αž‘αž½αž›αž”αžΆαž“αžαŸ’αžŸαŸ‚αž–αžΈαžœαžΆαž‚αžΊαžŸαžΆαž˜αž‰αŸ’αž‰αžŽαžΆαžŸαŸ‹:
get-active-slot service && echo BLUE || echo GREEN.

αžαžΎαž›αž€αŸ’αžαžαžŽαŸ’αžŒαž”αžΈαž–αž·αžαž‡αžΆαž‚αŸ’αžšαž”αŸ‹αž‚αŸ’αžšαžΆαž“αŸ‹αžŠαžΎαž˜αŸ’αž”αžΈαž”αŸ‚αž„αž…αŸ‚αž€αžšαžŠαŸ’αž‹αž‘αžΆαŸ†αž„αž’αžŸαŸ‹αž˜αŸ‚αž“αž‘αŸ?

αž€αžΆαžšαžŠαžΆαž€αŸ‹αž–αž„αŸ’αžšαžΆαž™αž–αžŽαŸŒαžαŸ€αžœαž”αŸƒαžαž„αž“αŸ…αž”αŸ’αžšαžΆαž€αŸ‹αžˆαŸ’αž“αž½αž›αž’αž”αŸ’αž”αž”αžšαž˜αžΆ

αžŸαžΌαž˜αŸ’αž”αžΈαžαŸ‚αž–αžΈαžšαž“αžΉαž„αž‚αŸ’αžšαž”αŸ‹αž‚αŸ’αžšαžΆαž“αŸ‹αž αžΎαž™ αž…αž»αž„αž€αŸ’αžšαŸ„αž™αž‚αžΊαž“αŸ…αž‘αžΈαž“αŸαŸ‡αžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž—αžΆαž–αž–αŸαž‰αž›αŸαž‰ αžŠαžΎαž˜αŸ’αž”αžΈαž€αž»αŸ†αž±αŸ’αž™αžŸαžšαžŸαŸαžš else.

αž˜αžΆαž“αžαŸ‚αž˜αž»αžαž„αžΆαžšαžŠαŸ‚αž›αžαŸ’αžšαž‘αž”αŸ‹αž€αžΆαžšαž€αŸ†αžŽαžαŸ‹αžšαž…αž“αžΆαžŸαž˜αŸ’αž–αŸαž“αŸ’αž’ nginx αž”αŸ‰αž»αžŽαŸ’αžŽαŸ„αŸ‡αžŠαŸ‚αž›αž˜αž·αž“αžαŸ’αžšαžΌαžœαž”αžΆαž“αž€αŸ†αžŽαžαŸ‹αŸ– get-nginx-config service_name deployment_slot. αžŠαŸ„αž™αž—αžΆαž–αžŸαŸ’αžšαžŠαŸ€αž„αž‚αŸ’αž“αžΆαž‡αžΆαž˜αž½αž™αž“αžΉαž„αž€αžΆαžšαž–αž·αž“αž·αžαŸ’αž™αžŸαž»αžαž—αžΆαž– αž“αŸ…αž‘αžΈαž“αŸαŸ‡αž’αŸ’αž“αž€αž’αžΆαž…αž€αŸ†αžŽαžαŸ‹αž€αžΆαžšαž€αŸ†αžŽαžαŸ‹αžŽαžΆαž˜αž½αž™αžŸαž˜αŸ’αžšαžΆαž”αŸ‹αžŸαŸαžœαžΆαž€αž˜αŸ’αž˜αžŽαžΆαž˜αž½αž™αŸ” αž“αŸƒαž’αŸ’αžœαžΈαžŠαŸ‚αž›αž‚αž½αžšαž±αŸ’αž™αž…αžΆαž”αŸ‹αž’αžΆαžšαž˜αŸ’αž˜αžŽαŸ - αžαŸ‚αž”αŸ‰αž»αžŽαŸ’αžŽαŸ„αŸ‡ cat <<- EOFαžŠαŸ‚αž›αž’αž“αž»αž‰αŸ’αž‰αžΆαžαž±αŸ’αž™αž’αŸ’αž“αž€αž›αž»αž”αž•αŸ’αž‘αžΆαŸ†αž„αž‘αžΆαŸ†αž„αž’αžŸαŸ‹αž“αŸ…αžŠαžΎαž˜αŸ” αž–αž·αžαž αžΎαž™ αžαž˜αŸ’αž›αŸƒαž“αŸƒαž€αžΆαžšαž’αŸ’αžœαžΎαž‘αŸ’αžšαž„αŸ‹αž‘αŸ’αžšαžΆαž™αž›αŸ’αž’αž‚αžΊαž‡αžΆαž•αŸ’αž‘αžΆαŸ†αž„αž…αž˜αŸ’αžšαž»αŸ‡αž‡αžΆαž˜αž½αž™αž…αž“αŸ’αž›αŸ„αŸ‡ αžŠαŸ‚αž›αžŸαž–αŸ’αžœαžαŸ’αž„αŸƒαž“αŸαŸ‡αžαŸ’αžšαžΌαžœαž”αžΆαž“αž…αžΆαžαŸ‹αž‘αž»αž€αžαžΆαž‡αžΆαž‘αž˜αŸ’αžšαž„αŸ‹αž’αžΆαž€αŸ’αžšαž€αŸ‹αžŽαžΆαžŸαŸ‹αŸ” αž”αŸ‰αž»αž“αŸ’αžαŸ‚αž•αŸ’αž‘αžΆαŸ†αž„αž€αž„αž€αž˜αŸ’αž›αžΆαŸ†αž„ bash αž αžΎαž™αžœαžΆαž€αŸαž›αŸ’αž’αž•αž„αžŠαŸ‚αžšαž€αŸ’αž“αž»αž„αž€αžΆαžšαž’αŸ’αžœαžΎαž‘αŸ’αžšαž„αŸ‹αž‘αŸ’αžšαžΆαž™αž’αž˜αŸ’αž˜αžαžΆαž“αŸ…αž€αŸ’αž“αž»αž„αž€αžΆαžšαž€αŸ†αžŽαžαŸ‹αžšαž…αž“αžΆαžŸαž˜αŸ’αž–αŸαž“αŸ’αž’ nginx αŸ” αžŸαžšαž»αž”αž˜αž€ αž€αžΆαžšαž›αžΆαž™αž•αŸ’αž‘αžΆαŸ†αž„αž‡αžΆαž˜αž½αž™αžŠαž€αžƒαŸ’αž›αžΆαž“αŸ…αž‘αžΈαž“αŸαŸ‡αž–αž·αžαž‡αžΆαž αžΆαž€αŸ‹αžŠαžΌαž…αž‡αžΆαžŠαŸ†αžŽαŸ„αŸ‡αžŸαŸ’αžšαžΆαž™αžŠαŸαž›αŸ’αž’αž”αŸ†αž•αž»αžαž…αŸαž‰αž–αžΈαž’αŸ’αžœαžΈαžŠαŸ‚αž›αž’αžΆαž€αŸ’αžšαž€αŸ‹αž”αŸ†αž•αž»αžαŸ” αž‘αŸ„αŸ‡αž‡αžΆαž™αŸ‰αžΆαž„αžŽαžΆαž€αŸαžŠαŸ„αž™ αž’αŸ’αž“αž€αž“αžΉαž„αž˜αž·αž“αžƒαžΎαž‰αžœαžΆαž“αŸ…αž€αŸ’αž“αž»αž„αž•αŸ’αž“αŸ‚αž€αžαžΆαž„αž€αŸ’αžšαŸ„αž˜αž‘αŸ αž…αžΆαž”αŸ‹αžαžΆαŸ†αž„αž–αžΈ Habr "αž’αŸ’αžœαžΎαžœαžΆαž”αžΆαž“αž›αŸ’αž’" αžŠαŸ„αž™αž€αžΆαžšαž•αŸ’αž›αžΆαžŸαŸ‹αž”αŸ’αžαžΌαžšαž•αŸ’αž‘αžΆαŸ†αž„αž‘αžΆαŸ†αž„αž’αžŸαŸ‹αž‘αŸ…αž‡αžΆ 4 spaces αž“αž·αž„αž’αŸ’αžœαžΎαž±αŸ’αž™ EOF αž˜αž·αž“αžαŸ’αžšαžΉαž˜αžαŸ’αžšαžΌαžœαŸ” αž αžΎαž™αž“αŸ…αž‘αžΈαž“αŸαŸ‡αžœαžΆαž‚αž½αžšαž±αŸ’αž™αž€αžαŸ‹αžŸαž˜αŸ’αž‚αžΆαž›αŸ‹.

αžŠαžΎαž˜αŸ’αž”αžΈαž€αž»αŸ†αž±αŸ’αž™αž€αŸ’αžšαŸ„αž€αž‘αžΎαž„αž–αžΈαžšαžŠαž„ αžαŸ’αž‰αž»αŸ†αž“αžΉαž„αž”αŸ’αžšαžΆαž”αŸ‹αž’αŸ’αž“αž€αž—αŸ’αž›αžΆαž˜αŸ—αž’αŸ†αž–αžΈ cat << 'EOF'αžŠαŸ‚αž›αž“αžΉαž„αž‡αž½αž”αž”αŸ’αžšαž‘αŸ‡αž“αŸ…αž–αŸαž›αž€αŸ’αžšαŸ„αž™αŸ” αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž’αŸ’αž“αž€αžŸαžšαžŸαŸαžšαž™αŸ‰αžΆαž„αžŸαžΆαž˜αž‰αŸ’αž‰ cat << EOFαž”αž“αŸ’αž‘αžΆαž”αŸ‹αž˜αž€αž“αŸ…αžαžΆαž„αž€αŸ’αž“αž»αž„ heredoc αžαŸ’αžŸαŸ‚αž’αž€αŸ’αžŸαžšαžαŸ’αžšαžΌαžœαž”αžΆαž“ interpolated (αž’αžαŸαžšαžαŸ’αžšαžΌαžœαž”αžΆαž“αž–αž„αŸ’αžšαžΈαž€ ($foo), αž€αžΆαžšαž αŸ…αž–αžΆαž€αŸ’αž™αž”αž‰αŸ’αž‡αžΆ ($(bar)) αŸ”αž›αŸ”) αž αžΎαž™αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž’αŸ’αž“αž€αž—αŸ’αž‡αžΆαž”αŸ‹αž…αž»αž„αž”αž‰αŸ’αž…αž”αŸ‹αž“αŸƒαž―αž€αžŸαžΆαžšαž€αŸ’αž“αž»αž„αžŸαž˜αŸ’αžšαž„αŸ‹αžαŸ‚αž˜αž½αž™ αž“αŸ„αŸ‡αž€αžΆαžšαž”αž€αžŸαŸ’αžšαžΆαž™αžαŸ’αžšαžΌαžœαž”αžΆαž“αž”αž·αž‘ αž αžΎαž™αž“αž·αž˜αž·αžαŸ’αžαžŸαž‰αŸ’αž‰αžΆ $ αžαŸ’αžšαžΌαžœαž”αžΆαž“αž”αž„αŸ’αž αžΆαž‰αžŠαžΌαž…αŸ” αž’αŸ’αžœαžΈαžŠαŸ‚αž›αž’αŸ’αž“αž€αžαŸ’αžšαžΌαžœαž€αžΆαžšαžŠαžΎαž˜αŸ’αž”αžΈαž”αž‰αŸ’αž…αžΌαž›αžŸαŸ’αž‚αŸ’αžšαžΈαž”αž“αŸ…αžαžΆαž„αž€αŸ’αž“αž»αž„αžŸαŸ’αž‚αŸ’αžšαžΈαž”αž•αŸ’αžŸαŸαž„αž‘αŸ€αžαŸ”

get-nginx-config() {
    local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"
    local service=${1?$usage_msg}
    local slot=${2?$usage_msg}
    [ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1

    local container_name=${service}_${slot}
    case $service in
        # Add specific nginx configs for your services here
        *) nginx-config-simple-service $container_name:8080 ;;
    esac
}

nginx-config-simple-service() {
    local usage_msg="Usage: ${FUNCNAME[0]} proxy_pass"
    local proxy_pass=${1?$usage_msg}

cat << EOF
server {
    listen 80;
    location / {
        proxy_pass http://$proxy_pass;
    }
}
EOF
}

αž“αŸαŸ‡αž‚αžΊαž‡αžΆαžŸαŸ’αž‚αŸ’αžšαžΈαž”αž‘αžΆαŸ†αž„αž˜αžΌαž›αŸ” αž αžΎαž™β€‹αžŠαžΌαž…αŸ’αž“αŸαŸ‡ gist αž‡αžΆαž˜αž½αž™αžŸαŸ’αž‚αŸ’αžšαžΈαž”αž“αŸαŸ‡αŸ” αžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž€αžΆαžšαž‘αžΆαž‰αž™αž€αžαžΆαž˜αžšαž™αŸˆ wget ឬ curl αŸ”

αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαžŸαŸ’αž‚αŸ’αžšαžΈαž”αžŠαŸ‚αž›αž”αžΆαž“αž€αŸ†αžŽαžαŸ‹αž”αŸ‰αžΆαžšαŸ‰αžΆαž˜αŸ‰αŸ‚αžαŸ’αžšαž“αŸ…αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸαž–αžΈαž…αž˜αŸ’αž„αžΆαž™

αžœαžΆαžŠαž›αŸ‹αž–αŸαž›αž αžΎαž™αžŠαžΎαž˜αŸ’αž”αžΈαž‚αŸ„αŸ‡αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸαž‚αŸ„αž›αžŠαŸ…αŸ” αž–αŸαž›αž“αŸαŸ‡ localhost αžŸαž˜αžšαž˜αŸ’αž™αžŽαžΆαžŸαŸ‹αŸ–

$ ssh-copy-id localhost
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
himura@localhost's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'localhost'"
and check to make sure that only the key(s) you wanted were added.

αž™αžΎαž„αž”αžΆαž“αžŸαžšαžŸαŸαžšαžŸαŸ’αž‚αŸ’αžšαžΈαž”αžŠαžΆαž€αŸ‹αž–αž„αŸ’αžšαžΆαž™αžŠαŸ‚αž›αž‘αžΆαž‰αž™αž€αžšαžΌαž”αž—αžΆαž–αžŠαŸ‚αž›αž”αžΆαž“αž”αž„αŸ’αž€αžΎαžαž‡αžΆαž˜αž»αž“αž‘αŸ…αž€αžΆαž“αŸ‹αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸαž‚αŸ„αž›αžŠαŸ… αž αžΎαž™αž‡αŸ†αž“αž½αžŸαž€αž“αŸ’αž›αŸ‚αž„αž•αŸ’αž‘αž»αž€αžŸαŸαžœαžΆαž€αž˜αŸ’αž˜αž™αŸ‰αžΆαž„αžšαž›αžΌαž“ αž”αŸ‰αž»αž“αŸ’αžαŸ‚αžαžΎαž™αžΎαž„αž’αžΆαž…αž”αŸ’αžšαžαž·αž”αžαŸ’αžαž·αžœαžΆαžŠαŸ„αž™αžšαž”αŸ€αž”αžŽαžΆαž“αŸ…αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž–αžΈαž…αž˜αŸ’αž„αžΆαž™? αžŸαŸ’αž‚αŸ’αžšαžΈαž”αž˜αžΆαž“αž’αžΆαž‚αž»αž™αž˜αŸ‰αž„αŸ‹ αž–αŸ’αžšαŸ„αŸ‡αžœαžΆαž˜αžΆαž“αž›αž€αŸ’αžαžŽαŸˆαž‡αžΆαžŸαž€αž› αž αžΎαž™αž’αžΆαž…αž”αŸ’αžšαžΎαž”αŸ’αžšαžΆαžŸαŸ‹αžŸαŸαžœαžΆαž€αž˜αŸ’αž˜αž‡αžΆαž…αŸ’αžšαžΎαž“αž€αŸ’αž“αž»αž„αž–αŸαž›αžαŸ‚αž˜αž½αž™αž€αŸ’αžšαŸ„αž˜αž”αŸ’αžšαžΌαž€αžŸαŸŠαžΈαž”αž‰αŸ’αž…αŸ’αžšαžΆαžŸαž˜αž½αž™ (αž’αŸ’αž“αž€αž’αžΆαž…αž”αŸ’αžšαžΎ nginx configs αžŠαžΎαž˜αŸ’αž”αžΈαž€αŸ†αžŽαžαŸ‹ url αž˜αž½αž™αžŽαžΆαž‡αžΆαžŸαŸαžœαžΆαž€αž˜αŸ’αž˜αž˜αž½αž™αžŽαžΆ)αŸ” αžŸαŸ’αž‚αŸ’αžšαžΈαž”αž˜αž·αž“αž’αžΆαž…αžšαž€αŸ’αžŸαžΆαž‘αž»αž€αž“αŸ…αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸαž”αžΆαž“αž‘αŸ αž–αŸ’αžšαŸ„αŸ‡αž€αŸ’αž“αž»αž„αž€αžšαžŽαžΈαž“αŸαŸ‡ αž™αžΎαž„αž“αžΉαž„αž˜αž·αž“αž’αžΆαž…αž’αžΆαž”αŸ‹αžŠαŸαžαžœαžΆαžŠαŸ„αž™αžŸαŸ’αžœαŸαž™αž”αŸ’αžšαžœαžαŸ’αžαž·αž”αžΆαž“αž‘αŸ (αžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž‚αŸ„αž›αž”αŸ†αžŽαž„αž‡αž½αžŸαž‡αž»αž›αž€αŸ†αž αž»αžŸ αž“αž·αž„αž”αž“αŸ’αžαŸ‚αž˜αžŸαŸαžœαžΆαž€αž˜αŸ’αž˜αžαŸ’αž˜αžΈ) αž αžΎαž™αž‡αžΆαž‘αžΌαž‘αŸ… αžŸαŸ’αžαžΆαž“αž—αžΆαž– = αž’αžΆαž€αŸ’αžšαž€αŸ‹αŸ”

αžŠαŸ†αžŽαŸ„αŸ‡αžŸαŸ’αžšαžΆαž™αž‘αžΈ 1: αž“αŸ…αžαŸ‚αžšαž€αŸ’αžŸαžΆαž‘αž»αž€αžŸαŸ’αž‚αŸ’αžšαžΈαž”αž“αŸ…αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸ αž”αŸ‰αž»αž“αŸ’αžαŸ‚αž…αž˜αŸ’αž›αž„αžœαžΆαžšαžΆαž›αŸ‹αž–αŸαž›αž†αŸ’αž›αž„αž€αžΆαžαŸ‹ scp. αž”αž“αŸ’αž‘αžΆαž”αŸ‹αž˜αž€αž—αŸ’αž‡αžΆαž”αŸ‹αžαžΆαž˜αžšαž™αŸˆ ssh αž“αž·αž„αž”αŸ’αžšαžαž·αž”αžαŸ’αžαž·αžŸαŸ’αž‚αŸ’αžšαžΈαž”αž‡αžΆαž˜αž½αž™αž“αžΉαž„αž’αžΆαž‚αž»αž™αž˜αŸ‰αž„αŸ‹αž…αžΆαŸ†αž”αžΆαž…αŸ‹αŸ”

αž‚αž»αžŽαžœαž·αž”αžαŸ’αžαž·:

  • αžŸαž€αž˜αŸ’αž˜αž—αžΆαž–αž–αžΈαžšαž‡αŸ†αž“αž½αžŸαž±αŸ’αž™αž˜αž½αž™αŸ”
  • αž”αŸ’αžšαž αŸ‚αž›αž‡αžΆαž˜αž·αž“αž˜αžΆαž“αž€αž“αŸ’αž›αŸ‚αž„αžŠαŸ‚αž›αž’αŸ’αž“αž€αž…αž˜αŸ’αž›αž„ αž¬αž”αŸ’αžšαž αŸ‚αž›αž‡αžΆαž˜αž·αž“αž˜αžΆαž“αžŸαž·αž‘αŸ’αž’αž·αž…αžΌαž›αž”αŸ’αžšαžΎαžœαžΆ αž¬αžŸαŸ’αž‚αŸ’αžšαžΈαž”αž’αžΆαž…αžαŸ’αžšαžΌαžœαž”αžΆαž“αž”αŸ’αžšαžαž·αž”αžαŸ’αžαž·αž“αŸ…αž–αŸαž›αž‡αŸ†αž“αž½αžŸαŸ”
  • αžœαžΆαžαŸ’αžšαžΌαžœαž”αžΆαž“αžŽαŸ‚αž“αžΆαŸ†αž±αŸ’αž™αžŸαž˜αŸ’αž’αžΆαžαž”αž“αŸ’αž‘αžΆαž”αŸ‹αž–αžΈαžαŸ’αž›αž½αž“αž’αŸ’αž“αž€ (αž›αž»αž”αžŸαŸ’αž‚αŸ’αžšαžΈαž”) αŸ”
  • αžŸαž€αž˜αŸ’αž˜αž—αžΆαž–αž”αžΈαžšαž½αž…αž αžΎαž™αŸ”

αžŠαŸ†αžŽαŸ„αŸ‡αžŸαŸ’αžšαžΆαž™ 2:

  • αž‘αž»αž€αžαŸ‚αž“αž·αž™αž˜αž“αŸαž™αž˜αž»αžαž„αžΆαžšαž“αŸ…αž€αŸ’αž“αž»αž„αžŸαŸ’αž‚αŸ’αžšαžΈαž” αž αžΎαž™αž˜αž·αž“αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž’αŸ’αžœαžΈαž‘αžΆαŸ†αž„αž’αžŸαŸ‹αŸ”
  • αžŠαŸ„αž™αž˜αžΆαž“αž‡αŸ†αž“αž½αž™αž–αžΈ sed αž”αž“αŸ’αžαŸ‚αž˜αž˜αž»αžαž„αžΆαžšαž αŸ…αž‘αŸ…αž…αž»αž„αž”αž‰αŸ’αž…αž”αŸ‹
  • αž•αŸ’αž‰αžΎαžœαžΆαž‘αžΆαŸ†αž„αž’αžŸαŸ‹αžŠαŸ„αž™αž•αŸ’αž‘αžΆαž›αŸ‹αž‘αŸ… shh αžαžΆαž˜αžšαž™αŸˆαž”αŸ†αž–αž„αŸ‹ (|)

αž”αŸ’αžšαž»αžŸ:

  • αž–αž·αžαž‡αžΆαž‚αŸ’αž˜αžΆαž“αžšαžŠαŸ’αž‹
  • αž˜αž·αž“αž˜αžΆαž“αž’αžΆαžαž» boilerplate
  • αž˜αžΆαž“αž’αžΆαžšαž˜αŸ’αž˜αžŽαŸαžαŸ’αžšαž‡αžΆαž€αŸ‹

αž…αžΌαžšαž™αžΎαž„αž’αŸ’αžœαžΎαžœαžΆαžŠαŸ„αž™αž‚αŸ’αž˜αžΆαž“ Ansible αŸ” αž”αžΆαž‘ αž’αŸ’αžœαžΈαŸ—αžαŸ’αžšαžΌαžœαž”αžΆαž“αž”αž„αŸ’αž€αžΎαžαžšαž½αž…αž αžΎαž™αŸ” αž”αžΆαž‘ αž€αž„αŸ‹αŸ” αž˜αž€β€‹αž˜αžΎαž›β€‹αžαžΆβ€‹αžαžΎβ€‹αž˜αŸ‰αžΌαžαžΌβ€‹αž“αŸαŸ‡β€‹αž˜αžΆαž“β€‹αž›αž€αŸ’αžαžŽαŸˆβ€‹αžŸαžΆαž˜αž‰αŸ’αž‰ αž†αžΎαžαž†αžΆαž™ αž“αž·αž„β€‹αžαžΌαž…β€‹αž”αŸ‰αž»αžŽαŸ’αžŽαžΆαŸ–

$ cat << 'EOF' > deploy.sh
#!/bin/bash

usage_msg="Usage: $0 ssh_address local_image_tag"
ssh_address=${1?$usage_msg}
image_name=${2?$usage_msg}

echo "Connecting to '$ssh_address' via ssh to seamlessly deploy '$image_name'..."
( sed "$a deploy $image_name" | ssh -T $ssh_address ) << 'END_OF_SCRIPT'
deploy() {
    echo "Yay! The '${FUNCNAME[0]}' function is executing on '$(hostname)' with argument '$1'"
}
END_OF_SCRIPT
EOF

$ chmod +x deploy.sh

$ ./deploy.sh localhost magic-porridge-pot
Connecting to localhost...
Yay! The 'deploy' function is executing on 'hut' with argument 'magic-porridge-pot'

αž‘αŸ„αŸ‡αž™αŸ‰αžΆαž„αžŽαžΆαž€αŸαžŠαŸ„αž™ αž™αžΎαž„αž˜αž·αž“αž’αžΆαž…αž”αŸ’αžšαžΆαž€αžŠαžαžΆαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž–αžΈαž…αž˜αŸ’αž„αžΆαž™αž˜αžΆαž“ bash αž‚αŸ’αžšαž”αŸ‹αž‚αŸ’αžšαžΆαž“αŸ‹αž‘αŸ αžŠαžΌαž…αŸ’αž“αŸαŸ‡αž™αžΎαž„αž“αžΉαž„αž”αž“αŸ’αžαŸ‚αž˜αž€αžΆαžšαžαŸ’αžšαž½αžαž–αž·αž“αž·αžαŸ’αž™αžαžΌαž…αž˜αž½αž™αž“αŸ…αžŠαžΎαž˜αžŠαŸ†αž”αžΌαž„ (αž“αŸαŸ‡αž‚αžΊαž‡αŸ†αž“αž½αžŸαž±αŸ’αž™ shellbang):

if [ "$SHELL" != "/bin/bash" ]
then
    echo "The '$SHELL' shell is not supported by 'deploy.sh'. Set a '/bin/bash' shell for '$USER@$HOSTNAME'."
    exit 1
fi

αž αžΎαž™αž₯αž‘αžΌαžœαž“αŸαŸ‡αžœαžΆαž‡αžΆαž€αžΆαžšαž–αž·αžαŸ–

$ docker exec reverse-proxy rm /etc/nginx/conf.d/default.conf

$ wget -qO deploy.sh https://git.io/JUURc

$ chmod +x deploy.sh

$ ./deploy.sh localhost uptimer
Sending gzipped image 'uptimer' to 'localhost' via ssh...
Loaded image: uptimer:latest
Connecting to 'localhost' via ssh to seamlessly deploy 'uptimer'...
Deploying 'uptimer_GREEN' in place of 'uptimer_BLUE'...
06f5bc70e9c4f930e7b1f826ae2ca2f536023cc01e82c2b97b2c84d68048b18a
Container started. Checking health...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
  HTTP/1.0 503 Service Unavailable
wget: server returned error: HTTP/1.0 503 Service Unavailable
New 'uptimer_GREEN' service is not ready yet. Waiting (1)...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
  HTTP/1.0 503 Service Unavailable
wget: server returned error: HTTP/1.0 503 Service Unavailable
New 'uptimer_GREEN' service is not ready yet. Waiting (2)...
Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:
  HTTP/1.0 200 OK
  Server: BaseHTTP/0.6 Python/3.8.3
  Date: Sat, 22 Aug 2020 20:15:50 GMT
  Content-Type: text/html

New 'uptimer_GREEN' service seems OK. Switching heads...
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
2020/08/22 20:15:54 [notice] 97#97: signal process started
The 'uptimer_GREEN' service is live!
Killing 'uptimer_BLUE'...
uptimer_BLUE
Total reclaimed space: 0B
Deployment successful!

αž₯αž‘αžΌαžœαž“αŸαŸ‡αž’αŸ’αž“αž€αž’αžΆαž…αž”αžΎαž€ http://localhost/ αž“αŸ…αž€αŸ’αž“αž»αž„αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαžšαž»αž€αžšαž€αžαžΆαž˜αž’αŸŠαžΈαž“αž’αžΊαžŽαž·αž αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž€αžΆαžšαžŠαžΆαž€αŸ‹αž–αž„αŸ’αžšαžΆαž™αž˜αŸ’αžαž„αž‘αŸ€αž αž αžΎαž™αžαŸ’αžšαžΌαžœαž”αŸ’αžšαžΆαž€αžŠαžαžΆαžœαžΆαžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαžŠαŸ„αž™αžšαž›αžΌαž“αžŠαŸ„αž™αž’αŸ’αžœαžΎαž”αž…αŸ’αž…αž»αž”αŸ’αž”αž“αŸ’αž“αž—αžΆαž–αž‘αŸ†αž–αŸαžšαžŠαŸ„αž™αž™αŸ„αž„αžαžΆαž˜αžŸαŸŠαžΈαžŒαžΈαž€αŸ†αž‘αž»αž„αž–αŸαž›αž”αŸ’αž›αž„αŸ‹αŸ”

αž€αž»αŸ†β€‹αž—αŸ’αž›αŸαž…β€‹αžŸαž˜αŸ’αž’αžΆαžβ€‹αž€αŸ’αžšαŸ„αž™β€‹αž–αŸαž›β€‹αž’αŸ’αžœαžΎβ€‹αž€αžΆαžšαŸ– ៣

$ docker rm -f uptimer_GREEN reverse-proxy 
uptimer_GREEN
reverse-proxy

$ docker network rm web-gateway 
web-gateway

$ cd ..

$ rm -r blue-green-deployment

αž”αŸ’αžšαž—αž–: www.habr.com