docker4-nginx容器反向代理springboot
docker运行nginx容器
快速安装运行
- docker-hub文档
https://hub.docker.com/_/nginx - 拉取nginx1.6.0
docker pull nginx:1.16.0
- 快速运行
docker run --name mynginx -d -p 80:80 ae893c58d83f
- 外部访问80端口验证
关键文件目录挂载
- 进入容器内部查看配置目录
docker exec -it mynginx /bin/bash
默认的关键目录位置:- 主(配置)目录 /etc/nginx
- 页面目录 /usr/share/nginx/html
- 日志目录 /var/log/nginx
- 新运行容器,挂载上面目录容器数据卷
docker run --name mynginx -d -p 80:80 \ -v /usr/local/dcv/nginx/html:/usr/share/nginx/html \ -v /usr/local/dcv/nginx/conf:/etc/nginx \ ae893c58d83f
- 运行失败:
本地fs有挂载目录,但是
docker ps
没有记录。
查看容器运行日志docker logs mynginx
:
2019/09/05 03:03:46 [emerg] 1#1: open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
nginx: [emerg] open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
容器内目录原有的东西被外部fs同步覆盖清空了..=_= - 解决:先从容器拷贝出需要的信息,然后子目录挂载
- 先简单运行一个容器,从其中拷贝出配置文件和页面的目录文件
//运行容器 docker run --name mynginx -d -p 80:80 ae893c58d83f docker ps //本地fs建立相关目录 mkdir -p /usr/local/dcv/nginx/conf mkdir -p /usr/local/dcv/nginx/html mkdir -p /usr/local/dcv/nginx/log //从容器内copy文件和目录到宿主机 docker cp mynginx:/etc/nginx/nginx.conf /usr/local/dcv/nginx/conf docker cp mynginx:/etc/nginx/conf.d /usr/local/dcv/nginx/conf docker cp mynginx:/usr/share/nginx/html /usr/local/dcv/nginx docker cp mynginx:/var/log/nginx /usr/local/dcv/nginx/log //移动外部日志目录 cp -rf $(pwd)/log/nginx/* $(pwd)/log rm -rf $(pwd)/log/nginx
- 重新挂载运行
docker rm -f mynginx //新挂载运行一个容器 docker run --name mynginx -d -p 80:80 \ -v /usr/local/dcv/nginx/html:/usr/share/nginx/html \ -v /usr/local/dcv/nginx/log:/var/log/nginx \ -v /usr/local/dcv/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ -v /usr/local/dcv/nginx/conf/conf.d:/etc/nginx/conf.d \ ae893c58d83f //验证 docker ps
- 宿主机修改index.html 外部访问81验证效果
- 先简单运行一个容器,从其中拷贝出配置文件和页面的目录文件
nginx容器代理宿主机的springboot服务(即tomcat)
简单的springboot包
- 新建一个极简的springboot应用,只有一个/hello接口返回JSON串,其他默认
@RestController public class HellowCtrller { @ResponseBody @RequestMapping("/hello") public String hello() { return "hello docker @" + LocalDateTime.now(); } }
- cmd到pom.xml目录,mvn package 打jar包,上传宿主机hello.jar
- 宿主机运行hello.jar
nohup java -jar hello-0.0.1-SNAPSHOT.jar &
- 宿主机8080访问验证
curl http://localhost:8080/hello
,OK
nginx代理配置
- nginx容器内访问宿主机ip的问题
- 直接在nginx容器内配置localhost:8080是访问不到外部tomcat的
- 因为nginx容器有自己的ip,外部使用 docker inspect mynginx可以看到如下信息:
"Gateway": "172.17.0.1", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "172.17.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "MacAddress": "02:42:ac:11:00:02", "Networks": { "bridge": { "IPAMConfig": null, "Links": null, "Aliases": null, "NetworkID": "77b4c6b988b085d697f896ec4d19493294c0296dc99e20e6492b2cf79d7a6682", "EndpointID": "3933c9ccb7de29d19d82247bd1c0b4a5ed971e505fdce4455acb0df3d48a64b1", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:02", "DriverOpts": null } }
- 其中的IPAddress就是容器自己的ip,Gateway则是外部docker安装是产生的虚拟网关docker0,外部使用 ip addr可以看到。
- 通过docker0的地址(即上面Gateway地址),容器内可以访问宿主机
- docker0网关的地址值在Linux上一般是固定的172.17.0.1,Mac系统则不同,移植时需要注意下。
- 修改/usr/local/dcv/nginx/conf/conf.d下的 default.conf
#后端负载集群 upstream hello_server { ip_hash; #session绑定节点 server 172.17.0.1:8080 weight=1; #server 172.17.0.1:8081 weight=1; #keepalive 100; #for长连接 } server { listen 80; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } location /api/ { proxy_pass http://hello_server/; #for 后端获取远程客户端真实ip proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #自定义添加header #proxy_set_header applicationto blog; #proxy_http_version 1.1; #for后端返回状态拦截 >> 后端404自定义错误页面 #proxy_intercept_errors on; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
- nginx容器内验证配置+重启
docker exec -t mynginx nginx -t docker exec -t mynginx nginx -s reload
- 外部访问验证
curl http://localhost/api/hello
nginx容器代理springboot容器服务
制作springboot镜像运行
- 简单的DockerFile文件(为方便名字就叫DockerFile)
FROM java:8 add hello-0.0.1-SNAPSHOT.jar app.jar expose 8080 entrypoint ["java","-jar","/app.jar"]
- build该文件为镜像 hello_img
docker build -t hello_img .
- 运行两个hello容器 端口分别8080和8081
docker run -d --name hello1 -p 8080:8080 hello_img docker run -d --name hello2 -p 8081:8080 hello_img
- 访问两个hello容器服务
curl http://localhost:8080/hello curl http://localhost:8081/hello
nginx配置代理访问hello容器
- 运行nginx容器时使用 docker run 的--link参数传递hello容器ip
- 重新运行mynginx容器
//停掉 docker stop mynginx docker rm mynginx //加--link运行 docker run --name mynginx \ --link=hello1:hello1ip --link=hello2:hello2ip \ -v /usr/local/dcv/nginx/html:/usr/share/nginx/html \ -v /usr/local/dcv/nginx/log:/var/log/nginx \ -v /usr/local/dcv/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ -v /usr/local/dcv/nginx/conf/conf.d:/etc/nginx/conf.d \ -p 80:80 -d ae893c58d83f //查看 docker ps
- 修改 default.conf 中的后端负载集群配置
upstream hello_server { ip_hash; #session绑定节点 server hello1ip:8080 weight=1; server hello2ip:8081 weight=1; #keepalive 100; #for长连接 }
- 验证重启nginx
docker exec -t mynginx nginx -t docker exec -t mynginx nginx -s reload
- 外部访问验证
curl http://localhost/api/hello
OK!
碰到的问题
外部访问虚拟机的springboot8080端口
关掉防火墙:
直接关闭防火墙: systemctl stop firewalld.service
禁止firewall开机启动: systemctl disable firewalld.service
重启docker(不然后面可能有问题):systemctl restart docker
容器内无法使用ping
apt-get update
apt install iputils-ping #for ping
//apt install net-tools #for ifconfig
容器中nginx访问日志问题
- docker logs mynginx 命令 stdout 出来的时间不对
- 原因:原生容器系统的时区为0时期,而国内系统为东八区,相差8小时
- 其他容器估计也会有这个问题
- 解决,关键要使容器时区和宿主机一致
主参考:https://blog.lqdev.cn/2018/07/15/docker/docker-time-diff/- 运行容器时挂载宿主机的时区
-v /etc/localtime:/etc/localtime:ro
- 运行后cp宿主机的时区到容器
docker cp /etc/localtime mynginx:/etc/localtime
执行出错=_=:具体原因还不知道,应该是容器内已经有的文件/夹不能通过docker cp覆盖。。还是使用第一种挂载吧
Error response from daemon: Error processing tar file(exit status 1): invalid symlink "/usr/share/zoneinfo/UCT" -> "../usr/share/zoneinfo/Asia/Shanghai"
- 使用DockerFile,生产上,建议有一个base_image,里面直接定义好时区等信息,后面的
from base_img
构建官方镜像,DockerFile待有时间单独整理文章。
- 运行容器时挂载宿主机的时区
感受
- 咔咔咔一顿操作下来(DockerFile待深入),使用体验并没有预期的辣么爽。
- 环境一层套一层,碰到问题和疑惑也就多了一个层次,在细节效果配置时难免会碰到一些壁垒(比如nginx更复杂的配置),解决下来的时间成本不小。
- 所以,在公司或者个人开发上没有docker刚需时,不用急着整治,但是整套环境打包镜像,解决换环境又得重新安装、环境配置的烦恼还是很爽的(比如mysql)。