docker搭建nginx负载均衡

news/2024/7/7 20:59:50

一点小背景

docker起了几个服务,没有配置端口映射,导致不能通过网络访问。当然,更简单的方式是加端口映射,笔者的情况更复杂一些,就想到了用nginx映射一下。

Nginx(发音同“engine X”)是异步框架的网页服务器,也可以用作反向代理、负载平衡器和HTTP缓存。

—— https://zh.wikipedia.org/wiki/Nginx

第一个demo

最简单启动

docker run -itd --name tnginx -p 10010:80 nginx

访问一下

wtnginx

静态网页

nginx默认读取/usr/share/nginx/html下面的静态文件

创建1个存放静态文件的文件夹映射给容器

mkdir html

echo "<h1>Hello Nginx!</h1>" > ./html/index.html		# 创建1个静态页面

# docker rm -f tnginx		# 删掉之前存在的,否则会冲突
docker run -itd --name tnginx -p 10010:80 -v "$PWD/html":/usr/share/nginx/html nginx

映射物理机文件的好处在于即使容器停止或者被清理,再次启动映射相同的文件还能恢复到同一状态。修改物理机文件也会在容器内生效。后续的配置修改只需要修改物理机上的配置文件,然后重启nginx容器即可docker restart tnginx

再访问一下

hnginx

配置文件

默认读/etc/nginx/文件夹下的nginx配置文件,咱也不知道长什么样,从docker容器中捞一个出来

docker cp tnginx:/etc/nginx ./

用的是nginx/nginx.conf配置文件

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    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;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

改一下静态文件的目录地址,在配置文件里http配置中添加以下配置

http {
    ...
    server {
    	listen 80 default_server;
    	root /www/html;
    	index index.html;
    }
}

映射配置文件和静态文件的目录

# docker rm -f tnginx		# 删掉之前存在的,否则会冲突
docker run -itd --name tnginx -p 10010:80 -v "$PWD/html":/www/html -v "$PWD/nginx":/etc/nginx nginx

再访问一下http://192.168.9.109:10010/仍然是work的

测试服务

为了直观体现下文操作的效果,用python简单起个http服务,不感兴趣可跳过~

# service.py
from flask import Flask, request
import multiprocessing
from multiprocessing import Process

multiprocessing.set_start_method("fork")  # 防止Process的pickle报错

class MyFlask(Flask):
    def getResponse(self):
        return "{}_{}> {}".format(self.name, multiprocessing.current_process().name, request.base_url), \
               200, {'Access-Control-Allow-Origin': '*'}    # 最后的header避免fetch访问时跨域报错

app1 = MyFlask('service_1')

@app1.route('/hello')
@app1.route('/user/add')
@app1.route('/user/get')
def service1_handler():
    return app1.getResponse()

app2 = MyFlask('service_2')

@app2.route('/book/add')
@app2.route('/book/get')
def service2_handler():
    return app2.getResponse()

if __name__ == '__main__':
    Process(target=app1.run, args=('0.0.0.0', 10001)).start()
    Process(target=app1.run, args=('0.0.0.0', 10002)).start()
    Process(target=app2.run, args=('0.0.0.0', 20001)).start()

Process-1、Process-2启动了app1,Process-3启动了app2

做了个页面直观一点看看响应

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
  <script src="https://cdn.staticfile.org/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<form class="form-inline">
  <button type="button" class="btn btn-primary" onclick="visit()">访问</button>
  <select class="form-control" id="s_url">
    <option><span>http://192.168.9.109:10010/hello</span></option>
    <option><span>http://192.168.9.109:10010/user/add</span></option>
    <option><span>http://192.168.9.109:10010/user/get</span></option>
    <option><span>http://192.168.9.109:10010/book/add</span></option>
    <option><span>http://192.168.9.109:10010/book/get</span></option>
  </select>
  <div class="form-group">
    <input type="text" id="num" class="form-control" style="width:50px" value="1">
  </div>
  <span></span>
</form>

<div id="result">
  <div>=================================================</div>
</div>
<script type="text/javascript">
  function visit(){
    var url=$("#s_url option:selected").val();
    var num=$("#num").val();
    for (var i=0;i<num;i++)
    {
        fetch(url)
          .then(function(data) {
              return data.text();
          }).then(function(data) {
              $("#result").append("<div>"+data+"</div>");
        });
    }
  }
</script>
</body>
</html>

效果如下
在这里插入图片描述

路由规则

规则优先更精准的匹配

http {
	...
	server {
    ...
    location / {	# 未被其他配置规则命中的走这里
    	proxy_pass  http://192.168.9.109:10001;		# app1(Process-1)
    }
    location /book {	# /book 开头的走这里
    	proxy_pass  http://192.168.9.109:20001;		# app2(Process-2)
    }
  }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
/book/add路由到了Process-3上的app2,/user/add路由到了默认去了app1,应为没有配置app2的/hello/hello去访问了app1

负载均衡

不加额外配置是轮询的

http {
	upstream myservice {	# 这里是负载均衡的配置
	    server 192.168.9.109:10001;		# 不加其他配置默认是轮询的
	    server 192.168.9.109:10002;
	}
  
	server {
	    ...
	    location / {
	    	proxy_pass  http://myservice;		# 给server指定proxy到下面的 myservice
	    }
    }
}

在这里插入图片描述

更改配置按权重选择

  upstream myservice {	# 这里是负载均衡的配置
    server 192.168.9.109:10001 weight=4;		# 配置权重
    server 192.168.9.109:10002 weight=1;
  }

weight

其他配置

  upstream myservice {
    server 192.168.9.109:10001 weight=4 max_fails=3 fail_timeout=15;
    server 192.168.9.109:10002 backup;		# 备份,所有不可用才会生效
    server 192.168.9.109:10003 down;		# 标识不可用	
    server 192.168.9.109:10004 max_conns=1000;
  }

其他

设置header

经由nginx转发以后,客户端的信息变成nginx的信息,可以通过设置header把客户端的信息传过去

   location / {
   		...
    	proxy_set_header Host $http_host;		# nginx的ip:port
    	proxy_set_header X-Upstream-Addr $upstream_addr;	# 理应是真实服务的ip:port,但是我这里没生效
    	proxy_set_header X-Real-IP $remote_addr;	# 发起请求的ip
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;	# 发起请求的ip
   }

内置变量

如上使用的$http_host是nginx的内置变量,更多可参见

http://nginx.org/en/docs/http/ngx_http_core_module.html#variables

内置变量还可以在nginx的输出日志中使用,即

http {
    ...

    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;
    ...
}

参考

配置路由的 https://www.cnblogs.com/jackylee92/p/6836948.html

负载均衡的策略 https://www.cnblogs.com/itzgr/p/13330613.html

https://www.ruanyifeng.com/blog/2018/02/nginx-docker.html

upsteam_addr http://nginx.org/en/docs/http/ngx_http_upstream_module.html#variables


http://lihuaxi.xjx100.cn/news/1126216.html

相关文章

课程《JavaWeb基础框架程序设计》考试题下篇——数据库与表单操作用题(人事管理平台的添加员工档案信息的操作题)

文章目录 &#x1f4cb;前言&#x1f3af;第三题&#xff08;40分&#xff09;&#x1f3af;报错以及解决方法&#x1f4dd;最后 &#x1f4cb;前言 这篇文章是大学课程《JavaWeb基础框架程序设计》考试题目的内容&#xff0c;包括了原题和答案。题目只包括了三道编程题&#…

React事件委托

React 事件委托&#xff08;Event Delegation&#xff09;是一种优化事件处理的技术&#xff0c;它通过将事件监听器添加到父级元素&#xff08;而不是子元素&#xff09;来实现。当事件触发时&#xff0c;事件会向上冒泡到父元素&#xff0c;然后在父元素上调用事件处理函数。…

power shell 入门

常用命令 powershell版本 不区分大小写: $psversiontable $psv + tab 自动补齐 // ps version get-command 或者 gcm //获取所有命令 get-help 命令名 // 获取命令帮助信息 路径相关 get-location 或者 gl 或者 pwd // 获取当前路径 pwd: print work directory 的缩写.

linux 下 ps、sort、top 命令详解

1、 ps命令 作用&#xff1a;查看系统进程&#xff0c;比如正在运行的进程有哪些&#xff0c;什么时候开始运行的&#xff0c;哪个用户运行的&#xff0c;占用了多少资源。 参数&#xff1a; -e 显示所有进程 -f 显示所有字段&#xff08;UID&#xff0c;PPIP&#xff0c;C…

内网渗透(六十九)之域权限维持之SID History 滥用

域权限维持之SID History 滥用 SID是用于标识主体的可变长度的唯一值,每个账户都有一个系统颁发的唯一SID,其存储在数据库(域中的话就存储在活动目录数据库中)。用户每次登录时,系统都会从数据库中检索该用户的SID,并将其放在该用户的访问令牌中。系统使用访问令牌中的S…

Hacking The Box----Awkward

信息收集 nmap扫描&#xff0c;发现22号端口和80号端口打开&#xff0c;80号端口上运行着http服务器。访问ip后URL变为hat-valley.htb 修改/etc/hosts文件&#xff0c;添加10.10.11.185 hat-valley.htb&#xff0c;然后就能正常访问网站。可以看到是一家卖帽子的公司的网站&a…

[元带你学NVMe协议] NVMe1.4 IO determinism | NVM Sets | Endurance Group 详解

声明 主页:元存储的博客_CSDN博客 依公开知识及经验整理,如有误请留言。 个人辛苦整理,付费内容,禁止转载。 内容摘要 全文2200字, 主要内容有 目录 前言 1 NVM Sets 1.1 概念 1.2 NVM set 与 namespace

Vue3+cropperjs 实现图片裁剪功能

安装cropperjs npm install cropperjs --save 或者 yarn add cropperjs封装一个cropperImg组件 //cropperImg.vue <template><div><!--使用ref属性给图片元素命名为imageRef--><img ref"imageRef" :src"imageSrc" alt"image&q…