نحوه ساخت و استقرار یک Node.js API آماده برای تولید در Cloud Run

یک Node.js API که در محل اجرا میشود، بهطور خودکار آماده ابر نیست. استقرار تولید نیازمندی هایی را معرفی می کند که به راحتی می توان آنها را نادیده گرفت: پورت های پویا، بررسی های سلامت، خاموش شدن های زیبا، امنیت کانتینر، گزارش های ساختاری، و پیکربندی مبتنی بر محیط.
در این آموزش، یک Node.js API کوچک می سازیم، آن را در یک ظرف Docker بسته بندی می کنیم، آن را به صورت محلی آزمایش می کنیم و آن را در Google Cloud Run مستقر می کنیم. همان اصول طراحی برای سایر پلت فرم های کانتینر مدیریت شده اعمال می شود.
این رویکرد یک مبنای عملی برای توسعه اپلیکیشن ابری همانطور که برنامه قابل حمل باقی می ماند. پلتفرم ابری کانتینر را مدیریت می کند، اما خود سرویس به شدت به کدهای پلتفرم خاص وابسته نیست.
قراره چی بسازیم؟
سرویس نمونه سه نقطه پایانی را ارائه می دهد:
GET /اطلاعات پایه API را برمی گرداند.GET /healthگزارش می دهد که آیا فرآیند در حال اجرا است یا خیر.POST /tasksیک کار را تایید می کند و می پذیرد.
همچنین:
- پیکربندی را از متغیرهای محیط بخوانید.
- در پورت تنظیم شده توسط پلت فرم گوش دهید.
- خطاهای JSON ساختار یافته را برمی گرداند.
- سیگنال های پایان کار را با ظرافت مدیریت کنید.
- به عنوان یک کاربر کانتینر غیرمجاز اجرا شود.
اگر میخواهید بخش استقرار را تکمیل کنید، به Node.js 20 یا جدیدتر، Docker، یک پروژه Google Cloud و Google Cloud CLI نیاز دارید.
پروژه Node.js را ایجاد کنید
یک دایرکتوری جدید ایجاد کنید و پروژه را مقداردهی اولیه کنید:
mkdir cloud-task-api
cd cloud-task-api
npm init -y
npm install express
به روز رسانی package.json:
{
"name": "cloud-task-api",
"version": "1.0.0",
"description": "A containerized Node.js API",
"type": "module",
"main": "src/server.js",
"scripts": {
"start": "node src/server.js"
},
"engines": {
"node": ">=20"
},
"dependencies": {
"express": "^5.0.0"
}
}
استفاده از یک نسخه صریح Node.js باعث می شود که نیاز زمان اجرا برای توسعه دهندگان و سیستم های ساخت قابل مشاهده باشد.
یک API بسازید
ایجاد یک src اشاره گر و اضافه کردن src/server.js:
import express from "express";
import process from "node:process";
import crypto from "node:crypto";
const app = express();
const config = {
port: Number(process.env.PORT ?? 8080),
environment: process.env.NODE_ENV ?? "development",
serviceName: process.env.SERVICE_NAME ?? "task-api"
};
app.disable("x-powered-by");
app.use(express.json({ limit: "32kb" }));
app.get("/", (request, response) => {
response.json({
service: config.serviceName,
environment: config.environment,
status: "available"
});
});
app.get("/health", (request, response) => {
response.json({
status: "ok",
uptimeSeconds: Math.floor(process.uptime())
});
});
app.post("/tasks", (request, response) => {
const { title, priority = "normal" } = request.body;
if (typeof title !== "string" || title.trim().length 3) {
return response.status(400).json({
error: {
code: "INVALID_TITLE",
message: "The title must contain at least three characters."
}
});
}
const allowedPriorities = new Set([
"low",
"normal",
"high"
]);
if (!allowedPriorities.has(priority)) {
return response.status(400).json({
error: {
code: "INVALID_PRIORITY",
message: "Priority must be low, normal, or high."
}
});
}
const task = {
id: crypto.randomUUID(),
title: title.trim(),
priority,
createdAt: new Date().toISOString()
};
console.log(JSON.stringify({
severity: "INFO",
event: "task_created",
taskId: task.id,
priority: task.priority
}));
return response.status(201).json({ task });
});
app.use((request, response) => {
response.status(404).json({
error: {
code: "NOT_FOUND",
message: "The requested endpoint does not exist."
}
});
});
API بدنه های JSON را به 32 کیلوبایت محدود می کند. محدودیت اندازه درخواست به محافظت از یک سرویس عمومی در برابر بارهای غیرمنتظره بزرگ و استفاده بیش از حد از حافظه کمک می کند.
این مثال Express را نیز غیرفعال می کند X-Powered-By هدر حذف هدر یک اقدام امنیتی کامل نیست، اما برنامهها معمولاً نباید چارچوب سرور خود را تبلیغ کنند.
سرور را به درستی راه اندازی کنید
کد زیر را در پایین صفحه اضافه کنید src/server.js:
const server = app.listen(
config.port,
"0.0.0.0",
() => {
console.log(JSON.stringify({
severity: "INFO",
event: "server_started",
port: config.port,
environment: config.environment
}));
}
);
function shutdown(signal) {
console.log(JSON.stringify({
severity: "INFO",
event: "shutdown_started",
signal
}));
server.close(error => {
if (error) {
console.error(JSON.stringify({
severity: "ERROR",
event: "shutdown_failed",
message: error.message
}));
process.exit(1);
}
console.log(JSON.stringify({
severity: "INFO",
event: "shutdown_complete"
}));
process.exit(0);
});
setTimeout(() => {
console.error(JSON.stringify({
severity: "ERROR",
event: "shutdown_timeout"
}));
process.exit(1);
}, 10_000).unref();
}
process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT"));
دارم گوش میدم 0.0.0.0 خدمات را در خارج از ظرف خود در دسترس قرار می دهد. خواندن PORT از محیط به پلت فرم اجازه می دهد تا پورت گوش دادن را انتخاب کند.
کنترلکنندههای خاموشی سرور را از پذیرش اتصالات جدید باز میدارند در حالی که به درخواستهای فعال زمان میدهند تا تکمیل شوند. تایمر بازگشتی از انتظار برای مدت نامحدود فرآیند جلوگیری می کند.
سرویس را به صورت محلی تست کنید
API را اجرا کنید:
npm start
ترمینال دیگری را باز کنید و نقطه پایان سلامت را بررسی کنید:
curl http://localhost:8080/health
ایجاد یک کار:
curl -X POST http://localhost:8080/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Review deployment logs","priority":"high"}'
یک پرس و جو نامعتبر را آزمایش کنید:
curl -X POST http://localhost:8080/tasks \
-H "Content-Type: application/json" \
-d '{"title":"A","priority":"urgent"}'
یک پرس و جو نامعتبر باید a را برگرداند 400 وضعیت و خطای JSON قابل پیش بینی.
یک ظرف تولید ایجاد کنید
ایجاد یک Dockerfile:
FROM node:20-alpine
ENV NODE_ENV=production
ENV PORT=8080
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --chown=node:node src ./src
USER node
EXPOSE 8080
CMD ["node", "src/server.js"]
تصویر فقط وابستگیهای تولید را نصب میکند و به وابستگیهای غیرمجاز موجود تغییر میدهد node کاربر قبل از شروع سرویس
ایجاد کنید .dockerignore:
node_modules
npm-debug.log
.git
.gitignore
.env
Dockerfile*
README.md
حذف فایلهای محلی، زمینه ساخت را کوچکتر میکند و احتمال کپی کردن اعتبارنامهها یا مصنوعات توسعه را در تصویر کاهش میدهد.
راهنمای SitePoint برای استفاده از Node.js با Docker مقدمهای دقیقتر از تصاویر، کانتینرها، مجموعههای اتصال و گردشهای کاری توسعه مبتنی بر Docker ارائه میدهد.
ظرف را بسازید:
docker build -t cloud-task-api .
آن را به صورت محلی اجرا کنید:
docker run --rm \
-p 8080:8080 \
-e SERVICE_NAME=container-task-api \
cloud-task-api
بازدید کنید http://localhost:8080 یا قبلی را تکرار کنید curl دستورات
بررسی سلامت ظرف را اضافه کنید
را /health نقطه پایانی همچنین می تواند توسط Docker استفاده شود:
HEALTHCHECK --interval=30s --timeout=3s \
--start-period=5s --retries=3 \
CMD wget -qO- http://127.0.0.1:8080/health || exit 1
این دستورالعمل را قبلا اضافه کنید CMD اگر به اطلاعات بهداشتی در سطح داکر نیاز دارید.
بررسی های سلامت را سریع و مستقل از خدمات خارجی کند انجام دهید. نقطه پایانی اولیه برای زنده بودن باید تأیید کند که فرآیند می تواند پاسخ دهد. یک بررسی آمادگی جداگانه میتواند تأیید کند که وابستگیهای مورد نیاز برای خدمات رسانی به ترافیک در دسترس هستند.
سرویس را در Cloud Run مستقر کنید
Google Cloud CLI را احراز هویت کنید و پروژه خود را انتخاب کنید:
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
دایرکتوری منبع را پیدا کنید:
gcloud run deploy cloud-task-api \
--source . \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars SERVICE_NAME=cloud-task-api
آموزش رسمی Google Cloud برای ساخت و استقرار سرویس Node.js در Cloud Run، راهاندازی پروژه و جریان استقرار مورد نیاز را توضیح میدهد.
را --allow-unauthenticated نمونه API را در دسترس عموم قرار می دهد. از آن برای سرویس های اداری خصوصی یا نقاط پایانی که داده های محافظت شده را در معرض دید قرار می دهند استفاده نکنید. به جای آن دسترسی مبتنی بر هویت را پیکربندی کنید.
پس از استقرار، دستور URL سرویس را چاپ می کند. تست کنید با:
curl https://YOUR_SERVICE_URL/health
پیکربندی را خارج از تصویر ذخیره کنید
تنظیمات خاص پیاده سازی باید از طریق متغیرهای محیطی ارائه شوند، نه اینکه در ظرف نوشته شوند.
برای پیکربندی غیر حساس، می توانید نسخه دیگری را با استفاده از:
gcloud run services update cloud-task-api \
--region us-central1 \
--set-env-vars NODE_ENV=production,SERVICE_NAME=tasks
رمزهای عبور پایگاه داده، کلیدهای API یا اسرار امضا را مستقیماً در Dockerfile، مخزن منبع یا دستور استقرار ذخیره شده در تاریخچه پوسته ذخیره نکنید. از یک فروشگاه مخفی مدیریت شده استفاده کنید و به سرویس اجازه دهید فقط به اسرار مورد نیاز خود دسترسی داشته باشد.
استفاده از تیم ها خدمات مشاوره ابری گوگل آنها همچنان باید مالکیت تصمیمات معماری، پیکربندی استقرار، سیاست های دسترسی و اسناد عملیاتی خود را حفظ کنند. راهنمایی خارجی می تواند به مهاجرت یا طراحی پلت فرم کمک کند، اما تیم برنامه باید بداند که چگونه سرویس ایمن، نظارت و بازیابی می شود.
بهبود دید
مثال، اشیاء JSON تک خطی را در خروجی استاندارد مینویسد. تجزیه و تحلیل گزارشهای ساختاریافته برای سیستم گزارشگیری ابری آسانتر از پیامهای غیرقابل خواندن توسط انسان است.
هر گزارش باید شامل زمینه عملیاتی مفید باشد، مانند:
console.log(JSON.stringify({
severity: "INFO",
event: "request_completed",
method: request.method,
path: request.path,
statusCode: response.statusCode,
durationMs
}));
از ثبت سرصفحههای مجوز، کوکیها، درخواستها، اطلاعات شخصی یا اسرار خودداری کنید.
در تولید، اندازه گیری های مفید عبارتند از:
- تعداد درخواست ها و تاخیر
- میزان خطا بر اساس نقطه پایانی و وضعیت.
- زمان راه اندازی ظرف است.
- استفاده از حافظه و CPU
- تعداد نمونه های فعال
- بازبینیهای استقرار ناموفق.
هشدارهایی را در مورد علائمی که کاربران را تحت تأثیر قرار می دهند، ایجاد کنید، نه فقط در مورد پیام های گزارش فردی.
از نگهداری حالت در داخل ظرف خودداری کنید
ظروف مدیریت شده باید به عنوان یک بار مصرف در نظر گرفته شوند. هنگامی که تقاضا تغییر می کند، یک پلت فرم ممکن است نمونه هایی را شروع یا متوقف کند، و فایل های محلی ممکن است همراه با نمونه ناپدید شوند.
اطلاعات پایدار را در یک پایگاه داده خارجی یا سرویس ذخیره سازی اشیاء ذخیره کنید. از آرایههای درون حافظه یا فایلهای JSON محلی بهعنوان یک ذخیره اطلاعات قابل اعتماد برای جلسات، سفارشها، وظایف یا سوابق کاربر استفاده نکنید.
API در این آموزش وظیفه ایجاد شده را برمی گرداند، اما آن را ذخیره نمی کند. نسخه تولیدی باید درخواست را تأیید کند و سپس کار را در یک پایگاه داده مدیریت شده از طریق یک لایه دسترسی به داده اختصاصی بنویسد.
چک لیست تولید
قبل از راهاندازی یک سرویس ابری، این موارد را بررسی کنید:
- برنامه در پورت ارائه شده توسط پلت فرم گوش می دهد.
- اسرار از طریق یک فروشگاه مخفی مدیریت شده تحویل داده می شود.
- کانتینر به عنوان یک کاربر غیرمجاز در حال اجرا است.
- بدنه های درخواستی دارای محدودیت اندازه هستند.
- داده های ورودی روی سرور تایید می شود.
- گزارشها مقادیر حساس را حذف میکنند.
- سیگنال های خاموش شدن پردازش می شوند.
- حالت بادوام در خارج از ظرف ذخیره می شود.
- دسترسی عمومی عمدی است.
- رویههای استقرار و بازگشت مستند شده است.
- هشدارها تأخیر، در دسترس بودن و میزان خطا را پوشش می دهند.
همچنین نسخههای زمان اجرا و وابستگی را تعمیر و بهروزرسانی کنید. قبل از استقرار نسخه جدید، آزمایشهای خودکار و ساخت کانتینر را در یکپارچگی مداوم اجرا کنید.
افکار نهایی
استقرار Node.js API در یک پلت فرم کانتینر مدیریت شده آسان است، اما آمادگی تولید بستگی به تصمیماتی دارد که قبل از اجرای فرمان استقرار گرفته می شود.
یک کانتینر قابل حمل، پیکربندی مبتنی بر محیط، ورود به سیستم ساختاریافته، اعتبارسنجی ورودی، خاموش شدن برازنده، و یک فروشگاه حالت خارجی پایه قویتری نسبت به میانبرهای مخصوص پلتفرم ایجاد میکند. با این شیوهها، آزمایش محلی، استقرار مداوم، نظارت بر تولید و در صورت تغییر نیازهای زیرساختی سرویس آسانتر میشود.




