Quay lại blog

Cài đặt proxy trong Docker để kéo hình ảnh và ứng dụng container: hướng dẫn đầy đủ

Phân tích cách cấu hình proxy trong Docker để tải hình ảnh qua tường lửa doanh nghiệp và cho các ứng dụng bên trong container - với ví dụ cấu hình và kịch bản thực tế.

📅4 tháng 4, 2026
```html

Bạn đang làm việc với Docker trong mạng doanh nghiệp, trên máy chủ có quyền truy cập hạn chế hoặc muốn các container của bạn truy cập internet qua một IP cụ thể? Nếu không có cấu hình proxy đúng, Docker sẽ không thể tải hình ảnh từ Docker Hub hoặc bất kỳ kho lưu trữ nào khác. Trong bài viết này, chúng ta sẽ phân tích tất cả các cấp độ proxy - từ daemon đến container riêng lẻ.

Tại sao Docker cần proxy

Docker là một công cụ thường xuyên truy cập các tài nguyên bên ngoài. Mỗi lần docker pull hoặc docker build, daemon sẽ truy cập vào Docker Hub, GitHub Container Registry, Google Container Registry hoặc kho lưu trữ riêng của bạn. Và đây là lúc các vấn đề bắt đầu.

Các tình huống mà không có proxy sẽ không thể hoạt động:

  • Mạng doanh nghiệp với tường lửa - tất cả lưu lượng phải đi qua máy chủ proxy doanh nghiệp, nếu không kết nối sẽ bị chặn.
  • Giới hạn địa lý - Docker Hub hoặc các kho lưu trữ riêng không khả dụng từ quốc gia hoặc trung tâm dữ liệu của bạn.
  • Máy chủ hạn chế không có kết nối trực tiếp đến internet - VPS trong vòng kín, nơi internet chỉ khả dụng qua cổng.
  • Kiểm soát lưu lượng đầu ra của các container - bạn muốn các ứng dụng bên trong container truy cập mạng qua một IP cụ thể, ví dụ cho việc phân tích, yêu cầu API hoặc kiểm tra nội dung phụ thuộc vào địa lý.
  • Ẩn danh yêu cầu - ẩn IP thực của máy chủ khi truy cập các dịch vụ bên ngoài từ container.
  • Giới hạn tỷ lệ - Docker Hub giới hạn số lượng yêu cầu pull cho người dùng ẩn danh (100 pull trong 6 giờ). Qua proxy với IP luân phiên có thể vượt qua giới hạn này.

Quan trọng là hiểu rằng Docker không phải là một ứng dụng đơn lẻ, mà là một hệ thống gồm nhiều thành phần. Daemon (dockerd) sống trên máy chủ và thực hiện việc kéo hình ảnh. Các container là các quy trình cách ly với các cài đặt mạng riêng của chúng. Do đó, cấu hình proxy cho daemon và cho các container là hai nhiệm vụ khác nhau, được giải quyết theo cách khác nhau.

Ba cấp độ proxy trong Docker

Trước khi đi vào các cấu hình, cần hiểu kiến trúc. Trong Docker có ba cấp độ độc lập, mỗi cấp độ yêu cầu cấu hình proxy riêng:

Cấp độ Chức năng Cấu hình ở đâu
Docker daemon Tải hình ảnh (docker pull), truy cập vào các kho lưu trữ systemd override hoặc daemon.json
Docker build Thực hiện các lệnh khi xây dựng hình ảnh (RUN apt-get, pip install, v.v.) ARG và ENV trong Dockerfile hoặc --build-arg
Runtime container Ứng dụng đang chạy thực hiện các yêu cầu HTTP ENV khi docker run hoặc trong docker-compose.yml

Một lỗi điển hình là chỉ cấu hình proxy ở một cấp độ và ngạc nhiên tại sao hình ảnh vẫn không được kéo hoặc ứng dụng không thấy proxy. Hãy cùng phân tích từng cấp độ một cách chi tiết.

Cấu hình proxy cho Docker daemon (kéo hình ảnh)

Đây là cấu hình quan trọng nhất nếu bạn muốn kéo hình ảnh qua proxy. Docker daemon là một dịch vụ hệ thống, được khởi động qua systemd. Các biến môi trường của người dùng của bạn hoặc thậm chí root sẽ không được nhìn thấy. Cần phải truyền proxy vào môi trường của dịch vụ.

Cách 1: qua systemd override (được khuyến nghị)

Tạo một thư mục để ghi đè cài đặt dịch vụ:

sudo mkdir -p /etc/systemd/system/docker.service.d

Tạo một tệp /etc/systemd/system/docker.service.d/http-proxy.conf với nội dung sau:

[Service]
Environment="HTTP_PROXY=http://username:password@proxy-host:port"
Environment="HTTPS_PROXY=http://username:password@proxy-host:port"
Environment="NO_PROXY=localhost,127.0.0.1,::1,your-private-registry.example.com"

Nếu proxy không yêu cầu xác thực - chỉ cần loại bỏ username:password@. Sau khi tạo tệp, hãy tải lại cấu hình systemd và khởi động lại Docker:

sudo systemctl daemon-reload
sudo systemctl restart docker

Kiểm tra xem các cài đặt đã được áp dụng:

sudo systemctl show --property=Environment docker

Trong đầu ra, bạn sẽ thấy các biến HTTP_PROXYHTTPS_PROXY của bạn.

Cách 2: qua ~/.docker/config.json (Docker Desktop và các phiên bản mới)

Bắt đầu từ Docker Engine 23.0, có một cách thuận tiện hơn - cấu hình proxy qua tệp cấu hình của khách hàng. Tạo hoặc chỉnh sửa tệp ~/.docker/config.json:

{
  "proxies": {
    "default": {
      "httpProxy": "http://username:password@proxy-host:port",
      "httpsProxy": "http://username:password@proxy-host:port",
      "noProxy": "localhost,127.0.0.1,::1"
    }
  }
}

Cách này thuận tiện vì không yêu cầu quyền root và không cần khởi động lại daemon. Các cài đặt sẽ tự động được truyền vào các container khi xây dựng như các đối số build. Tuy nhiên, để quản lý các yêu cầu pull của daemon, vẫn cần cách qua systemd.

💡 Quan trọng về NO_PROXY

Luôn thêm vào NO_PROXY các địa chỉ của các kho lưu trữ và dịch vụ nội bộ của bạn. Nếu không, Docker sẽ cố gắng truy cập chúng qua proxy và gặp lỗi kết nối. Danh sách điển hình: localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16

Proxy bên trong container khi xây dựng (build-time)

Khi Docker xây dựng hình ảnh và thực hiện các lệnh như RUN apt-get install, RUN pip install hoặc RUN npm install - các lệnh này được thực hiện bên trong một container tạm thời. Nó không có quyền truy cập vào proxy của máy chủ theo mặc định. Cần phải truyền proxy một cách rõ ràng qua các đối số build.

Truyền proxy qua --build-arg

docker build \
  --build-arg HTTP_PROXY=http://proxy-host:port \
  --build-arg HTTPS_PROXY=http://proxy-host:port \
  --build-arg NO_PROXY=localhost,127.0.0.1 \
  -t my-image .

Cấu hình trong Dockerfile

Nếu proxy chỉ cần trong giai đoạn xây dựng và bạn không muốn nó xuất hiện trong hình ảnh cuối cùng - hãy sử dụng ARG, thay vì ENV:

FROM ubuntu:22.04

# Khai báo các đối số xây dựng
ARG HTTP_PROXY
ARG HTTPS_PROXY
ARG NO_PROXY

# Sử dụng chúng trong các lệnh
RUN apt-get update && apt-get install -y curl wget

# Sau khối này, proxy sẽ không có trong ENV của container

Nếu proxy cần thiết cả trong container đang chạy - hãy sử dụng ENV:

FROM python:3.11

ENV HTTP_PROXY=http://proxy-host:port
ENV HTTPS_PROXY=http://proxy-host:port
ENV NO_PROXY=localhost,127.0.0.1

RUN pip install requests beautifulsoup4

CMD ["python", "app.py"]

⚠️ Cảnh báo: bảo mật

Không mã hóa tên đăng nhập và mật khẩu proxy trực tiếp trong Dockerfile, nếu hình ảnh sẽ công khai hoặc được đưa vào kho lưu trữ. Sử dụng --build-arg và truyền các giá trị từ các biến môi trường của hệ thống CI/CD. Lệnh docker history có thể hiển thị các giá trị ARG, vì vậy hãy sử dụng Docker BuildKit secrets cho các bí mật.

Proxy cho container đang chạy (runtime)

Đây là kịch bản phổ biến nhất cho các ứng dụng thực hiện các yêu cầu HTTP trong quá trình hoạt động - các trình phân tích, bot, microservices cần truy cập qua một IP cụ thể. Ở đây, việc cấu hình rất đơn giản: cần truyền các biến môi trường khi khởi động container.

Qua docker run

docker run \
  -e HTTP_PROXY=http://username:password@proxy-host:port \
  -e HTTPS_PROXY=http://username:password@proxy-host:port \
  -e NO_PROXY=localhost,127.0.0.1 \
  my-image

Hầu hết các thư viện HTTP phổ biến tự động nhận các biến HTTP_PROXYHTTPS_PROXY: Python requests, curl, wget, Go's net/http, Node.js https-proxy-agent và nhiều thư viện khác. Không cần phải viết thêm gì trong mã.

Qua tệp .env

Tiện hơn khi lưu trữ cài đặt trong tệp .env và truyền toàn bộ nó:

# Tệp .env
HTTP_PROXY=http://username:password@proxy-host:port
HTTPS_PROXY=http://username:password@proxy-host:port
NO_PROXY=localhost,127.0.0.1
docker run --env-file .env my-image

SOCKS5 proxy trong container

Nếu bạn sử dụng SOCKS5 proxy (ví dụ, proxy cư trú thường hỗ trợ giao thức này), cú pháp sẽ hơi khác:

docker run \
  -e ALL_PROXY=socks5://username:password@proxy-host:port \
  my-image

Lưu ý: không phải tất cả các thư viện đều hỗ trợ SOCKS5 qua biến môi trường mà không cần phụ thuộc bổ sung. Python requests yêu cầu cài đặt requests[socks], curl cần được biên dịch với hỗ trợ libcurl-socks.

Proxy trong Docker Compose

Trong các dự án thực tế, hiếm khi sử dụng docker run đơn lẻ. Thường thì làm việc với Docker Compose, nơi nhiều dịch vụ được mô tả trong một tệp. Cấu hình proxy ở đây được thực hiện ở cấp độ của từng dịch vụ hoặc qua tệp biến.

Tùy chọn 1: môi trường trong docker-compose.yml

version: '3.8'

services:
  scraper:
    image: my-scraper:latest
    environment:
      - HTTP_PROXY=http://username:password@proxy-host:port
      - HTTPS_PROXY=http://username:password@proxy-host:port
      - NO_PROXY=localhost,127.0.0.1
    restart: unless-stopped

  api:
    image: my-api:latest
    # Dịch vụ này không có proxy - chỉ truy cập vào các tài nguyên nội bộ
    ports:
      - "8080:8080"

Tùy chọn 2: qua tệp .env (được khuyến nghị)

Tạo một .env trong thư mục chứa docker-compose.yml. Docker Compose sẽ tự động nhận tệp này:

# .env
PROXY_HOST=proxy-host
PROXY_PORT=8080
PROXY_USER=username
PROXY_PASS=password
version: '3.8'

services:
  scraper:
    image: my-scraper:latest
    environment:
      - HTTP_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
      - HTTPS_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
      - NO_PROXY=localhost,127.0.0.1

Proxy khi xây dựng trong Compose

Nếu trong Compose có phần build và cần truyền proxy trong giai đoạn xây dựng:

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        HTTP_PROXY: http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
        HTTPS_PROXY: http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
    environment:
      - HTTP_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
      - HTTPS_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}

Loại proxy nào nên chọn cho Docker

Việc chọn loại proxy phụ thuộc vào nhiệm vụ. Đối với các môi trường Docker, có ba loại chính, mỗi loại có một vị trí riêng:

Loại proxy Tốc độ Độ ẩn danh Kịch bản tốt nhất cho Docker
Data center ⚡ Cao Trung bình Kéo hình ảnh, pipeline CI/CD, vượt qua giới hạn tỷ lệ của Docker Hub
Residential 🔄 Trung bình Cao Phân tích các trang web có bảo vệ, API với giới hạn địa lý, kiểm tra
Mobile 🔄 Trung bình Tối đa Các ứng dụng làm việc với API di động (Instagram, TikTok)

Đối với việc kéo hình ảnh và CI/CD, các proxy data center là tối ưu - chúng cung cấp tốc độ tối đa khi tải xuống các hình ảnh lớn (vài gigabyte) và kết nối ổn định cho các quá trình xây dựng lâu dài.

Đối với các container phân tích, cần vượt qua bảo vệ của các trang web và trông giống như người dùng bình thường, proxy cư trú là lựa chọn tốt hơn. Chúng có IP của người dùng thực, giảm khả năng bị chặn ngay cả khi có yêu cầu nhiều.

Đối với các ứng dụng làm việc với các nền tảng di động - Instagram Graph API, TikTok API, các phiên bản di động của dịch vụ - nên xem xét proxy di động. Chúng sử dụng IP của các nhà mạng di động và gây ra ít nghi ngờ nhất cho các hệ thống chống bot.

Giao thức: HTTP vs SOCKS5

Docker daemon chỉ hỗ trợ proxy HTTP/HTTPS - SOCKS5 không hoạt động cho việc kéo hình ảnh. Nếu bạn có proxy SOCKS5 và cần tải hình ảnh, bạn sẽ phải sử dụng một bộ chuyển đổi cục bộ như privoxy hoặc microsocks, sẽ nhận HTTP và chuyển tiếp qua SOCKS5.

Đối với các container runtime, tình hình tốt hơn: hầu hết các thư viện HTTP hỗ trợ SOCKS5 trực tiếp qua biến ALL_PROXY=socks5://....

Các lỗi thường gặp và cách khắc phục

Hãy cùng phân tích những vấn đề phổ biến nhất mà mọi người gặp phải khi cấu hình proxy trong Docker:

Lỗi 1: Proxy được cấu hình trên máy chủ, nhưng Docker không thấy

Triệu chứng:

Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp: connection refused

Nguyên nhân: Docker daemon chạy như một dịch vụ hệ thống và không kế thừa các biến môi trường của người dùng hiện tại, ngay cả khi bạn đã thiết lập export HTTPS_PROXY=... trong terminal.

Giải pháp: Cấu hình proxy qua systemd override (cách 1 trong phần trên). Nhớ thực hiện systemctl daemon-reload && systemctl restart docker.

Lỗi 2: apt-get / pip / npm không hoạt động khi xây dựng

Triệu chứng:

Err:1 http://archive.ubuntu.com/ubuntu focal InRelease
Could not connect to archive.ubuntu.com:80

Nguyên nhân: Cấu hình proxy cho daemon không tự động áp dụng cho các lệnh bên trong RUN trong Dockerfile. Đây là hai bối cảnh khác nhau.

Giải pháp: Truyền proxy qua --build-arg hoặc sử dụng ~/.docker/config.json với phần proxies (Docker 23.0+, tự động truyền các đối số khi xây dựng).

Lỗi 3: Các dịch vụ nội bộ không khả dụng qua proxy

Triệu chứng:

curl: (7) Failed to connect to internal-service.local port 8080: Connection refused

Nguyên nhân: Proxy cố gắng chuyển tiếp các yêu cầu đến các địa chỉ nội bộ không khả dụng từ bên ngoài.

Giải pháp: Thêm tất cả các miền và mạng con nội bộ vào NO_PROXY. Đối với các mạng doanh nghiệp, danh sách điển hình: localhost,127.0.0.1,::1,.internal,.local,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16

Lỗi 4: Xác thực proxy không hoạt động - ký tự đặc biệt trong mật khẩu

Nếu mật khẩu chứa các ký tự đặc biệt (@, #, %), URL sẽ không được phân tích chính xác.

Giải pháp: Mã hóa mật khẩu trong URL-encoding. Ví dụ, p@ss#wordp%40ss%23word. Trong Python, bạn có thể nhanh chóng nhận được phiên bản đã mã hóa:

python3 -c "from urllib.parse import quote; print(quote('p@ss#word', safe=''))"
# Đầu ra: p%40ss%23word

Lỗi 5: Proxy hoạt động, nhưng chứng chỉ SSL không được xác minh

Các proxy doanh nghiệp thường thực hiện kiểm tra SSL (MITM) và thay thế các chứng chỉ. Docker sẽ báo lỗi:

x509: certificate signed by unknown authority

Giải pháp: Thêm chứng chỉ gốc doanh nghiệp vào danh sách tin cậy trên máy chủ và khởi động lại Docker. Trên Ubuntu/Debian:

sudo cp corporate-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
sudo systemctl restart docker

Lỗi 6: Kubernetes / Docker Swarm - proxy không hoạt động trên tất cả các nút

Trong các môi trường cụm, cần cấu hình proxy trên từng nút riêng biệt. Đối với Kubernetes, cần thêm cấu hình các biến môi trường trong các bản khai pod hoặc qua ConfigMap. Có thể tự động hóa điều này qua Ansible hoặc các công cụ quản lý cấu hình khác.

Kết luận

Cấu hình proxy trong Docker không phải là một cài đặt đơn lẻ, mà là ba cấp độ độc lập: daemon cho việc kéo hình ảnh, build-time cho việc xây dựng và runtime cho các container đang chạy. Khi đã hiểu kiến trúc này, bạn có thể linh hoạt quản lý lưu lượng nào đi qua proxy và lưu lượng nào đi trực tiếp.

Danh sách kiểm tra ngắn gọn cho cấu hình đúng:

  • ✅ Đối với việc kéo hình ảnh - cấu hình systemd override với các biến HTTP_PROXY và HTTPS_PROXY
  • ✅ Đối với việc xây dựng - truyền proxy qua --build-arg hoặc ~/.docker/config.json
  • ✅ Đối với runtime - sử dụng các biến môi trường qua -e hoặc --env-file
  • ✅ Luôn cấu hình NO_PROXY cho các địa chỉ nội bộ
  • ✅ Không lưu trữ mật khẩu proxy trong Dockerfile - sử dụng build-args và tệp .env
  • ✅ Đối với các pipeline CI/CD, chọn các proxy data center nhanh chóng
  • ✅ Đối với các container phân tích - proxy cư trú hoặc di động

Nếu các ứng dụng container của bạn thực hiện các yêu cầu đến các dịch vụ bên ngoài, phân tích dữ liệu hoặc làm việc với API có giới hạn địa lý, chúng tôi khuyên bạn nên sử dụng proxy cư trú - chúng cung cấp mức độ tin cậy cao từ các dịch vụ và rủi ro bị chặn tối thiểu ngay cả khi hoạt động mạnh mẽ.

```