مقدمه


سلام!

به آزمون ورودی بوت‌کمپ دوآپس ابرآروان خوش آمدید. توجه داشته باشید از آنجا که این مسابقه با مسابقات دوآپس پیشین کوئرا کمی متفاوت است، دانستن جزئیات زیر به پیشروی شما در این مسابقه کمک می‌کند:

  • این مسابقه دارای سناریو یک‌ پارچه است، یعنی پروژه سوالات شبیه به هم و دنباله‌دار هستند و روی یک فضا پیش می‌روند.
  • در این مسابقه تلاش شده تا مهارت شما هم در سطح توسعه دهنده یا همان developer و هم در سطح operation سنجیده شود. برای همین شما در برخی تمرین‌ها مجبور به توسعه و پیاده‌سازی هستید. در این گونه سوالات تلاش شده تا شما را محدود به استفاده از زبان برنامه‌نویسی مخصوصی نکنیم و دست شما را باز بگذاریم. بنابراین می‌توانید با زبان دلخواه خود به حل سوالات بپردازید.

تمرین ورودی🔗

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

agree
└── agree.sh
Plain text

از شما می‌خواهیم تا اسکریپتی بنویسید که صرفا عبارت زیر را چاپ کند.

Seen!
Plain text

به علامت‌های نگارشی و بزرگی و کوچکی توجه کنید. خروجی اسکریپت شما باید دقیقا برابر مقدار خواسته شده باشد.

راه‌حل خود را درون فایل agree.sh موجود در پروژه اولیه، پیاده‌سازی کنید.

توجه کنید🔗

  • فراموش نکنید که اسکریپتی کامل را در پاسخ‌تان تحویل دهید.
  • فراموش نکنید که اسکریپت خود را درون فایلی با نام agree.sh وارد نمایید.
  • برای ارسال پاسخ خود کافی‌ست فایل agree.sh را آپلود کنید.

شمارنده


شمارش درخواست‌های یک کاربر به سامانه‌ی غذای دانشگاه در چین یکی از سخت‌ترین کارهایی است که می‌تواند اتفاق بیافتد. امروزه چینی‌ها به‌دنبال ایجاد یک API برای شمارش اتوماتیک و نرم‌افزاری غذاهایی هستند که ملت به‌صورت رایگان از سلف دانشگاه خود سفارش می‌دهند!

برای دانلود پروژه‌ی اولیه روی این لینک کلیک کنید.

توجه کنید که شما باید از راه‌حل این سؤال در سؤالات بعدی نیز استفاده کنید.

ای‌پی‌آی🔗

در ابتدا به یک API نیاز داریم که بتواند بر اساس کد ملی منحصر به هر فرد، تعداد درخواست‌های غذای او را بشمرد. این API باید در پورت 80 و در مسیر / در دسترس باشد؛ به‌شکلی که با ارسال یک ریکوئست به این آدرس به همراه یک هدر CLIENT-KEY باید تعداد ریکوئست‌های او به‌روزرسانی شده و در پاسخ، تعداد کل ریکوئست‌ها توسط همه‌ی کاربران را در کلیدی به نام "state" برگراند. به‌عنوان مثال:

$ curl -H "CLIENT-KEY: one" localhost
{'state': {'one': 1}}

$ curl -H "CLIENT-KEY: one" localhost
{'state': {'one': 2}}

$ curl -H "CLIENT-KEY: two" localhost
{'state': {'one': 2, 'two': 1}}
Bash

نصب نیازمندی‌ها و اجرا🔗

برای حل این سؤال می‌توانید از هر زبان و هر تکنولوژی‌ای که می‌خواهید استفاده کنید. به‌صورتی که در یک پوشه به نام api کد برنامه را نوشته و در فایلی به نام runner.sh که توسط sh اجرا می‌شود، باید برنامه‌ی خود را اجرا کنید. توجه کنید که حتماً باید Dockerfile مربوط به پروژه‌ی خود را برای ما ارسال کنید.

در پروژه‌ی اولیه، ۴ داکرفایل برای php، python، golang و node قرار دادیم که می‌توانید از آن‌ها مستقیماً استفاده کنید. در صورتی که از یکی از این زبان‌ها برای حل سؤال استفاده می‌کنید، کافیست که Dockerfile مربوط به آن را در پوشه‌ی api کپی کنید و طبق توضیحات داده شده، سؤال را حل کنید. برای نصب نیازمندی‌های پایتون از requirements.txt، برای پی‌اچ‌پی از composer.json، برای گولنگ از go.mod و برای نودجی‌اس از package.json استفاده کنید.

در صورتی که زبان مورد استفاده‌ی شما، چیزی به جز این ۴ مورد است، باید خودتان داکرفایلی در پوشه‌ی api به‌شکلی بنویسید که بتواند نیازمندی‌های پروژه‌ی شما را نصب کرده و برنامه‌ی شما را مانند داکرفایل‌های موجود اجرا کند.

تغییر Dockerfile🔗

شما آزادید که Dockerfile را به هرصورتی که علاقمندید تغییر دهید. در صورتی که با داکر و داکرفایل آشنایی ندارید، هم کافیست که با استفاده از توضیحات داده شده صرفاً با پیاده‌سازی این API سؤال را حل کنید.

فرد بی‌هویت🔗

نکته‌ی مهمی که در این API وجود دارد این است که درصورتی که هدر CLIENT-KEY وجود نداشت، آخرین وضعیت باید در پاسخ برگردانده شود. درواقع تعداد ریکوئست‌هایی که فردی بی‌هویت به API می‌زند شمرده نمی‌شود.

$ curl localhost
{'state': {'one': 2, 'two': 1}}
Bash

نحوه ارسال پاسخ🔗

شما می‌توانید تمامی محتوای موجود در پوشه‌ی api را تغییر دهید و هر فایلی که می‌خواهید اضافه یا کم کنید.

api
├──  api.py   # or main.go somefile.js anyfile.php name.any ...
├── Dockerfile
├── requirements.txt  # or go.mod package.json composer.json
└── runner.sh
Plain text

توجه کنید که نام فایل کد شما برای سیستم داوری اهمیتی ندارد و این خود شما هستید که در runner.sh از نام آن برای اجرای پروژه استفاده می‌کنید.

در نهایت این پوشه را zip کرده و ارسال کنید. توجه کنید که پس از extract کردن فایل zip شما، باید پوشه‌ی api را ببینیم که درون آن Dockerfile وجود دارد.

ذخیره‌ساز سریع


محمد برنامه‌نویسی است که برای سرعت اجرای نرم‌افزار اهمیت ویژه‌ای قائل است. برای همین منظور او پیشنهاد داده که ذخیره‌سازی اطلاعات مربوط به هدر CLIENT-KEY، در دیتابیس Redis انجام شود. در نتیجه شما باید پاسخی که برای سوال دوم مسابقه ارسال کرده‌اید را به گونه‌ای تغییر دهید که از Redis برای خواندن و ذخیره‌سازی اطلاعات استفاده کند.

برای دانلود پروژه‌ی اولیه روی این لینک کلیک کنید.

کانتینرها🔗

در ابتدای کار، برای پیاده سازی این سیستم، نیاز است که فایل docker-compose.yml را تکمیل کنید. توجه کنید که امکان build کردن یک Image جدید را دارید. برای دیتابیس Redis می‌توانید از image زیر استفاده نمایید:

registry.gitlab.com/qio/standard/redis:latest
Plain text

توجه داشته باشید که باید فایل docker-compose.yml را به‌ شکلی تکمیل کنید که پس از up کردن آن، هر ۲ کانتینر (Api, Redis) به درستی اجرا شوند. سپس با توجه به درخواست‌هایی که از سمت سیستم داوری برای api ارسال می‌شود، باید کلیدهای CLIENT-KEY در دیتابیس Redis ذخیره شده باشد. در غیر این صورت نمره‌ کامل به شما تعلق نمی‌گیرد.

شرایط containerها🔗

۱. نام containerها باید حتماً به شکل زیر باشد:

api
redis
Plain text

۲. هریک از این containerها باید روی پورت مشخصی که مربوط به آن‌ها می‌باشد قرار گیرند. دقیقاً به شکل زیر:

port of api = 80
port of redis = 6379
Plain text

دقت کنید که تنظیمات پورت‌ها به گونه‌ای انجام شود که وقتی سیستم داوری دستور docker-compose up را اجرا کرد، بتواند به هر دو پورت ۸۰ و ۶۳۷۹ دسترسی داشته باشد.

۳. نام دیتابیس ردیس که ذخیره سازی باید در آن انجام شود، 0 است.

۴. هر کلید باید بصورت جداگانه در Redis ذخیره شود. به این صورت که اگر n بار درخواست با هدر CLIENT-KEY=test ارسال شد، دیتا باید به گونه‌ای ذخیره شده باشد که اگر از Redis کلید test خوانده شود، مقدار n را برگرداند.

ایجاد Volume🔗

در صورتی‌که نیاز دارید فایل یا مسیری را به درون هریک از containerها Volume کنید، می‌توانید از پوشه‌ای به نام api استفاده کنید.

services:
  sample_container:
    build: ./api
    volumes:
      - ./api/somefile:/somefile
Plain text

فراموش نکنید که فایل somefile را درون پوشه‌ی api قرارداده و آن را برای داوری ارسال نمایید. برای مثال، پوشه‌ی ارسالی شما برای داوری کوئرا به شکل زیر می‌تواند باشد:

.
├── api
│   └── somefile
└── docker-compose.yml
Plain text

*توجه:* سیستم داوری کوئرا به‌صورت خودکار فایل docker-compose.yml را با کامند up اجرا می‌کند. شما نیازی به کد یا اسکریپتی برای اجرای این کار ندارید.

نحوه ارسال پاسخ🔗

شما فقط می‌توانید مسیرهای های‌لایت شده را تغییر دهید:

.
├──  api 
│   └── ...
└──  docker-compose.yml 
Plain text

در نهایت این پوشه را zip کرده و ارسال کنید. توجه کنید که پس از extract کردن فایل zip شما، باید فایل docker-compose.yml را ببینیم.

شمارنده‌ی محدود


مصطفی که در زمینه امنیت اطلاعات شخص بسیار حرفه‌ای می‌باشد، پیشنهاد داده است که تعداد درخواست‌های قابل ارسال به api که در سوال سوال دوم مسابقه نوشته شده است را به شکلی محدود کنیم که بتواند حملات احتمالی به سیستم را کنترل نماید. برای همین منظور شما باید پاسخ سوال سوال دوم مسابقه را به گونه‌ای که در زیر توضیح داده شده است اصلاح نمایید.

برای دانلود پروژه‌ی اولیه روی این لینک کلیک کنید.

محدودیت rate-limit🔗

در این api هر client-key مجاز باشد در هر دقیقه حداکثر ۱۰ request ارسال کند.

یعنی هر کلاینت اگر در ۶۰ ثانیه‌ی اخیر ۱۰ request ارسال کرده باشد باید برای request بعدی خود ۶۰ ثانیه منتظر بماند.

در صورتی که تعداد request های یک client-key از این محدودیت بیشتر شد؛ پاسخ‌ها به درخواست‌های آن client-key باید تا یک دقیقه دارای مشخصات زیر باشد:

  • Status code: 429
  • Body:
    {"message": "Too many request from <client-key>"}
    JSON

مقدار <client-key> در message باید بر اساس هدر client-key درخواست جای‌گذاری شود.

برای rate limit مجاز به استفاده از nginx نیستید و باید با توجه به زبان مورد نظر خود، پیاده‌سازی را در آن زبان انجام دهید. (به نحوه ارسال پاسخ و فایل‌های مجاز در انتهای سوال دقت نمایید.)


نکات تکمیلی🔗

نصب نیازمندی‌ها و اجرا

برای حل این سؤال می‌توانید از هر زبان و هر تکنولوژی‌ای که می‌خواهید استفاده کنید. به‌صورتی که در یک پوشه به نام api کد برنامه را نوشته و در فایلی به نام runner.sh که توسط sh اجرا می‌شود، باید برنامه‌ی خود را اجرا کنید. توجه کنید که حتماً باید Dockerfile مربوط به پروژه‌ی خود را برای ما ارسال کنید.

در پروژه‌ی اولیه، ۴ داکرفایل برای php، python، golang و node قرار دادیم که می‌توانید از آن‌ها مستقیماً استفاده کنید. در صورتی که از یکی از این زبان‌ها برای حل سؤال استفاده می‌کنید، کافیست که Dockerfile مربوط به آن را در پوشه‌ی api کپی کنید و طبق توضیحات داده شده، سؤال را حل کنید. برای نصب نیازمندی‌های پایتون از requirements.txt، برای پی‌اچ‌پی از composer.json، برای گولنگ از go.mod و برای نودجی‌اس از package.json استفاده کنید.

در صورتی که زبان مورد استفاده‌ی شما، چیزی به جز این ۴ مورد است، باید خودتان داکرفایلی در پوشه‌ی api به‌شکلی بنویسید که بتواند نیازمندی‌های پروژه‌ی شما را نصب کرده و برنامه‌ی شما را مانند داکرفایل‌های موجود اجرا کند.

تغییر Dockerfile

شما آزادید که Dockerfile را به هرصورتی که علاقمندید تغییر دهید. در صورتی که با داکر و داکرفایل آشنایی ندارید، هم کافیست که با استفاده از توضیحات داده شده صرفاً با پیاده‌سازی این API سؤال را حل کنید.

فرد بی‌هویت

نکته‌ی مهمی که در این API وجود دارد این است که درصورتی که هدر CLIENT-KEY وجود نداشت، آخرین وضعیت باید در پاسخ برگردانده شود. درواقع تعداد ریکوئست‌هایی که فردی بی‌هویت به API می‌زند شمرده نمی‌شود؛ ولی state کلی تا آن لحظه برگردانده می‌شود:

{
    "state": {
      "client-key-arvancloud": 5,
      "client-key-quera": 7,    
      "client-key-bood-key-bood-man-naboodam": 35
    }
}
JSON

نحوه ارسال پاسخ🔗

شما می‌توانید تمامی محتوای موجود در پوشه‌ی api را تغییر دهید و هر فایلی که می‌خواهید اضافه یا کم کنید.

api
├──  api.py   # or main.go somefile.js anyfile.php name.any ...
├── Dockerfile
├── requirements.txt  # or go.mod package.json composer.json
└── runner.sh
Plain text

توجه کنید که نام فایل کد شما برای سیستم داوری اهمیتی ندارد و این خود شما هستید که در runner.sh از نام آن برای اجرای پروژه استفاده می‌کنید.

در نهایت این پوشه را zip کرده و ارسال کنید. توجه کنید که پس از extract کردن فایل zip شما، باید پوشه‌ی api را ببینیم که درون آن Dockerfile وجود دارد.

محدودیت با nginx


یک سرویس http api روی یک docker container با نام api و روی پورت 8080 ران می‌شود.

از شما می‌خواهیم:🔗

۱. با استفاده از nginx درخواست‌ها به پورت 80 را به این سرویس proxy کنید.

۲. با استفاده از nginx یک rate limit اضافه کنید.

این rate limit باید بر اساس ‌header با نام CLIENT-KEY کار کند و هر client مجاز باشد در هر دقیقه ۱۰ request ارسال کند و پس از آن به مدت ۱ دقیقه response با status code برابر 429 دریافت کند.

همچنین در تنظیم rate limit توجه داشته باشید که یک client-key باید بتواند همه ۱۰ درخواست مجاز خود در دقیقه را، در یک ثانیه بزند و محدودیتی از این لحاظ نداشته باشد.


محتوای فایل docker-compose.yml که در داوری اجرا می‌شود:

extensionFromNamedocker-compose.yml
version: "3"

services:
  api:
    build: "./api"
    container_name: "api"
    volumes:
      - ./api:/api
    ports:
      - "8080:8080"

  nginx:
    image: registry.gitlab.com/qio/standard/nginx:1.21-alpine
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
YAML

شما تنها مجاز به ارسال فایل nginx.conf هستید و هر فایل دیگری که ارسال کنید در داوری اثر نخواهد داشت.

لاگ وب‌سرور


جف بزوس برای حل مشکلات و‌ب سرورهایی که در آمازون استفاده می‌شود، نیازمند این است که بتواند لاگ هر وب سرور را بصورت مرتب بررسی نماید. برای حل این مشکل او می‌خواهد سیستمی پیاده کند که خروجی لاگ وب سرور مورد نظر او، داخل سیستم Elasticsearch ذخیره شود که هم به راحتی آنها را بخواند و هم سیستم لاگ عملکرد بهینه‌ای داشته باشد. برای این منظور شما باید پاسخ سوال‌های سوال دوم مسابقه و سوال پنجم مسابقه را خود را به این صورت با هم ترکیب کنید که هم api خود در آن اجرا شود و هم اینکه درخواست ها توسط nginx به سرور api پروکسی شود. سپس کانتینر Filebeat مسئولیت خواندن لاگ‌های وب سرور مورد نظر (nginx) را بر عهده داشته باشد و در صورت اضافه شدن هر لاگ جدید آنرا برای کانتینر Elasticsearch ارسال نماید.

برای دانلود پروژه‌ی اولیه روی این لینک کلیک کنید.

کانتینرهای Elasticsearch🔗

در ابتدای کار، برای پیاده سازی این سیستم، نیاز است که فایل docker-compose.yml را تکمیل کنید. توجه داشته باشید که شما اجازه‌ی build کردن یک Image جدید خواهید داشت. برای این سوال شما می‌توانید از لیست زیر استفاده نمایید:

registry.gitlab.com/qio/standard/elasticsearch:7.16.1
registry.gitlab.com/qio/standard/filebeat:7.16.1
registry.gitlab.com/qio/standard/nginx:1.21-alpine
Plain text

توجه داشته باشید که باید فایل docker-compose.yml را به‌ شکلی تکمیل کنید که پس از up کردن آن، هر ۴ کانتینر (Elasticsearc, Filebeat , Nginx, Api) به درستی اجرا شوند. سپس با توجه به درخواست‌هایی که از سمت سیستم داوری برای وب سرور nginx ارسال می‌شود، تعدادی لاگ در فایل مربوطه ذخیره می‌شود که در نهایت انتظار می‌رود با تنظیماتی که شما انجام داده‌اید، این لاگ‌های جدید، به سیستم Elasticsearch منتقل شود.

شرایط containerها🔗

۱. نام containerها باید حتماً به شکل زیر باشد:

elasticsearch
filebeat
nginx
api
Plain text

۲. هریک از این containerها باید روی پورت مشخصی که مربوط به آن‌ها می‌باشد قرار گیرند. دقیقاً به شکل زیر:

port of elasticsearch = 9200
port of nginx = 80
Plain text

دقت کنید که تنظیمات پورت‌ها به گونه‌ای انجام شود که وقتی سیستم داوری دستور docker-compose up را اجرا کرد، بتواند به هر دو پورت ۹۲۰۰ و ۸۰ دسترسی داشته باشد.

۳. نام ایندکس (index) که لاگ‌ها باید در آن ذخیره شود log-index می‌باشد.

۴. آدرسی که لاگ‌های nginx باید در آن ذخیره شود، آدرس /var/log/nginx/access.log می‌باشد.

۵. توجه کنید تمامی مواردی که برای ratelimit در سوال ۵ پیاده کرده بودید باید حذف کنید و محدودیتی از بابت ارسال درخواست به سیستم وجود نداشته باشد.

۶. محتویات فایل docker-compose.yml شما باید به گونه‌ای باشد که در کمترین زمان ممکن کانتینرها آماده باشند و کار خود را انجام بدهند. برای این منظور حداکثر ۹۰ ثانیه در نظر گرفته شده است و در صورتی که زمان بیشتری طول بکشد، نمره کامل را نخواهید گرفت.

ایجاد Volume🔗

در صورتی‌که نیاز دارید فایل یا مسیری را به درون هریک از containerها Volume کنید، می‌توانید از پوشه‌های api و data استفاده کنید.

services:
  sample_container:
    image: "registry.gitlab.com/qio/standard/filebeat:7.16.1"
    volumes:
      - ./api/somefile:/somefile
      - ./data/somefile2:/somefile2
Plain text

فراموش نکنید که فایل somefile را درون پوشه‌ی api و فایل somefile2 را درون پوشه‌ی data قرارداده و آن را برای داوری ارسال نمایید. برای مثال، پوشه‌ی ارسالی شما برای داوری کوئرا به شکل زیر می‌تواند باشد:

.
├── api
│   └── somefile
├── data
│   └── somefile2
├── docker-compose.yml
└── requirements.txt
Plain text

*توجه:* سیستم داوری کوئرا به‌صورت خودکار فایل docker-compose.yml را با کامند up اجرا می‌کند. شما نیازی به کد یا اسکریپتی برای اجرای این کار ندارید.

شرایط نمره‌دهی🔗

نمره‌ای که شما دریافت می‌کنید، مربوط به اجرای درست و دقیق کلاستر خواهد بود. به این صورت که هر ۴ کانتینر مورد نظر باید با نامی که در بالا توضیح داده شده باید بدون مشکل اجرا شوند و همچنین لاگی که توسط وب سرور nginx ایجاد می‌شود، باید در ایندکس log-index کانتینر Elasticsearch ثبت شده باشد.

نحوه ارسال پاسخ🔗

شما فقط می‌توانید مسیرهای های‌لایت شده را تغییر دهید:

.
├──  data 
│   └── ...
├──  api 
│   └── ...
├──  docker-compose.yml 
└── requirements.txt
Plain text

در نهایت این پوشه را zip کرده و ارسال کنید. توجه کنید که پس از extract کردن فایل zip شما، باید فایل docker-compose.yml را ببینیم.