Phát triển InsideLoop với Python

1. Tổng quan

Phòng thí nghiệm này trình bày các tính năng và chức năng được thiết kế để đơn giản hoá quy trình phát triển dành cho các kỹ sư phần mềm được giao nhiệm vụ phát triển các ứng dụng Python trong môi trường vùng chứa. Thông thường, quá trình phát triển vùng chứa yêu cầu người dùng hiểu thông tin chi tiết về vùng chứa và quy trình xây dựng vùng chứa. Ngoài ra, các nhà phát triển thường phải ngắt luồng của mình, rời khỏi IDE để kiểm thử và gỡ lỗi ứng dụng trong môi trường từ xa. Với các công cụ và công nghệ được đề cập trong hướng dẫn này, nhà phát triển có thể làm việc hiệu quả với các ứng dụng trong vùng chứa mà không cần rời khỏi IDE của họ.

Kiến thức bạn sẽ học được

Trong phòng thí nghiệm này, bạn sẽ tìm hiểu các phương pháp phát triển bằng vùng chứa trong GCP, bao gồm:

  • Tạo ứng dụng khởi đầu Python mới
  • Tìm hiểu quá trình phát triển
  • Phát triển một dịch vụ nghỉ ngơi CRUD đơn giản

2. Thiết lập và yêu cầu

Thiết lập môi trường theo tiến độ riêng

  1. Đăng nhập vào Google Cloud Console rồi tạo dự án mới hoặc sử dụng lại dự án hiện có. Nếu chưa có tài khoản Gmail hoặc Google Workspace, bạn phải tạo một tài khoản.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Tên dự án là tên hiển thị của những người tham gia dự án này. Đây là một chuỗi ký tự không được API của Google sử dụng và bạn có thể cập nhật chuỗi này bất cứ lúc nào.
  • Mã dự án phải là duy nhất trong tất cả các dự án Google Cloud và không thể thay đổi (không thể thay đổi sau khi đã đặt). Cloud Console sẽ tự động tạo một chuỗi duy nhất; thường bạn không quan tâm đến sản phẩm đó là gì. Trong hầu hết các lớp học lập trình, bạn sẽ cần tham chiếu đến Mã dự án (và mã này thường được xác định là PROJECT_ID). Vì vậy, nếu không thích, bạn có thể tạo một mã ngẫu nhiên khác hoặc bạn có thể thử mã của riêng mình để xem có mã này chưa. Sau đó, video sẽ được "đóng băng" sau khi tạo dự án.
  • Có giá trị thứ ba là Project Number (Số dự án) mà một số API sử dụng. Tìm hiểu thêm về cả ba giá trị này trong tài liệu này.
  1. Tiếp theo, bạn sẽ cần bật tính năng thanh toán trong Cloud Console để sử dụng tài nguyên/API trên Cloud. Việc chạy qua lớp học lập trình này sẽ không tốn nhiều chi phí. Để tắt các tài nguyên để bạn không phải chịu thanh toán ngoài hướng dẫn này, hãy làm theo mọi thao tác "dọn dẹp" hướng dẫn ở cuối lớp học lập trình. Người dùng mới của Google Cloud đủ điều kiện tham gia chương trình Dùng thử miễn phí 300 USD.

Khởi động Cloudshell Editor

Phòng thí nghiệm này được thiết kế và thử nghiệm để sử dụng với Google Cloud Shell Editor. Để truy cập vào trình chỉnh sửa,

  1. truy cập vào dự án google của bạn tại https://console.cloud.google.com.
  2. Ở góc trên cùng bên phải, hãy nhấp vào biểu tượng trình chỉnh sửa vỏ đám mây

8560cc8d45e8c112.pngS

  1. Một ngăn mới sẽ mở ra ở cuối cửa sổ
  2. Nhấp vào nút Open Editor (Mở trình chỉnh sửa)

9e504cb98a6a8005.pngS

  1. Trình chỉnh sửa sẽ mở ra cùng với một trình khám phá ở bên phải và trình chỉnh sửa ở khu vực trung tâm
  2. Ngăn thiết bị đầu cuối cũng sẽ xuất hiện ở cuối màn hình
  3. Nếu cửa sổ dòng lệnh KHÔNG mở, hãy sử dụng tổ hợp phím "ctrl+" để mở cửa sổ dòng lệnh mới

Thiết lập môi trường

Trong Cloud Shell, hãy đặt mã dự án và số dự án cho dự án của bạn. Lưu chúng dưới dạng biến PROJECT_IDPROJECT_ID.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
    --format='value(projectNumber)')

Lấy mã nguồn

  1. Mã nguồn cho phòng thí nghiệm này nằm tại Container-developer-workshop trong GoogleCloudPlatform trên GitHub. Hãy sao chép tệp đó bằng lệnh bên dưới rồi thay đổi vào thư mục.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git &&
cd container-developer-workshop/labs/python
mkdir music-service && cd music-service 
cloudshell workspace .

Nếu cửa sổ dòng lệnh KHÔNG mở, hãy sử dụng tổ hợp phím "ctrl+" để mở cửa sổ dòng lệnh mới

Cung cấp cơ sở hạ tầng dùng trong phòng thí nghiệm này

Trong phòng thí nghiệm này, bạn sẽ triển khai mã cho GKE và truy cập vào dữ liệu được lưu trữ trong cơ sở dữ liệu Spanner. Tập lệnh thiết lập dưới đây chuẩn bị cơ sở hạ tầng này cho bạn. Quá trình cấp phép sẽ mất hơn 10 phút. Bạn có thể tiếp tục thực hiện một vài bước tiếp theo trong khi quá trình thiết lập đang diễn ra.

../setup.sh

3. Tạo ứng dụng khởi đầu Python mới

  1. Tạo một tệp có tên là requirements.txt rồi sao chép nội dung sau đây vào tệp đó
Flask
gunicorn
google-cloud-spanner
ptvsd==4.3.2
  1. Tạo tệp có tên app.py rồi dán mã sau vào tệp đó
import os
from flask import Flask, request, jsonify
from google.cloud import spanner

app = Flask(__name__)

@app.route("/")
def hello_world():
    message="Hello, World!"
    return message

if __name__ == '__main__':
    server_port = os.environ.get('PORT', '8080')
    app.run(debug=False, port=server_port, host='0.0.0.0')

  1. Tạo một tệp có tên Dockerfile và dán nội dung sau đây vào tệp đó
FROM python:3.8
ARG FLASK_DEBUG=0
ENV FLASK_DEBUG=$FLASK_DEBUG
ENV FLASK_APP=app.py
WORKDIR /app
COPY requirements.txt .
RUN pip install --trusted-host pypi.python.org -r requirements.txt
COPY . .
ENTRYPOINT ["python3", "-m", "flask", "run", "--port=8080", "--host=0.0.0.0"]

Lưu ý: FLASK_DEBUG=1 cho phép bạn tự động tải lại các thay đổi về mã đối với ứng dụng Python. Dockerfile này cho phép bạn truyền giá trị này dưới dạng một đối số bản dựng.

Tạo tệp kê khai

Trong cửa sổ dòng lệnh, hãy thực thi lệnh sau để tạo một skaffold.yaml và triển khai.yaml mặc định

  1. Khởi chạy Skaffold bằng lệnh sau
skaffold init --generate-manifests

Khi được nhắc, hãy sử dụng các mũi tên để di chuyển con trỏ và phím cách để chọn các tuỳ chọn.

Chọn:

  • 8080 cho cổng
  • y để lưu cấu hình

Cập nhật cấu hình Skaffold

  • Thay đổi tên mặc định của ứng dụng
  • Mở skaffold.yaml
  • Chọn tên hình ảnh hiện được đặt là dockerfile-image
  • Nhấp chuột phải rồi chọn Thay đổi tất cả lần xuất hiện
  • Nhập tên mới là python-app
  • Chỉnh sửa thêm phần bản dựng để
  • thêm docker.buildArgs để vượt qua FLASK_DEBUG=1
  • Đồng bộ hoá các chế độ cài đặt để tải mọi thay đổi đối với tệp *.py từ IDE đến vùng chứa đang chạy

Sau khi chỉnh sửa, phần bản dựng trong tệp skaffold.yaml sẽ có dạng như sau:

build:
 artifacts:
 - image: python-app
   docker:
     buildArgs:
       FLASK_DEBUG: 1
     dockerfile: Dockerfile
   sync:
     infer:
     - '**/*.py'

Sửa đổi tệp cấu hình Kubernetes

  1. Thay đổi tên mặc định
  • Mở tệp deployment.yaml
  • Chọn tên hình ảnh hiện được đặt là dockerfile-image
  • Nhấp chuột phải rồi chọn Thay đổi tất cả lần xuất hiện
  • Nhập tên mới là python-app

4. Tìm hiểu về quá trình phát triển

Khi thêm logic nghiệp vụ, giờ đây bạn có thể triển khai và thử nghiệm ứng dụng của mình. Phần sau đây sẽ trình bày nổi bật cách sử dụng trình bổ trợ Cloud Code. Ngoài ra, trình bổ trợ này còn tích hợp với skaffold để đơn giản hoá quy trình phát triển của bạn. Khi bạn triển khai GKE ở các bước sau, Cloud Code và Skaffold sẽ tự động tạo hình ảnh vùng chứa của bạn, đẩy hình ảnh vùng chứa đó vào Container Registry, sau đó triển khai ứng dụng của bạn lên GKE. Quá trình này xảy ra trong nền, trừu tượng chi tiết khỏi quy trình của nhà phát triển.

Triển khai cho Kubernetes

  1. Trong ngăn ở cuối Cloud Shell Editor, hãy chọn Cloud Code \t

fdc797a769040839.png

  1. Trong bảng điều khiển xuất hiện ở trên cùng, hãy chọn Run on Kubernetes (Chạy trên Kubernetes). Nếu được nhắc, hãy chọn Có để sử dụng ngữ cảnh Kubernetes hiện tại.

cfce0d11ef307087.png

Lệnh này sẽ khởi động một bản dựng của mã nguồn rồi chạy các hoạt động kiểm thử. Quá trình tạo bản dựng và kiểm thử sẽ mất vài phút để chạy. Các kiểm thử này bao gồm kiểm thử đơn vị và một bước xác thực để kiểm tra các quy tắc được thiết lập cho môi trường triển khai. Bước xác thực này đã được định cấu hình, đồng thời đảm bảo rằng bạn sẽ nhận được cảnh báo về các vấn đề của việc triển khai ngay cả khi bạn vẫn đang làm việc trong môi trường phát triển.

  1. Trong lần đầu tiên bạn chạy lệnh, một lời nhắc sẽ xuất hiện ở đầu màn hình và hỏi xem bạn có muốn sử dụng bối cảnh kubernetes hiện tại hay không. Hãy chọn "Yes" (Có) để chấp nhận và sử dụng ngữ cảnh hiện tại.
  2. Tiếp theo, một lời nhắc sẽ xuất hiện hỏi xem bạn nên dùng sổ đăng ký vùng chứa nào. Nhấn phím Enter để chấp nhận giá trị mặc định được cung cấp
  3. Chọn thẻ Output (Kết quả) trong ngăn phía dưới để xem tiến trình và thông báo

f95b620569ba96c5.png

  1. Chọn "Kubernetes: Chạy/Gỡ lỗi – Chi tiết" trong trình đơn thả xuống ở bên phải của kênh để xem thêm thông tin chi tiết và nhật ký phát trực tiếp từ các vùng chứa

94acdcdda6d2108.pngS

Khi quá trình tạo và kiểm thử hoàn tất, thẻ Đầu ra sẽ cho biết: Attached debugger to container "python-app-8476f4bbc-h6dsl" successfully. và URL http://localhost:8080 sẽ được liệt kê.

  1. Trong cửa sổ dòng lệnh Mã đám mây, hãy di chuột qua URL đầu tiên trong dữ liệu đầu ra (http://localhost:8080), sau đó trong phần mẹo công cụ xuất hiện, hãy chọn Open Web Preview (Xem trước trên web).
  2. Một thẻ trình duyệt mới sẽ mở ra và hiển thị thông báo Hello, World!

Tải lại nhanh

  1. Mở tệp app.py
  2. Thay đổi tin nhắn lời chào thành Hello from Python

Lưu ý ngay rằng trong cửa sổ Output, khung hiển thị Kubernetes: Run/Debug, trình theo dõi sẽ đồng bộ hoá các tệp đã cập nhật với vùng chứa trong Kubernetes

Update initiated
Build started for artifact python-app
Build completed for artifact python-app

Deploy started
Deploy completed

Status check started
Resource pod/python-app-6f646ffcbb-tn7qd status updated to In Progress
Resource deployment/python-app status updated to In Progress
Resource deployment/python-app status completed successfully
Status check succeeded
...
  1. Nếu chuyển sang khung hiển thị Kubernetes: Run/Debug - Detailed, bạn sẽ thấy khung hiển thị này nhận ra các thay đổi về tệp, sau đó tạo và triển khai lại ứng dụng
files modified: [app.py]
Syncing 1 files for gcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Copying files:map[app.py:[/app/app.py]]togcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Watching for changes...
[python-app] * Detected change in '/app/app.py', reloading
[python-app] * Restarting with stat
[python-app] * Debugger is active!
[python-app] * Debugger PIN: 744-729-662
  1. Làm mới trình duyệt của bạn để xem kết quả được cập nhật.

Gỡ lỗi

  1. Chuyển đến chế độ xem Debug (Gỡ lỗi) và dừng luồng hiện tại 647213126d7a4c7b.pngS.
  2. Nhấp vào Cloud Code ở trình đơn dưới cùng rồi chọn Debug on Kubernetes để chạy ứng dụng ở chế độ debug.
  • Trong khung hiển thị Kubernetes Run/Debug - Detailed của cửa sổ Output, hãy lưu ý rằng skaffold sẽ triển khai ứng dụng này ở chế độ gỡ lỗi.
  1. Trong lần đầu tiên chạy lệnh này, một lời nhắc sẽ hỏi xem nguồn nằm ở đâu trong vùng chứa. Giá trị này liên quan đến các thư mục trong Dockerfile.

Nhấn Enter để chấp nhận chế độ mặc định

583436647752e410.pngS

Sẽ mất vài phút để xây dựng và triển khai ứng dụng.

  1. Khi quá trình này hoàn tất. Bạn sẽ thấy một trình gỡ lỗi được đính kèm.
Port forwarding pod/python-app-8bd64cf8b-cskfl in namespace default, remote port 5678 -> http://127.0.0.1:5678
  1. Thanh trạng thái ở dưới cùng thay đổi màu từ màu xanh dương sang màu cam cho biết đang ở chế độ Gỡ lỗi.
  2. Trong khung hiển thị Kubernetes Run/Debug, lưu ý rằng vùng chứa có thể Gỡ lỗi đã được bắt đầu
**************URLs*****************
Forwarded URL from service python-app: http://localhost:8080
Debuggable container started pod/python-app-8bd64cf8b-cskfl:python-app (default)
Update succeeded
***********************************

Khai thác các điểm ngắt

  1. Mở tệp app.py
  2. Tìm câu lệnh có nội dung return message
  3. Thêm điểm ngắt vào dòng đó bằng cách nhấp vào khoảng trống ở bên trái số dòng. Một chỉ báo màu đỏ sẽ xuất hiện để ghi chú điểm ngắt đã được đặt
  4. Tải lại trình duyệt và lưu ý rằng trình gỡ lỗi sẽ dừng quá trình này tại điểm ngắt và cho phép bạn điều tra các biến cũng như trạng thái của ứng dụng đang chạy từ xa trong GKE
  5. Nhấp vào mục "VARIABLES" (Biến)
  6. Nhấp vào Locals (Nhóm địa phương), bạn sẽ thấy biến "message".
  7. Nhấp đúp vào tên biến "tin nhắn" và trong cửa sổ bật lên, hãy thay đổi giá trị thành giá trị khác như "Greetings from Python"
  8. Nhấp vào nút Tiếp tục trong bảng điều khiển gỡ lỗi 607c33934f8d6b39.pngS
  9. Xem lại phản hồi trong trình duyệt để hiện giá trị được cập nhật mà bạn vừa nhập.
  10. Dừng "Gỡ lỗi" bằng cách nhấn nút dừng 647213126d7a4c7b.pngS và xoá điểm ngắt bằng cách nhấp lại vào điểm ngắt đó.

5. Phát triển dịch vụ nghỉ ngơi CRUD đơn giản

Tại thời điểm này, ứng dụng của bạn đã được định cấu hình đầy đủ để phát triển theo vùng chứa và bạn đã tìm hiểu quy trình phát triển cơ bản bằng Cloud Code. Trong các phần sau, bạn sẽ thực hành những gì đã học được bằng cách thêm thiết bị đầu cuối cho dịch vụ nghỉ ngơi kết nối với một cơ sở dữ liệu được quản lý trong Google Cloud.

Viết mã cho dịch vụ còn lại

Đoạn mã dưới đây tạo một dịch vụ nghỉ ngơi đơn giản sử dụng Spanner làm cơ sở dữ liệu hỗ trợ ứng dụng. Tạo ứng dụng bằng cách sao chép mã sau đây vào ứng dụng của bạn.

  1. Tạo ứng dụng chính bằng cách thay thế app.py bằng nội dung sau
import os
from flask import Flask, request, jsonify
from google.cloud import spanner


app = Flask(__name__)


instance_id = "music-catalog"

database_id = "musicians"

spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)


@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

@app.route('/singer', methods=['POST'])
def create():
    try:
        request_json = request.get_json()
        singer_id = request_json['singer_id']
        first_name = request_json['first_name']
        last_name = request_json['last_name']
        def insert_singers(transaction):
            row_ct = transaction.execute_update(
                f"INSERT Singers (SingerId, FirstName, LastName) VALUES" \
                f"({singer_id}, '{first_name}', '{last_name}')"
            )
            print("{} record(s) inserted.".format(row_ct))

        database.run_in_transaction(insert_singers)

        return {"Success": True}, 200
    except Exception as e:
        return e



@app.route('/singer', methods=['GET'])
def get_singer():

    try:
        singer_id = request.args.get('singer_id')
        def get_singer():
            first_name = ''
            last_name = ''
            with database.snapshot() as snapshot:
                results = snapshot.execute_sql(
                    f"SELECT SingerId, FirstName, LastName FROM Singers " \
                    f"where SingerId = {singer_id}",
                    )
                for row in results:
                    first_name = row[1]
                    last_name = row[2]
                return (first_name,last_name )
        first_name, last_name = get_singer()  
        return {"first_name": first_name, "last_name": last_name }, 200
    except Exception as e:
        return e


@app.route('/singer', methods=['PUT'])
def update_singer_first_name():
    try:
        singer_id = request.args.get('singer_id')
        request_json = request.get_json()
        first_name = request_json['first_name']
        
        def update_singer(transaction):
            row_ct = transaction.execute_update(
                f"UPDATE Singers SET FirstName = '{first_name}' WHERE SingerId = {singer_id}"
            )

            print("{} record(s) updated.".format(row_ct))

        database.run_in_transaction(update_singer)
        return {"Success": True}, 200
    except Exception as e:
        return e


@app.route('/singer', methods=['DELETE'])
def delete_singer():
    try:
        singer_id = request.args.get('singer')
    
        def delete_singer(transaction):
            row_ct = transaction.execute_update(
                f"DELETE FROM Singers WHERE SingerId = {singer_id}"
            )
            print("{} record(s) deleted.".format(row_ct))

        database.run_in_transaction(delete_singer)
        return {"Success": True}, 200
    except Exception as e:
        return e

port = int(os.environ.get('PORT', 8080))
if __name__ == '__main__':
    app.run(threaded=True, host='0.0.0.0', port=port)

Thêm cấu hình cơ sở dữ liệu

Để kết nối an toàn với Spanner, hãy thiết lập ứng dụng để dùng Workload Identities. Điều này cho phép ứng dụng của bạn hoạt động như một tài khoản dịch vụ riêng và có các quyền riêng khi truy cập vào cơ sở dữ liệu.

  1. Cập nhật deployment.yaml. Thêm mã sau vào cuối tệp (đảm bảo bạn giữ cho thẻ được thụt lề như trong ví dụ bên dưới)
      serviceAccountName: python-ksa
      nodeSelector:
        iam.gke.io/gke-metadata-server-enabled: "true" 

Triển khai và xác thực ứng dụng

  1. Trong ngăn ở cuối Cloud Shell Editor, chọn Cloud Code rồi chọn Debug on Kubernetes ở đầu màn hình.
  2. Khi quá trình tạo và kiểm thử hoàn tất, thẻ Đầu ra sẽ cho biết: Resource deployment/python-app status completed successfully, và một URL có trong danh sách: "Được chuyển tiếp URL từ dịch vụ python-app: http://localhost:8080"
  3. Hãy thêm một vài mục nhập.

Trên Cloudshell Terminal, hãy chạy lệnh dưới đây

curl -X POST http://localhost:8080/singer -H 'Content-Type: application/json' -d '{"first_name":"Cat","last_name":"Meow", "singer_id": 6}'
  1. Kiểm thử GET bằng cách chạy lệnh dưới đây trong dòng lệnh
curl -X GET http://localhost:8080/singer?singer_id=6
  1. Kiểm thử Xoá: Bây giờ, hãy thử xoá một mục bằng cách chạy lệnh sau. Thay đổi giá trị của item-id nếu cần.
curl -X DELETE http://localhost:8080/singer?singer_id=6
    This throws an error message
500 Internal Server Error

Xác định và khắc phục vấn đề

  1. Chế độ gỡ lỗi và tìm vấn đề. Dưới đây là một số mẹo:
  • Chúng ta biết đã xảy ra lỗi với DELETE vì thành phần này không trả về kết quả mong muốn. Vì vậy, bạn cần đặt điểm ngắt trong app.py trong phương thức delete_singer.
  • Chạy thực thi từng bước và theo dõi các biến ở mỗi bước để quan sát giá trị của các biến cục bộ trong cửa sổ bên trái.
  • Để quan sát các giá trị cụ thể như singer_idrequest.args trong phần thêm các biến này vào Cửa sổ theo dõi.
  1. Lưu ý rằng giá trị được gán cho singer_idNone. Hãy thay đổi mã để khắc phục vấn đề.

Đoạn mã cố định sẽ có dạng như sau.

@app.route('/delete-singer', methods=['DELETE', 'GET'])
def delete_singer():
    try:
        singer_id = request.args.get('singer_id')
  1. Sau khi ứng dụng được khởi động lại, hãy kiểm tra lại bằng cách cố gắng xoá.
  2. Dừng phiên gỡ lỗi bằng cách nhấp vào hình vuông màu đỏ trên thanh công cụ gỡ lỗi 647213126d7a4c7b.pngS

6. Dọn dẹp

Xin chúc mừng! Trong phòng thí nghiệm này, bạn đã tạo một ứng dụng Python mới từ đầu và định cấu hình để ứng dụng hoạt động hiệu quả với vùng chứa. Sau đó, bạn đã triển khai và gỡ lỗi ứng dụng cho một cụm GKE từ xa theo cùng một quy trình dành cho nhà phát triển trong các ngăn xếp ứng dụng truyền thống.

Cách làm sạch sau khi hoàn thành phòng thí nghiệm:

  1. Xoá các tệp được sử dụng trong phòng thí nghiệm
cd ~ && rm -rf container-developer-workshop
  1. Xoá dự án để xoá tất cả cơ sở hạ tầng và tài nguyên có liên quan