# 网络艺术:Docker建站与反向代理

2024-07-14
:: tags: #Network

前言 Docker的出现极大简化了建站流程，较过去的LAMP方式优雅了许多，配合Nginx反向代理可以快速上线HTTPS站点。

## 安装Docker

这里以Debian12为例：

- 官方安装脚本:

    curl -fsSL https://get.docker.com -o get-docker.sh
    sudo sh get-docker.sh

- 使用 Docker 存储库安装

使用以下命令安装此方法的先决条件：

    sudo apt update && sudo apt install ca-certificates curl gnupg

创建一个目录来存储密钥环：

    sudo install -m 0755 -d /etc/apt/keyrings

使用给定的命令下载 GPG 密钥并将其存储在 `/etc/apt/keyrings/etc/apt/keyrings` 目录中：

    curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

使用 chmod 命令更改 docker.gpg 文件的权限：

    sudo chmod a+r /etc/apt/keyrings/docker.gpg

使用以下命令为 Docker 设置存储库：

    echo \
      "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
      "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

现在可以使用以下命令更新存储库索引并安装 Docker：

    sudo apt update && sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

## 使用Docker-Compose

- 目标：创建一个`Searxng服务`并对外开放。
- 方法：创建两个 docker-compose 文件，并`使用同一个外部 Docker 网络`使两个服务互联。

0. 首先`创建好工作目录`,例如：

    .
    └── docker
        ├── docker-compose.nginx.yml
        ├── docker-compose.searxng.yml
        └── nginx
            ├── certs
            │   ├── fullchain.pem
            │   └── privkey.pem
            └── searxng.conf

1. 在启动服务前，首先创建一个 Docker 外部网络（例如命名为 nginx）：

    docker network create nginx

这样，无论是哪个 docker-compose 项目中的容器，只要加入此网络，就能直接通信。

2. 编写 searxng 的 docker-compose 文件

    version: '3'

    services:
      searxng:
        image: searxng/searxng
        container_name: searxng
        restart: unless-stopped
        ulimits:
          nproc: 65535
          nofile:
            soft: 65535
            hard: 65535
        volumes:
          - /var/lib/docker/volumes/searxng/_data:/etc/searxng
        networks:
          - nginx
        ports:
          # 如果希望 searxng 只对内部服务开放，则可不映射外部端口
          - "127.0.0.1:18080:8080"

    networks:
      nginx:
        external: true

3. 编写 Nginx 的 docker-compose 文件

创建 nginx 的 docker-compose 文件，例如：

    version: '3'

    services:
      nginx:
        image: nginx:latest
        container_name: nginx
        restart: unless-stopped
        ports:
          - "80:80"
          # 如需要 HTTPS，请映射 443 端口并挂载证书目录
          #- "443:443"
        volumes:
          - ./nginx/searxng.conf:/etc/nginx/conf.d/default.conf:ro
          #- ./nginx/certs:/etc/nginx/certs:ro
        networks:
          - nginx

    networks:
      nginx:
        external: true

4. 编写 Nginx 配置文件

    server {
        listen 80;
        server_name searxng.dich.bid;

        client_max_body_size 10M;

        location / {
            proxy_pass http://searxng:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }

        error_page 502 /502.html;
        location = /502.html {
            root /usr/share/nginx/html;
            internal;
        }
    }

5. 启动服务

- 启动 searxng 服务：

    docker-compose -f docker-compose.searxng.yml up -d

- 启动 nginx 服务：

    docker-compose -f docker-compose.nginx.yml up -d

由于两者都加入了外部网络 nginx，nginx 内的`proxy_pass http://searxng:8080`就能解析到 searxng 容器，实现反向代理效果。现在，访问`http://ip:18080`就可以访问Searxng搜索引擎。

## 添加HTTPS

在实际生产环境中我们不能使用IP直接访问，因此需要为我们的站点开启SSL证书，也就是要申请证书并在配置文件中声明。

1. 证书生成

- 如果只是用于测试可以生成自签名证书：

    mkdir -p /home/dich/docker/nginx/certs
    openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
      -keyout /home/dich/docker/nginx/certs/privkey.pem \
      -out /home/dich/docker/nginx/certs/fullchain.pem \
      -subj "/CN=your-domain.com"

2. 更改searxng.conf:

    server {
        listen 443 ssl;
        server_name searxng.dich.bid;

        # SSL 证书配置
        ssl_certificate /home/dich/docker/nginx/certs/fullchain.pem;
        ssl_certificate_key /home/dich/docker/nginx/certs/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;

        client_max_body_size 10M;

        location / {
            proxy_pass http://searxng:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }

        error_page 502 /502.html;
        location = /502.html {
            root /usr/share/nginx/html;
            internal;
        }
    }

    # HTTP 服务器块，将所有流量重定向到 HTTPS
    server {
        listen 80;
        server_name searxng.dich.bid;
        return 301 https://$host$request_uri;
    }

3. 更改docker-compose.nginx.yml

    version: '3'

    services:
      nginx:
        image: nginx:latest
        container_name: nginx
        restart: unless-stopped
        ports:
          - "80:80"
          # 如需要 HTTPS，请映射 443 端口并挂载证书目录
          - "443:443"
        volumes:
          - ./nginx/searxng.conf:/etc/nginx/conf.d/default.conf:ro
          - ./nginx/certs:/home/dich/docker/nginx/certs
        networks:
          - nginx

    networks:
      nginx:
        external: true

4. 启动新配置

- 重启容器

    sudo docker compose -f docker-compose.nginx.yml up -d

- 查看日志

    sudo docker logs searxng

## Caddy

> Caddy 自 2015 年起用 Go 语言重写，定位为“开箱即用”的现代 Web 服务器，内置自动 Let’s Encrypt 证书管理和续期，默认支持 HTTP/2 及 HTTP/3（QUIC），并通过简洁明了的 Caddyfile 语法极大降低配置成本.

0. 示例结构：

    .
    └── compose
        ├── certs
        │   ├── cert.pem
        │   └── key.pem
        ├── compose.caddy.yml
        ├── compose.miniflux.yml
        ├── compose.searxng.yml
        └── conf
            └── Caddyfile

1. 同样创建名为Caddy的docker网络：

    docker network create caddy

2. 编写Caddy的compose，可以看到caddy可以自带签发证书：

    version: '3.7'

    # 自动签发模式

    services:
      caddy:
        image: caddy:latest
        container_name: caddy
        restart: unless-stopped
        volumes:
          - ./conf:/etc/caddy:ro
          - caddy_data:/data
          - caddy_config:/config
        ports:
          - "80:80"
          - "443:443"
        networks:
          - caddy

    networks:
      caddy:
        external: true

    volumes:
      caddy_data:
      caddy_config:

    # 自备证书模式

    services:
      caddy:
        image: caddy:latest
        container_name: caddy
        restart: unless-stopped
        environment:
          - CADDYPATH=/etc/caddycerts
        volumes:
          - ./conf:/etc/caddy
          - ./certs:/etc/caddycerts
          - caddy_data:/data
          - caddy_config:/config
        ports:
          - "80:80"
          - "443:443"
        networks:
          - caddy

    volumes:
      caddy_data:
      caddy_config:
    networks:
      caddy:
        external: true

3. 编写Caddyfile，可以看到自动开启HTTPS模式：

    # 自动签发模式
    searxng.dich.bid {
        reverse_proxy searxng:8080 {
            header_up Host {upstream_hostport}
        }
    }

    miniflux.dich.bid {
        reverse_proxy miniflux:8080 {
            header_up Host {upstream_hostport}
        }
    }

    # 自备证书模式
    searxng.dich.bid {
        reverse_proxy searxng:8080
        tls /etc/caddycerts/cert.pem /etc/caddycerts/key.pem
    }
    miniflux.dich.bid {
        reverse_proxy miniflux:8080
        tls /etc/caddycerts/cert.pem /etc/caddycerts/key.pem
    }

4. Docker compose的用法不再赘述。

FAQ

- 使用nginx的docker版本而非apt安装的版本；
- 注意相对路径和绝对路径，不同容器可能冲突；
- 使用网络创建的方法简化了配置；
- 使用127.0.0.1:port的配置增加了安全性，无法ip访问；
- conf中的服务端口是compose中的port：port的后一个；
- 更改配置后需要删除现有的容器再生成；
- version字段可以不需要；
- 注意加上container_name；
- 每增加一个服务需要在nginx中更新volume；

Done.

Thanks for reading! Read other posts?

← 乱七八糟:服务器初始化与安全设置 乱七八糟:2024年度总结 →