เจ˜เฉฑเจŸเฉ‹-เจ˜เฉฑเจŸ เจ‰เจœเจฐเจคเจพเจ‚ 'เจคเฉ‡ เจจเฉ€เจฒเจพ-เจนเจฐเจพ เจคเฉˆเจจเจพเจคเฉ€

เจ‡เจธ เจฒเฉ‡เจ– เจตเจฟเจš เจ…เจธเฉ€เจ‚ เจตเจฐเจคเจฆเฉ‡ เจนเจพเจ‚ bash, SSH, เจกเฉŒเจ•เจฐ ะธ nginx เจ…เจธเฉ€เจ‚ เจตเฉˆเจฌ เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจฆเจพ เจ‡เฉฑเจ• เจธเจนเจฟเจœ เจ–เจพเจ•เจพ เจตเจฟเจตเจธเจฅเจฟเจค เจ•เจฐเจพเจ‚เจ—เฉ‡เฅค เจจเฉ€เจฒเจพ-เจนเจฐเจพ เจคเฉˆเจจเจพเจคเฉ€ เจ‡เฉฑเจ• เจคเจ•เจจเฉ€เจ• เจนเฉˆ เจœเฉ‹ เจคเฉเจนเจพเจจเฉ‚เฉฐ เจ‡เฉฑเจ• เจฌเฉ‡เจจเจคเฉ€ เจจเฉ‚เฉฐ เจฐเฉฑเจฆ เจ•เฉ€เจคเฉ‡ เจฌเจฟเจจเจพเจ‚ เจ‡เฉฑเจ• เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจจเฉ‚เฉฐ เจคเฉเจฐเฉฐเจค เจ…เจชเจกเฉ‡เจŸ เจ•เจฐเจจ เจฆเฉ€ เจ†เจ—เจฟเจ† เจฆเจฟเฉฐเจฆเฉ€ เจนเฉˆเฅค เจ‡เจน เจœเจผเฉ€เจฐเฉ‹ เจกเจพเจŠเจจเจŸเจพเจˆเจฎ เจคเฉˆเจจเจพเจคเฉ€ เจฐเจฃเจจเฉ€เจคเฉ€เจ†เจ‚ เจตเจฟเฉฑเจšเฉ‹เจ‚ เจ‡เฉฑเจ• เจนเฉˆ เจ…เจคเฉ‡ เจ‡เฉฑเจ• เจ‰เจฆเจพเจนเจฐเจจ เจฆเฉ‡ เจจเจพเจฒ เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจเจพเจ‚ เจฒเจˆ เจธเจญ เจคเฉ‹เจ‚ เจ…เจจเฉเจ•เฉ‚เจฒ เจนเฉˆ, เจชเจฐ เจจเฉ‡เฉœเฉ‡-เจคเฉ‡เฉœเฉ‡ เจ‡เฉฑเจ• เจธเฉˆเจ•เจฟเฉฐเจก, เจฐเฉˆเจกเฉ€-เจŸเฉ‚-เจฐเจจ เจ‰เจฆเจพเจนเจฐเจจ เจฒเฉ‹เจก เจ•เจฐเจจ เจฆเฉ€ เจธเจฎเจฐเฉฑเจฅเจพ เจนเฉˆเฅค

เจฎเฉฐเจจ เจฒเจ“ เจ•เจฟ เจคเฉเจนเจพเจกเฉ‡ เจ•เฉ‹เจฒ เจ‡เฉฑเจ• เจตเฉˆเจฌ เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจนเฉˆ เจœเจฟเจธ เจจเจพเจฒ เจฌเจนเฉเจค เจธเจพเจฐเฉ‡ เจ—เจพเจนเจ• เจธเจฐเจ—เจฐเจฎเฉ€ เจจเจพเจฒ เจ•เฉฐเจฎ เจ•เจฐ เจฐเจนเฉ‡ เจนเจจ, เจ…เจคเฉ‡ เจ‡เจธเจฆเฉ‡ เจฒเจˆ เจ•เฉเจ เจธเจ•เจฟเฉฐเจŸเจพเจ‚ เจฒเจˆ เจฒเฉ‡เจŸเจฃ เจฆเจพ เจ•เฉ‹เจˆ เจคเจฐเฉ€เจ•เจพ เจจเจนเฉ€เจ‚ เจนเฉˆเฅค เจ…เจคเฉ‡ เจคเฉเจนเจพเจจเฉ‚เฉฐ เจ…เจธเจฒ เจตเจฟเฉฑเจš เจ‡เฉฑเจ• เจฒเจพเจ‡เจฌเฉเจฐเฉ‡เจฐเฉ€ เจ…เฉฑเจชเจกเฉ‡เจŸ, เจ‡เฉฑเจ• เจฌเฉฑเจ— เจซเจฟเจ•เจธ, เจœเจพเจ‚ เจ‡เฉฑเจ• เจจเจตเฉ€เจ‚ เจธเจผเจพเจจเจฆเจพเจฐ เจตเจฟเจธเจผเฉ‡เจธเจผเจคเจพ เจจเฉ‚เฉฐ เจฐเฉ‹เจฒ เจ†เจŠเจŸ เจ•เจฐเจจ เจฆเฉ€ เจฒเฉ‹เฉœ เจนเฉˆเฅค เจ‡เฉฑเจ• เจ†เจฎ เจธเจฅเจฟเจคเฉ€ เจตเจฟเฉฑเจš, เจคเฉเจนเจพเจจเฉ‚เฉฐ เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจจเฉ‚เฉฐ เจฐเฉ‹เจ•เจฃ, เจ‡เจธเจจเฉ‚เฉฐ เจฌเจฆเจฒเจฃ เจ…เจคเฉ‡ เจ‡เจธเจจเฉ‚เฉฐ เจฆเฉเจฌเจพเจฐเจพ เจธเจผเฉเจฐเฉ‚ เจ•เจฐเจจ เจฆเฉ€ เจฒเฉ‹เฉœ เจนเฉ‹เจตเฉ‡เจ—เฉ€เฅค เจกเฉŒเจ•เจฐ เจฆเฉ‡ เจฎเจพเจฎเจฒเฉ‡ เจตเจฟเฉฑเจš, เจคเฉเจธเฉ€เจ‚ เจชเจนเจฟเจฒเจพเจ‚ เจ‡เจธเจจเฉ‚เฉฐ เจฌเจฆเจฒ เจธเจ•เจฆเฉ‡ เจนเฉ‹, เจซเจฟเจฐ เจ‡เจธเจจเฉ‚เฉฐ เจฎเฉเฉœ เจšเจพเจฒเฉ‚ เจ•เจฐ เจธเจ•เจฆเฉ‡ เจนเฉ‹, เจชเจฐ เจ…เจœเฉ‡ เจตเฉ€ เจ‡เฉฑเจ• เจธเจฎเจพเจ‚ เจนเฉ‹เจตเฉ‡เจ—เจพ เจœเจฟเจธ เจตเจฟเฉฑเจš เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจฒเจˆ เจฌเฉ‡เจจเจคเฉ€เจ†เจ‚ 'เจคเฉ‡ เจ•เจพเจฐเจตเจพเจˆ เจจเจนเฉ€เจ‚ เจ•เฉ€เจคเฉ€ เจœเจพเจตเฉ‡เจ—เฉ€, เจ•เจฟเจ‰เจ‚เจ•เจฟ เจ†เจฎ เจคเฉŒเจฐ 'เจคเฉ‡ เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจจเฉ‚เฉฐ เจฒเฉ‹เจก เจนเฉ‹เจฃ เจตเจฟเฉฑเจš เจ•เฉเจ เจธเจฎเจพเจ‚ เจฒเฉฑเจ—เจฆเจพ เจนเฉˆเฅค เจ•เฉ€ เจœเฉ‡ เจ‡เจน เจธเจผเฉเจฐเฉ‚ เจนเฉเฉฐเจฆเจพ เจนเฉˆ, เจชเจฐ เจ…เจฏเฉ‹เจ— เจนเฉ‹ เจœเจพเจ‚เจฆเจพ เจนเฉˆ? เจ‡เจน เจธเจฎเฉฑเจธเจฟเจ† เจนเฉˆ, เจ†เจ“ เจ‡เจธเจจเฉ‚เฉฐ เจ˜เฉฑเจŸ เจคเฉ‹เจ‚ เจ˜เฉฑเจŸ เจธเจพเจงเจจเจพเจ‚ เจจเจพเจฒ เจ…เจคเฉ‡ เจœเจฟเฉฐเจจเจพ เจธเฉฐเจญเจต เจนเฉ‹ เจธเจ•เฉ‡ เจธเจผเจพเจจเจฆเจพเจฐ เจขเฉฐเจ— เจจเจพเจฒ เจนเฉฑเจฒ เจ•เจฐเฉ€เจเฅค

เจฌเฉ‡เจฆเจพเจ…เจตเจพ: เจœเจผเจฟเจ†เจฆเจพเจคเจฐ เจฒเฉ‡เจ– เจ‡เฉฑเจ• เจชเฉเจฐเจฏเฉ‹เจ—เจพเจคเจฎเจ• เจซเจพเจฐเจฎเฉˆเจŸ เจตเจฟเฉฑเจš เจชเฉ‡เจธเจผ เจ•เฉ€เจคเฉ‡ เจ—เจ เจนเจจ - เจ‡เฉฑเจ• เจ•เฉฐเจธเฉ‹เจฒ เจธเฉˆเจธเจผเจจ เจฆเฉ€ เจฐเจฟเจ•เจพเจฐเจกเจฟเฉฐเจ— เจฆเฉ‡ เจฐเฉ‚เจช เจตเจฟเฉฑเจšเฅค เจ‰เจฎเฉ€เจฆ เจนเฉˆ เจ•เจฟ เจ‡เจน เจธเจฎเจเจฃเจพ เจฌเจนเฉเจค เจ”เจ–เจพ เจจเจนเฉ€เจ‚ เจนเฉ‹เจตเฉ‡เจ—เจพ เจ…เจคเฉ‡ เจ•เฉ‹เจก เจ†เจชเจฃเฉ‡ เจ†เจช เจจเฉ‚เฉฐ เจ•เจพเจซเจผเฉ€ เจฆเจธเจคเจพเจตเฉ‡เจœเจผ เจฆเฉ‡เจตเฉ‡เจ—เจพเฅค เจตเจพเจฏเฉ‚เจฎเฉฐเจกเจฒ เจฒเจˆ, เจ•เจฒเจชเจจเจพ เจ•เจฐเฉ‹ เจ•เจฟ เจ‡เจน เจธเจฟเจฐเจซเจผ เจ•เฉ‹เจก เจธเจจเจฟเฉฑเจชเจŸ เจจเจนเฉ€เจ‚ เจนเจจ, เจชเจฐ เจ‡เฉฑเจ• "เจ†เจ‡เจฐเจจ" เจŸเฉˆเจฒเฉ€เจŸเจพเจˆเจช เจคเฉ‹เจ‚ เจ•เจพเจ—เจœเจผ เจนเจจเฅค

เจ˜เฉฑเจŸเฉ‹-เจ˜เฉฑเจŸ เจ‰เจœเจฐเจคเจพเจ‚ 'เจคเฉ‡ เจจเฉ€เจฒเจพ-เจนเจฐเจพ เจคเฉˆเจจเจพเจคเฉ€

เจฆเจฟเจฒเจšเจธเจช เจคเจ•เจจเฉ€เจ•เจพเจ‚ เจœเฉ‹ เจธเจฟเจฐเจซเจผ เจ•เฉ‹เจก เจจเฉ‚เฉฐ เจชเฉœเฉเจน เจ•เฉ‡ เจ—เฉ‚เจ—เจฒ เจฒเจˆ เจฎเฉเจธเจผเจ•เจฒ เจนเจจ, เจนเจฐเฉ‡เจ• เจญเจพเจ— เจฆเฉ‡ เจธเจผเฉเจฐเฉ‚ เจตเจฟเฉฑเจš เจตเจฐเจฃเจจ เจ•เฉ€เจคเฉ€เจ†เจ‚ เจ—เจˆเจ†เจ‚ เจนเจจเฅค เจœเฉ‡ เจ•เฉเจ เจนเฉ‹เจฐ เจ…เจธเจชเจธเจผเจŸ เจนเฉˆ, เจคเจพเจ‚ เจ‡เจธ เจจเฉ‚เฉฐ เจ—เฉ‚เจ—เจฒ เจ•เจฐเฉ‹ เจ…เจคเฉ‡ เจ‡เจธ เจฆเฉ€ เจœเจพเจ‚เจš เจ•เจฐเฉ‹เฅค เจตเจฟเจ†เจ–เจฟเจ† เจธเจผเฉˆเฉฑเจฒ (เจ–เฉเจฆเจ•เจฟเจธเจฎเจคเฉ€ เจจเจพเจฒ, เจ‡เจน เจŸเฉˆเจฒเฉ€เจ—เฉเจฐเจพเจฎ เจจเฉ‚เฉฐ เจ…เจจเจฌเจฒเฉŒเจ• เจ•เจฐเจจ เจฆเฉ‡ เจ•เจพเจฐเจจ, เจฆเฉเจฌเจพเจฐเจพ เจ•เฉฐเจฎ เจ•เจฐเจฆเจพ เจนเฉˆ)เฅค เจœเฉ‡เจ•เจฐ เจคเฉเจธเฉ€เจ‚ เจ•เฉเจ เจตเฉ€ เจ—เฉ‚เจ—เจฒ เจจเจนเฉ€เจ‚ เจ•เจฐ เจธเจ•เจฆเฉ‡, เจคเจพเจ‚ เจŸเจฟเฉฑเจชเจฃเฉ€เจ†เจ‚ เจตเจฟเฉฑเจš เจชเฉเฉฑเจ›เฉ‹เฅค เจฎเฉˆเจจเฉ‚เฉฐ เจ…เจจเฉเจธเจพเจฐเฉ€ เจญเจพเจ— "เจฆเจฟเจฒเจšเจธเจช เจคเจ•เจจเฉ€เจ•เจพเจ‚" เจตเจฟเฉฑเจš เจœเฉ‹เฉœ เจ•เฉ‡ เจ–เฉเจธเจผเฉ€ เจนเฉ‹เจตเฉ‡เจ—เฉ€เฅค

เจ†เจ“ เจธเจผเฉเจฐเฉ‚ เจ•เจฐเฉ€เจเฅค

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

เจธเฉ‡เจตเจพ

เจ†เจ‰ เจ‡เฉฑเจ• เจชเฉเจฐเจฏเฉ‹เจ—เจพเจคเจฎเจ• เจธเฉ‡เจตเจพ เจ•เจฐเฉ€เจ เจ…เจคเฉ‡ เจ‡เจธเจจเฉ‚เฉฐ เจ‡เฉฑเจ• เจกเฉฑเจฌเฉ‡ เจตเจฟเฉฑเจš เจฐเฉฑเจ–เฉ‹.

เจฆเจฟเจฒเจšเจธเจช เจคเจ•เจจเฉ€เจ•

  • cat << EOF > file-name (เจ‡เฉฑเจฅเฉ‡ เจฆเจธเจคเจพเจตเฉ‡เจœเจผ + I/O เจฐเฉ€เจกเจพเจ‡เจฐเฉˆเจ•เจธเจผเจจ) เจ‡เฉฑเจ• เจ•เจฎเจพเจ‚เจก เจจเจพเจฒ เจฎเจฒเจŸเฉ€-เจฒเจพเจˆเจจ เจซเจพเจˆเจฒ เจฌเจฃเจพเจ‰เจฃ เจฆเจพ เจ‡เฉฑเจ• เจคเจฐเฉ€เจ•เจพ เจนเฉˆเฅค เจธเจญ เจ•เฉเจ bash เจคเฉฑเจ• เจชเฉœเฉเจนเจฆเจพ เจนเฉˆ /dev/stdin เจ‡เจธ เจฒเจพเจˆเจจ เจคเฉ‹เจ‚ เจฌเจพเจ…เจฆ เจ…เจคเฉ‡ เจฒเจพเจˆเจจ เจคเฉ‹เจ‚ เจชเจนเจฟเจฒเจพเจ‚ EOF เจตเจฟเฉฑเจš เจฆเจฐเจœ เจ•เฉ€เจคเจพ เจœเจพเจตเฉ‡เจ—เจพ file-name.
  • wget -qO- URL (เจตเจฟเจ†เจ–เจฟเจ† เจธเจผเฉˆเฉฑเจฒ) โ€” HTTP เจฆเฉเจ†เจฐเจพ เจชเฉเจฐเจพเจชเจค เจฆเจธเจคเจพเจตเฉ‡เจœเจผ เจจเฉ‚เฉฐ เจ†เจ‰เจŸเจชเฉเฉฑเจŸ เจ•เจฐเฉ‹ /dev/stdout (เจเจจเจพเจฒเจพเจ— curl URL).

เจชเฉเจฐเจฟเฉฐเจŸ เจ†เจŠเจŸ

เจฎเฉˆเจ‚ เจตเจฟเจธเจผเฉ‡เจธเจผ เจคเฉŒเจฐ 'เจคเฉ‡ เจชเจพเจˆเจฅเจจ เจฒเจˆ เจนเจพเจˆเจฒเจพเจˆเจŸเจฟเฉฐเจ— เจจเฉ‚เฉฐ เจธเจฎเจฐเฉฑเจฅ เจ•เจฐเจจ เจฒเจˆ เจธเจจเจฟเฉฑเจชเจŸ เจจเฉ‚เฉฐ เจคเฉ‹เฉœเจฆเจพ เจนเจพเจ‚. เจ…เฉฐเจค เจตเจฟเฉฑเจš เจ‡เจธ เจคเจฐเฉเจนเจพเจ‚ เจฆเจพ เจ‡เฉฑเจ• เจนเฉ‹เจฐ เจŸเฉเจ•เฉœเจพ เจนเฉ‹เจตเฉ‡เจ—เจพเฅค เจตเจฟเจšเจพเจฐ เจ•เจฐเฉ‹ เจ•เจฟ เจ‡เจนเจจเจพเจ‚ เจฅเจพเจตเจพเจ‚ 'เจคเฉ‡ เจนเจพเจˆเจฒเจพเจˆเจŸเจฟเฉฐเจ— เจตเจฟเจญเจพเจ— (เจœเจฟเฉฑเจฅเฉ‡ เจ•เฉ‹เจก เจจเฉ‚เฉฐ เจนเจพเจˆเจฒเจพเจˆเจŸเจฐเจพเจ‚ เจจเจพเจฒ เจนเฉฑเจฅเจพเจ‚ เจจเจพเจฒ เจฐเฉฐเจ—เจฟเจ† เจนเฉ‹เจ‡เจ† เจธเฉ€) เจจเฉ‚เฉฐ เจญเฉ‡เจœเจฃ เจฒเจˆ เจ•เจพเจ—เจœเจผ เจ•เฉฑเจŸเจฟเจ† เจ—เจฟเจ† เจธเฉ€, เจ…เจคเฉ‡ เจซเจฟเจฐ เจ‡เจนเจจเจพเจ‚ เจŸเฉเจ•เฉœเจฟเจ†เจ‚ เจจเฉ‚เฉฐ เจตเจพเจชเจธ เจšเจฟเจชเจ•เจพเจ‡เจ† เจ—เจฟเจ† เจธเฉ€เฅค

$ 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

เจ‰เจฒเจŸเจพ เจชเฉเจฐเฉŒเจ•เจธเฉ€

เจธเจพเจกเฉ€ เจ…เจฐเจœเจผเฉ€ เจจเฉ‚เฉฐ เจฌเจฟเจจเจพเจ‚ เจ•เจฟเจธเฉ‡ เจงเจฟเจ†เจจ เจฆเฉ‡ เจฌเจฆเจฒเจฃ เจฆเฉ‡ เจฏเฉ‹เจ— เจฌเจฃเจพเจ‰เจฃ เจฒเจˆ, เจ‡เจน เจœเจผเจฐเฉ‚เจฐเฉ€ เจนเฉˆ เจ•เจฟ เจ‡เจธเจฆเฉ‡ เจธเจพเจนเจฎเจฃเฉ‡ เจ•เฉ‹เจˆ เจนเฉ‹เจฐ เจนเจธเจคเฉ€ เจนเฉ‹เจตเฉ‡ เจœเฉ‹ เจ‡เจธเจฆเฉ€ เจคเจฌเจฆเฉ€เจฒเฉ€ เจจเฉ‚เฉฐ เจฒเฉเจ•เจพเจตเฉ‡เฅค เจ‡เจน เจ‡เฉฑเจ• เจตเฉˆเฉฑเจฌ เจธเจฐเจตเจฐ เจนเฉ‹ เจธเจ•เจฆเจพ เจนเฉˆ nginx ะฒ เจฐเจฟเจตเจฐเจธ เจชเฉเจฐเฉŒเจ•เจธเฉ€ เจฎเฉ‹เจก. เจ•เจฒเจพเจ‡เฉฐเจŸ เจ…เจคเฉ‡ เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจตเจฟเจšเจ•เจพเจฐ เจ‡เฉฑเจ• เจฐเจฟเจตเจฐเจธ เจชเฉเจฐเฉŒเจ•เจธเฉ€ เจธเจฅเจพเจชเจฟเจค เจ•เฉ€เจคเฉ€ เจœเจพเจ‚เจฆเฉ€ เจนเฉˆเฅค เจ‡เจน เจ—เจพเจนเจ•เจพเจ‚ เจฆเฉ€เจ†เจ‚ เจฌเฉ‡เจจเจคเฉ€เจ†เจ‚ เจจเฉ‚เฉฐ เจธเจตเฉ€เจ•เจพเจฐ เจ•เจฐเจฆเจพ เจนเฉˆ เจ…เจคเฉ‡ เจ‰เจนเจจเจพเจ‚ เจจเฉ‚เฉฐ เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจจเฉ‚เฉฐ เจ…เฉฑเจ—เฉ‡ เจญเฉ‡เจœเจฆเจพ เจนเฉˆ เจ…เจคเฉ‡ เจ—เจพเจนเจ•เจพเจ‚ เจจเฉ‚เฉฐ เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจฆเฉ‡ เจœเจตเจพเจฌเจพเจ‚ เจจเฉ‚เฉฐ เจ…เฉฑเจ—เฉ‡ เจญเฉ‡เจœเจฆเจพ เจนเฉˆเฅค

เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจ…เจคเฉ‡ เจฐเจฟเจตเจฐเจธ เจชเฉเจฐเฉŒเจ•เจธเฉ€ เจจเฉ‚เฉฐ เจกเฉŒเจ•เจฐ เจฆเฉ‡ เจ…เฉฐเจฆเจฐ เจœเฉ‹เฉœเจฟเจ† เจœเจพ เจธเจ•เจฆเจพ เจนเฉˆ เจกเฉŒเจ•เจฐ เจจเฉˆเฉฑเจŸเจตเจฐเจ•. เจ‡เจธ เจคเจฐเฉเจนเจพเจ‚, เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจตเจพเจฒเฉ‡ เจ•เฉฐเจŸเฉ‡เจจเจฐ เจจเฉ‚เฉฐ เจนเฉ‹เจธเจŸ เจธเจฟเจธเจŸเจฎ 'เจคเฉ‡ เจชเฉ‹เจฐเจŸ เจจเฉ‚เฉฐ เจ…เฉฑเจ—เฉ‡ เจญเฉ‡เจœเจฃ เจฆเฉ€ เจตเฉ€ เจฒเฉ‹เฉœ เจจเจนเฉ€เจ‚ เจนเฉˆ; เจ‡เจน เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจจเฉ‚เฉฐ เจฌเจพเจนเจฐเฉ€ เจ–เจคเจฐเจฟเจ†เจ‚ เจคเฉ‹เจ‚ เจตเฉฑเจง เจคเฉ‹เจ‚ เจตเฉฑเจง เจ…เจฒเฉฑเจ— เจ•เจฐเจจ เจฆเฉ€ เจ†เจ—เจฟเจ† เจฆเจฟเฉฐเจฆเจพ เจนเฉˆเฅค

เจœเฉ‡เจ•เจฐ เจฐเจฟเจตเจฐเจธ เจชเฉเจฐเฉŒเจ•เจธเฉ€ เจ•เจฟเจธเฉ‡ เจนเฉ‹เจฐ เจนเฉ‹เจธเจŸ 'เจคเฉ‡ เจฐเจนเจฟเฉฐเจฆเฉ€ เจนเฉˆ, เจคเจพเจ‚ เจคเฉเจนเจพเจจเฉ‚เฉฐ เจกเฉŒเจ•เจฐ เจจเฉˆเฉฑเจŸเจตเจฐเจ• เจจเฉ‚เฉฐ เจ›เฉฑเจกเจฃเจพ เจชเจตเฉ‡เจ—เจพ เจ…เจคเฉ‡ เจชเฉ‹เจฐเจŸ เจจเฉ‚เฉฐ เจ…เฉฑเจ—เฉ‡ เจญเฉ‡เจœเจฆเฉ‡ เจนเฉ‹เจ, เจนเฉ‹เจธเจŸ เจจเฉˆเฉฑเจŸเจตเจฐเจ• เจฐเจพเจนเฉ€เจ‚ เจฐเจฟเจตเจฐเจธ เจชเฉเจฐเฉŒเจ•เจธเฉ€ เจจเจพเจฒ เจเจชเจฒเฉ€เจ•เฉ‡เจธเจผเจจ เจจเฉ‚เฉฐ เจ•เจจเฉˆเจ•เจŸ เจ•เจฐเจจเจพ เจนเฉ‹เจตเฉ‡เจ—เจพเฅค เจเจชเจธ เจชเฉˆเจฐเจพเจฎเฉ€เจŸเจฐ --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 เจธเจฟเจธเจŸเจฎ เจฒเจˆ) เจจเจฟเจฐเจงเจพเจฐเจค เจ•เจฐเจจเจพ เจนเฉˆ เจ…เจคเฉ‡ เจซเจฟเจฐ เจ‡เจธเจจเฉ‚เฉฐ เจธเจฐเจตเจฐ เจคเฉ‡ เจŸเฉเจฐเจพเจ‚เจธเจซเจฐ เจ•เจฐเจจเจพ เจนเฉˆเฅค

เจšเจฟเฉฑเจคเจฐเจพเจ‚ เจฆเจพ เจคเจฌเจพเจฆเจฒเจพ เจ•เจฐเจจเจพ

เจฌเจฆเจ•เจฟเจธเจฎเจคเฉ€ เจจเจพเจฒ, เจšเจฟเฉฑเจคเจฐเจพเจ‚ เจจเฉ‚เฉฐ เจฒเฉ‹เจ•เจฒเจนเฉ‹เจธเจŸ เจคเฉ‹เจ‚ เจฒเฉ‹เจ•เจฒเจนเฉ‹เจธเจŸ เจตเจฟเฉฑเจš เจŸเฉเจฐเจพเจ‚เจธเจซเจฐ เจ•เจฐเจจ เจฆเจพ เจ•เฉ‹เจˆ เจฎเจคเจฒเจฌ เจจเจนเฉ€เจ‚ เจนเฉˆ, เจ‡เจธเจฒเจˆ เจ‡เจธ เจญเจพเจ— เจฆเฉ€ เจ–เฉ‹เจœ เจคเจพเจ‚ เจนเฉ€ เจ•เฉ€เจคเฉ€ เจœเจพ เจธเจ•เจฆเฉ€ เจนเฉˆ เจœเฉ‡เจ•เจฐ เจคเฉเจนเจพเจกเฉ‡ เจ•เฉ‹เจฒ เจกเฉŒเจ•เจฐ เจฆเฉ‡ เจจเจพเจฒ เจฆเฉ‹ เจนเฉ‹เจธเจŸ เจนเจจ. เจ˜เฉฑเจŸเฉ‹ เจ˜เฉฑเจŸ เจ‡เจน เจ‡เจธ เจคเจฐเฉเจนเจพเจ‚ เจฆเจฟเจ–เจพเจˆ เจฆเจฟเฉฐเจฆเจพ เจนเฉˆ:

$ 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. เจ•เจฟเจธเฉ‡ เจนเฉ‹เจฐ เจนเฉ‹เจธเจŸ เจคเฉ‹เจ‚ เจกเฉŒเจ•เจฐ เจกเฉˆเจฎเจจ เจธเจฐเจตเจฐ เจจเจพเจฒ เจœเฉเฉœเฉ‹:
    1. เจตเจพเจคเจพเจตเจฐเจฃ เจตเฉ‡เจฐเฉ€เจเจฌเจฒ DOCKER_HOST.
    2. เจ•เจฎเจพเจ‚เจก เจฒเจพเจˆเจจ เจตเจฟเจ•เจฒเจช -H เจœ --host เจธเจพเจงเจจ docker-compose.
    3. docker context

เจฆเฉ‚เจœเฉ€ เจตเจฟเจงเฉ€ (เจ‡เจธ เจฆเฉ‡ เจฒเจพเจ—เฉ‚ เจ•เจฐเจจ เจฒเจˆ เจคเจฟเฉฐเจจ เจตเจฟเจ•เจฒเจชเจพเจ‚ เจฆเฉ‡ เจจเจพเจฒ) เจฒเฉ‡เจ– เจตเจฟเฉฑเจš เจšเฉฐเจ—เฉ€ เจคเจฐเฉเจนเจพเจ‚ เจฆเฉฑเจธเจฟเจ† เจ—เจฟเจ† เจนเฉˆ เจกเฉŒเจ•เจฐ-เจ•เฉฐเจชเฉ‹เจœเจผ เจจเจพเจฒ เจฐเจฟเจฎเฉ‹เจŸ เจกเฉŒเจ•เจฐ เจนเฉ‹เจธเจŸเจพเจ‚ 'เจคเฉ‡ เจ•เจฟเจตเฉ‡เจ‚ เจคเฉˆเจจเจพเจค เจ•เจฐเจจเจพ เจนเฉˆ.

deploy.sh

เจนเฉเจฃ เจ†เจ‰ เจ‰เจน เจธเจญ เจ•เฉเจ เจ‡เจ•เฉฑเจ เจพ เจ•เจฐเฉ€เจ เจœเฉ‹ เจ…เจธเฉ€เจ‚ เจนเฉฑเจฅเฉ€เจ‚ เจ•เฉ€เจคเจพ เจ‡เฉฑเจ• เจธเจ•เฉเจฐเจฟเจชเจŸ เจตเจฟเฉฑเจšเฅค เจ†เจ‰ เจธเจฟเจ–เจฐ-เจชเฉฑเจงเจฐ เจฆเฉ‡ เจซเฉฐเจ•เจธเจผเจจ เจจเจพเจฒ เจธเจผเฉเจฐเฉ‚ เจ•เจฐเฉ€เจ, เจ…เจคเฉ‡ เจซเจฟเจฐ เจ‡เจธ เจตเจฟเฉฑเจš เจตเจฐเจคเฉ‡ เจ—เจ เจนเฉ‹เจฐเจพเจ‚ เจจเฉ‚เฉฐ เจตเฉ‡เจ–เฉ€เจเฅค

เจฆเจฟเจฒเจšเจธเจช เจคเจ•เจจเฉ€เจ•

  • ${parameter?err_msg} - เจฌเฉˆเจธเจผ เจœเจพเจฆเฉ‚ เจฆเฉ‡ เจœเจพเจฆเฉ‚ เจตเจฟเฉฑเจšเฉ‹เจ‚ เจ‡เฉฑเจ• (เจ‰เจฐเจซเจผ เจชเฉˆเจฐเจพเจฎเฉ€เจŸเจฐ เจฌเจฆเจฒ). เจœเฉ‡ parameter เจจเจฟเจฐเจฆเจฟเจธเจผเจŸ เจจเจนเฉ€เจ‚, เจ†เจ‰เจŸเจชเฉเฉฑเจŸ err_msg เจ…เจคเฉ‡ เจ•เฉ‹เจก 1 เจจเจพเจฒ เจฌเจพเจนเจฐ เจœเจพเจ“เฅค
  • docker --log-driver journald โ€” เจฎเฉ‚เจฒ เจฐเฉ‚เจช เจตเจฟเฉฑเจš, เจกเฉŒเจ•เจฐ เจฒเฉŒเจ—เจฟเฉฐเจ— เจกเจฐเจพเจˆเจตเจฐ เจฌเจฟเจจเจพเจ‚ เจ•เจฟเจธเฉ‡ เจฐเฉ‹เจŸเฉ‡เจธเจผเจจ เจฆเฉ‡ เจ‡เฉฑเจ• เจŸเฉˆเจ•เจธเจŸ เจซเจพเจˆเจฒ เจนเฉˆเฅค เจ‡เจธ เจชเจนเฉเฉฐเจš เจจเจพเจฒ, เจฒเจพเจ— เจคเฉ‡เจœเจผเฉ€ เจจเจพเจฒ เจชเฉ‚เจฐเฉ€ เจกเจฟเจธเจ• เจจเฉ‚เฉฐ เจญเจฐ เจฆเจฟเฉฐเจฆเฉ‡ เจนเจจ, เจ‡เจธเจฒเจˆ เจ‰เจคเจชเจพเจฆเจจ เจฆเฉ‡ เจตเจพเจคเจพเจตเจฐเจฃ เจฒเจˆ เจกเจฐเจพเจˆเจตเจฐ เจจเฉ‚เฉฐ เจ‡เฉฑเจ• เจšเฉเจธเจค เจตเจฟเฉฑเจš เจฌเจฆเจฒเจฃเจพ เจœเจผเจฐเฉ‚เจฐเฉ€ เจนเฉˆเฅค

เจคเฉˆเจจเจพเจคเฉ€ เจธเจ•เฉเจฐเจฟเจชเจŸ

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 เจฅเฉ‹เฉœเฉ€ เจตเจฟเจ†เจ–เจฟเจ† เจฆเฉ€ เจฒเฉ‹เฉœ เจนเฉˆ:

เจ‡เจน เจ‡เฉฑเจ• เจจเฉฐเจฌเจฐ เจ•เจฟเจ‰เจ‚ เจตเจพเจชเจธ เจ•เจฐเจฆเจพ เจนเฉˆ เจ…เจคเฉ‡ เจ‡เฉฑเจ• เจธเจคเจฐ เจจเฉ‚เฉฐ เจ†เจ‰เจŸเจชเฉเฉฑเจŸ เจจเจนเฉ€เจ‚ เจ•เจฐเจฆเจพ เจนเฉˆ?

เจตเฉˆเจธเฉ‡ เจตเฉ€, เจ•เจพเจฒเจฟเฉฐเจ— เจซเฉฐเจ•เจธเจผเจจ เจตเจฟเฉฑเจš เจ…เจธเฉ€เจ‚ เจ‡เจธเจฆเฉ‡ เจ•เฉฐเจฎ เจฆเฉ‡ เจจเจคเฉ€เจœเฉ‡ เจฆเฉ€ เจœเจพเจ‚เจš เจ•เจฐเจฆเฉ‡ เจนเจพเจ‚, เจ…เจคเฉ‡ เจฌเฉˆเจธเจผ เจฆเฉ€ เจตเจฐเจคเฉ‹เจ‚ เจ•เจฐเจ•เฉ‡ เจเจ—เจœเจผเจฟเจŸ เจ•เฉ‹เจก เจฆเฉ€ เจœเจพเจ‚เจš เจ•เจฐเจจเจพ เจ‡เฉฑเจ• เจธเจคเจฐ เจฆเฉ€ เจœเจพเจ‚เจš เจ•เจฐเจจ เจจเจพเจฒเฉ‹เจ‚ เจฌเจนเฉเจค เจธเฉŒเจ–เจพ เจนเฉˆเฅค เจ‡เจธ เจคเฉ‹เจ‚ เจ‡เจฒเจพเจตเจพ, เจ‡เจธ เจคเฉ‹เจ‚ เจ‡เฉฑเจ• เจธเจคเจฐ เจชเฉเจฐเจพเจชเจค เจ•เจฐเจจเจพ เจฌเจนเฉเจค เจธเฉŒเจ–เจพ เจนเฉˆ:
get-active-slot service && echo BLUE || echo GREEN.

เจ•เฉ€ เจคเจฟเฉฐเจจ เจธเจผเจฐเจคเจพเจ‚ เจ…เจธเจฒ เจตเจฟเฉฑเจš เจธเจพเจฐเฉ‡ เจฐเจพเจœเจพเจ‚ เจจเฉ‚เฉฐ เจตเฉฑเจ– เจ•เจฐเจจ เจฒเจˆ เจ•เจพเจซเจผเฉ€ เจนเจจ?

เจ˜เฉฑเจŸเฉ‹-เจ˜เฉฑเจŸ เจ‰เจœเจฐเจคเจพเจ‚ 'เจคเฉ‡ เจจเฉ€เจฒเจพ-เจนเจฐเจพ เจคเฉˆเจจเจพเจคเฉ€

เจ‡เฉฑเจฅเฉ‹เจ‚ เจคเฉฑเจ• เจ•เจฟ เจฆเฉ‹ เจ•เจพเจซเจผเฉ€ เจนเฉ‹เจฃเจ—เฉ‡, เจ†เจ–เจฐเฉ€ เจ‡เฉฑเจ• เจ‡เฉฑเจฅเฉ‡ เจธเจฟเจฐเจซ เจธเฉฐเจชเฉ‚เจฐเจจเจคเจพ เจฒเจˆ เจนเฉˆ, เจคเจพเจ‚ เจœเฉ‹ เจฒเจฟเจ–เจฃ เจฒเจˆ เจจเจพ เจนเฉ‹เจตเฉ‡ else.

เจธเจฟเจฐเจซ เจ‰เจน เจซเฉฐเจ•เจธเจผเจจ เจœเฉ‹ nginx เจธเฉฐเจฐเจšเจจเจพ เจจเฉ‚เฉฐ เจตเจพเจชเจธ เจ•เจฐเจฆเจพ เจนเฉˆ เจชเจฐเจฟเจญเจพเจธเจผเจฟเจค เจฐเจนเจฟเฉฐเจฆเจพ เจนเฉˆ: get-nginx-config service_name deployment_slot. เจธเจฟเจนเจค เจœเจพเจ‚เจš เจฆเฉ‡ เจธเจฎเจพเจจเจคเจพ เจจเจพเจฒ, เจ‡เฉฑเจฅเฉ‡ เจคเฉเจธเฉ€เจ‚ เจ•เจฟเจธเฉ‡ เจตเฉ€ เจธเฉ‡เจตเจพ เจฒเจˆ เจ•เฉ‹เจˆ เจตเฉ€ เจธเฉฐเจฐเจšเจจเจพ เจธเฉˆเจŸ เจ•เจฐ เจธเจ•เจฆเฉ‡ เจนเฉ‹เฅค เจฆเจฟเจฒเจšเจธเจช เจšเฉ€เจœเจผเจพเจ‚ เจตเจฟเฉฑเจšเฉ‹เจ‚ - เจ•เฉ‡เจตเจฒ cat <<- EOF, เจœเฉ‹ เจคเฉเจนเจพเจจเฉ‚เฉฐ เจธเจผเฉเจฐเฉ‚ เจตเจฟเฉฑเจš เจธเจพเจฐเฉ€เจ†เจ‚ เจŸเฉˆเจฌเจพเจ‚ เจจเฉ‚เฉฐ เจนเจŸเจพเจ‰เจฃ เจฆเฉ€ เจ†เจ—เจฟเจ† เจฆเจฟเฉฐเจฆเจพ เจนเฉˆเฅค เจ‡เจน เจธเฉฑเจš เจนเฉˆ เจ•เจฟ เจšเฉฐเจ—เฉ€ เจซเจพเจฐเจฎเฉˆเจŸเจฟเฉฐเจ— เจฆเฉ€ เจ•เฉ€เจฎเจค เจธเจชเฉ‡เจธ เจฆเฉ‡ เจจเจพเจฒ เจฎเจฟเจ•เจธเจก เจŸเฉˆเจฌเจธ เจนเฉˆ, เจœเจฟเจธเจจเฉ‚เฉฐ เจ…เฉฑเจœ เจฌเจนเฉเจค เจฌเฉเจฐเจพ เจฐเฉ‚เจช เจฎเฉฐเจจเจฟเจ† เจœเจพเจ‚เจฆเจพ เจนเฉˆ. เจชเจฐ bash เจŸเฉˆเจฌเจพเจ‚ เจจเฉ‚เฉฐ เจฌเจฒ เจฆเจฟเฉฐเจฆเจพ เจนเฉˆ, เจ…เจคเฉ‡ เจ‡เจน เจตเฉ€ เจšเฉฐเจ—เจพ เจนเฉ‹เจตเฉ‡เจ—เจพ เจ•เจฟ nginx เจธเฉฐเจฐเจšเจจเจพ เจตเจฟเฉฑเจš เจธเจงเจพเจฐเจจ เจซเจพเจฐเจฎเฉˆเจŸเจฟเฉฐเจ— เจนเฉ‹เจตเฉ‡เฅค เจธเฉฐเจ–เฉ‡เจช เจตเจฟเฉฑเจš, เจ‡เฉฑเจฅเฉ‡ เจธเจชเฉ‡เจธ เจฆเฉ‡ เจจเจพเจฒ เจŸเฉˆเจฌเจพเจ‚ เจจเฉ‚เฉฐ เจฎเจฟเจฒเจพเจ‰เจฃเจพ เจ…เจธเจฒ เจตเจฟเฉฑเจš เจธเจญ เจคเฉ‹เจ‚ เจญเฉˆเฉœเฉ‡ เจตเจฟเฉฑเจšเฉ‹เจ‚ เจธเจญ เจคเฉ‹เจ‚ เจตเจงเฉ€เจ† เจนเฉฑเจฒ เจœเจพเจชเจฆเจพ เจนเฉˆเฅค เจนเจพเจฒเจพเจ‚เจ•เจฟ, เจคเฉเจธเฉ€เจ‚ เจ‡เจธเจจเฉ‚เฉฐ เจนเฉ‡เจ เจพเจ‚ เจฆเจฟเฉฑเจคเฉ‡ เจธเจจเจฟเฉฑเจชเจŸ เจตเจฟเฉฑเจš เจจเจนเฉ€เจ‚ เจฆเฉ‡เจ– เจธเจ•เฉ‹เจ—เฉ‡, เจ•เจฟเจ‰เจ‚เจ•เจฟ เจนเฉˆเจฌเจฐ เจธเจพเจฐเฉ€เจ†เจ‚ เจŸเฉˆเจฌเจพเจ‚ เจจเฉ‚เฉฐ 4 เจธเจชเฉ‡เจธ เจตเจฟเฉฑเจš เจฌเจฆเจฒ เจ•เฉ‡ เจ…เจคเฉ‡ EOF เจจเฉ‚เฉฐ เจ…เจตเฉˆเจง เจฌเจฃเจพ เจ•เฉ‡ "เจ‡เจน เจตเจงเฉ€เจ† เจ•เจฐเจฆเจพ เจนเฉˆ"เฅค เจ…เจคเฉ‡ เจ‡เฉฑเจฅเฉ‡ เจ‡เจน เจงเจฟเจ†เจจ เจฆเฉ‡เจฃ เจฏเฉ‹เจ— เจนเฉˆ.

เจฆเฉ‹ เจตเจพเจฐ เจจเจพ เจ‰เฉฑเจ เจฃ เจฒเจˆ, เจฎเฉˆเจ‚ เจคเฉเจนเจพเจจเฉ‚เฉฐ เจคเฉเจฐเฉฐเจค เจ‡เจธ เจฌเจพเจฐเฉ‡ เจฆเฉฑเจธเจพเจ‚เจ—เจพ cat << 'EOF', เจœเจฟเจธ เจฆเจพ เจธเจพเจนเจฎเจฃเจพ เจฌเจพเจ…เจฆ เจตเจฟเฉฑเจš เจ•เฉ€เจคเจพ เจœเจพเจตเฉ‡เจ—เจพเฅค เจœเฉ‡ เจคเฉเจธเฉ€เจ‚ เจธเจพเจฆเจ—เฉ€ เจจเจพเจฒ เจฒเจฟเจ–เจฆเฉ‡ เจนเฉ‹ cat << EOF, เจซเจฟเจฐ heredoc เจฆเฉ‡ เจ…เฉฐเจฆเจฐ เจธเจคเจฐ เจจเฉ‚เฉฐ เจ‡เฉฐเจŸเจฐเจชเฉ‹เจฒเฉ‡เจŸ เจ•เฉ€เจคเจพ เจœเจพเจ‚เจฆเจพ เจนเฉˆ (เจตเฉ‡เจฐเฉ€เจเจฌเจฒ เจซเฉˆเจฒเจพเจ เจœเจพเจ‚เจฆเฉ‡ เจนเจจ ($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
}

เจ‡เจน เจธเจพเจฐเฉ€ เจธเจ•เฉเจฐเจฟเจชเจŸ เจนเฉˆเฅค เจ…เจคเฉ‡ เจคเจพเจ‚ เจ‡เจธ เจฒเจฟเจชเฉ€ เจฆเฉ‡ เจจเจพเจฒ เจธเฉฐเจ–เฉ‡เจช 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 เจธเฉฐเจฐเจšเจจเจพ เจฆเฉ€ เจตเจฐเจคเฉ‹เจ‚ เจ•เจฐ เจธเจ•เจฆเฉ‡ เจนเฉ‹ เจ•เจฟ เจ•เจฟเจนเฉœเจพ url เจ•เจฟเจนเฉœเฉ€ เจธเฉ‡เจตเจพ เจนเฉ‹เจตเฉ‡เจ—เฉ€)เฅค เจธเจ•เฉเจฐเจฟเจชเจŸ เจจเฉ‚เฉฐ เจธเจฐเจตเจฐ 'เจคเฉ‡ เจธเจŸเฉ‹เจฐ เจจเจนเฉ€เจ‚ เจ•เฉ€เจคเจพ เจœเจพ เจธเจ•เจฆเจพ เจนเฉˆ, เจ•เจฟเจ‰เจ‚เจ•เจฟ เจ‡เจธ เจธเจฅเจฟเจคเฉ€ เจตเจฟเฉฑเจš เจ…เจธเฉ€เจ‚ เจ‡เจธเจจเฉ‚เฉฐ เจ†เจชเจฃเฉ‡ เจ†เจช เจ…เจชเจกเฉ‡เจŸ เจ•เจฐเจจ เจฆเฉ‡ เจฏเฉ‹เจ— เจจเจนเฉ€เจ‚ เจนเฉ‹เจตเจพเจ‚เจ—เฉ‡ (เจฌเฉฑเจ— เจซเจฟเจ•เจธ เจ•เจฐเจจ เจ…เจคเฉ‡ เจจเจตเฉ€เจ†เจ‚ เจธเฉ‡เจตเจพเจตเจพเจ‚ เจœเฉ‹เฉœเจจ เจฆเฉ‡ เจ‰เจฆเฉ‡เจธเจผ เจฒเจˆ), เจ…เจคเฉ‡ เจ†เจฎ เจคเฉŒเจฐ 'เจคเฉ‡, เจฐเจพเจœ = เจฌเฉเจฐเจพเจˆเฅค

เจนเฉฑเจฒ 1: เจ…เจœเฉ‡ เจตเฉ€ เจธเจ•เฉเจฐเจฟเจชเจŸ เจจเฉ‚เฉฐ เจธเจฐเจตเจฐ 'เจคเฉ‡ เจธเจŸเฉ‹เจฐ เจ•เจฐเฉ‹, เจชเจฐ เจนเจฐ เจตเจพเจฐ เจ‡เจธเจจเฉ‚เฉฐ เจ•เจพเจชเฉ€ เจ•เจฐเฉ‹ scp. เจซเจฟเจฐ เจ‡เจธ เจฐเจพเจนเฉ€เจ‚ เจœเฉเฉœเฉ‹ ssh เจ…เจคเฉ‡ เจฒเฉ‹เฉœเฉ€เจ‚เจฆเฉ‡ เจ†เจฐเจ—เฉ‚เจฎเฉˆเจ‚เจŸเจพเจ‚ เจจเจพเจฒ เจธเจ•เฉเจฐเจฟเจชเจŸ เจจเฉ‚เฉฐ เจšเจฒเจพเจ“เฅค

เจจเฉเจ•เจธเจพเจจ:

  • เจ‡เฉฑเจ• เจฆเฉ€ เจฌเจœเจพเจ เจฆเฉ‹ เจ•เจพเจฐเจตเจพเจˆเจ†เจ‚
  • เจนเฉ‹ เจธเจ•เจฆเจพ เจนเฉˆ เจ•เจฟ เจ•เฉ‹เจˆ เจ…เจœเจฟเจนเฉ€ เจฅเจพเจ‚ เจจเจพ เจนเฉ‹เจตเฉ‡ เจœเจฟเฉฑเจฅเฉ‡ เจคเฉเจธเฉ€เจ‚ เจ•เจพเจชเฉ€ เจ•เจฐเจฆเฉ‡ เจนเฉ‹, เจœเจพเจ‚ เจนเฉ‹ เจธเจ•เจฆเจพ เจนเฉˆ เจ•เจฟ เจ‡เจธ เจคเฉฑเจ• เจชเจนเฉเฉฐเจš เจจเจพ เจนเฉ‹เจตเฉ‡, เจœเจพเจ‚ เจธเจ•เฉเจฐเจฟเจชเจŸ เจจเฉ‚เฉฐ เจฌเจฆเจฒเจฃ เจฆเฉ‡ เจธเจฎเฉ‡เจ‚ เจšเจฒเจพเจ‡เจ† เจœเจพ เจธเจ•เจฆเจพ เจนเฉˆเฅค
  • เจ†เจชเจฃเฉ‡ เจ†เจช เจคเฉ‹เจ‚ เจฌเจพเจ…เจฆ เจธเจพเจซเจผ เจ•เจฐเจจ เจฆเฉ€ เจธเจฒเจพเจน เจฆเจฟเฉฑเจคเฉ€ เจœเจพเจ‚เจฆเฉ€ เจนเฉˆ (เจธเจ•เฉเจฐเจฟเจชเจŸ เจจเฉ‚เฉฐ เจฎเจฟเจŸเจพเจ“).
  • เจชเจนเจฟเจฒเจพเจ‚ เจนเฉ€ เจคเจฟเฉฐเจจ เจ•เจพเจฐเจตเจพเจˆเจ†เจ‚เฅค

เจนเฉฑเจฒ 2:

  • เจธเจ•เฉเจฐเจฟเจชเจŸ เจตเจฟเฉฑเจš เจธเจฟเจฐเจซ เจซเฉฐเจ•เจธเจผเจจ เจชเจฐเจฟเจญเจพเจธเจผเจพเจตเจพเจ‚ เจฐเฉฑเจ–เฉ‹ เจ…เจคเฉ‡ เจ•เฉเจ เจตเฉ€ เจจเจพ เจšเจฒเจพเจ“
  • เจฆเฉ€ เจธเจนเจพเจ‡เจคเจพ เจจเจพเจฒ sed เจ…เฉฐเจค เจตเจฟเฉฑเจš เจ‡เฉฑเจ• เจซเฉฐเจ•เจธเจผเจจ เจ•เจพเจฒ เจธเจผเจพเจฎเจฒ เจ•เจฐเฉ‹
  • เจ‡เจน เจธเจญ เจธเจฟเฉฑเจงเฉ‡ เจชเจพเจˆเจช เจฐเจพเจนเฉ€เจ‚ shh เจจเฉ‚เฉฐ เจญเฉ‡เจœเฉ‹ (|)

เจชเฉเจฐเฉ‹:

  • เจธเฉฑเจšเจฎเฉเฉฑเจš เจฐเจพเจœ เจฐเจนเจฟเจค
  • เจ•เฉ‹เจˆ เจฌเจพเจ‡เจฒเจฐเจชเจฒเฉ‡เจŸ เจ‡เจ•เจพเจˆเจ†เจ‚ เจจเจนเฉ€เจ‚ เจนเจจ
  • เจ เฉฐเจกเจพ เจฎเจนเจฟเจธเฉ‚เจธ เจนเฉ‹ เจฐเจฟเจนเจพ เจนเฉˆ

เจ†เจ“ เจ‡เจธ เจจเฉ‚เฉฐ เจฌเจฟเจจเจพเจ‚ เจœเจตเจพเจฌ เจฆเฉ‡ เจ•เจฐเฉ€เจ. เจนเจพเจ‚, เจธเจญ เจ•เฉเจ เจชเจนเจฟเจฒเจพเจ‚ เจนเฉ€ เจ–เฉ‹เจœเจฟเจ† เจ—เจฟเจ† เจนเฉˆ. เจนเจพเจ‚, เจ‡เฉฑเจ• เจธเจพเจˆเจ•เจฒเฅค เจฆเฉ‡เจ–เฉ‹ เจ•เจฟ เจฌเจพเจˆเจ• เจ•เจฟเฉฐเจจเฉ€ เจธเจฐเจฒ, เจธเจผเจพเจจเจฆเจพเจฐ เจ…เจคเฉ‡ เจจเจฟเจŠเจจเจคเจฎ เจนเฉˆ:

$ 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'

เจนเจพเจฒเจพเจ‚เจ•เจฟ, เจ…เจธเฉ€เจ‚ เจจเจฟเจธเจผเจšเจค เจจเจนเฉ€เจ‚ เจนเฉ‹ เจธเจ•เจฆเฉ‡ เจ•เจฟ เจฐเจฟเจฎเฉ‹เจŸ เจนเฉ‹เจธเจŸ เจ•เฉ‹เจฒ เจขเฉเจ•เจตเฉ€เจ‚ เจฌเฉˆเจธเจผ เจนเฉˆ, เจ‡เจธ เจฒเจˆ เจ…เจธเฉ€เจ‚ เจธเจผเฉเจฐเฉ‚ เจตเจฟเฉฑเจš เจ‡เฉฑเจ• เจ›เฉ‹เจŸเฉ€ เจœเจฟเจนเฉ€ เจœเจพเจ‚เจš เจœเฉ‹เฉœเจพเจ‚เจ—เฉ‡ (เจ‡เจน เจ‡เจธ เจฆเฉ€ เจฌเจœเจพเจ เจนเฉˆ เจธเจผเฉˆเฉฑเจฒเจฌเฉˆเจ‚เจ—):

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/ เจฌเฉเจฐเจพเจŠเจœเจผเจฐ เจตเจฟเฉฑเจš, เจคเฉˆเจจเจพเจคเฉ€ เจจเฉ‚เฉฐ เจฆเฉเจฌเจพเจฐเจพ เจšเจฒเจพเจ“ เจ…เจคเฉ‡ เจฏเจ•เฉ€เจจเฉ€ เจฌเจฃเจพเจ“ เจ•เจฟ เจ‡เจน เจ–เจพเจ•เฉ‡ เจฆเฉŒเจฐเจพเจจ เจธเฉ€เจกเฉ€ เจฆเฉ‡ เจ…เจจเฉเจธเจพเจฐ เจชเฉฐเจจเฉ‡ เจจเฉ‚เฉฐ เจ…เฉฑเจชเจกเฉ‡เจŸ เจ•เจฐเจ•เฉ‡ เจจเจฟเจฐเจตเจฟเจ˜เจจ เจšเฉฑเจฒเจฆเจพ เจนเฉˆเฅค

เจ•เฉฐเจฎ เจคเฉ‹เจ‚ เจฌเจพเจ…เจฆ เจธเจพเจซเจผ เจ•เจฐเจจเจพ เจจเจพ เจญเฉเฉฑเจฒเฉ‹: 3

$ 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