ไฟล์ในเครื่องเมื่อย้ายแอปพลิเคชันไปยัง Kubernetes

ไฟล์ในเครื่องเมื่อย้ายแอปพลิเคชันไปยัง Kubernetes

เมื่อสร้างกระบวนการ CI/CD โดยใช้ Kubernetes บางครั้งปัญหาเกิดจากการไม่เข้ากันระหว่างข้อกำหนดของโครงสร้างพื้นฐานใหม่และแอปพลิเคชันที่กำลังถ่ายโอนไปยังกระบวนการนั้น โดยเฉพาะอย่างยิ่งในขั้นตอนการสร้างแอปพลิเคชัน สิ่งสำคัญคือต้องได้รับ หนึ่ง ภาพที่จะใช้ใน ทั้งหมด สภาพแวดล้อมและคลัสเตอร์ของโครงการ หลักการนี้รองรับความถูกต้อง ตาม Google การจัดการคอนเทนเนอร์ (มากกว่าหนึ่งครั้งเกี่ยวกับเรื่องนี้ กล่าวว่า และฝ่ายเทคนิคของเรา)

อย่างไรก็ตาม คุณจะไม่เห็นใครเลยในสถานการณ์ที่โค้ดของไซต์ใช้เฟรมเวิร์กสำเร็จรูป ซึ่งการใช้งานดังกล่าวจะกำหนดข้อจำกัดในการใช้งานต่อไป และในขณะที่อยู่ใน “สภาพแวดล้อมปกติ” สิ่งนี้จะจัดการได้ง่าย แต่ใน Kubernetes พฤติกรรมนี้อาจกลายเป็นปัญหาได้ โดยเฉพาะอย่างยิ่งเมื่อคุณพบมันเป็นครั้งแรก ในขณะที่ความคิดสร้างสรรค์สามารถคิดวิธีแก้ปัญหาโครงสร้างพื้นฐานที่ดูเหมือนชัดเจนหรือแม้กระทั่งดีเมื่อมองแวบแรก... สิ่งสำคัญคือต้องจำไว้ว่าสถานการณ์ส่วนใหญ่สามารถและควร ได้รับการแก้ไขในเชิงสถาปัตยกรรม.

มาดูวิธีแก้ปัญหายอดนิยมสำหรับการจัดเก็บไฟล์ที่อาจนำไปสู่ผลที่ไม่พึงประสงค์เมื่อใช้งานคลัสเตอร์ และยังชี้ให้เห็นเส้นทางที่ถูกต้องมากขึ้นอีกด้วย

พื้นที่เก็บข้อมูลแบบคงที่

เพื่อแสดงให้เห็น ให้พิจารณาเว็บแอปพลิเคชันที่ใช้ตัวสร้างคงที่บางประเภทเพื่อรับชุดรูปภาพ สไตล์ และสิ่งอื่นๆ ตัวอย่างเช่น กรอบงาน Yii PHP มีตัวจัดการสินทรัพย์ในตัวที่สร้างชื่อไดเร็กทอรีที่ไม่ซ้ำกัน ดังนั้น ผลลัพธ์จึงเป็นชุดของเส้นทางสำหรับไซต์คงที่ซึ่งเห็นได้ชัดว่าไม่ได้ตัดกัน (ซึ่งเกิดขึ้นด้วยเหตุผลหลายประการ - ตัวอย่างเช่น เพื่อกำจัดรายการที่ซ้ำกันเมื่อองค์ประกอบหลายรายการใช้ทรัพยากรเดียวกัน) ดังนั้น ในครั้งแรกที่คุณเข้าถึงโมดูลทรัพยากรบนเว็บ ไฟล์คงที่ (อันที่จริงมักจะเป็นลิงก์สัญลักษณ์ แต่จะเพิ่มเติมในภายหลัง) จะถูกสร้างและจัดวางด้วยไดเร็กทอรีรากทั่วไปที่ไม่ซ้ำกันสำหรับการปรับใช้นี้:

  • webroot/assets/2072c2df/css/…
  • webroot/assets/2072c2df/images/…
  • webroot/assets/2072c2df/js/…

สิ่งนี้หมายความว่าอย่างไรในแง่ของคลัสเตอร์?

ตัวอย่างที่ง่ายที่สุด

มาดูกรณีทั่วไปกัน เมื่อ PHP นำหน้าด้วย nginx เพื่อกระจายข้อมูลคงที่และประมวลผลคำของ่ายๆ วิธีที่ง่ายที่สุด - การใช้งาน ด้วยสองภาชนะ:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: site
spec:
  selector:
    matchLabels:
      component: backend
  template:
    metadata:
      labels:
        component: backend
    spec:
      volumes:
        - name: nginx-config
          configMap:
            name: nginx-configmap
      containers:
      - name: php
        image: own-image-with-php-backend:v1.0
        command: ["/usr/local/sbin/php-fpm","-F"]
        workingDir: /var/www
      - name: nginx
        image: nginx:1.16.0
        command: ["/usr/sbin/nginx", "-g", "daemon off;"]
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: nginx.conf

ในรูปแบบที่เรียบง่าย การกำหนดค่า nginx มีลักษณะดังนี้:

apiVersion: v1
kind: ConfigMap
metadata:
  name: "nginx-configmap"
data:
  nginx.conf: |
    server {
        listen 80;
        server_name _;
        charset utf-8;
        root  /var/www;

        access_log /dev/stdout;
        error_log /dev/stderr;

        location / {
            index index.php;
            try_files $uri $uri/ /index.php?$args;
        }

        location ~ .php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            include fastcgi_params;
        }
    }

เมื่อคุณเข้าถึงไซต์เป็นครั้งแรก เนื้อหาจะปรากฏในคอนเทนเนอร์ PHP แต่ในกรณีของคอนเทนเนอร์สองตัวภายในพ็อดเดียว nginx ไม่รู้อะไรเลยเกี่ยวกับไฟล์สแตติกเหล่านี้ ซึ่งควรมอบให้ (ตามการกำหนดค่า) ด้วยเหตุนี้ ไคลเอ็นต์จะเห็นข้อผิดพลาด 404 สำหรับคำขอทั้งหมดไปยังไฟล์ CSS และ JS วิธีแก้ปัญหาที่ง่ายที่สุดคือการจัดระเบียบไดเร็กทอรีทั่วไปสำหรับคอนเทนเนอร์ ตัวเลือกดั้งเดิม - ทั่วไป emptyDir:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: site
spec:
  selector:
    matchLabels:
      component: backend
  template:
    metadata:
      labels:
        component: backend
    spec:
      volumes:
        - name: assets
          emptyDir: {}
        - name: nginx-config
          configMap:
            name: nginx-configmap
      containers:
      - name: php
        image: own-image-with-php-backend:v1.0
        command: ["/usr/local/sbin/php-fpm","-F"]
        workingDir: /var/www
        volumeMounts:
        - name: assets
          mountPath: /var/www/assets
      - name: nginx
        image: nginx:1.16.0
        command: ["/usr/sbin/nginx", "-g", "daemon off;"]
        volumeMounts:
        - name: assets
          mountPath: /var/www/assets
        - name: nginx-config
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: nginx.conf

ตอนนี้ไฟล์คงที่ที่สร้างขึ้นในคอนเทนเนอร์จะถูกให้บริการโดย nginx อย่างถูกต้อง แต่ฉันขอเตือนคุณว่านี่เป็นวิธีแก้ปัญหาแบบดั้งเดิมซึ่งหมายความว่ามันยังห่างไกลจากอุดมคติและมีความแตกต่างและข้อบกพร่องในตัวเองซึ่งจะกล่าวถึงด้านล่าง

พื้นที่จัดเก็บข้อมูลขั้นสูงยิ่งขึ้น

ตอนนี้ลองจินตนาการถึงสถานการณ์ที่ผู้ใช้เยี่ยมชมไซต์ โหลดหน้าเว็บที่มีสไตล์ที่มีอยู่ในคอนเทนเนอร์ และในขณะที่เขาอ่านหน้านี้ เราก็ปรับใช้คอนเทนเนอร์อีกครั้ง แค็ตตาล็อกสินทรัพย์ว่างเปล่า และจำเป็นต้องร้องขอ PHP เพื่อเริ่มสร้างรายการใหม่ อย่างไรก็ตาม แม้ว่าหลังจากนี้ ลิงก์ไปยังสถิติเก่าจะไม่เกี่ยวข้อง ซึ่งจะนำไปสู่ข้อผิดพลาดในการแสดงสถิติ

นอกจากนี้ เราน่าจะมีโปรเจ็กต์ที่โหลดไม่มากก็น้อย ซึ่งหมายความว่าสำเนาแอปพลิเคชันหนึ่งชุดจะไม่เพียงพอ:

  • มาขยายขนาดกันเถอะ การใช้งาน มากถึงสองแบบจำลอง
  • เมื่อเข้าถึงไซต์เป็นครั้งแรก เนื้อหาจะถูกสร้างขึ้นในแบบจำลองเดียว
  • ในบางจุด Ingress ตัดสินใจ (เพื่อวัตถุประสงค์ในการจัดสรรภาระงาน) ให้ส่งคำขอไปยังแบบจำลองที่สอง และเนื้อหาเหล่านี้ยังไม่มีอยู่ หรืออาจจะไม่มีแล้วเพราะเราใช้ RollingUpdate และในขณะนี้ เรากำลังดำเนินการปรับใช้

โดยทั่วไปแล้วผลลัพธ์ที่ได้คือความผิดพลาดอีกครั้ง

เพื่อหลีกเลี่ยงการสูญเสียทรัพย์สินเก่า คุณสามารถเปลี่ยนแปลงได้ emptyDir บน hostPathเพิ่มสแตติกฟิสิคัลให้กับโหนดคลัสเตอร์ วิธีนี้ไม่ดีเพราะเราต้องทำจริงๆ เชื่อมโยงกับโหนดคลัสเตอร์เฉพาะ แอปพลิเคชันของคุณ เนื่องจาก - ในกรณีที่ย้ายไปยังโหนดอื่น - ไดเร็กทอรีจะไม่มีไฟล์ที่จำเป็น หรือจำเป็นต้องมีการซิงโครไนซ์ไดเร็กทอรีพื้นหลังบางประเภทระหว่างโหนด

โซลูชั่นมีอะไรบ้าง?

  1. หากฮาร์ดแวร์และทรัพยากรอนุญาตคุณสามารถใช้ได้ เซเซฟ เพื่อจัดระเบียบไดเร็กทอรีที่สามารถเข้าถึงได้เท่าเทียมกันสำหรับความต้องการคงที่ เอกสารอย่างเป็นทางการ แนะนำไดรฟ์ SSD การจำลองแบบอย่างน้อยสามเท่าและการเชื่อมต่อ "หนา" ที่เสถียรระหว่างโหนดคลัสเตอร์
  2. ตัวเลือกที่มีความต้องการน้อยกว่าคือการจัดระเบียบเซิร์ฟเวอร์ NFS อย่างไรก็ตาม คุณต้องคำนึงถึงเวลาตอบสนองที่เพิ่มขึ้นที่เป็นไปได้สำหรับการประมวลผลคำขอของเว็บเซิร์ฟเวอร์ และความทนทานต่อข้อผิดพลาดจะทำให้เป็นที่ต้องการอีกมาก ผลที่ตามมาของความล้มเหลวถือเป็นหายนะ: การสูญเสียภูเขาทำให้กระจุกดาวตายภายใต้แรงกดดันจากภาระของแอลเอที่พุ่งขึ้นไปบนท้องฟ้า

เหนือสิ่งอื่นใด จำเป็นต้องมีตัวเลือกทั้งหมดสำหรับการสร้างที่เก็บข้อมูลถาวร การทำความสะอาดพื้นหลัง ชุดไฟล์ที่ล้าสมัยสะสมในช่วงระยะเวลาหนึ่ง หน้าคอนเทนเนอร์ด้วย PHP คุณสามารถใส่ได้ DaemonSet จากการแคช nginx ซึ่งจะจัดเก็บสำเนาของเนื้อหาในระยะเวลาที่จำกัด ลักษณะการทำงานนี้สามารถกำหนดค่าได้อย่างง่ายดายโดยใช้ proxy_cache ด้วยความลึกของการจัดเก็บเป็นวันหรือพื้นที่ดิสก์กิกะไบต์

การรวมวิธีการนี้เข้ากับระบบไฟล์แบบกระจายที่กล่าวถึงข้างต้นทำให้เกิดพื้นที่กว้างใหญ่สำหรับจินตนาการ ซึ่งจำกัดด้วยงบประมาณและศักยภาพทางเทคนิคของผู้ที่จะนำไปใช้และสนับสนุนเท่านั้น จากประสบการณ์เราสามารถพูดได้ว่ายิ่งระบบยิ่งเรียบง่ายก็ยิ่งมีเสถียรภาพมากขึ้นเท่านั้น เมื่อมีการเพิ่มเลเยอร์ดังกล่าว การบำรุงรักษาโครงสร้างพื้นฐานจะยากขึ้นมาก และในขณะเดียวกัน เวลาที่ใช้ในการวินิจฉัยและกู้คืนจากความล้มเหลวก็เพิ่มขึ้น

ข้อเสนอแนะ

หากการใช้งานตัวเลือกพื้นที่เก็บข้อมูลที่นำเสนอนั้นดูไม่ยุติธรรมสำหรับคุณ (ซับซ้อน มีราคาแพง...) ก็คุ้มค่าที่จะพิจารณาสถานการณ์จากอีกด้านหนึ่ง กล่าวคือเพื่อเจาะลึกสถาปัตยกรรมโครงการและ แก้ไขปัญหาในรหัสซึ่งเชื่อมโยงกับโครงสร้างข้อมูลคงที่ในภาพ ซึ่งเป็นคำจำกัดความที่ชัดเจนของเนื้อหาหรือขั้นตอนสำหรับการ "อุ่นเครื่อง" และ/หรือการคอมไพล์เนื้อหาล่วงหน้าในขั้นตอนการประกอบภาพ วิธีนี้ทำให้เราได้รับพฤติกรรมที่คาดเดาได้อย่างแน่นอนและไฟล์ชุดเดียวกันสำหรับทุกสภาพแวดล้อมและแบบจำลองของแอปพลิเคชันที่ทำงานอยู่

หากเรากลับไปที่ตัวอย่างเฉพาะด้วยเฟรมเวิร์ก Yii และไม่เจาะลึกโครงสร้างของมัน (ซึ่งไม่ใช่จุดประสงค์ของบทความ) ก็เพียงพอที่จะชี้ให้เห็นแนวทางยอดนิยมสองวิธี:

  1. เปลี่ยนกระบวนการสร้างอิมเมจเพื่อวางเนื้อหาในตำแหน่งที่คาดเดาได้ นี่คือการแนะนำ/นำไปใช้ในส่วนขยายเช่น yii2-สินทรัพย์คงที่.
  2. กำหนดแฮชเฉพาะสำหรับไดเร็กทอรีสินทรัพย์ ตามที่กล่าวไว้ใน เช่น การนำเสนอนี้ (เริ่มจากสไลด์หมายเลข 35) อย่างไรก็ตาม ผู้เขียนรายงานในท้ายที่สุด (และไม่ใช่โดยไม่มีเหตุผล!) แนะนำว่าหลังจากรวบรวมเนื้อหาบนเซิร์ฟเวอร์บิลด์แล้ว ให้อัปโหลดไปยังที่จัดเก็บข้อมูลกลาง (เช่น S3) ซึ่งด้านหน้าจะวาง CDN

ดาวน์โหลด

อีกกรณีหนึ่งที่จะเข้ามามีบทบาทอย่างแน่นอนเมื่อย้ายแอปพลิเคชันไปยังคลัสเตอร์ Kubernetes คือการจัดเก็บไฟล์ผู้ใช้ในระบบไฟล์ ตัวอย่างเช่น เรามีแอปพลิเคชัน PHP อีกครั้งที่รับไฟล์ผ่านแบบฟอร์มอัปโหลด ทำบางอย่างกับไฟล์เหล่านั้นระหว่างดำเนินการ และส่งไฟล์เหล่านั้นกลับมา

ใน Kubernetes ตำแหน่งที่ควรวางไฟล์เหล่านี้ควรเป็นสถานที่เดียวกับแบบจำลองทั้งหมดของแอปพลิเคชัน ขึ้นอยู่กับความซับซ้อนของแอปพลิเคชันและความจำเป็นในการจัดระเบียบความคงอยู่ของไฟล์เหล่านี้ตัวเลือกอุปกรณ์แชร์ที่กล่าวถึงข้างต้นอาจเป็นสถานที่ดังกล่าว แต่อย่างที่เราเห็นพวกเขามีข้อเสีย

ข้อเสนอแนะ

ทางออกหนึ่งคือ ใช้พื้นที่จัดเก็บข้อมูลที่เข้ากันได้กับ S3 (แม้ว่าจะเป็นหมวดหมู่ที่โฮสต์เองเช่น minio ก็ตาม) การเปลี่ยนไปใช้ S3 จะต้องมีการเปลี่ยนแปลง ในระดับโค้ดและวิธีที่เนื้อหาจะถูกส่งไปที่ส่วนหน้า เราก็มีอยู่แล้ว เขียน.

เซสชันผู้ใช้

แยกเป็นมูลค่า noting องค์กรการจัดเก็บเซสชันผู้ใช้ บ่อยครั้งที่ไฟล์เหล่านี้ยังเป็นไฟล์บนดิสก์ด้วย ซึ่งในบริบทของ Kubernetes จะนำไปสู่การร้องขอการอนุญาตอย่างต่อเนื่องจากผู้ใช้ หากคำขอของเขาจบลงในคอนเทนเนอร์อื่น

ปัญหาได้รับการแก้ไขบางส่วนโดยการเปิดเครื่อง stickySessions เมื่อเข้า (คุณลักษณะนี้ได้รับการสนับสนุนในตัวควบคุมทางเข้ายอดนิยมทั้งหมด - สำหรับรายละเอียดเพิ่มเติม โปรดดู รีวิวของเรา)เพื่อผูกผู้ใช้เข้ากับพ็อดเฉพาะด้วยแอปพลิเคชัน:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx-test
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "route"
    nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"

spec:
  rules:
  - host: stickyingress.example.com
    http:
      paths:
      - backend:
          serviceName: http-svc
          servicePort: 80
        path: /

แต่สิ่งนี้จะไม่ช่วยขจัดปัญหาในการใช้งานซ้ำหลายครั้ง

ข้อเสนอแนะ

วิธีที่ถูกต้องกว่าคือโอนใบสมัครไปที่ การจัดเก็บเซสชันใน memcached, Redis และโซลูชันที่คล้ายกัน - โดยทั่วไปแล้วจะละทิ้งตัวเลือกไฟล์โดยสิ้นเชิง

ข้อสรุป

โซลูชันโครงสร้างพื้นฐานที่กล่าวถึงในข้อความนั้นคุ้มค่าที่จะใช้ในรูปแบบของ "ไม้ค้ำยัน" ชั่วคราวเท่านั้น (ซึ่งฟังดูสวยงามกว่าในภาษาอังกฤษเป็นวิธีแก้ปัญหา) อาจมีความเกี่ยวข้องในขั้นตอนแรกของการย้ายแอปพลิเคชันไปยัง Kubernetes แต่ไม่ควรหยั่งราก

เส้นทางที่แนะนำทั่วไปคือการกำจัดสิ่งเหล่านี้เพื่อสนับสนุนการปรับเปลี่ยนสถาปัตยกรรมของแอปพลิเคชันให้สอดคล้องกับสิ่งที่หลายคนรู้อยู่แล้ว แอป 12 ปัจจัย. อย่างไรก็ตาม การนำแอปพลิเคชันไปสู่รูปแบบไร้สัญชาติ หมายความว่าจะต้องมีการเปลี่ยนแปลงโค้ดอย่างหลีกเลี่ยงไม่ได้ และสิ่งสำคัญคือต้องหาสมดุลระหว่างความสามารถ/ข้อกำหนดของธุรกิจและโอกาสในการนำไปใช้และรักษาเส้นทางที่เลือก .

PS

อ่านเพิ่มเติมในบล็อกของเรา:

ที่มา: will.com

เพิ่มความคิดเห็น