Let's Go Further استقرار و میزبانی › استفاده از Caddy به عنوان یک پروکسی معکوس
قبلی · فهرست مطالب · بعدی
فصل ۲۰.۵.

استفاده از Caddy به عنوان یک پروکسی معکوس

ما اکنون در موقعیتی قرار داریم که برنامه API Greenlight ما به عنوان یک background service روی droplet ما در حال اجرا است و روی پورت 4000 به درخواست‌های HTTP گوش می‌دهد. همچنین Caddy به عنوان یک background service در حال اجرا است و روی پورت 80 به درخواست‌های HTTP گوش می‌دهد.

بنابراین مرحله بعدی در تنظیم محیط production ما، پیکربندی Caddy برای عمل به عنوان یک reverse proxy و ارسال درخواست‌های HTTP دریافتی به API ما است.

ساده‌ترین راه برای پیکربندی Caddy، ایجاد یک Caddyfile است — که شامل مجموعه‌ای از قوانین توصیف‌کننده عملکرد مورد نظر Caddy می‌باشد. اگر همراه ما هستید، لطفاً یک فایل جدید remote/production/Caddyfile در دایرکتوری پروژه خود ایجاد کنید:

$ touch remote/production/Caddyfile

و سپس محتوای زیر را اضافه کنید و مطمئن شوید آدرس IP را با آدرس droplet خود جایگزین کرده‌اید:

فایل: remote/production/Caddyfile
http://161.35.71.158 {
  reverse_proxy localhost:4000
}

همانطور که احتمالاً با دیدن آن حدس می‌زنید، این قانون به Caddy می‌گوید که می‌خواهیم به درخواست‌های HTTP روی 161.35.71.158 گوش دهد و سپس به عنوان یک reverse proxy عمل کند و درخواست را به پورت 4000 روی ماشین محلی (جایی که API ما گوش می‌دهد) ارسال کند.

کار بعدی که باید انجام دهیم آپلود این Caddyfile به droplet و بازخوانی Caddy برای اعمال تغییرات است.

برای مدیریت این کار، بیایید rule production/deploy/api خود را به‌روزرسانی کنیم. این از همان الگوی پایه‌ای که در فصل قبلی برای آپلود فایل api.service استفاده کردیم پیروی می‌کند، اما به جای آن Caddyfile خود را به /etc/caddy/Caddyfile روی سرور کپی خواهیم کرد.

به این صورت:

فایل: Makefile
# ==================================================================================== #
# PRODUCTION
# ==================================================================================== #

production_host_ip = '161.35.71.158'

...

## production/deploy/api: deploy the api to production
.PHONY: production/deploy/api
production/deploy/api:
	rsync -P ./bin/linux_amd64/api greenlight@${production_host_ip}:~
	rsync -rP --delete ./migrations greenlight@${production_host_ip}:~
	rsync -P ./remote/production/api.service greenlight@${production_host_ip}:~
	rsync -P ./remote/production/Caddyfile greenlight@${production_host_ip}:~
	ssh -t greenlight@${production_host_ip} '\
		migrate -path ~/migrations -database $$GREENLIGHT_DB_DSN up \
		&& sudo mv ~/api.service /etc/systemd/system/ \
		&& sudo systemctl enable api \
		&& sudo systemctl restart api \
		&& sudo mv ~/Caddyfile /etc/caddy/ \
		&& sudo systemctl reload caddy \
	'

پس از اعمال این تغییر، rule را برای استقرار Caddyfile در production اجرا کنید:

$ make production/deploy/api
rsync -P ./bin/linux_amd64/api greenlight@"161.35.71.158":~
api
      7,634,944 100%   70.67MB/s    0:00:00 (xfr#1, to-chk=0/1)
rsync -rP --delete ./migrations greenlight@"161.35.71.158":~
sending incremental file list
migrations/000001_create_movies_table.down.sql
             28 100%    0.00kB/s    0:00:00 (xfr#1, to-chk=11/13)
migrations/000001_create_movies_table.up.sql
            286 100%  279.30kB/s    0:00:00 (xfr#2, to-chk=10/13)
migrations/000002_add_movies_check_constraints.down.sql
            198 100%  193.36kB/s    0:00:00 (xfr#3, to-chk=9/13)
migrations/000002_add_movies_check_constraints.up.sql
            289 100%  282.23kB/s    0:00:00 (xfr#4, to-chk=8/13)
migrations/000003_add_movies_indexes.down.sql
             78 100%   76.17kB/s    0:00:00 (xfr#5, to-chk=7/13)
migrations/000003_add_movies_indexes.up.sql
            170 100%  166.02kB/s    0:00:00 (xfr#6, to-chk=6/13)
migrations/000004_create_users_table.down.sql
             27 100%   26.37kB/s    0:00:00 (xfr#7, to-chk=5/13)
migrations/000004_create_users_table.up.sql
            294 100%  287.11kB/s    0:00:00 (xfr#8, to-chk=4/13)
migrations/000005_create_tokens_table.down.sql
             28 100%   27.34kB/s    0:00:00 (xfr#9, to-chk=3/13)
migrations/000005_create_tokens_table.up.sql
            203 100%  198.24kB/s    0:00:00 (xfr#10, to-chk=2/13)
migrations/000006_add_permissions.down.sql
             73 100%   71.29kB/s    0:00:00 (xfr#11, to-chk=1/13)
migrations/000006_add_permissions.up.sql
            452 100%  441.41kB/s    0:00:00 (xfr#12, to-chk=0/13)
rsync -P ./remote/production/api.service greenlight@"161.35.71.158":~
api.service
          1,266 100%    0.00kB/s    0:00:00 (xfr#1, to-chk=0/1)
rsync -P ./remote/production/Caddyfile greenlight@"161.35.71.158":~
Caddyfile
            293 100%    0.00kB/s    0:00:00 (xfr#1, to-chk=0/1)
ssh -t greenlight@"161.35.71.158" '\
        migrate -path ~/migrations -database $GREENLIGHT_DB_DSN up \
        && sudo mv ~/api.service /etc/systemd/system/ \
        && sudo systemctl enable api \
        && sudo systemctl restart api \
        && sudo mv ~/Caddyfile /etc/caddy/ \
        && sudo systemctl reload caddy \
'
no change
[sudo] password for greenlight: 
Connection to 161.35.71.158 closed.

باید ببینید که Caddyfile کپی شده و بازخوانی بدون هیچ خطایی اجرا می‌شود.

در این مرحله می‌توانید http://<your_droplet_ip>/v1/healthcheck را در مرورگر وب خود باز کنید و باید ببینید که درخواست با موفقیت از Caddy به API ما ارسال شده است. به این صورت:

20.05-01.png

مسدود کردن دسترسی به معیارهای برنامه

در حالی که در مرورگر هستیم، بیایید به endpoint GET /debug/vars برویم که معیارهای برنامه ما را نمایش می‌دهد. باید پاسخی مشابه این ببینید:

20.05-02.png

همانطور که قبلاً اشاره کردیم، در دسترس بودن عمومی این اطلاعات حساس واقعاً ایده خوبی نیست.

خوشبختانه، مسدود کردن دسترسی به این بخش با اضافه کردن یک respond directive جدید به Caddyfile بسیار آسان است. به این صورت:

فایل: remote/production/Caddyfile
http://161.35.71.158 {
  respond /debug/* "Not Permitted" 403
  reverse_proxy localhost:4000
}

با این directive جدید، به Caddy دستور می‌دهیم که برای تمام درخواست‌هایی که مسیر URL آن‌ها با /debug/ شروع می‌شود، پاسخ 403 Forbidden ارسال کند.

این تغییر را دوباره در production مستقر کنید و هنگامی که صفحه را در مرورگر وب خود بازخوانی می‌کنید، باید ببینید که اکنون مسدود شده است.

$ make production/deploy/api
20.05-03.png

اگرچه معیارها دیگر به صورت عمومی در دسترس نیستند، همچنان می‌توانید با اتصال به droplet از طریق SSH و ارسال درخواست به http://localhost:4000/debug/vars به آن‌ها دسترسی داشته باشید.

$ make production/connect 
greenlight@greenlight-production:~$ curl http://localhost:4000/debug/vars
{
"cmdline": ...,
"database": ...,
"goroutines": 7,
"memstats": ...,
"timestamp": 1618820037,
"total_processing_time_μs": 1305,
"total_requests_received": 8,
"total_responses_sent": 7,
"total_responses_sent_by_status": {"200": 3, "404": 4},
"version": "v1.0.0-1-gf27fd0f"
}

یا به طور جایگزین، می‌توانید یک SSH tunnel به droplet باز کنید و آن‌ها را با استفاده از مرورگر وب روی ماشین محلی خود مشاهده کنید. به عنوان مثال، می‌توانید یک SSH tunnel بین پورت 4000 روی droplet و پورت 9999 روی ماشین محلی خود با اجرای دستور زیر باز کنید (مطمئن شوید هر دو آدرس IP را با آدرس IP droplet خود جایگزین کرده‌اید):

$ ssh -L :9999:161.35.71.158:4000 greenlight@161.35.71.158

در حالی که آن tunnel فعال است، باید بتوانید http://localhost:9999/debug/vars را در مرورگر وب خود باز کنید و معیارهای برنامه خود را ببینید، به این صورت:

20.05-04.png

استفاده از یک نام دامنه

برای مرحله بعدی استقرار خود، Caddy را طوری پیکربندی خواهیم کرد که بتوانیم از طریق یک نام دامنه به droplet خود دسترسی داشته باشیم، به جای نیاز به استفاده از آدرس IP.

اگر می‌خواهید در این مرحله از کتاب همراه ما باشید، به یک نام دامنه و توانایی به‌روزرسانی رکوردهای DNS برای آن نام دامنه نیاز دارید. اگر در حال حاضر نام دامنه‌ای در دسترس ندارید، می‌توانید از طریق سرویس Freenom یکی به صورت رایگان دریافت کنید.

من از دامنه greenlight.alexedwards.net در کد نمونه اینجا استفاده خواهم کرد، اما اگر همراه ما هستید باید آن را با دامنه خود جایگزین کنید.

اولین کاری که باید انجام دهید پیکربندی رکوردهای DNS برای نام دامنه خود به گونه‌ای است که شامل یک رکورد A باشد که به آدرس IP droplet شما اشاره کند. بنابراین در مورد من، رکورد DNS به این صورت خواهد بود:

A     greenlight.alexedwards.net     161.35.71.158

پس از ایجاد رکورد DNS، وظیفه بعدی به‌روزرسانی Caddyfile برای استفاده از نام دامنه به جای آدرس IP droplet است. آن را به این صورت جایگزین کنید (به یاد داشته باشید greenlight.alexedwards.net را با نام دامنه خود جایگزین کنید):

فایل: remote/production/Caddyfile
http://greenlight.alexedwards.net {
  respond /debug/* "Not Permitted" 403
  reverse_proxy localhost:4000
}

و سپس Caddyfile را دوباره به droplet خود مستقر کنید:

$ make production/deploy/api

پس از انجام این کار، اکنون باید بتوانید از طریق نام دامنه خود به API دسترسی داشته باشید. http://<your_domain_name>/v1/healthcheck را در مرورگر خود باز کنید:

20.05-05.png

فعال‌سازی HTTPS

اکنون که یک نام دامنه تنظیم کرده‌ایم، می‌توانیم از یکی از ویژگی‌های اصلی Caddy استفاده کنیم: automatic HTTPS.

Caddy به طور خودکار تأمین و تمدید گواهی‌نامه‌های TLS دامنه شما از طریق Let's Encrypt یا ZeroSSL (بسته به در دسترس بودن) و همچنین انتقال تمام درخواست‌های HTTP به HTTPS را مدیریت می‌کند. راه‌اندازی آن ساده، بسیار قوی و صرفه‌جویی در هزینه‌های پیگیری دستی تمدید گواهی‌نامه‌ها است.

برای فعال‌سازی این قابلیت، فقط باید Caddyfile خود را به‌روزرسانی کنیم تا به این صورت درآید:

فایل: remote/production/Caddyfile
# Set the email address that should be used to contact you if there is a problem with 
# your TLS certificates.
{
  email you@example.com
}

# Remove the http:// prefix from your site address.
greenlight.alexedwards.net {
  respond /debug/* "Not Permitted" 403
  reverse_proxy localhost:4000
}

برای آخرین بار، این به‌روزرسانی Caddyfile را به droplet خود مستقر کنید…

$ make production/deploy/api

و سپس هنگامی که صفحه را در مرورگر وب خود بازخوانی می‌کنید، باید ببینید که به طور خودکار به نسخه HTTPS صفحه هدایت شده است.

20.05-06.png

اگر از Firefox استفاده می‌کنید، همچنین می‌توانید با فشار دادن Ctrl+i اطلاعات صفحه را در مرورگر خود مشاهده کنید. باید مشابه این باشد:

20.05-07.png

از اینجا می‌توانیم ببینیم که اتصال با موفقیت با استفاده از TLS 1.3 و مجموعه رمز TLS_AES_128_GCM_SHA256 رمزگذاری شده است.

در نهایت، اگر می‌خواهید، می‌توانید از curl برای ارسال یک درخواست HTTP به برنامه استفاده کنید. باید ببینید که یک 308 Permanent Redirect به نسخه HTTPS برنامه صادر می‌شود، به این صورت:

$ curl -i http://greenlight.alexedwards.net
HTTP/1.1 308 Permanent Redirect
Connection: close
Location: https://greenlight.alexedwards.net/
Server: Caddy
Date: Mon, 19 Apr 2021 08:36:20 GMT
Content-Length: 0

اطلاعات تکمیلی

مقیاس‌بندی زیرساخت

قبل از راه‌اندازی یک سرویس جدید، انجام یک آزمایش فکری و پرسیدن این سؤال از خود معمولاً مفید است: با افزایش ترافیک به سرویس چه اتفاقی می‌افتد؟ چگونه آن را مدیریت می‌کنم؟

برای این پروژه، مسیر مشخصی وجود دارد که می‌توانیم برای مقیاس‌بندی و تطبیق زیرساخت با رشد دنبال کنیم. به طور بسیار کلی، این مسیر تقریباً به این صورت است:

در پروژه‌های دیگر، این مراحل و ترتیب آن‌ها احتمالاً متفاوت خواهد بود. و در برخی موارد، ممکن است بر اساس الزامات تجاری، برآوردهای ترافیک و نتایج تست بار، منطقی باشد که مستقیماً به یک معماری پیچیده‌تر بپردازیم.

اما اختصاص دادن لحظه‌ای کوتاه برای انجام چنین آزمایش فکری خوب است. اگر بتوانید مسیر مشخصی ببینید، این اطمینان مثبتی فراهم می‌کند که رشد می‌تواند بدون بهینه‌سازی زودهنگام زیرساخت یا برنامه مدیریت شود. در حالی که اگر نتوانید مسیر مشخصی برای مدیریت رشد ببینید، این مشکل بالقوه‌ای را برجسته می‌کند و ممکن است منطقی باشد انتخاب‌های زیرساخت خود را بازنگری کنید.