优雅地让docker,nginx,acme.sh一起工作

前阵子买了个配置不错的阿里云,先搭了个Minecraft上去,Minecraft也跑在Docker里,并加载了DynMap显示实时地图。为了日后好扩展,方便上一些比较好看的页面,先用一个nginx反代一下,顺便挂上HTTPS,证书当然要从Letsencrypt搞。

看了下现有的方案,感觉都不太好看,都需要独立维护一个Dockerfile,不能直接用Nginx mainline,刷新证书的时候需要先关闭服务器,而且acme.sh和nginx跑在同一个容器里。想了一下,打算让acme.sh和nginx分开跑,nginx把challenge内容转发给acme.sh,这样应该就妥了,惟一的不好的地方是需要写个crontab,每隔2个月重启一下nginx的容器。不过其他方案也有downtime,也算可以接受。

目录结构和配置文件:

/
    acme.sh  -  SSL证书存放和acme帐号信息,配置信息
    nginx  -  自定义nginx配置存放的地方
        conf.d  -  一些nginx全局配置
        sites-enabled - 各个domain设置
        sites-conf  -  domain之间共用的设置

nignx/conf.d/acme.sh.conf

upstream acme-sh {
  server acme-sh:80;
}

nignx/conf.d/enabled-sites.conf

include /etc/nginx/sites-enabled/*.conf;

nginx/sites-conf/acme.sh.conf

location ~ "^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)$" {
    default_type text/plain;
    proxy_read_timeout    60;
    proxy_connect_timeout 60;
    proxy_redirect        off;
    proxy_pass http://acme-sh;

    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_set_header      Host $host;
}

nginx/sites-enabled/example.com.conf

server {
    server_name example.com;

    listen 0.0.0.0:443 ssl;
    listen [::]:443 ssl;

    ssl on;
    ssl_certificate /etc/ssl/letsencrypt/example.com/fullchain.cer;
    ssl_certificate_key /etc/ssl/letsencrypt/example.com/example.com.key;
    include sites-conf/acme.sh.conf;

    ...
}

server {
    server_name example.com;

    listen 0.0.0.0:80;
    listen [::]:80;

    include sites-conf/acme.sh.conf;

    location / {
        return 301 https://example.com$request_uri;
    }
}

第一次要先获取一个有效证书:

docker run --rm  -it  \
  -v "$(pwd)/acme.sh":/acme.sh  \
  --net=host \
  neilpang/acme.sh  --issue -d example.com  --standalone

使用docker-compose做容器管理,yml内容如下:

some-other-service:
    ...

nginx-srv:
    image: nginx:mainline
    volumes:
        - ./nginx/conf.d:/etc/nginx/conf.d
        - ./nginx/sites-conf:/etc/nginx/sites-conf
        - ./nginx/sites-enabled:/etc/nginx/sites-enabled
        - ./acme.sh:/etc/ssl/letsencrypt
    links:
        - some-other-service:upstream
        - acme-sh:acme-sh
    ports:
        - "443:443/tcp"
        - "80:80/tcp"

acme-sh:
    image: neilpang/acme.sh
    command: daemon
    volumes:
        - ./acme.sh:/acme.sh

调 docker-compose up就可以了。

最后加一个cronjob让nginx定期重新读取证书:

/var/spool/cron/root:

cd /path/to/docker-compose.yaml && /usr/bin/docker-compose exec nginx-srv nginx -s reload

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.