Docker 建站小记,我使用了四个镜像来搭建:nginx,certbot,mysql,gradle。欢迎访问:https://www.zzk0.tophtml
这个网页是从 github 上找的我的主页,背景用的是 bing 的壁纸,中间有两个连接,一个指向个人 github,一个指向博客园。后端项目目前只有一个接口,当有人访问这个网站的时候,就会向后端项目发出一个请求,由 nginx 负责将对 api.zzk0.top 的请求转发到后端项目。mysql
最理想的状态是,我直接 docker-compose up
,我就能够将项目运行起来了。不过个人方法,须要本身手动配置一下才行,一个是初始化申请 SSL 的脚本,一个是定时分割 NGINX 日志。以后就只须要 docker-compose up
就好了。nginx
这里简单讲讲这四个镜像分别是干什么的吧。git
修改了 NGINX 的配置,可是又不想从新启动容器。咱们能够经过向容器发送命令来加载新配置。github
# docker exec -it your-name nginx -s reload 2021/01/23 02:30:58 [notice] 23#23: signal process started
首先咱们须要证书和密钥,咱们可使用免费的 Let's Encrypt 来生成,不过三个月就会过时,因此须要刷新一下。web
证书的生成能够选择在本机上仍是在 Docker 里,若是在本机上生成,咱们能够选择 standalone 模式,可是这样的话,它须要短暂占用一下 80 端口来验证这个域名是不是你的。所以当你有一个正在运行的 NGINX 而且占用了 80 端口的话,咱们须要去暂停它。若是在 Docker 中生成,而且你的网站有一个根目录,那么可使用 webroot 模式,它会在你的网站根目录下生成一些文件,而后经过域名去访问它来验证这个域名是否是你的,使用 webroot 模式就不须要暂停 NGINX,所以对正在运行的网站影响会小一些。spring
选择前者的好处是,不须要定制 Docker,可是须要短暂中止服务器;选择后者的好处是,不须要中止服务器,可移植性更好一点,本机只须要有 Docker 就能跑,可是须要定制镜像比较麻烦。不过,这种比较常见的需求,咱们上 docker hub 搜一搜,一找就有了:https://hub.docker.com/r/staticfloat/nginx-certbot/ 。再搜一搜,咱们会发现另外一个方案:为何不搞一个 certbot 的容器呢?而后将 nginx 的容器和 certbot 的容器链接起来!好,那咱们就加多一个容器,来帮咱们生成 SSL 证书好了。sql
申请docker
这篇文章能够参考看看 certbot 的操做。这篇文章提供了 certbot 和 nginx 的方案。shell
从文章配套的 Github 仓库 中把它的脚本和配置文件复制粘贴过来,把全部的 example.org 域名改为本身的域名,而后运行它的脚本。若是失败了,建议检查一下是否是 NGINX 没有启动。这个脚本不会提醒你 NGINX 没有启动,若是失败了,颇有多是 NGINX 没有配置好。
一开始跑这个脚本,我失败了几回。以后逐步执行脚本,而后启动容器看看什么问题。一看,才知道原来是 https 的推荐参数文件没有下载好,因此手动下载了那几个文件,而后修改脚本为复制而不是下载。
重定向
nginx 将 http 重定向到 https,完整的配置文件能够看附录。
server { listen 80; server_name zzk0.top www.zzk0.top; server_tokens off; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name zzk0.top www.zzk0.top; root /var/www/html; server_tokens off; ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; location / { try_files $uri $uri/ =404; } }
假设有一个域名 test.com,咱们须要将对 api.test.com 的请求转发到 Tomcat,对 test.com 的请求则直接提供静态网站。
这个需求比较简单,在 server 下面设置好 server_name 就能够了。另外,咱们还须要注意不要让别人用 ip 访问咱们的网站,网上的说法是,别人能够恶意使用未备案的域名指向这个 ip,而后致使网站被封掉了。
server { listen 80 default_server; server_name _; return 403; } server { listen 443 default_server; server_name _; ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; return 403; }
为了记录网站的访问记录,可使用 NGINX 的访问日志来作记录,按天分割。NGINX 的镜像里面没有 crontab,因此这里就用主机来作。方法是天天定时将文件重命名,而后 reload NGINX。不过这个部分比较不智能,每次更换主机的时候,都须要手动去配置,还须要修改下面的路径。
#!/bin/sh LOGS_PATH=/root/home/data/nginx TODAY=$(date -d 'today' +%Y-%m-%d) mv ${LOGS_PATH}/error.log ${LOGS_PATH}/error_${TODAY}.log mv ${LOGS_PATH}/access.log ${LOGS_PATH}/access_${TODAY}.log docker exec -i home-nginx nginx -s reload
接下来,将如下内容加入到 /etc/crontab
中,这样就能够执行每日任务了。天天凌晨4点的时候,自动分割日志。
0 4 * * * root /root/home/nginx/daily_log.sh >> /root/home/data/nginx/daily_log.log 2>&1
有时候咱们须要进入数据库的镜像里面去看看数据。
# 进入镜像 docker exec -it your-name bash # 登陆 mysql -uroot -p # 罗列数据库 show database
后端项目使用 SpringBoot 来作。需求是:启动 Docker 后构建源码并运行。这个部分使用 gradle 的镜像,启动的时候,运行 gradle bootRun
就能够了。
当咱们更新了后端项目以后,咱们只须要重启这个容器,就能够构建了。个人后端项目容器名字叫作 springboot,重启一下就能够将接口更新了,不太重启过程当中的请求会失败。
docker restart springboot
下面是 docker-compose.yml 中的配置。
web: container_name: springboot restart: always image: gradle:6.7.1-jdk8 depends_on: - db volumes: - ./api:/home/gradle/project environment: TZ : 'Asia/Shanghai' command: bash -c "cd /home/gradle/project && gradle bootRun"
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; upstream tomcat { server web:8080; } log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; limit_req_zone $binary_remote_addr zone=api_limit_req:10m rate=30r/m; sendfile on; keepalive_timeout 65; gzip on; include /etc/nginx/conf.d/*.conf; # forbid access via ip address server { listen 80 default_server; server_name _; return 403; } server { listen 443 default_server; server_name _; ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; return 403; } # configure zzk0.top, redirect to https and serve static files server { listen 80; server_name zzk0.top www.zzk0.top; server_tokens off; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name zzk0.top www.zzk0.top; root /var/www/html; server_tokens off; ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; location / { try_files $uri $uri/ =404; } } # configure api.zzk0.top, backend api server { listen 80; server_name api.zzk0.top; server_tokens off; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name api.zzk0.top; server_tokens off; ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; location / { limit_req zone=api_limit_req; proxy_pass http://tomcat; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }
version: '3' services: certbot: container_name: home-certbot restart: always image: certbot/certbot volumes: - ./data/certbot/conf:/etc/letsencrypt - ./data/certbot/www:/var/www/certbot entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 720h & wait $${!}; done;'" nginx: container_name: home-nginx restart: always image: nginx:1.18.0 ports: - 80:80 - 443:443 depends_on: - web links: - web:web environment: TZ : 'Asia/Shanghai' volumes: - ./html:/var/www/html - ./nginx/nginx.conf:/etc/nginx/nginx.conf - ./data/certbot/conf:/etc/letsencrypt - ./data/certbot/www:/var/www/certbot - ./data/nginx:/var/log/nginx command: "/bin/sh -c 'while :; do sleep 720h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'" db: container_name: home-db restart: always image: mysql:8.0.13 ports: - 7706:3306 volumes: - ./data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: backend_database MYSQL_USER: root MYSQL_PASSWORD: root web: container_name: springboot restart: always image: gradle:6.7.1-jdk8 depends_on: - db volumes: - ./api:/home/gradle/project environment: TZ : 'Asia/Shanghai' command: bash -c "cd /home/gradle/project && gradle bootRun"