Từ tập lệnh đến nền tảng riêng của chúng tôi: cách chúng tôi tự động hóa quá trình phát triển tại CIAN

Từ tập lệnh đến nền tảng riêng của chúng tôi: cách chúng tôi tự động hóa quá trình phát triển tại CIAN

Tại RIT 2019, đồng nghiệp của chúng tôi Alexander Korotkov đã thực hiện báo cáo về tự động hóa phát triển tại CIAN: để đơn giản hóa cuộc sống và công việc, chúng tôi sử dụng nền tảng Integro của riêng mình. Nó theo dõi vòng đời của các nhiệm vụ, giúp nhà phát triển giảm bớt các hoạt động thường ngày và giảm đáng kể số lượng lỗi trong quá trình sản xuất. Trong bài đăng này, chúng tôi sẽ bổ sung cho báo cáo của Alexander và cho bạn biết cách chúng tôi đi từ các tập lệnh đơn giản đến kết hợp các sản phẩm nguồn mở thông qua nền tảng của riêng chúng tôi và nhóm tự động hóa riêng biệt của chúng tôi làm gì.
 

Cấp độ XNUMX

“Không có thứ gì gọi là cấp độ XNUMX, tôi không biết thứ đó”
Sư phụ Shifu trong phim "Kung Fu Panda"

Tự động hóa tại CIAN bắt đầu 14 năm sau khi công ty được thành lập. Vào thời điểm đó, nhóm phát triển có 35 người. Thật khó tin phải không? Tất nhiên, tự động hóa đã tồn tại ở một số dạng, nhưng một hướng riêng để tích hợp liên tục và phân phối mã đã bắt đầu hình thành vào năm 2015. 

Vào thời điểm đó, chúng tôi có một lượng lớn Python, C# và PHP, được triển khai trên các máy chủ Linux/Windows. Để triển khai con quái vật này, chúng tôi đã có một bộ tập lệnh chạy thủ công. Ngoài ra còn có việc lắp ráp khối đá nguyên khối, mang lại đau đớn và đau khổ do xung đột khi hợp nhất các nhánh, sửa chữa các khiếm khuyết và xây dựng lại “với một nhóm nhiệm vụ khác trong công trình”. Một quy trình đơn giản hóa trông như thế này:

Từ tập lệnh đến nền tảng riêng của chúng tôi: cách chúng tôi tự động hóa quá trình phát triển tại CIAN

Chúng tôi không hài lòng với điều này và chúng tôi muốn xây dựng một quy trình triển khai và xây dựng có thể lặp lại, tự động và dễ quản lý. Để làm được điều này, chúng tôi cần một hệ thống CI/CD và chúng tôi đã chọn giữa phiên bản Teamcity miễn phí và phiên bản miễn phí của Jenkins, vì chúng tôi đã làm việc với họ và cả hai đều phù hợp với chúng tôi về bộ chức năng. Chúng tôi đã chọn Teamcity làm sản phẩm mới hơn. Vào thời điểm đó, chúng tôi chưa sử dụng kiến ​​trúc microservice và không mong đợi một số lượng lớn nhiệm vụ và dự án.

Chúng tôi nảy ra ý tưởng về hệ thống của riêng mình

Việc triển khai Teamcity chỉ loại bỏ một phần công việc thủ công: phần còn lại là tạo Yêu cầu kéo, quảng bá các vấn đề theo trạng thái trong Jira và lựa chọn các vấn đề để phát hành. Hệ thống Teamcity không còn có thể đối phó với điều này. Cần phải chọn con đường tự động hóa hơn nữa. Chúng tôi đã xem xét các tùy chọn để làm việc với tập lệnh trong Teamcity hoặc chuyển sang hệ thống tự động hóa của bên thứ ba. Nhưng cuối cùng, chúng tôi quyết định rằng chúng tôi cần sự linh hoạt tối đa mà chỉ có giải pháp của riêng chúng tôi mới có thể cung cấp. Đây là cách phiên bản đầu tiên của hệ thống tự động hóa nội bộ có tên Integro xuất hiện.

Teamcity giải quyết vấn đề tự động hóa ở cấp độ khởi chạy các quy trình xây dựng và triển khai, trong khi Integro tập trung vào tự động hóa cấp cao nhất của các quy trình phát triển. Cần phải kết hợp công việc giải quyết các vấn đề trong Jira với việc xử lý mã nguồn liên quan trong Bitbucket. Ở giai đoạn này, Integro bắt đầu có quy trình làm việc riêng để xử lý các loại nhiệm vụ khác nhau. 

Do sự gia tăng tự động hóa trong quy trình kinh doanh, số lượng dự án và hoạt động trong Teamcity đã tăng lên. Vì vậy, một vấn đề mới đã xuất hiện: một phiên bản Teamcity miễn phí là không đủ (3 đại lý và 100 dự án), chúng tôi đã thêm một phiên bản khác (thêm 3 đại lý và 100 dự án), sau đó là một phiên bản khác. Kết quả là chúng tôi đã có một hệ thống gồm nhiều cụm rất khó quản lý:

Từ tập lệnh đến nền tảng riêng của chúng tôi: cách chúng tôi tự động hóa quá trình phát triển tại CIAN

Khi đặt ra câu hỏi về trường hợp thứ 4, chúng tôi nhận ra mình không thể tiếp tục sống như thế này, vì tổng chi phí hỗ trợ cho 4 trường hợp đã không còn trong giới hạn nào cả. Câu hỏi đặt ra là mua Teamcity trả phí hay chọn Jenkins miễn phí. Chúng tôi đã tính toán các phiên bản và kế hoạch tự động hóa rồi quyết định rằng chúng tôi sẽ sử dụng Jenkins. Sau một vài tuần, chúng tôi chuyển sang Jenkins và loại bỏ một số vấn đề đau đầu liên quan đến việc duy trì nhiều phiên bản Teamcity. Do đó, chúng tôi có thể tập trung vào việc phát triển Integro và tùy chỉnh Jenkins cho riêng mình.

Với sự phát triển của tự động hóa cơ bản (dưới dạng tự động tạo Yêu cầu kéo, thu thập và xuất bản Phạm vi mã cũng như các kiểm tra khác), người ta mong muốn từ bỏ việc phát hành thủ công càng nhiều càng tốt và giao công việc này cho robot. Ngoài ra, công ty bắt đầu chuyển sang các dịch vụ vi mô trong nội bộ công ty, yêu cầu phát hành thường xuyên và tách biệt với nhau. Đây là cách chúng tôi dần dần tiến tới việc tự động phát hành các vi dịch vụ của mình (chúng tôi hiện đang phát hành nguyên khối theo cách thủ công do tính phức tạp của quy trình). Nhưng, như thường lệ, một sự phức tạp mới nảy sinh. 

Chúng tôi tự động hóa việc kiểm tra

Từ tập lệnh đến nền tảng riêng của chúng tôi: cách chúng tôi tự động hóa quá trình phát triển tại CIAN

Do tự động hóa các bản phát hành, quá trình phát triển đã tăng tốc, một phần do bỏ qua một số giai đoạn thử nghiệm. Và điều này dẫn đến mất chất lượng tạm thời. Nghe có vẻ tầm thường nhưng cùng với việc tăng tốc phát hành, cần phải thay đổi phương pháp phát triển sản phẩm. Cần phải nghĩ đến việc tự động hóa quá trình thử nghiệm, nâng cao trách nhiệm cá nhân (ở đây chúng ta đang nói về việc “chấp nhận ý tưởng trong đầu”, không phải phạt tiền) của nhà phát triển đối với mã đã phát hành và các lỗi trong đó, cũng như quyết định loại bỏ phát hành/không giải phóng một nhiệm vụ thông qua triển khai tự động. 

Loại bỏ các vấn đề về chất lượng, chúng tôi đi đến hai quyết định quan trọng: chúng tôi bắt đầu tiến hành thử nghiệm canary và giới thiệu tính năng giám sát tự động nền lỗi với phản hồi tự động đối với mức vượt quá của nó. Giải pháp đầu tiên giúp tìm ra các lỗi rõ ràng trước khi mã được đưa hoàn toàn vào sản xuất, giải pháp thứ hai giúp giảm thời gian phản hồi các vấn đề trong sản xuất. Tất nhiên, sai lầm vẫn xảy ra, nhưng chúng ta dành phần lớn thời gian và công sức không phải để sửa chữa mà để giảm thiểu chúng. 

Nhóm tự động hóa

Chúng tôi hiện có đội ngũ nhân viên gồm 130 nhà phát triển và chúng tôi vẫn tiếp tục lớn lên. Nhóm tích hợp và phân phối mã liên tục (sau đây gọi là nhóm Triển khai và Tích hợp hoặc DI) gồm 7 người và làm việc theo 2 hướng: phát triển nền tảng tự động hóa Integro và DevOps. 

DevOps chịu trách nhiệm về môi trường Dev/Beta của trang CIAN, môi trường Integro, giúp các nhà phát triển giải quyết vấn đề và phát triển các phương pháp tiếp cận mới đối với môi trường mở rộng quy mô. Hướng phát triển Integro liên quan đến cả bản thân Integro và các dịch vụ liên quan, chẳng hạn như plugin cho Jenkins, Jira, Confluence, đồng thời phát triển các tiện ích và ứng dụng phụ trợ cho các nhóm phát triển. 

Nhóm DI hợp tác làm việc với nhóm Nền tảng, nhóm phát triển kiến ​​trúc, thư viện và các phương pháp phát triển trong nội bộ. Đồng thời, bất kỳ nhà phát triển nào trong CIAN đều có thể đóng góp vào quá trình tự động hóa, chẳng hạn như tạo ra tự động hóa vi mô để phù hợp với nhu cầu của nhóm hoặc chia sẻ ý tưởng hay về cách tự động hóa thậm chí còn tốt hơn.

Bánh lớp tự động hóa tại CIAN

Từ tập lệnh đến nền tảng riêng của chúng tôi: cách chúng tôi tự động hóa quá trình phát triển tại CIAN

Tất cả các hệ thống liên quan đến tự động hóa có thể được chia thành nhiều lớp:

  1. Các hệ thống bên ngoài (Jira, Bitbucket, v.v.). Nhóm phát triển làm việc với họ.
  2. Nền tảng Integro. Thông thường, các nhà phát triển không làm việc trực tiếp với nó, nhưng nó là thứ giúp tất cả hoạt động tự động hóa diễn ra.
  3. Dịch vụ giao hàng, điều phối và khám phá (ví dụ: Jeknins, Consul, Nomad). Với sự trợ giúp của họ, chúng tôi triển khai mã trên máy chủ và đảm bảo rằng các dịch vụ hoạt động với nhau.
  4. Lớp vật lý (máy chủ, hệ điều hành, phần mềm liên quan). Mã của chúng tôi hoạt động ở cấp độ này. Đây có thể là máy chủ vật lý hoặc máy chủ ảo (LXC, KVM, Docker).

Dựa trên khái niệm này, chúng tôi phân chia các lĩnh vực trách nhiệm trong nhóm DI. Hai cấp độ đầu tiên nằm trong lĩnh vực chịu trách nhiệm về định hướng phát triển Integro và hai cấp độ cuối cùng nằm trong lĩnh vực chịu trách nhiệm của DevOps. Sự tách biệt này cho phép chúng tôi tập trung vào nhiệm vụ và không cản trở sự tương tác, vì chúng tôi ở gần nhau và không ngừng trao đổi kiến ​​​​thức và kinh nghiệm.

Đầy

Hãy tập trung vào Integro và bắt đầu với nhóm công nghệ:

  • CentOS 7
  • Docker + Nomad + Lãnh sự + Vault
  • Java 11 (bản nguyên khối Integro cũ sẽ vẫn còn trên Java 8)
  • Spring Boot 2.X + Cấu hình Spring Cloud
  • PostgreSql 11
  • ThỏMQ 
  • Đốt cháy Apache
  • Camunda (nhúng)
  • Grafana + Than chì + Prometheus + Jaeger + ELK
  • Giao diện người dùng web: Phản ứng (CSR) + MobX
  • SSO: Áo choàng khóa

Chúng tôi tuân thủ nguyên tắc phát triển vi dịch vụ, mặc dù chúng tôi có di sản ở dạng nguyên khối của phiên bản đầu tiên của Integro. Mỗi vi dịch vụ chạy trong vùng chứa Docker riêng và các dịch vụ giao tiếp với nhau thông qua các yêu cầu HTTP và tin nhắn RabbitMQ. Các vi dịch vụ tìm thấy nhau thông qua Consul và đưa ra yêu cầu cho nó, chuyển ủy quyền thông qua SSO (Keycloak, OAuth 2/OpenID Connect).

Từ tập lệnh đến nền tảng riêng của chúng tôi: cách chúng tôi tự động hóa quá trình phát triển tại CIAN

Lấy một ví dụ thực tế, hãy xem xét việc tương tác với Jenkins, bao gồm các bước sau:

  1. Vi dịch vụ quản lý quy trình công việc (sau đây gọi là vi dịch vụ Flow) muốn chạy một bản dựng trong Jenkins. Để làm điều này, anh ta sử dụng Consul để tìm IP:PORT của vi dịch vụ để tích hợp với Jenkins (sau đây gọi là vi dịch vụ Jenkins) và gửi yêu cầu không đồng bộ tới vi dịch vụ đó để bắt đầu quá trình xây dựng trong Jenkins.
  2. Sau khi nhận được yêu cầu, microservice Jenkins sẽ tạo và phản hồi bằng ID công việc, sau đó có thể sử dụng ID này để xác định kết quả công việc. Đồng thời, nó kích hoạt quá trình xây dựng trong Jenkins thông qua lệnh gọi API REST.
  3. Jenkins thực hiện quá trình xây dựng và sau khi hoàn thành, sẽ gửi một webhook kèm theo kết quả thực thi tới vi dịch vụ Jenkins.
  4. Dịch vụ vi mô Jenkins, sau khi nhận được webhook, sẽ tạo một thông báo về việc hoàn tất quá trình xử lý yêu cầu và đính kèm kết quả thực thi vào đó. Tin nhắn được tạo sẽ được gửi đến hàng đợi RabbitMQ.
  5. Thông qua RabbitMQ, tin nhắn được xuất bản sẽ đến vi dịch vụ Flow, dịch vụ này tìm hiểu về kết quả xử lý tác vụ của nó bằng cách khớp ID công việc từ yêu cầu và tin nhắn nhận được.

Hiện tại chúng tôi có khoảng 30 microservice, có thể chia thành nhiều nhóm:

  1. Quản lý cấu hình.
  2. Thông tin và tương tác với người dùng (tin nhắn, thư).
  3. Làm việc với mã nguồn.
  4. Tích hợp với các công cụ triển khai (jenkins, nomad, lãnh sự, v.v.).
  5. Giám sát (phát hành, lỗi, v.v.).
  6. Tiện ích web (UI để quản lý môi trường thử nghiệm, thu thập số liệu thống kê, v.v.).
  7. Tích hợp với trình theo dõi tác vụ và các hệ thống tương tự.
  8. Quản lý quy trình làm việc cho các nhiệm vụ khác nhau.

Nhiệm vụ quy trình làm việc

Integro tự động hóa các hoạt động liên quan đến vòng đời nhiệm vụ. Nói một cách đơn giản thì vòng đời của một task sẽ được hiểu là quy trình làm việc của một task trong Jira. Quy trình phát triển của chúng tôi có một số biến thể quy trình công việc tùy thuộc vào dự án, loại nhiệm vụ và các tùy chọn được chọn trong một nhiệm vụ cụ thể. 

Hãy xem quy trình làm việc mà chúng tôi sử dụng thường xuyên nhất:

Từ tập lệnh đến nền tảng riêng của chúng tôi: cách chúng tôi tự động hóa quá trình phát triển tại CIAN

Trong sơ đồ, bánh răng cho biết rằng quá trình chuyển đổi được Integro gọi tự động, trong khi hình người cho biết rằng quá trình chuyển đổi được một người gọi theo cách thủ công. Hãy xem xét một số đường dẫn mà một tác vụ có thể thực hiện trong quy trình làm việc này.

Thử nghiệm hoàn toàn thủ công trên DEV+BETA mà không cần thử nghiệm canary (thường đây là cách chúng tôi phát hành nguyên khối):

Từ tập lệnh đến nền tảng riêng của chúng tôi: cách chúng tôi tự động hóa quá trình phát triển tại CIAN

Có thể có những sự kết hợp chuyển tiếp khác. Đôi khi, đường dẫn của một vấn đề có thể được chọn thông qua các tùy chọn trong Jira.

chuyển động nhiệm vụ

Hãy xem các bước chính được thực hiện khi một tác vụ chuyển qua quy trình làm việc “Thử nghiệm DEV + Thử nghiệm Canary”:

1. Nhà phát triển hoặc PM tạo nhiệm vụ.

2. Nhà phát triển nhận nhiệm vụ làm việc. Sau khi hoàn thành, nó chuyển sang trạng thái IN REVIEW.

3. Jira gửi Webhook tới microservice Jira (chịu trách nhiệm tích hợp với Jira).

4. Vi dịch vụ Jira gửi yêu cầu đến dịch vụ Flow (chịu trách nhiệm về quy trình làm việc nội bộ trong đó công việc được thực hiện) để bắt đầu quy trình làm việc.

5. Bên trong dịch vụ Flow:

  • Người đánh giá được giao nhiệm vụ (Người dùng microservice biết mọi thứ về người dùng + Jira microservice).
  • Thông qua microservice Nguồn (nó biết về các kho lưu trữ và các nhánh, nhưng không hoạt động với chính mã), một tìm kiếm được thực hiện đối với các kho chứa một nhánh của vấn đề của chúng tôi (để đơn giản hóa việc tìm kiếm, tên của nhánh trùng với vấn đề số trong Jira). Thông thường, một tác vụ chỉ có một nhánh trong một kho lưu trữ; điều này giúp đơn giản hóa việc quản lý hàng đợi triển khai và giảm khả năng kết nối giữa các kho lưu trữ.
  • Đối với mỗi nhánh được tìm thấy, chuỗi hành động sau được thực hiện:

    i) Cập nhật nhánh chính (Git microservice để làm việc với mã).
    ii) Chi nhánh bị nhà phát triển chặn thay đổi (Bitbucket microservice).
    iii) Yêu cầu kéo được tạo cho nhánh này (microservice Bitbucket).
    iv) Một thông báo về Yêu cầu kéo mới sẽ được gửi đến cuộc trò chuyện của nhà phát triển (Thông báo cho microservice để làm việc với thông báo).
    v) Các tác vụ xây dựng, thử nghiệm và triển khai được bắt đầu trên DEV (Jenkins microservice for doing with Jenkins).
    vi) Nếu tất cả các bước trước đó được hoàn thành thành công thì Integro sẽ đưa Phê duyệt của nó vào Yêu cầu Kéo (Bitbucket microservice).

  • Integro đang chờ Phê duyệt trong Yêu cầu Kéo từ những người đánh giá được chỉ định.
  • Ngay sau khi nhận được tất cả các phê duyệt cần thiết (bao gồm cả các thử nghiệm tự động đã đạt kết quả tích cực), Integro sẽ chuyển nhiệm vụ sang trạng thái Thử nghiệm trên Dev (Jira microservice).

6. Người kiểm tra kiểm tra nhiệm vụ. Nếu không có vấn đề gì thì nhiệm vụ sẽ được chuyển sang trạng thái Sẵn sàng xây dựng.

7. Integro “thấy” rằng tác vụ đã sẵn sàng để phát hành và bắt đầu triển khai ở chế độ canary (Jenkins microservice). Sự sẵn sàng phát hành được xác định bởi một bộ quy tắc. Ví dụ: tác vụ ở trạng thái bắt buộc, không có khóa nào đối với các tác vụ khác, hiện không có hoạt động tải lên nào của vi dịch vụ này, v.v.

8. Nhiệm vụ được chuyển sang trạng thái Canary (Jira microservice).

9. Jenkins khởi chạy tác vụ triển khai thông qua Nomad ở chế độ canary (thường là 1-3 phiên bản) và thông báo cho dịch vụ giám sát phát hành (microservice DeployWatch) về việc triển khai.

10. Microservice DeployWatch thu thập thông tin cơ bản về lỗi và phản hồi lại thông tin đó, nếu cần. Nếu vượt quá nền lỗi (định mức nền được tính toán tự động), nhà phát triển sẽ được thông báo qua vi dịch vụ Notify. Nếu sau 5 phút nhà phát triển không phản hồi (nhấp vào Hoàn nguyên hoặc Ở lại), thì quá trình khôi phục tự động các phiên bản canary sẽ được khởi chạy. Nếu nền không bị vượt quá thì nhà phát triển phải khởi chạy triển khai tác vụ sang Sản xuất theo cách thủ công (bằng cách nhấp vào nút trong Giao diện người dùng). Nếu trong vòng 60 phút, nhà phát triển chưa triển khai triển khai sang Sản xuất thì các phiên bản canary cũng sẽ được khôi phục vì lý do bảo mật.

11. Sau khi triển khai triển khai lên Sản xuất:

  • Nhiệm vụ được chuyển sang trạng thái Sản xuất (Jira microservice).
  • Vi dịch vụ Jenkins bắt đầu quá trình triển khai và thông báo cho vi dịch vụ DeployWatch về việc triển khai.
  • Microservice DeployWatch kiểm tra xem tất cả các vùng chứa trên Sản xuất đã được cập nhật chưa (có những trường hợp không phải tất cả đều được cập nhật).
  • Thông qua vi dịch vụ Notify, một thông báo về kết quả triển khai sẽ được gửi đến Sản xuất.

12. Nhà phát triển sẽ có 30 phút để bắt đầu khôi phục tác vụ khỏi Sản xuất nếu phát hiện hành vi dịch vụ vi mô không chính xác. Sau thời gian này, tác vụ sẽ tự động được sáp nhập vào master (Git microservice).

13. Sau khi hợp nhất thành công vào master, trạng thái tác vụ sẽ được thay đổi thành Đã đóng (Jira microservice).

Sơ đồ không có vẻ hoàn toàn chi tiết (trên thực tế thậm chí còn có nhiều bước hơn), nhưng nó cho phép bạn đánh giá mức độ tích hợp vào các quy trình. Chúng tôi không coi kế hoạch này là lý tưởng và đang cải thiện các quy trình hỗ trợ triển khai và phát hành tự động.

những gì tiếp theo

Chúng tôi có các kế hoạch lớn để phát triển tự động hóa, chẳng hạn như loại bỏ các thao tác thủ công trong quá trình phát hành nguyên khối, cải thiện khả năng giám sát trong quá trình triển khai tự động và cải thiện sự tương tác với các nhà phát triển.

Nhưng bây giờ chúng ta hãy dừng lại ở đây. Chúng tôi đã đề cập đến nhiều chủ đề trong bài đánh giá tự động hóa một cách hời hợt, một số chủ đề chưa hề được đề cập đến, vì vậy chúng tôi sẽ sẵn lòng trả lời các câu hỏi. Chúng tôi đang chờ gợi ý về những gì cần trình bày chi tiết, hãy viết trong phần bình luận.

Nguồn: www.habr.com

Thêm một lời nhận xét