JupyterHub proxy: cấu hình route cho hub, single-user và kiểm thử
JupyterHub proxy là thành phần đứng ở cửa vào, nhận mọi yêu cầu từ trình duyệt rồi chuyển tới hub hoặc tới máy chủ notebook của từng người. Nó không phải proxy dùng để notebook đi ra Internet bằng IP khác. ProxyVN chạy thật Configurable HTTP Proxy 5.3.0 và ghi lại kết quả từng route.
JupyterHub proxy là gì và khác gì proxy IP mua ngoài
Tên gọi dễ gây hiểu nhầm. Trong thế giới proxy IP, anh em quen với proxy là máy chủ trung gian giúp máy mình ra Internet bằng địa chỉ khác. JupyterHub proxy làm việc ngược hướng: nó đứng trước một cụm máy chủ nội bộ và quyết định yêu cầu từ ngoài vào sẽ tới máy nào.
Gọi chuẩn hơn, đây là một reverse proxy có bảng định tuyến thay đổi được lúc đang chạy. Anh em có thể đọc thêm về cặp khái niệm này ở bài forward proxy và reverse proxy.
Ba thành phần của JupyterHub
Tài liệu Technical Overview của JupyterHub chia hệ thống thành ba phần. Hub viết bằng Python, quản lý tài khoản, đăng nhập và điều phối các máy chủ notebook. Proxy là phần hướng ra ngoài, định tuyến yêu cầu HTTP tới hub hoặc tới từng máy chủ notebook. Single-user server là máy chủ notebook riêng cho mỗi người.
Theo tài liệu, proxy là tiến trình duy nhất lắng nghe trên giao diện công khai. Hub nằm sau proxy ở đường dẫn /hub, còn máy chủ notebook của từng người nằm ở /user/tên. Mặc định proxy lắng nghe ở cổng 8000 và chuyển mọi yêu cầu chưa có tuyến riêng tới hub.
Vì sao anh em quan tâm đến nó
Có hai lý do thực tế. Lý do đầu là khi triển khai hub cho nhóm, anh em cần hiểu vì sao một đường dẫn lại tới đúng máy chủ của một người. Khi người dùng báo vào được trang đăng nhập nhưng notebook không mở, phần lớn lỗi nằm ở bảng tuyến này.
Lý do thứ hai là sự nhầm lẫn khi đi tìm cấu hình proxy. Anh em tra Google chữ JupyterHub proxy, gặp cả hướng dẫn đặt HTTP_PROXY cho notebook lẫn hướng dẫn chỉnh Configurable HTTP Proxy. Hai việc này khác hẳn nhau, bài này tách chúng ra rõ ràng.
Phân biệt JupyterHub proxy với forward proxy ra Internet của notebook

Đường vào và đường ra của notebook
Đây là điểm mấu chốt của bài. Mình đặt hai loại proxy cạnh nhau để anh em khỏi lẫn.
Tiêu chí |
JupyterHub proxy (CHP) |
Forward proxy của notebook |
|---|---|---|
Hướng đi |
Từ trình duyệt vào hub và notebook |
Từ notebook ra Internet |
Ai cấu hình |
Quản trị JupyterHub |
Người dùng hoặc quản trị qua biến môi trường |
Thiết lập điển hình |
Bảng route, token API, target |
HTTP_PROXY, HTTPS_PROXY, NO_PROXY |
Nhìn thấy gì |
Đường dẫn và Host của yêu cầu vào |
Địa chỉ đích mà code trong notebook gọi |
Liên quan tới IP của người dùng |
Chuyển IP khách trong header X-Forwarded-For |
Quyết định IP website nhìn thấy |
Proxy ra Internet của notebook nằm ở đâu
Khi code trong notebook gọi requests.get tới một website, kết nối đó đi từ máy chủ đang chạy notebook, không đi qua JupyterHub proxy. Muốn nó đi qua một proxy ngoài, anh em đặt biến môi trường cho tiến trình notebook.
Tài liệu của JupyterHub có tùy chọn Spawner.environment, một từ điển các biến môi trường đặt thêm cho máy chủ single-user. Tài liệu cũng có env_keep, danh sách biến mà máy chủ notebook kế thừa từ tiến trình hub. Hai chỗ đó là nơi cài đặt biến proxy cho mọi người dùng.
# jupyterhub_config.py (ví dụ, mình chưa chạy trên JupyterHub thật)
c.Spawner.environment = {
"HTTPS_PROXY": "http://user:matkhau@proxy.example.net:8080",
"HTTP_PROXY": "http://user:matkhau@proxy.example.net:8080",
"NO_PROXY": "127.0.0.1,localhost",
}
Bài này không đi sâu vào cấu hình đó. Phần proxy ra Internet cho notebook trên môi trường đám mây miễn phí đã có ở bài Google Colab proxy, và cách đặt chung cho dòng lệnh có ở bài No_proxy là gì.
Một bẫy khi đặt biến proxy cho cả hub
Nếu anh em đặt HTTP_PROXY trong môi trường của cả tiến trình hub, các lời gọi nội bộ có thể đi vào proxy ngoài, ví dụ lời gọi từ hub tới API quản trị của proxy ở 127.0.0.1. Việc đó có xảy ra hay không phụ thuộc thư viện HTTP đang dùng. Cách an toàn là luôn thêm địa chỉ nội bộ vào NO_PROXY. Mình chưa thử điều này trên hub thật nên ghi như một lưu ý phòng ngừa.
Hai câu hỏi giúp phân loại nhanh
Khi gặp một yêu cầu cấu hình, anh em hỏi hai câu. Thứ nhất, lưu lượng này đi vào hay đi ra. Thứ hai, ai là bên gửi yêu cầu, trình duyệt của người dùng hay code trong notebook. Nếu là trình duyệt đi vào, đó là việc của JupyterHub proxy. Nếu là code đi ra, đó là việc của biến môi trường.
Chuẩn bị topology lab cho JupyterHub proxy
Mình không dựng cả JupyterHub. Máy dùng để viết bài này chạy Windows, và mình không thử dựng hub trên đó. Thay vào đó mình chạy thật phần proxy, còn hub và máy chủ notebook là các máy chủ HTTP giả viết bằng Python.
Sơ đồ ba tầng
- Proxy thật: Configurable HTTP Proxy 5.3.0, cài bằng npm, chạy bằng Node 24. Cổng công khai 8000, cổng API quản trị 8001, chỉ nghe 127.0.0.1.
- Hub giả ở cổng 8081 làm đích mặc định.
- Máy chủ notebook giả của người dùng alice ở cổng 8082 và một dịch vụ API giả ở cổng 8083.
Mỗi máy chủ giả trả về một dòng JSON ghi tên nó, đường dẫn nhận được, giá trị Host và header X-Forwarded-For. Nhờ vậy mình biết chính xác yêu cầu đã tới đâu.
Điều lab này chứng minh và không chứng minh
Lab chứng minh cách CHP lưu và khớp route, cách API quản trị xác thực bằng token, và proxy cư xử thế nào với yêu cầu lạ. Lab không chứng minh cách Hub gọi API, cách spawner đăng ký route, cách đăng nhập OAuth, và hành vi WebSocket. Mình không thử WebSocket. Tài liệu JupyterHub yêu cầu proxy phải hỗ trợ WebSocket mà không cần biết trước đường dẫn, nên điều này quan trọng khi đưa vào dùng thật.
Khởi động proxy
CHP đọc token xác thực API từ biến môi trường CONFIGPROXY_AUTH_TOKEN. Lệnh mình dùng:
configurable-http-proxy --ip 127.0.0.1 --port 8000 --api-ip 127.0.0.1 --api-port 8001 --default-target http://127.0.0.1:8081 --log-level debug
Tài liệu của CHP trên GitHub mô tả hai máy chủ chạy song song. Máy chủ thứ nhất là giao diện công khai điều khiển bởi hai tham số --ip và --port, mặc định nghe mọi giao diện. Máy chủ thứ hai là API quản trị, mặc định nghe localhost. Tham số --default-target quy định nơi nhận yêu cầu chưa khớp tuyến nào.
Cấu hình tối thiểu cho JupyterHub proxy và các tuyến cần kiểm tra
Một bảng tuyến của CHP gồm các cặp đường dẫn và đích. Hub là người thêm bớt tuyến khi người dùng khởi động hoặc dừng máy chủ notebook.
Tuyến mặc định và tuyến theo người dùng
Tuyến gốc dấu gạch chéo trỏ tới hub. Khi alice khởi động máy chủ, hub gọi API thêm tuyến /user/alice trỏ tới cổng của máy chủ đó. Mình mô phỏng đúng việc này bằng một lệnh gọi API.
# thêm tuyến, đúng token mới được
req("http://127.0.0.1:8001/api/routes/user/alice", "POST", token,
{"target": "http://127.0.0.1:8082", "user": "alice"})
Bảng tuyến sau lệnh thêm có hai dòng, mỗi dòng ghi target và last_activity. Trường user mình gửi kèm được lưu lại, đúng như tài liệu JupyterHub mô tả về dữ liệu đính kèm tuyến.
Cấu hình phía JupyterHub khi proxy chạy riêng
Trong triển khai thật, người ta thường chạy CHP ngoài tiến trình hub. Tài liệu API proxy của JupyterHub có các tùy chọn tương ứng cho ConfigurableHTTPProxy: should_start đặt False để hub không tự khởi động proxy, api_url là địa chỉ API quản trị, và auth_token mặc định nạp từ biến CONFIGPROXY_AUTH_TOKEN.
# jupyterhub_config.py khi chạy CHP riêng (theo tài liệu, mình chưa chạy trên hub thật)
c.ConfigurableHTTPProxy.should_start = False
c.ConfigurableHTTPProxy.api_url = "http://127.0.0.1:8001"
c.ConfigurableHTTPProxy.auth_token = "đặt-chuỗi-ngẫu-nhiên-dài"
Token này là chìa khóa của toàn bộ bảng tuyến. Ai có token sẽ trỏ được /user/alice sang máy chủ khác. Đừng ghi token vào kho mã nguồn.
Khóa tuyến trong bảng và dấu gạch chéo cuối

Bảng tuyến và cách khớp đường dẫn
Tài liệu JupyterHub định nghĩa routespec là chuỗi luôn kết thúc bằng dấu gạch chéo, ví dụ /user/tên/. Khi mình thêm tuyến bằng đường dẫn có dấu gạch chéo cuối, bảng của CHP vẫn ghi khóa là /user/alice, không có dấu. Cả /user/alice lẫn /user/alice/ đều tới đúng máy chủ.
Điều này không gây lỗi, nhưng nếu anh em viết công cụ đọc bảng tuyến rồi so khóa với chuỗi có dấu gạch chéo cuối, phép so sẽ lệch. Hãy chuẩn hóa hai đầu trước khi so.
Trường last_activity dùng để làm gì
Mỗi tuyến trong bảng có trường last_activity, ghi thời điểm có yêu cầu gần nhất đi qua tuyến. Tài liệu JupyterHub giải thích rằng việc theo dõi hoạt động là trách nhiệm của proxy, và các dịch vụ dọn máy chủ nhàn rỗi dựa vào đó. Nếu một proxy không ghi được trường này, chỉ hoạt động tới hub mới được tính.
Trong lab, tuyến /user/alice có last_activity cập nhật mỗi lần mình gọi. Anh em có thể dùng đúng trường này để tìm tuyến đã lâu không ai dùng.
Tuyến dài hơn thắng
Hướng dẫn viết Proxy tùy biến của JupyterHub yêu cầu proxy hỗ trợ định tuyến theo cây, tức là cho phép các tuyến /foo và /foo/bar cùng tồn tại và chọn theo độ cụ thể. Mình thêm tuyến /user/alice/api trỏ sang cổng 8083 rồi gọi /user/alice/api/x. Yêu cầu đi tới dịch vụ ở cổng 8083, không đi tới máy chủ của alice. Khi gỡ tuyến dài, yêu cầu quay về tuyến ngắn hơn.
Chạy ca đúng sai và chứng minh hành vi của JupyterHub proxy
Mình chạy mười mấy ca và ghi kết quả. Ngày chạy là 27/10/2026, trên Windows, Node 24.20, Python 3.12.
API quản trị và token
Ca thử |
Kết quả |
|---|---|
Gọi API không token |
403, thân rỗng |
Gọi API sai token |
403, thân rỗng |
Gọi API đúng token |
200, trả bảng tuyến |
Thêm tuyến thiếu trường target |
400, thông báo Must specify 'target' as string |
Hai ca đầu cho thấy API quản trị không tự mở cho ai. Dù vậy cổng API vẫn nên nghe trên 127.0.0.1, đừng nghe ra mạng ngoài chỉ vì đã có token.
Định tuyến yêu cầu của người dùng
Yêu cầu |
Đích nhận |
|---|---|
/user/alice/lab khi chưa có tuyến |
Hub (đích mặc định) |
/hub/login |
Hub |
/user/alice/lab sau khi thêm tuyến |
Máy chủ của alice, đường dẫn và query giữ nguyên |
/user/alice và /user/alice/ |
Máy chủ của alice |
/user/alicia/x |
Hub, không rơi vào tuyến của alice |
/user/ALICE/x |
Hub, tuyến phân biệt hoa thường |
/user/alice/lab sau khi gỡ tuyến |
Quay về hub |
Hai dòng cuối cùng đáng chú ý. Ranh giới tuyến tính theo từng đoạn đường dẫn, nên /user/alicia không bị tuyến /user/alice nuốt. Việc phân biệt hoa thường nghĩa là nếu hệ thống đăng nhập đổi hoa thường tên người dùng, anh em cần biết route được tạo theo dạng nào.
Một quan sát nữa: yêu cầu tới /user/alice%2Fx vẫn rơi vào tuyến alice. Mình chỉ ghi lại, chưa tìm hiểu nguyên nhân. Nếu anh em đặt quy tắc truy cập dựa trên đường dẫn ở tầng trước CHP, hãy thử trường hợp mã hóa này.
Khi không có tuyến và cũng không có đích mặc định
Tài liệu CHP nói lỗi 404 trả về khi client xin một URL không có tuyến và proxy không có đích mặc định. Mình chạy một bản CHP khác không có tham số --default-target, rồi gọi /user/x. Kết quả đúng như tài liệu: 404 với dòng No service is registered at this URL.
Trong JupyterHub thật, hub luôn là đích mặc định, nên anh em hiếm khi gặp 404 này. Gặp nó nghĩa là proxy đang chạy một mình, hub chưa đăng ký lại tuyến gốc sau khi proxy khởi động lại.
Khởi động không có token
Khi mình bỏ biến CONFIGPROXY_AUTH_TOKEN, CHP vẫn chạy và in cảnh báo REST API is not authenticated. Gọi API không token trả 200 với bảng tuyến rỗng. Nghĩa là bất kỳ tiến trình nào trên máy gọi được cổng API đều đọc và sửa được bảng.
Đây là cái bẫy dễ thấy nhất của bài. Quên đặt biến môi trường trong tệp dịch vụ là chuyện thường gặp, và hệ thống vẫn chạy bình thường nên không ai biết. Hãy kiểm tra bằng cách gọi thử API không token sau mỗi lần triển khai. Phải ra 403 mới đúng.
Khi máy chủ phía sau không chạy
Mình thêm tuyến /user/bob trỏ tới cổng 9 không có dịch vụ nào. Gọi /user/bob/x trả 503 với trang Proxy Target Missing. Đây là lỗi anh em sẽ thấy khi người dùng bấm vào notebook đã chết nhưng tuyến chưa được dọn. Bảng tuyến vẫn lưu tuyến, nhưng đích không còn.
CHP có hai tham số để thay trang lỗi là --error-target và --error-path. Mình chưa thử hai tham số này.
Header khách
Mỗi máy chủ giả đều nhận X-Forwarded-For với giá trị 127.0.0.1 và X-Forwarded-Proto với giá trị http. Trong lab, trình duyệt giả cũng ở 127.0.0.1 nên không thấy sự khác biệt. Trên hệ thống thật, giá trị này là IP của người dùng từ góc nhìn của CHP. Nếu có thêm một lớp reverse proxy phía trước, IP khách có thể bị che. Bài X-Forwarded-For là gì giải thích chuỗi header này.
Đọc log của CHP
Với --log-level debug, CHP ghi một dòng cho mỗi quyết định định tuyến. Khi thêm tuyến, log có dòng Adding route rồi Route added, kèm đích. Khi có yêu cầu, log có dòng dạng PROXY WEB cùng đường dẫn và đích được chọn. Khi gỡ tuyến, log ghi Removing route.
Ba dòng này đủ để trả lời câu hỏi tuyến nào được dùng cho yêu cầu nào. Khi người dùng báo vào nhầm notebook của người khác, hãy xem dòng PROXY WEB của đúng yêu cầu đó trước khi đổ lỗi cho hub.
Tài liệu CHP ghi mức log có bốn giá trị: debug, info, warn, error, mặc định là info. Mức debug ghi nhiều, chỉ bật khi cần kiểm tra.
Định tuyến theo tên miền
CHP có tham số --host-routing. Khi bật, proxy dùng tên miền của yêu cầu để chọn đích, và API xem tên miền như phần đầu tiên của đường dẫn, ví dụ tuyến có khóa /example.com. Đây là cách JupyterHub phục vụ từng người dùng ở một tên miền con riêng. Mình chưa thử tham số này.
Khi người dùng báo không vào được notebook
Mình sắp xếp các bước chẩn đoán theo thứ tự rẻ nhất trước.
1. Xem mã trả về. 503 nghĩa là có tuyến nhưng đích không trả lời. 404 nghĩa là không có tuyến và không có đích mặc định. 403 từ API nghĩa là sai token.
2. Gọi API với token để đọc bảng tuyến và tìm khóa /user/tên. Không có khóa thì hub chưa đăng ký tuyến.
3. Có khóa nhưng 503: kiểm tra cổng trong target có tiến trình lắng nghe không.
4. Xem last_activity. Nếu rất cũ, tiến trình notebook có thể đã bị dọn mà tuyến còn sót.
5. Xem log của proxy tìm dòng PROXY WEB của đúng yêu cầu để biết nó đã tới đích nào.
Chỉ khi các bước trên đều ổn mới đi tìm lỗi trong hub hay trình duyệt.
JupyterHub proxy không phải forward proxy: bằng chứng chạy thật
Mình muốn kiểm chứng điều đã nói ở phần đầu, bằng thao tác cụ thể.
Gửi yêu cầu kiểu forward proxy vào CHP

CHP không phục vụ yêu cầu forward proxy
Một client dùng forward proxy gửi yêu cầu có địa chỉ đầy đủ, ví dụ GET http://example.test/ với Host là example.test. Mình gửi nguyên văn dòng đó vào cổng 8000 của CHP.
CHP không chuyển yêu cầu tới example.test. Nó trả lời từ hub giả, trong đó giá trị Host mà hub nhận là example.test. Log gỡ lỗi của CHP ghi dòng PROXY WEB http://example.test/ to http://127.0.0.1:8081. Nghĩa là CHP coi yêu cầu như mọi yêu cầu khác, khớp tuyến gốc và đẩy về đích mặc định.
Kết luận cho anh em: trỏ trình duyệt hay requests vào cổng của JupyterHub như một proxy sẽ không đưa anh em ra Internet. Anh em chỉ nhận lại trang của hub.
Gửi lệnh CONNECT vào CHP
Mình gửi CONNECT example.test:443 vào cùng cổng. CHP đóng kết nối mà không trả dòng trạng thái nào. Client dùng proxy cho HTTPS sẽ báo lỗi đường hầm. Bài lỗi ERR_TUNNEL_CONNECTION_FAILED mô tả triệu chứng này ở phía trình duyệt.
Phép thử này cũng chỉ ra một nguyên nhân thực tế của lỗi kiểu đó: ai đó cấu hình nhầm địa chỉ JupyterHub làm proxy trong trình duyệt hay máy chủ. Kết quả là mọi trang HTTPS đều báo lỗi đường hầm.
Điều rút ra
JupyterHub proxy hiểu đường dẫn và Host của yêu cầu để chọn đích. Nó không hiểu yêu cầu kiểu proxy ra ngoài. Vì vậy hai nhu cầu cần hai công cụ. Cổng vào dùng CHP hoặc một bộ định tuyến khác. Đường ra dùng proxy thật, đặt qua biến môi trường.
Hoàn nguyên, giới hạn và dùng JupyterHub proxy trong hệ thống thật
Dọn lab
Sau khi chạy, mình xóa hai tuyến thử bằng DELETE. API trả 204. Yêu cầu tới /user/alice/lab lại rơi về hub. Cuối cùng dừng tiến trình CHP và xóa thư mục cài npm. Không còn tiến trình hay cổng nào để lại.
Giới hạn của CHP
Hướng dẫn viết Proxy tùy biến nói rõ CHP là tiến trình đơn, và bảng tuyến không được lưu bền. Nếu tiến trình chết, toàn bộ hub không truy cập được cho tới khi hub phát hiện, khởi động lại proxy và khôi phục bảng tuyến. Tài liệu còn nêu cách thay thế như viết proxy riêng hoặc dùng các giải pháp có sẵn, ví dụ ingress trên Kubernetes.
Với nhóm nhỏ, CHP là đủ. Với nhóm lớn hoặc yêu cầu độ sẵn sàng cao, anh em cần theo dõi tiến trình proxy và chuẩn bị phương án khởi động lại.
Đặt nginx hoặc Apache phía trước CHP
Tài liệu JupyterHub có một ví dụ hoàn chỉnh về việc dùng reverse proxy làm điểm truy cập công khai. Hub chạy ở 127.0.0.1 cổng 8000 nhờ tùy chọn bind_url, còn nginx giữ cổng 443, giải quyết TLS và chuyển yêu cầu vào hub. Như vậy chuỗi đi vào có hai lớp: nginx rồi CHP.
Điểm mấu chốt trong ví dụ nginx là phần WebSocket. Cấu hình cần một khối map để biến header Upgrade thành Connection, rồi trong khối location đặt proxy_http_version 1.1 cùng hai header Upgrade và Connection. Thiếu phần này, giao diện trông bình thường nhưng kernel của notebook không kết nối được.
Mô hình hai lớp còn kéo theo chuyện địa chỉ IP khách. nginx đặt header X-Real-IP và X-Forwarded-For theo ví dụ trong tài liệu, để phía sau còn biết khách thật là ai. Nếu bỏ hai header này, mọi yêu cầu trong log của hub đều có nguồn 127.0.0.1.
Anh em cũng cần chắc rằng cổng 8000 của CHP không nghe ra mạng ngoài. Nếu cả nginx và CHP cùng mở ra Internet, người ngoài có thể đi vòng qua lớp TLS.
Danh sách kiểm tra khi triển khai

Checklist triển khai JupyterHub proxy
- Gọi thử API quản trị không token, phải ra 403.
- Gọi thử cổng 8000 từ máy khác trong mạng, xem có bị chặn bởi tường lửa như dự định không.
- Tạo một người dùng thử, khởi động notebook, xem tuyến xuất hiện trong bảng.
- Dừng notebook, xem tuyến biến mất hoặc báo 503 đúng như kỳ vọng.
- Khởi động lại CHP trên môi trường thử, đo thời gian hub khôi phục bảng tuyến.
- Ghi phiên bản CHP, Node và JupyterHub vào tài liệu vận hành.
Giới hạn của chính bài này
Mình nói rõ để anh em không hiểu nhầm. Bài này chạy thật CHP, không chạy JupyterHub. Chưa thử đăng nhập, spawner, WebSocket, TLS, host-based routing và thay trang lỗi. Cấu hình jupyterhub_config.py trong bài bám theo tài liệu API, chưa chạy trên hub thật.
Bảo mật khi đưa vào dùng thật
- Cổng API quản trị chỉ nghe 127.0.0.1 hoặc socket Unix.
- Token API dài, ngẫu nhiên, nạp từ biến môi trường, không để trong mã nguồn.
- Cổng công khai đặt sau một lớp TLS, theo hướng dẫn dùng reverse proxy của JupyterHub.
- Đừng để tuyến của người dùng đã dừng tồn tại lâu, vì sẽ gặp 503.
- Ghi lại phiên bản CHP và Node, vì hành vi có thể khác giữa các bản.
Nếu notebook cần IP khác, anh em có thể mua proxy tại ProxyVN, có proxy dân cư tĩnh Viettel, VNPT, FPT, datacenter Việt Nam và proxy xoay, hỗ trợ HTTP và SOCKS5 với tài khoản user và mật khẩu.
JupyterHub proxy lo đường vào. Proxy IP lo đường ra. Hai thứ này không thay được nhau.
Câu hỏi thường gặp
JupyterHub proxy có phải là proxy để đổi IP không. Không. Nó là reverse proxy định tuyến yêu cầu vào hub và notebook. Muốn đổi IP khi notebook gọi website, anh em đặt biến môi trường proxy cho tiến trình notebook.
Có thể thay Configurable HTTP Proxy bằng nginx hay Traefik không. Tài liệu JupyterHub cho phép viết lớp Proxy riêng và liệt kê một số triển khai, trong đó có bản dùng Traefik. Điều kiện là proxy hỗ trợ WebSocket, định tuyến theo cây, và thêm bớt tuyến không làm rớt kết nối đang chạy. Mình chưa thử các lựa chọn này.
Vì sao người dùng thấy 503 sau khi máy chủ notebook dừng. Tuyến của họ còn trong bảng nhưng đích không còn. Trong lab, đích không có dịch vụ cho trang 503 Proxy Target Missing.
Có cần đặt nginx trước JupyterHub không. Tài liệu có mục dùng reverse proxy, ví dụ nginx hay Apache làm điểm truy cập công khai, đặt TLS ở đó và chuyển WebSocket. Cách làm nginx chung có trong bài cấu hình nginx làm reverse proxy.
Chạy JupyterHub trên Windows được không. Mình không thử. Mình chỉ chạy được CHP và các máy chủ giả trên Windows, không chạy hub thật.
Khi nào nên tách CHP chạy riêng khỏi hub. Tài liệu cho biết hub mặc định quản lý CHP như tiến trình con, còn môi trường thật thường chạy riêng. Chạy riêng giúp khởi động lại hub mà không làm rớt kết nối đang mở. Đổi lại anh em phải tự quản lý tiến trình CHP và token.
Tuyến có bị mất khi CHP khởi động lại không. Có. Theo tài liệu, bảng tuyến không được lưu bền. Trong lab, mình chỉ dựng bảng mới sau mỗi lần chạy. Hub thật có nhiệm vụ khôi phục bảng, mình chưa thử bước đó.
Biến proxy đặt trong Spawner.environment có đi vào mọi notebook không. Tài liệu mô tả đây là các biến đặt thêm cho tiến trình của máy chủ single-user. Biến có tác dụng với code nào đọc nó. Thư viện requests đọc HTTP_PROXY và HTTPS_PROXY, còn một số công cụ khác cần cấu hình riêng. Mình chưa thử trên hub thật.
Nên dùng cùng một proxy cho cả nhóm hay mỗi người một proxy. Với mỗi người một proxy, anh em cần cơ chế đặt biến theo người dùng, và JupyterHub có hook cho spawner để làm việc đó. Mình chưa thử. Điều nên cân nhắc là quản lý mật khẩu, vì mọi mật khẩu nằm trong tiến trình notebook mà người dùng đọc được bằng lệnh in biến môi trường.
Có thể xem hoạt động theo người dùng từ CHP không. Trường last_activity trong bảng tuyến cho biết thời điểm yêu cầu gần nhất của từng tuyến. CHP cũng có tham số --metrics-port để mở máy chủ số liệu riêng. Mình chưa thử phần số liệu.
JupyterHub proxy và những điều cần nhớ
JupyterHub proxy là Configurable HTTP Proxy định tuyến /hub và /user/tên bằng bảng route có token API. Tuyến dài thắng tuyến ngắn, đích chết cho 503, và nó không phục vụ yêu cầu kiểu forward proxy. Cần proxy ổn định, liên hệ Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam cho JupyterHub proxy.