Docker và Cloud
Mục tiêu
Phần tiêu đề “Mục tiêu”Sau bài này, bạn sẽ:
- Phân biệt được image (bản đóng gói chỉ đọc, gồm nhiều lớp) và container (một lần chạy của image).
- Viết được một Dockerfile cho ứng dụng Gradio, dựng image bằng
docker buildvà chạy nó bằngdocker run. - Đưa API key vào container lúc chạy, và biết vì sao không bao giờ để key nằm trong image.
- Hiểu một dịch vụ chạy container trên cloud đòi gì ở image của bạn: cổng qua biến
PORT, nghe ở0.0.0.0, hệ tệp chỉ là tạm.
Cần biết trước
Phần tiêu đề “Cần biết trước”- Làm giao diện với Gradio/Streamlit, thuộc phần Mô hình ngôn ngữ lớn (Đã học)
Trực giác
Phần tiêu đề “Trực giác”Ứng dụng chat của bạn chạy tốt. Bạn gửi code cho một người bạn. Một tiếng sau họ nhắn lại: “Lỗi ở dòng 28”. Máy họ cài Gradio 5, còn bạn dùng Gradio 6. Sửa xong, họ lại gặp lỗi khác: Python của họ là 3.9, cũ hơn mức openai đòi.
Code giống hệt nhau. Thứ khác nhau là môi trường: bản Python, bản của từng gói, thư viện hệ thống, hệ điều hành.
Docker giải quyết bằng cách đóng gói chương trình cùng với môi trường của nó thành một khối, gọi là image. Image chứa một hệ điều hành Linux tối giản, đúng bản Python, đúng bản từng gói, và code của bạn. Ở đâu có Docker, image đó chạy giống hệt nhau. Mỗi lần chạy một image, bạn có một container.
Hãy hình dung image như một cuốn sách dạy nấu ăn đã in xong: không ai sửa được nội dung. Container là một bữa ăn nấu theo sách đó. Từ một cuốn sách, bạn nấu được nhiều bữa, bữa nào cũng như nhau. Đổ bữa ăn đi thì sách vẫn còn nguyên, nấu lại ra đúng món cũ.
Image cũng là thứ nhiều dịch vụ cloud nhận để chạy: bạn đưa image, dịch vụ chạy nó và cho bạn một địa chỉ web. Chỉ có một thứ không được đóng vào image: API key. Image sẽ được chia sẻ và chạy trên máy người khác, nên key phải được đưa vào riêng, đúng lúc container chạy.
Bài toán: đóng gói ứng dụng Gradio thành một image chạy giống nhau ở mọi nơi có Docker, đưa key vào lúc chạy chứ không đóng vào image, rồi đưa image lên cloud để người khác dùng được.
Lý thuyết
Phần tiêu đề “Lý thuyết”Bài này không có công thức. “Lý thuyết” là cách Docker dựng và chạy image. Bài dùng Docker 29 (bản trên máy thử, 10/2026). Cách cài nằm ở trang Get Docker của Docker.
Image và container
Phần tiêu đề “Image và container”Image là một bản đóng gói chỉ đọc, gồm nhiều lớp (layer) xếp chồng lên nhau. Lớp dưới cùng là base image, ví dụ python:3.12-slim: một Debian tối giản đã cài sẵn Python 3.12. Mỗi lệnh dựng image (chép file, cài gói) đặt thêm một lớp lên trên. Mỗi lớp chỉ ghi phần thay đổi so với lớp dưới nó.
Container là một lần chạy của image. Docker lấy các lớp của image, đặt thêm một lớp ghi mỏng ở trên cùng, rồi chạy lệnh khởi động. Mọi file chương trình ghi ra nằm ở lớp này. Xóa container là xóa lớp ghi; image không hề đổi. Từ đó có ba hệ quả:
- Một image, nhiều container. Chạy hai lần là có hai container độc lập, mỗi cái một lớp ghi riêng.
- Sửa code trên máy không làm container đổi theo. Container chạy từ image đã dựng. Muốn có code mới thì phải dựng lại image.
- Dữ liệu ghi trong container là dữ liệu tạm. Lịch sử chat hay file người dùng tải lên mà lưu trong container thì mất khi container bị xóa.
Dockerfile: công thức dựng image
Phần tiêu đề “Dockerfile: công thức dựng image”Dockerfile là một file chữ, mỗi dòng một lệnh, nói cách dựng image. Đây là Dockerfile cho ứng dụng chat của bài. Bạn sẽ dựng nó ở mục Ví dụ code.
FROM python:3.12-slimWORKDIR /appCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txtCOPY . .ENV GRADIO_SERVER_NAME=0.0.0.0ENV PYTHONUNBUFFERED=1EXPOSE 7860CMD ["python", "app.py"]| Lệnh | Làm gì | Chạy khi nào |
|---|---|---|
FROM | Chọn base image | Lúc dựng |
WORKDIR | Đặt thư mục làm việc trong image (tạo nếu chưa có) | Lúc dựng |
COPY | Chép file từ thư mục của bạn vào image | Lúc dựng |
RUN | Chạy một lệnh, ví dụ pip install | Lúc dựng |
ENV | Đặt biến môi trường, lưu lại trong image | Có hiệu lực cả lúc dựng lẫn lúc chạy |
EXPOSE | Ghi chú ứng dụng dùng cổng nào. Không mở cổng nào cả | Không chạy gì |
CMD | Lệnh mặc định khi container khởi động | Lúc chạy |
Điểm dễ nhầm nhất là RUN và CMD. RUN pip install chạy một lần, lúc dựng, và các gói đã cài thành một lớp của image. CMD ["python", "app.py"] không chạy lúc dựng; nó được ghi vào image để mỗi container chạy khi khởi động. Có nhiều CMD thì chỉ cái cuối có hiệu lực. Dạng danh sách JSON như trên (dạng exec) cho Docker chạy thẳng python, không qua shell.
Ba chi tiết còn lại:
- Base image
python:3.12-slim. Bảnslimbỏ bớt gói Debian nên nhỏ hơn, đổi lại thiếu trình biên dịch: gói pip phải biên dịch từ mã nguồn có thể lỗi, khi đó cài thêm gói Debian hoặc dùng image đầy đủ. Ghi rõ phiên bản trong tag, không dùnglatest: tag này trỏ tới bản mới hơn theo thời gian, nên dựng hôm nay và tháng sau ra hai image khác nhau.3.12-slimvẫn nhận bản vá 3.12.x nhưng không nhảy sang 3.13; muốn cố định tuyệt đối thì ghim digest (python:3.12-slim@sha256:…). Bài chọn 3.12 vì cả Gradio 6.30 (cần Python 3.10 trở lên) lẫn Streamlit 1.66 (cần 3.11 trở lên) đều chạy được. requirements.txtliệt kê các gói, mỗi dòng một gói. Trong image nên ghim đúng bản đã thử (gradio==6.30.0), cùng lý do như với tag của base image.--no-cache-dirbảo pip không giữ file tải về, cho image nhỏ hơn.- Hai dòng
ENV.GRADIO_SERVER_NAME=0.0.0.0bảo Gradio nghe mọi địa chỉ (mục Cổng bên dưới).PYTHONUNBUFFERED=1bảo Python in log ra ngay. Khi thử mà thiếu dòng này,docker logskhông hiện gì, kể cả dòng “Running on local URL” của Gradio, vì Python gom chữ in ra vào bộ đệm khi không chạy trong cửa sổ dòng lệnh. Hai biến này không bí mật, nên được phép nằm trong image.
Thứ tự lệnh và bộ nhớ đệm
Phần tiêu đề “Thứ tự lệnh và bộ nhớ đệm”Khi dựng lại, Docker xét các lệnh theo thứ tự. Lệnh nào có đầu vào không đổi (cùng lệnh, cùng file được chép) thì dùng lại lớp cũ, gọi là dùng bộ nhớ đệm (build cache). Gặp lệnh đầu tiên có đầu vào đổi, Docker dựng lại lệnh đó và mọi lệnh sau nó.
Vì vậy Dockerfile của bài chép requirements.txt và cài gói trước, rồi mới COPY . . phần code: bạn sửa code thường xuyên, nhưng ít khi đổi danh sách gói. Sửa app.py thì chỉ bước COPY . . chạy lại. Bước pip install, lâu nhất (45 đến 85 giây ở các lần thử, tùy mạng), được lấy từ bộ nhớ đệm.
.dockerignore: thứ không được vào image
Phần tiêu đề “.dockerignore: thứ không được vào image”Khi bạn gõ docker build ., dấu chấm là ngữ cảnh dựng (build context): thư mục mà Docker được phép đọc khi dựng. COPY . . chép mọi thứ trong đó vào image, kể cả file ẩn như .env.llm, thư mục .venv/, .git/.
File .dockerignore, đặt cạnh Dockerfile, liệt kê những thứ phải loại khỏi ngữ cảnh dựng. Cú pháp gần giống .gitignore, nhưng mỗi mẫu tính từ gốc ngữ cảnh dựng; muốn khớp ở mọi thư mục con thì viết **/ phía trước:
**/.env***/.venv*/.git/**/__pycache__/Dòng **/.env* quan trọng nhất: nó chặn file chứa key, dù file nằm ở gốc thư mục dự án hay trong một thư mục con. Nếu chỉ viết .env*, Docker chỉ loại file ở gốc: khi thử, pkg/.env.llm vẫn lọt vào image. Các dòng còn lại giữ cho image nhỏ và sạch.
Cổng: EXPOSE, -p và 0.0.0.0
Phần tiêu đề “Cổng: EXPOSE, -p và 0.0.0.0”Mỗi container có mạng riêng. Bên trong container, 127.0.0.1 (localhost) là chính container đó, không phải máy bạn. Hệ quả là hai việc phải làm cùng lúc:
- Ứng dụng phải nghe ở
0.0.0.0, tức mọi địa chỉ mạng của container. Docker chuyển yêu cầu từ ngoài vào qua mạng của container, không qua127.0.0.1bên trong nó, nên ứng dụng chỉ nghe127.0.0.1thì không ai ở ngoài vào được. Gradio đọc địa chỉ nghe từ biếnGRADIO_SERVER_NAME, nên Dockerfile đặt biến này là0.0.0.0. - Bạn phải nối cổng khi chạy, bằng cờ
-p <cổng trên máy>:<cổng trong container>, ví dụ-p 7860:7860.EXPOSE 7860không làm việc này; nó chỉ là ghi chú.
Viết -p 7860:7860 thì máy khác trong cùng mạng cũng có thể vào ứng dụng của bạn (tùy tường lửa), và mỗi lượt chat của họ tính vào key của bạn. Khi chỉ thử trên máy mình, ghi thêm địa chỉ: -p 127.0.0.1:7860:7860.
Biến môi trường và secret
Phần tiêu đề “Biến môi trường và secret”Secret là giá trị bí mật như key, mật khẩu. Ứng dụng cần ba biến: OPENAI_API_KEY, OPENAI_BASE_URL, LLM_MODEL. Có ba chỗ trông như đặt được chúng, nhưng chỉ một chỗ đúng với key:
ENV OPENAI_API_KEY=...trong Dockerfile: sai. Giá trị củaENVnằm lại trong image. Ai có image cũng xem được bằngdocker image inspect.ARG(tham số lúc dựng) hayCOPY .env.llm: cũng sai. Tài liệu Docker cảnh báo không dùngARGcho mật khẩu hay token, vì giá trị có thể lộ quadocker history. File chép vào bằngCOPYthì nằm trong một lớp của image, ai chạy image cũng đọc được.- Đưa vào lúc chạy: đúng.
docker run -e OPENAI_API_KEY ...lấy giá trị từ biến môi trường cùng tên đã đặt trong phiên dòng lệnh, hoặcdocker run --env-file .env.llm ...đọc từ một file trên máy. Key không nằm trong image. Nó vẫn có trong cấu hình của container: ai dùng được Docker trên máy đó xem được bằngdocker inspect <container>, nhưng image bạn chia sẻ thì không chứa nó.
Code không cần đổi gì: nó vẫn đọc os.environ["OPENAI_API_KEY"], chỉ khác là giá trị do Docker đặt vào.
File cho --env-file có mỗi dòng TÊN=giá trị; dòng bắt đầu bằng # bị bỏ qua. Docker không bỏ dấu nháy: khi thử với Docker 29.8, dòng B="abc" cho giá trị "abc" có cả hai dấu nháy. Một dấu # ở giữa dòng cũng thuộc về giá trị.
Có trường hợp cần secret ngay lúc dựng, ví dụ để tải gói từ một kho riêng. Khi đó dùng docker build --secret cùng RUN --mount=type=secret, không dùng ARG hay ENV. Ứng dụng của bài không cần.
Registry: nơi chia sẻ image
Phần tiêu đề “Registry: nơi chia sẻ image”Registry là kho chứa image trên mạng, giống GitHub nhưng cho image. Docker Hub là registry mặc định: python:3.12-slim được kéo về từ đó. Ngoài ra còn GitHub Container Registry và registry của các nhà cung cấp cloud.
Tên image có dạng <registry>/<tài-khoản>/<tên>:<tag>; bỏ trống phần registry nghĩa là Docker Hub. Quy trình: dựng một lần, gắn tên theo registry (docker tag), đăng nhập (docker login), đẩy lên (docker push). Máy khác kéo về (docker pull) và chạy, không cần dựng lại. Image công khai thì ai cũng kéo về và đọc được mọi file bên trong, thêm một lý do để key không nằm trong image.
Chạy container trên cloud
Phần tiêu đề “Chạy container trên cloud”Nhiều dịch vụ cloud nhận image và chạy nó cho bạn, theo cùng một cách ở mức chung: bạn chỉ ra image (thường trên một registry), đặt biến môi trường và secret trong phần cấu hình, rồi dịch vụ chạy container và cấp một địa chỉ HTTPS. Bài không chọn dịch vụ nào và không nói giá: những thứ này đổi thường xuyên, hãy xem trang giá chính thức của dịch vụ bạn chọn.
Mỗi dịch vụ có một hợp đồng (container contract): những điều image của bạn phải tuân theo. Lấy hợp đồng của Cloud Run (Google Cloud) làm ví dụ:
- Cổng do dịch vụ báo qua biến
PORT(Cloud Run mặc định là 8080), và ứng dụng phải nghe ở0.0.0.0trên cổng đó. Ghi cứng 7860 mà để cổng mặc định của dịch vụ thì dịch vụ không gọi được ứng dụng. Gradio không tự đọcPORT, nên code của bài tự đọc:PORT = int(os.environ.get("PORT", "7860")). - Hệ tệp chỉ là tạm. File ghi ra mất khi container dừng. Dữ liệu cần giữ phải để ở một dịch vụ lưu trữ hay cơ sở dữ liệu bên ngoài.
- Container có thể bị tắt bất cứ lúc nào khi không có yêu cầu, và có thể có nhiều bản chạy song song khi đông người dùng.
- Key đặt qua phần biến môi trường hoặc secret của dịch vụ, tương tự
--env-filetrên máy bạn.
Ứng dụng đã lên Internet thì ai có địa chỉ cũng chat được, và mỗi lượt tính vào key của bạn. Hãy đặt mật khẩu (tham số auth của launch trong Gradio, như ở bài trước) và đặt giới hạn chi tiêu ở trang quản lý của nhà cung cấp API.
Giới hạn
Phần tiêu đề “Giới hạn”- Container chạy bằng người dùng root theo mặc định. Docker khuyên tạo người dùng riêng bằng lệnh
USER; bài bỏ qua cho Dockerfile ngắn. - Image khá nặng. Image của bài chiếm 676 MB trên đĩa khi thử, trong đó riêng lớp
pip installlà 386 MB (Gradio kéo theo pandas, NumPy, Pillow…). Dựng nhiều giai đoạn (multi-stage build) giúp nhỏ hơn. - Kiến trúc CPU. Image dựng trên máy chip ARM (ví dụ Mac Apple Silicon) mặc định là bản ARM, có thể không chạy trên máy chủ x86; khi đó dựng với
--platform linux/amd64. Dựng kiểu này chạy giả lập nên chậm hơn. - Dừng chậm.
docker stopgửi tín hiệu SIGTERM. Python chạy ở PID 1 trong container bỏ qua tín hiệu này, nên container chỉ dừng khi Docker buộc tắt nó sau thời gian chờ (mã thoát 137). Ctrl+C (SIGINT) thì ứng dụng thoát gọn. - Chưa có chỗ lưu dữ liệu bền, nên lịch sử chat vẫn mất. Đưa mô hình của riêng bạn vào container là việc của bài Deploy mô hình riêng.
Minh họa
Phần tiêu đề “Minh họa”Bảng dưới là các lần chạy thật với Dockerfile của bài trên máy thử (Docker 29.8.1). Bạn nên nhìn vào cột cuối: cùng một image, chỉ đổi cách chạy là kết quả đổi hẳn. Cổng trên máy được chọn ngẫu nhiên khi thử, ở đây ghi 7860 cho dễ đọc. fake.env là file có key giả.
| Thử | Lệnh (rút gọn) | Kết quả |
|---|---|---|
| Chạy đúng cách | docker run -p 127.0.0.1:7860:7860 --env-file fake.env atlas-chat | Trang trả mã HTTP 200. Log: Running on local URL: http://0.0.0.0:7860 |
| Không đưa key | docker run atlas-chat | Container dừng ngay, mã thoát 1, log kết thúc bằng KeyError: 'OPENAI_API_KEY' |
| Không nối cổng | docker run --env-file fake.env atlas-chat | Ứng dụng vẫn chạy. docker ps chỉ ghi 7860/tcp, không có cổng nào trên máy nối vào |
Ứng dụng nghe 127.0.0.1 | thêm -e GRADIO_SERVER_NAME=127.0.0.1 | Từ máy: kết nối bị cắt, không có trả lời. Từ trong container: vẫn HTTP 200 |
Cổng theo PORT | -e PORT=8080 -p 127.0.0.1:8080:8080 | HTTP 200. Log: Running on local URL: http://0.0.0.0:8080 |
| Xem image có key không | docker image inspect atlas-chat | Mục Env chỉ có các biến của base image và hai biến ENV của bài. Không có key |
Bỏ .dockerignore | docker run atlas-chat ls -a /app | Có .env.llm, .venv, __pycache__. Với .dockerignore: chỉ Dockerfile, .dockerignore, app.py, requirements.txt |
Sửa app.py, dựng lại | docker build -t atlas-chat . | Ba bước WORKDIR, COPY requirements.txt, pip install ghi CACHED. Chỉ COPY . . chạy lại; cả lần dựng khoảng 3 giây |
Dòng “Xem image có key không” và dòng “Bỏ .dockerignore” đi cùng nhau: docker image inspect không thấy key không có nghĩa là image sạch, vì lệnh này không cho xem nội dung file.
Ví dụ code
Phần tiêu đề “Ví dụ code”Code trong bài chạy thử lần cuối tháng 10/2026, với Docker 29.8, image python:3.12-slim (Python 3.12.15), openai 3.37 và gradio 6.30.0. Không có ô nào chạy trên trang: mọi thứ chạy trên máy bạn và cần Docker đã cài. Lệnh docker gõ giống nhau trong bash và PowerShell.
Bước 1: ứng dụng
Phần tiêu đề “Bước 1: ứng dụng”Tạo một thư mục, ví dụ atlas-chat/, rồi lưu code dưới đây thành app.py. Đây là ứng dụng chat Gradio của bài trước, sửa hai chỗ: cổng đọc từ biến PORT (không có thì dùng 7860), và một ghi chú rằng địa chỉ nghe do biến GRADIO_SERVER_NAME quyết định. Hai câu hỏi mẫu (examples) được bỏ cho gọn.
Chạy trên máy bạn
Ứng dụng chat Gradio, sẵn sàng cho container
- Cần cài
pip install openai 'gradio>=6'- Biến môi trường
OPENAI_BASE_URL(tùy chọn),OPENAI_API_KEY,LLM_MODEL,PORT(tùy chọn)
# Ứng dụng chat Gradio của bài trước, sửa để chạy được trong container.import os
import gradio as grfrom openai import OpenAI
client = OpenAI( base_url=os.environ.get("OPENAI_BASE_URL"), # không đặt biến: dùng OpenAI api_key=os.environ["OPENAI_API_KEY"], # đưa vào lúc chạy, không dựng)MODEL = os.environ["LLM_MODEL"]SYSTEM = "Bạn là trợ lý thân thiện, trả lời ngắn gọn bằng tiếng Việt."# Cổng: nền tảng cloud thường báo qua biến PORT. Không có thì dùng 7860.PORT = int(os.environ.get("PORT", "7860"))
def to_text(content): # Gradio 6 lưu nội dung mỗi tin thành danh sách mảnh; ghép thành chuỗi. if isinstance(content, str): return content return "".join(p["text"] for p in content if p.get("type") == "text")
def chat(message, history): messages = [{"role": "system", "content": SYSTEM}] for turn in history: messages.append( {"role": turn["role"], "content": to_text(turn["content"])} ) messages.append({"role": "user", "content": message})
stream = client.chat.completions.create( model=MODEL, messages=messages, stream=True ) reply = "" for chunk in stream: if not chunk.choices: # có nơi gửi mảnh không có choices continue piece = chunk.choices[0].delta.content if piece: # mảnh có thể là None hoặc chuỗi rỗng reply += piece yield reply # trả cả đoạn đã có, Gradio vẽ lại tin nhắn
demo = gr.ChatInterface(chat, title="Trợ lý AI Atlas")
if __name__ == "__main__": # Địa chỉ nghe lấy từ biến GRADIO_SERVER_NAME (Dockerfile đặt 0.0.0.0); # không có biến đó thì Gradio chỉ nghe 127.0.0.1. demo.launch(server_port=PORT)Chạy thẳng python app.py trên máy vẫn được như ở bài trước: không có GRADIO_SERVER_NAME nên Gradio chỉ nghe 127.0.0.1, đúng điều bạn muốn trên máy mình. Thiếu OPENAI_API_KEY hay LLM_MODEL thì chương trình dừng ngay với KeyError, trong container cũng vậy (dòng “Không đưa key” ở bảng Minh họa).
Bước 2: ba file cho Docker
Phần tiêu đề “Bước 2: ba file cho Docker”Cạnh app.py, tạo requirements.txt với đúng bản đã thử:
openai==3.37.0gradio==6.30.0Tạo Dockerfile (không có đuôi) với nội dung ở mục Lý thuyết, và .dockerignore với bốn dòng ở mục .dockerignore. Thư mục lúc này có dạng:
atlas-chat/├── .dockerignore├── .env.llm (bị .dockerignore loại, không vào image)├── Dockerfile├── app.py└── requirements.txt.env.llm chứa ba biến, không bọc nháy. Nếu thư mục là một repo Git, thêm .env* vào cả .gitignore.
OPENAI_API_KEY=<key-của-bạn>OPENAI_BASE_URL=<địa chỉ API của nhà cung cấp>LLM_MODEL=<tên model>Dùng OpenAI thì xóa hẳn dòng OPENAI_BASE_URL; để OPENAI_BASE_URL= (rỗng) thì SDK nhận chuỗi rỗng và báo lỗi kết nối. Bảng nhà cung cấp và OPENAI_BASE_URL của từng nơi ở bài Sử dụng API văn bản.
Bước 3: dựng và chạy
Phần tiêu đề “Bước 3: dựng và chạy”Trong thư mục atlas-chat/:
docker build -t atlas-chat .docker run --rm -p 127.0.0.1:7860:7860 --env-file .env.llm atlas-chatLệnh đầu dựng image tên atlas-chat từ thư mục hiện tại. Lần đầu mất một đến hai phút trên máy thử, tùy mạng: Docker kéo base image, rồi pip cài 56 gói (Gradio kéo theo nhiều gói phụ thuộc). Lệnh thứ hai chạy một container: --rm xóa container khi nó dừng, -p nối cổng, --env-file đưa key vào. Khi thử, log của container là:
* Running on local URL: http://0.0.0.0:7860* To create a public link, set `share=True` in `launch()`.0.0.0.0 là địa chỉ nghe bên trong container, không phải địa chỉ để mở. Trên máy bạn, mở http://localhost:7860; bấm Ctrl+C để dừng. Muốn chạy nền thì thêm -d, rồi dùng docker ps (liệt kê container và <id>), docker logs <id>, docker stop <id>. Sửa app.py thì chạy lại cả hai lệnh: dựng lại image (nhanh, nhờ bộ nhớ đệm) rồi chạy container mới.
Bước 4: đẩy lên registry
Phần tiêu đề “Bước 4: đẩy lên registry”Với Docker Hub, <tài-khoản> là tên tài khoản Docker Hub của bạn:
docker logindocker tag atlas-chat <tài-khoản>/atlas-chat:1.0docker push <tài-khoản>/atlas-chat:1.0docker tag không chép gì, chỉ gắn thêm một tên cho cùng image. Repo chưa có thì docker push tạo mới, theo mục Default privacy trong phần cài đặt tài khoản Docker Hub, với tài khoản mới thường là công khai. Muốn riêng tư thì kiểm mục này trước khi đẩy. Từ đây, máy nào cũng chạy được bằng docker run ... <tài-khoản>/atlas-chat:1.0: Docker tự kéo image về. Khi thử, bài đẩy và kéo lại image qua một registry chạy ngay trên máy (image registry:3), không qua Docker Hub. Đưa lên dịch vụ cloud thì làm theo tài liệu của dịch vụ bạn chọn: chỉ ra image, đặt biến môi trường và secret, kiểm cổng.
Lỗi hay gặp
Phần tiêu đề “Lỗi hay gặp”Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Tóm tắt và đọc thêm
Phần tiêu đề “Tóm tắt và đọc thêm”Ý chính:
- Docker đóng gói chương trình cùng môi trường của nó. Image là bản đóng gói chỉ đọc gồm nhiều lớp; container là một lần chạy của image, có lớp ghi tạm mất khi container bị xóa.
- Dockerfile:
FROMbase image ghim phiên bản, chéprequirements.txtvàpip installtrước, chép code sau để dùng lại bộ nhớ đệm.RUNchạy lúc dựng,CMDchạy lúc container khởi động;EXPOSEchỉ là ghi chú. - Trong container, ứng dụng nghe
0.0.0.0; cổng mở bằngdocker run -p. Key vào lúc chạy bằng--env-file, không quaENV,ARGhayCOPY;.dockerignorechặn**/.env*. - Registry để chia sẻ image (
docker tag,docker push). Dịch vụ cloud chạy image theo hợp đồng của nó: cổng quaPORT, hệ tệp tạm, key đặt trong phần secret của dịch vụ.
Đọc thêm:
- Docker. Building best practices. Chọn base image, ghim phiên bản,
.dockerignore,USER, dựng nhiều giai đoạn: những điều bài chỉ nhắc qua. - Docker. Dockerfile reference, các mục
FROM,COPY,RUN,ENV,ARG,EXPOSE,CMD. Tra cứu chính xác từng lệnh, kể cả cảnh báo vềARGvà secret. - Docker. Build secrets. Cách đưa secret vào lúc dựng mà không để lại trong image, khi bạn thật sự cần.
- Gradio. Deploying Gradio with Docker. Hướng dẫn chính thức, cùng cách đặt
GRADIO_SERVER_NAME. Ví dụ trên trang dùng Python 3.10; bài đã thử với 3.12 và Gradio 6.30.0. Với Streamlit, xem Deploy Streamlit using Docker. - Google Cloud. Container runtime contract. Hợp đồng của Cloud Run: cổng qua
PORT, hệ tệp tạm, vòng đời container. Đọc như một ví dụ cụ thể về điều dịch vụ cloud đòi ở image.
Trình duyệt đang chặn lưu dữ liệu. Tiến độ sẽ mất khi bạn tải lại trang.
Nội dung theo giấy phép CC BY-NC-SA 4.0. Mã nguồn theo giấy phép MIT.Giới thiệu