دیپلوی voting-app (قسمت بیستم)

توی این قسمت میریم به سراغ اینکه بررسی کنیم چطوری می‌تونیم یه اپلیکیشن رو روی کلاستر کوبرنتیزمون دیپلوی کنیم و بالا بیاریم:
خب یه مروری کنیم پست‌های قبلی رو:
یه پروژه خیلی خوبی که من معمولا تو سمپل‌هایی که موقع آموزش برای بچه‌ها میزنم ازش استفاده می‌کنم پروژه voting-app هست.
Voting-app
همونطور که در HLD بالا می‌بینید این پروژه از چند بخش تشکیل شده که شامل دیتابیس Postgresql و ابزار redis برای cache و سه قسمت که هرکدام با یک زبان نوشته شده اند.
قسمت vote برای رای‌دادن که به زبان پایتون هست و قسمت result که به زبان nodejs هست و قسمت ورکر به زبان NET. که نهایتا این پروژه به ما دوتا url می‌دهد که در یکی میتونیم رای بدیم بین دو گزینه‌ای که وجود داره و در دیگری می‌تونیم نتایج رو ببینیم.
توی این بلاگ پست به ترتیب مانیفست‌های مختلف رو بررسی می‌کنیم تا ببینیم که به چه صورت می‌توان همچین اپلیکیشنی رو روی کلاستر کوبرنتیز دیپلوی کرد.
				
					apiVersion: v1
kind: Service
metadata:
  labels:
    app: db
  name: db
spec:
  type: ClusterIP
  ports:
  - name: "db-service"
    port: 5432
    targetPort: 5432
  selector:
    app: db
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-pvc
  labels:
    app: db
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: local-path
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: db
  name: db
spec:
  replicas: 1
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
    spec:
      containers:
      - image: postgres:15-alpine
        name: postgres
        env:
        - name: POSTGRES_USER
          value: postgres
        - name: POSTGRES_PASSWORD
          value: postgres
        ports:
        - containerPort: 5432
          name: postgres
        volumeMounts:
        - mountPath: /var/lib/postgresql/data
          name: db-data
      volumes:
      - name: db-data
        persistentVolumeClaim:
          claimName: db-pvc 
---
				
			
از پایین به بالا به ترتیب یک deployment، یک PVC و یک سرویس توی این مانیفست نوشته که شده که در ادامه در کدوم رو توضیح خواهیم داد.
همونطور که میدونید توصیف هر ریسورس کوبرنتیز در قالب فایل مانیفست شامل سه بخش اصلی apiVersion و kind و metadata هست که به ترتیب نسخه‌ای از api کلاستر که میخوایم از آن استفاده کنیم و نوع ریسورسی که میخوایم بسازیم و یه سری متا دیتا رو شامل میشن. معمولا دو بخش spec و status هم در مانیفست‌ها هستند.
بنابراین برای پتسگرس نیاز داریم که در قالب یک deployment یک پاد پستگرس ایجاد کنیم، برای این پاد لیبل app: db رو اضافه می‌کنیم و در قسمت spec بایستی که تمپلت پادهای این دیپلویمنت رو مشخص کنیم که نوشتیم یک رپلیکا ازش بالا بیاد و با سلکتور app: db، در ادامه برای کانتینرهای این پاد spec مشخص می‌کنیم و ایمیج و متغییرهای محیطی و والیوم مربوط به این پاد رو مشخص می‌کنیم.
همانطور که می‌بینید در والیوم کانتینر نام یک pvc داده شده که بالاتر اون رو ایجاد کردیم که در ادامه بررسی‌ش می‌کنیم.
ورژن api رو مشخص می‌کنیم و یک ریسورس از نوع PersistentVolumeClaim ایجاد می‌کنیم و بعد از اینکه اسم و لیبل‌ش رو مشخص کردیم نوع دسترسی بهش رو مشخص می‌کنیم و میزان استورجی که میخوایم همچنین storageClassName رو هم مشخص می‌کنیم که اگه میخواید بیشتر در موردش بدونید میتونید این بلاگ پست رو بخونید.

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

deploy app
				
					apiVersion: v1
kind: Service
metadata:
  labels:
    app: redis
  name: redis
spec:
  type: ClusterIP
  ports:
  - name: "redis-service"
    port: 6379
    targetPort: 6379
  selector:
    app: redis
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-pvc
  labels:
    app: redis
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: local-path
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: redis
  name: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - image: redis:alpine
        name: redis
        ports:
        - containerPort: 6379
          name: redis
        volumeMounts:
        - mountPath: /data
          name: redis-data
      volumes:
      - name: redis-data
        persistentVolumeClaim:
          claimName: redis-pvc
				
			

بعد از اینکه مانیفست دیتابیس رو به کلاستر اضافه کردیم میریم سراغ مانیفست سرویس redis، همونطور که میدونید فایل های مانیفست رو با دستور kubectl apply -f file-name.yml میتونیم به کلاستر اپلای کنیم و این ریسورس‌ها رو اضافه کنیم. خب بریم مانیفست ردیس رو بررسی کنیم ابتدا از پایین یه deployment داریم که با رپلیکای یک، یک پاد ردیس رو از روی ایمیج redis:alpine میاره بالا که روی پورت ۶۳۷۹ سرویس میده و والیوم redis-data به اون مانت شده که از طریق pvc ساخته میشه. ریسورس بعدی همین pvc هست که مثل دیتابیس از استورج کلاس local-path با حجم یک گیگ اونو میسازیم و همچنین با دسترسی خواندن و نوشتن.

redis
و نهایتا ریسورس بعدی که سرویسی هست که ردیس رو میخوایم بندازیم پشت اون تا بقیه کامپوننت‌های سرویس بتونن بهش وصل بشن ازین طریق که این سرویس هم از نوع ClusterIP هست.
خب حالا ایمیج‌های کدهای اپلیکیشن رو میخوایم که از طریق ci/cd بیلد شدن و توی رجیستری ما قرار دارن و باید اونها رو دریافت کنیم و کامپوننت‌های بعدی اپلیکیشن رو بیاریم بالا. بنابراین ابتدا بایستی که دسترسی به رجیستری رو داشته باشیم پس پسورد رو در قالب یک ریسورس secret ایجاد می‌کنیم.
				
					apiVersion: v1
kind: Secret
metadata:
  name: registry-login
type: kubernetes.io/basic-auth
data:
  username: a3ViZQ==
  password: QW5JcXlpajFEWWw3cmU2WHRJaHY5R3JLYVBnMURYQXRNTTh1TkF1dQ==
# base64
				
			
این مانیفست یک آبجکت از نوع Secret در کوبرنتیز می‌سازه با نام registry-login و نوع kubernetes.io/basic-auth.
زیرشاخهٔ data شامل دو فیلد username و password است که مقدار آن‌ها با Base64 کدگذاری شده‌اند.
هدف از این نوع سکرت، معمولاً نگه‌داری اطلاعات ورود (نام کاربری و گذرواژه) برای یک سرویس (مثلاً رجیستری داکر یا هر جای دیگری) است تا پادها بتوانند از آن استفاده کنند. دقت کنید که این مقدار به راحتی میتونه دیکود بشه و اگه از مواردی مثل hashicorp-vault استفاده نمی‌کنید و میخواید که سکرت‌هاتون رو توی کوبرنتیز رمزگذاری کنید میتونید از روش‌های زیر استفاده کنید.
راه‌های رمزگذاری (Encryption) سکرت در کوبرنتیز:
۱. Encryption at Rest:
می‌توان در سطح کوبرنتیز، رمزگذاری سکرت‌ها را در etcd فعال کرد.
این کار با پیکربندی EncryptionConfiguration روی API Server انجام می‌شود.
داده‌های سکرت به صورت رمزگذاری‌شده در دیتابیس etcd ذخیره می‌شوند، اما وقتی به‌صورت YAML (مثل همین مانیفست) در مخزن گیت یا جای دیگر نگهداری می‌کنید، همچنان مشکل Base64 باقی می‌ماند.
۲. Sealed Secrets:
سرویسی به‌نام Sealed Secrets (محصول بیتنامی) وجود دارد که با استفاده از کلید عمومی/خصوصی، سکرت‌ها را رمزگذاری می‌کند. شما یک فایل به‌نام SealedSecret می‌سازید و آن را در ریپازیتوری خود نگهداری می‌کنید. این فایل حاوی داده رمزگذاری‌شده است که فقط کنترلر Sealed Secrets در Cluster کوبرنتیز قادر به رمزگشایی آن خواهد بود.
در این روش، حتی در مخزن گیت هم اطلاعات واقعا به صورت رمزنگاری‌شده نگه‌داری می‌شوند و امنیت بیشتری وجود دارد.
۳. ابزارهای دیگر مانند SOPS یا helm-secrets:
می‌توانید از ابزارهایی مثل Mozilla SOPS یا پلاگین helm-secrets برای رمزگذاری داده‌های حساس در گیت استفاده کنید.
این ابزارها قبل از اعمال Manifest در کوبرنتیز، رمزنگاری را انجام می‌دهند یا رمزگشایی می‌کنند.
 
بعد از این مرحله حالا مانیفست‌های مربوط به قسمت‌های بعدی اپلیکیشن رو داریم که قسمت worker به شکل زیر هست:
				
					apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: worker
  name: worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
    spec:
      imagePullSecrets:
      - name: registry-login
      containers:
      - image: registry.mecan.ir/devops_certification/kubernetes/voting-app/worker:v0.1.0
        name: worker
				
			
مثل دیپلویمنت‌هایی قبلی مون فقط اینبار ایمیج رو از رجیستری که داریم میگیره و لیبل و سلکتورها رو هم برای این app اضافه می‌کنیم ازونجایی که میکروسرویس worker نیازی به ارتباط نتورکی و اینکه از بیرون بهش صفحه وب بدیم نداره، براش سرویس ایجاد نکردیم.
برای سرویس vote به شکل زیر داریم:
				
					apiVersion: v1
kind: Service
metadata:
  labels:
    app: vote
  name: vote
spec:
  type: NodePort
  ports:
  - name: "vote-service"
    port: 8080
    targetPort: 80
    nodePort: 31000
  selector:
    app: vote
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: vote
  name: vote
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vote
  template:
    metadata:
      labels:
        app: vote
    spec:
      imagePullSecrets:
      - name: registry-login
      containers:
      - image: registry.mecan.ir/devops_certification/kubernetes/voting-app/vote:v0.1.0
        name: vote
        ports:
        - containerPort: 80
          name: vote
				
			
یک دیپلویمنت با رپلیکای یک برای ایمیج vote ایجاد می‌کنیم و توی سرویسی که بهش وصل می‌کنیم از طریق لیبل و سلکتور می‌گیم که روی nodePort شماره ۳۱۰۰۰ بیا پورت ۸۰۸۰ سرویس رو که میرسونه مارو به پورت ۸۰ پادهای تارگت، سرویس مارو پابلیش کن. حالا اینجا ما از nodePort استفاده کردیم اما شما در صورت وجود امکان زیرساختی شبکه میتونید از Load balancer استفاده کنید یا اینکه از اینگرس کنید که قبل‌تر توضیح دادیم به نوعی ریورس پروکسی ما توی کوبرنتیز هست و میتونید لایه هفتی بر اساس دامنه توی هدر ریکوئست‌های http درخواست هارو تفکیک کنید و بفرستید سمت پادهای مقصدی که دارن.
برای سرویس result هم به شکل زیر داریم:
				
					apiVersion: v1
kind: Service
metadata:
  labels:
    app: result
  name: result
spec:
  type: NodePort
  ports:
  - name: "result-service"
    port: 8081
    targetPort: 80
    nodePort: 31001
  selector:
    app: result
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: result
  name: result
spec:
  replicas: 1
  selector:
    matchLabels:
      app: result
  template:
    metadata:
      labels:
        app: result
    spec:
      imagePullSecrets:
      - name: registry-login
      containers:
      - image: registry.mecan.ir/devops_certification/kubernetes/voting-app/result:v0.1.0
        name: result
        ports:
        - containerPort: 80
          name: result
				
			
مشابه سرویس vote سرویس result رو هم با یک رپلیکا دیپلویمنتش رو ایجاد می‌کنیم و لیبل و سلکتورهارو میزنیم و پاد تمپلتی شبیه موارد قبلی براش میذاریم. مشابه سرویس قبل مجددا سرویسی از نوع nodePort ایجاد می‌کنیم و پورت ۸۰ این پاد رو از طریق پورت ۳۱۰۰۱ نودهای کوبرنتیزمون پابلیش می‌کنیم. حالا میتونید از طریق این پورت‌ها روی نودهای کوبرنتیز که دارید این سرویس رو ببینید و توش رای بدید و لذت ببرید 🙂 دقت کنید که برای گرفتن سرتیفیکیت و اینکه سرویس‌تون رو پشت یک دامنه به همراه ssl داشته باشید روشی که پیشنهاد میشه این هست که از ingress controller nginx توی کلاسترتون استفاده کنید که certmanager هم داره و میتونید ازش استفاده کنید.
 

دیدگاه‌ خود را بنویسید

مقاله های داکرمی

Kubernetes

دیپلوی voting-app (قسمت بیستم)

توی این قسمت میریم به سراغ اینکه بررسی کنیم چطوری می‌تونیم یه اپلیکیشن رو روی کلاستر کوبرنتیزمون دیپلوی کنیم و بالا بیاریم: خب یه مروری کنیم پست‌های قبلی رو: دواپس چیه و چرا لازمه؟  اینجا در مورد دواپس و ضرورت

توضیحات بیشتر »
Kubernetes

نصب کلاستر با rancher (قسمت نوزدهم)

توی این قسمت میریم به سراغ اینکه بررسی کنیم چطوری می‌تونیم یه کلاستر کوبرنتیز رو با استفاده از rancher ستاپ کنیم و بیاریم بالا. خب یه مروری کنیم پست‌های قبلی رو: دواپس چیه و چرا لازمه؟ اینجا در مورد دواپس و

توضیحات بیشتر »
Kubernetes

نصب کلاستر با kubespray (قسمت هجدهم)

توی این قسمت میریم سراغ اینکه بررسی کنیم چطوری می‌تونیم یه کلاستر کوبرنتیز رو با استفاده از kubespray ستاپ کنیم و بیاریم بالا. روشی که می‌تونیم باهاش کلاستر پروداکش رو ستاپ و نگهداری کنیم. خب یه مروری کنیم پست‌های قبلی

توضیحات بیشتر »
پیمایش به بالا