در این راهنمای فنی به بررسی مفهوم StatefulSet و شیوهی استفاده از آن، همچنین اجزای سازنده یک StatefulSet و تعریف آن میپردازیم. مفهوم StatefulSet در سکوی ابری آروان با مفهوم StatefulSet در Kubernetes کاملا سازگار است.
پیشنیازها
تنها پیشنیاز این راهنما، داشتن حساب و دسترسی به سکوی ابری آروان است. برای شروع کار، ابتدا وارد حساب کاربری خود شوید، به بخش پروفایل رفته و در سربرگ API KEYS برای خود یک API KEY جدید بسازید و آن را ذخیره کنید.
برای انجام مراحل این راهنما نیاز است که از کامندلاین ابر آروان استفاده کنید. با استفاده از این لینک کامندلاین را دانلود کرده (در صورت نیاز آن را در PATH خود قرار داده) و از طریق خط فرمان لاگین کنید:
arvan login
سپس API KEY که از سایت دریافت کردهاید را در ادامهی آن قرار دهید.
StatefulSet چیست؟
در سکوی ابری آروان هنگام استفاده از persistentVolumeClaim، هر دیسک را میتوان همزمان به یک Pod متصل کرد و در نتیجه، نمیتوان چند Replica از یک Pod دارای دیسک در یک Deployment داشت.
برای استفاده از سرویسهای Stateful و یا سیستمهای توزیعشده، میتوان از StatefulSet استفاده کرد. دو ویژگی مهم StatefulSet که آن را مناسب جهت استفاده در این سیستمها میکند، توان اتصال دیسکهای مجزا به Replicaهای مربوط به Pod و همچنین نامگذاری Podها بر اساس شمارههای افزایشی است. یعنی در StatefulSet، هر Pod شامل نام StatefulSet به همراه یک شماره افزایشی است. برای مثال: Pod-1، Pod-2 و… .
ساخت StatefulSet
برای ساخت StatefulSet، میبایست اطلاعات مورد نیاز را در قالب yaml در یک فایل وارد کرده و سپس از طریق کامندلاین آن را به سکوی ابری آروان ارایه کرد. در ادامه مثالی ساده از یک StatefulSet برای یک سرویس nginx آورده و هر یک از بخشهای آن توضیح داده شده است. در این مثال فرض شده دادههای مربوط به Log سیستم نیاز به ذخیرهسازی در دیسک دایم دارند.
apiVersion: apps/v1 kind: StatefulSet metadata: annotations: labels: app: nginx name: nginx spec: selector: matchLabels: app: nginx serviceName: "nginx" replicas: 3 template: metadata: labels: app: nginx spec: terminationGracePeriodSeconds: 10 containers: - name: nginx image: nginx ports: - containerPort: 80 name: http resources: limits: cpu: '2' ephemeral-storage: 4G memory: 1G requests: cpu: '2' ephemeral-storage: 4G memory: 1G volumeMounts: - name: nginx-log mountPath: /var/log/nginx volumeClaimTemplates: - metadata: name: nginx-log spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "standard" resources: requests: storage: 4Gi
نکته: توجه کنید که دندانهگذاری (Indentation) در فایلهای yaml مهم است و کوچکترین جابهجایی میتواند باعث برگرداندن خطا و یا تنظیمات ناخواسته شود.
در ادامه فیلدهای مربوطه توضیح داده میشود.
- kind: مشخص کننده نوع ماهیت است. این فیلد میتواند مقادیری مانند: Pod، Service، StatefulSet، Deploymentو… داشته باشد. در واقع مانند ماهیتهایی که در Kubernetes وجود دارد. در این مثال، هدف تعریف StatefulSet است. به همین دلیل، این مقدار مشخص شده است.
- metadata.name: مشخصکننده نام StatefulSet است.
- spec.replica: مشخصکننده تعداد Pod هایی است که StatefulSet اجرا و مدیریت میکند. Pod در قسمت spec.template.spec تعریف میشود و فیلد spec.replica تعیین میکند چه تعداد Pod (کاملا مشابه) مطابق تعریف در حال اجرا باشد.
- spec.selector.matchLabels: از طریق این فیلد، StatefulSet متوجه میشود که کدام Podها را میبایست کنترل کند. مقداری که برای این فیلد تعیین میشود باید به صورت key:value معادل فیلد spec.template.metadata.labels باشد. در این مثال این مقدار معادل app: nginx قرار گرفته شده است.
- spec.serviceName: این فیلد مشخصکنندهی نام سرویسی است که امکان دسترسی به nodeهای nginx را فراهم میکند. نام این فیلد باید معادل metadata.name آن Serviceای باشد که برای دسترسی به Podهای nginx استفاده میشود.
- spec.template.metadata.labels: این فیلد Labelهایی را بهشکل key: value به pod های ساخته شده اختصاص میدهد. بهشکل کلی ارتباطات بین ماهیتهای سکوی ابری آروان از طریق Labelها و Selectorها انجام میپذیرد. به همین دلیل باید در انتخاب و تعیین این مقادیر دقت کرد.
- spec.template.spec: این بخش مشخصات Podای که StatefulSet میبایست کنترل کند را مشخص میکند. در این بخش زیرمجموعه spec.template.spec.containers مشخص کننده Container هایی است که قرار درون هر pod اجرا شود.
- spec.template.spec.containers.image: مشخص کننده آدرس Docker Image اجرا کنندهی Pod است.
- spec.template.spec.containers.name: مشخص کننده نام Containerای است که قرار است درون Pod اجرا شود.
- spec.template.spec.containers.imagePullPolicy: تعیین میکند در چه صورت Docker Image مجددن Pull شود. در این مثال برابر IfNotPresent قرار گرفته، که مقدار پیشفرض این فیلد است، تا در هر بار اجرا در صورتی که Image روی نود اجرا شده یافت نشد، این Image دریافت شود. سایر مقادیر ممکن برای این فیلد برابر با Always و Never است. در صورتی که Always انتخاب شود، همواره هنگام اجرای مجدد برنامه (Pod) سکوی ابری Image را مجددن دریافت میکند. توصیه میشود به هیچ وجه حالت Never را انتخاب نکنید، چرا که در این حالت، سکوی ابری فرض میکند همواره Image بر روی نودها موجود است و تلاشی برای Pull کردن Image نمیکند.
- spec.template.spec.containers.ports: لیست Portهایی که از خارج از Container قابل دیدن میباشند را مشخص میکند.
- spec.template.spec.containers.resources: این بخش مشخص کننده میزان منابعی است که Container برای اجرا نیاز خواهد داشت. مشخص کردن این منابع در سکوی ابری آروان اجباری است. هنگام تعیین این منابع میبایست دقت کافی را به خرج داد تا هنگام اجرا، Container با کمبود منابع مواجه نشده و اجرای آن دچار اختلال نشود. این بخش از دو زیر بخش Limits و Requests تشکیل شده که هر زیر بخش شامل CPU، Memory و Ephemeral-Storage است. مقادیر هریک باید برای Limits و Requests مساوی قرار داده شود. همچنین Ephemeral-Storage مشخص کننده میزان دیسک مورد استفاده Container است. دادههای ذخیره شده در آن پایدار نیست؛ یعنی اگر Pod ریستارت شود، دادههای موجود در آن به مقادیر پیشفرض Container باز میگردد و تغییرات از بین میرود. درصورت نیاز به دیسک پایدار ، میتوان از persistentVolumeClaim استفاده کرد.
- spec.template.spec.containers.volumeMounts: در این بخش، مسیری که نیاز است دیسک Mount شود مشخص میشود.
- spec.template.spec.containers.volumeMounts.name: از آنجا که میشود بیش از چند دیسک به یک کانتینر متصل کرد، جهت تفکیک مسیرها، آنها را نامگذاری باید کرد.
- spec.template.spec.containers.volumeMounts.mountPath: مشخص کننده آدرس مسیری درون کانتینر است که میخواهیم دیتای آن بر روی دیسک دایمی نوشته و از آن خوانده شود.
- spec.volumeClaimTemplate: این بخش مشخصات دیسکهایی است که باید به Pod ها متصل شوند.
- spec.volumeClaimTemplate.metadata.name: نام دیسکهایی که باید متصل شوند میبایست مشابه spec.template.spec.containers.volumeMounts.name باشد.
- spec.volumeClaimTemplate.spec.accessMode: در سکوی ابری آروان این مقدار میبایست معادل ReadWriteOnce باشد.
- spec.volumeClaimTemplate.spec.resources.requests.storage: حجم هر دیسک را مشخص میکند.
خطوط بالا را در یک فایل به نام nginx-StatefulSet.yaml وارد کرده و ذخیره کنید. سپس از طریق کامند لاین با دستور زیر، StatefulSet خود را به سکوی ابری آروان ارایه کنید.
arvan paas apply -f nginx-StatefulSet.yaml
سپس با دستور زیر میتوانید از وضعیت StatefulSet خود و اجرای آن بر روی سکوی ابری آروان آگاه شوید.
arvan paas get sts
خروجی مشابه زیر خواهد بود:
$ arvan paas get sts NAME DESIRED CURRENT AGE nginx-StatefulSet 3 3 2h
در خروجی بالا، NAME مشخص کننده نام StatefulSet است. DESIRED تعیین کننده تعداد Podهایی است که توسط این StatefulSet اجرا و بهوسیلهی فیلد spec.replica درون StatefulSet تعیین میشود. مقدار CURRENT بیان میکند در این لحظه چه تعداد POD در حال اجرا است. همچنین AGE مدت زمانی که StatefulSet اجرا شده را مشخص میکند.
همچنین با دستور زیر میتوانید Podهای در حال اجرا را مشاهده کنید.
$ arvan paas get pods NAME READY STATUS RESTARTS AGE nginx-0 1/1 Running 0 14m nginx-1 1/1 Running 0 5m nginx-2 1/1 Running 0 1m
روش دیگر تغییر تعداد Podها تغییر فیلد spec.replica در فایل کانفیگ StatefulSet است. کافی است این مقدار را در فایل خود تغییر دهید و مجددن آن را به سکوی ابری آروان ارایه کنید.
همچنین میتوانید با دستور زیر تنظیمات StatefulSet را مستقیم بر روی سکوی ابری آروان تغییر دهید.
arvan paas edit statefulset nginx-StatefulSet
دستور بالا از طریق Editor پیشفرض سکوی ابری آروان تنظیمات StatefulSet را در قالب yaml به شما نمایش میدهد. میتوانید تغییرات خود را اعمال کرده و سپس ذخیره کنید. پس از بستن Editor، تنظیمات بهشکل خودکار اعمال خواهد شد و نیاز به انجام عمل دیگری نیست.
Headless Service
برای دسترسی به Podهای StatefulSet از درون سکوی ابری آروان، میتوان از Headless Service استفاده کرد. Headless Service این امکان را فراهم میکند تا به شکل مستقیم به Podها بدون Load Balancing ترافیک ورودی، دسترسی داشت. برای تعریف Headless Service کافی است spec.clusterIP را در تنظیمات Service برابر با none قرار دهید. در این حالت میتوان به Podها با نام دامنه به شکل Pod-1.svc دست یافت. دستورهای زیر شیوهی تعریف یک Headless Service را نمایش میدهد.
apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: http clusterIP: None selector: app: nginx
در ادامه فیلدهای مربوطه توضیح داده میشود.
- metadata.name: نام service میباشد که در این مثال باید معادل مقدار spec.serviceName مشخص شده در توصیف statefulSet باشد.
- spec.clusterIP: این مقدار باید معادل none قرار گیرد تا مشخص شود قصد تعریف یک headless service داریم.
- spec.selector: در این بخش مشخص میشود که ترافیک ورودی به کدام Pod ها میبایست ارسال شود. در این مثال Pod های مربوط به statefulSet مشخص شدهاند.
با توجه به تعریفهای بالا، برای دسترسی به هر یک از Podهای StatefulSet تعریف شده، میتوان از نام دامنههای زیر استفاده کرد.
nginx-0.nginx nginx-1.nginx nginx-2.nginx
با توجه به اینکه از پورت ۸۰ استفاده شده نیازی به مشخص کردن پورت به صورت مستقیم نیست. اگر پورت دیگری انتخاب شده بود، میبایست آن را مشخصن به کار برد برای مثال nginx-0.nginx:80.
برای اطلاعات بیشتر میتوانید به مستندات Kubernetes و OKD مراجعه کنید.