Linux网络命名空间

引言 Namespaces(命名空间)和cgroups是两种主要的内核技术,他们是容器化技术的基石。简而言之,cgroup是一种计量和限制机制,它能控制你可以使用多少系统资源(CPU、内存)。另一方面,Namespaces限制了你所看到的内容。得益于Namespaces,进程有其独立的系统资源视图。 Linux内核提供了6种类型的Namespaces:pid、net、mnt、uts、ipc、user。例如,pid命名空间的进程只能看到同一个命名空间的进程。使用mnt命名空间,可以将进程附加到其自己的文件系统(就像chroot)。在本文中我们仅关注网络命名空间。 网络命名空间为命名空间内的所有进程提供了全新的网络堆栈。其中包括网络接口,路由表和iptables规则。 网络命名空间 从系统的角度来看,当通过clone()系统调用创建新进程时,传递标志CLONE_NEWNET将在新进程中创建一个全新的网络命名空间。从用户的角度来看,我们仅使用工具ip(软件包为iproute2)来创建新的持久网络命名空间: $ ip netns add ns1 此命令将创建一个名为ns1的新网络命名空间。创建命名空间后,ip命令会在/var/run/netns下为其添加绑定挂载点。这样,即使没有附加任何进程,命名空间也可以保留。列出系统中可用的命名空间: $ ls /var/run/netns ns1 或通过ip: $ ip netns ns1 如前所述,网络命名空间包含其自己的网络资源:接口,路由表等。让我们向ns1添加回环接口: 1 $ ip netns exec ns1 ip link set dev lo up 2 $ ip netns exec ns1 ping 127.0.0.1 3 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 4 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.115 ms 第1行在网络命名空间ns1内部启用回环接口。 第2行在网络命名空间内部执行命令ping 127.0.0.1。 启用回环接口的另一种语法可以是: $ ip netns exec ns1 ifconfig lo up 废弃旧的但更熟悉ifconfig,route等命令,我倾向于使用命令ip,因为它已经成为Linux的首选网络工具。请注意,ip需要root权限,因此请以root身份或前置sudo运行。 网络命名空间也有自己的路由表: $ ip netns exec ns1 ip route show 由于我们尚未添加任何路由表规则,因此该命令什么都不返回。一般而言,在网络命名空间中运行的任何命令都由以下内容开头: ...

March 20, 2021

设计模式之策略模式(Strategy)

策略模式的书面解释:定义算法簇,分别封装起来,让它们之间可以互相替换。此模式让算法的变化独立于使用算法的客户。 策略模式的通俗解释:通过抽象、封装,实现某种算法/策略的动态替换。 模式组成 环境(Context)角色:持有一个Strategy的引用。 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。 应用场景 自定义排序 游戏角色行为策略 通信协议中的封包与拆包算法 实现方式 动物的自定义排序问题 设计动物游戏中,需要对动物进行排序显示,各类动物具有不同的比较算法,比如猫可以根据身高或者体重进行排序。 示例实现代码 定义动物:Cat public class Cat { int weight, height; public Cat(int weight, int height) { this.weight = weight; this.height = height; } @Override public String toString() { return "Cat{" + "weight=" + weight + ", height=" + height + '}'; } } 定义抽象策略角色:Comparator public interface Comparator<T> { int compare(T o1, T o2); } 定义具体策略角色1:CatWeightComparator public class CatWeightComparator implements Comparator<Cat> { @Override public int compare(Cat o1, Cat o2) { if(o1.weight < o2.weight) return -1; else if (o1.weight > o2.weight) return 1; else return 0; } } 定义具体策略角色2:CatHeightComparator ...

July 30, 2020

设计模式之单例(Singleton)的七种实现

单例是设计模式中最经典最简单的设计模式,但是它却有至少七种实现方式,你真的会单例吗? 应用场景:只需要一个实例 比如各种Mgr 比如各种Factory 实现方式 基本思想:私有化构造函数,提供公共的静态方法getInstance获取实例,保证实例唯一性 1. 饿汉式:不使用也会在JVM装载类时占用资源 使用final声明INSTANCE private static final Mgr02 INSTANCE 声明时初始化实例 或 静态代码块中初始化实例 public class Mgr01 { private static final Mgr01 INSTANCE = new Mgr01(); private Mgr01() {}; public static Mgr01 getInstance() { return INSTANCE; } public void m() { System.out.println("m"); } public static void main(String[] args) { Mgr01 m1 = Mgr01.getInstance(); Mgr01 m2 = Mgr01.getInstance(); System.out.println(m1 == m2); } } 2. 懒汉式:线程不安全 在获取前实例前,如果为空则初始化 public class Mgr03 { private static Mgr03 INSTANCE; private Mgr03() { } public static Mgr03 getInstance() { if (INSTANCE == null) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Mgr03(); } return INSTANCE; } public void m() { System.out.println("m"); } public static void main(String[] args) { for(int i=0; i<100; i++) { new Thread(()-> System.out.println(Mgr03.getInstance().hashCode()) ).start(); } } } 3. synchronized方法+懒汉式:效率降低 4. synchronized代码块+懒汉式:不可行 5. volatile+双重检查(DCL):实现过于复杂 public class Mgr06 { private static volatile Mgr06 INSTANCE; //JIT private Mgr06() { } public static Mgr06 getInstance() { if (INSTANCE == null) { //双重检查 synchronized (Mgr06.class) { if(INSTANCE == null) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Mgr06(); } } } return INSTANCE; } public void m() { System.out.println("m"); } public static void main(String[] args) { for(int i=0; i<100; i++) { new Thread(()->{ System.out.println(Mgr06.getInstance().hashCode()); }).start(); } } } 6. 静态内部类:懒加载(JVM保证唯一性) public class Mgr07 { private Mgr07() { } private static class Mgr07Holder { private final static Mgr07 INSTANCE = new Mgr07(); } public static Mgr07 getInstance() { return Mgr07Holder.INSTANCE; } public void m() { System.out.println("m"); } public static void main(String[] args) { for(int i=0; i<100; i++) { new Thread(()->{ System.out.println(Mgr07.getInstance().hashCode()); }).start(); } } } 7. 枚举:不仅可以解决线程同步,还可以防止反序列化 public enum Mgr08 { INSTANCE; public void m() {} public static void main(String[] args) { for(int i=0; i<100; i++) { new Thread(()->{ System.out.println(Mgr08.INSTANCE.hashCode()); }).start(); } } } 总结 单例设计模式存在多种实现方式,深入研究好似孔乙己的“回”字不同写法,作为一个实用的程序员,不要忘了设计模式的初衷。 ...

July 30, 2020

ssh隧道

何谓SSH隧道 隧道是把一种网络协议封装进另外一种网络协议进行传输的技术。这里我们研究ssh隧道,所以所有的网络通讯都是加密的。又被称作端口转发,因为ssh隧道通常会绑定一个本地端口,所有发向这个端口的数据包,都会被加密并透明地传输到远端系统。 SSH隧道的类型 ssh隧道有3种类型: 动态端口转发(Sockets 代理) 本地端口转发 远端端口转发 动态端口转发 动态端口允许通过配置一个本地端口,通过隧道将数据转发到远端的所有地址。本地的应用程序需要使用Socks协议与本地端口通讯。此时SSH充当Socks代理服务器的角色。 命令格式 ssh -D [bind_address:]port 参数说明 bind_address 指定绑定的IP地址,默认情况会绑定在本地的回环地址(即127.0.0.1),如果空值或者为*会绑定本地所有的IP地址,如果希望绑定的端口仅供本机使用,可以指定为localhost。 port 指定本地绑定的端口 使用场景 假设X网络(192.168.18.0/24)有主机A(192.168.18.100),Y网络(192.168.2.0/24)有主机B(192.168.2.100)和主机C(192.168.2.101),已知主机A可以连接主机B,但无法连接主机C。 在主机A执行 ssh -D localhost:8080 root@192.168.2.100 然后主机A上的应用程序就可以通过 SOCKS5 localhost:8080 访问主机C上的服务 优点:配置一个代理服务就可以访问远端机器和与其所在子网络的所有服务 缺点:应用程序需要额外配置SOCKS代理,若应用程序不支持代理配置则无法使用 本地端口转发 通过SSH隧道,将一个远端机器能够访问到的地址和端口,映射为一个本地的端口。 命令格式 ssh -L [bind_address:]port:host:hostport 参数说明 bind_address 指定绑定的IP地址,默认情况会绑定在本地的回环地址(即127.0.0.1),如果空值或者为*会绑定本地所有的IP地址,如果希望绑定的端口仅供本机使用,可以指定为localhost。 port 指定本地绑定的端口 host 指定数据包转发目标地址的IP,如果目标主机和ssh server是同一台主机时该参数指定为localhost host_port 指定数据包转发目标端口 使用场景 假设X网络(192.168.18.0/24)有主机A(192.168.18.100),Y网络(192.168.2.0/24)有主机B(192.168.2.100)和主机C(192.168.2.101),已知主机A可以连接主机B,但无法连接主机C。A主机需要访问C主机的mysql服务(3306) 在A主机上建立本地转发端口33306 ssh -L 192.168.18.100:33306:192.168.2.101:3306 root@192.168.2.100 然后本地mysql客户端通过33306端口访问c主机的mysql服务 mysql -h 192.168.18.100 -P33306 -p 注意mysql账号的权限,源地址为主机B的地址。 优点:无需设置代理 缺点:每个服务都需要配置不同的端口转发 远端端口转发 远程端口转发用于某些单向阻隔的内网环境,比如说NAT,网络防火墙。在NAT设备之后的内网主机可以直接访问公网主机,但外网主机却无法访问内网主机的服务。如果内网主机向外网主机建立一个远程转发端口,就可以让外网主机通过该端口访问该内网主机的服务。可以把这个内网主机理解为“内应”和“开门者”。 命令格式 ssh -R [bind_address:]port:host:hostport 参数说明 bind_address 指定绑定的IP地址,默认情况会绑定在本地的回环地址(即127- .0.0.1),如果空值或者为*会绑定本地所有的IP地址,如果希望绑定的端口- 仅供本机使用,可以指定为localhost。 port 指定本地绑定的端口 host 指定数据包转发源地址的IP,如果源主机和ssh - server是同一台主机时该参数指定为localhost host_port 指定数据包转发源端口 使用场景 假设X网络(192.168.18.0/24)有主机A(192.168.18.100),Y网络(192.168.2.0/24)有主机B(192.168.2.100)和主机C(192.168.2.101),已知主机A可以通过SSH访问登录B主机,但反向直接连接被禁止,主机B和主机C可以相互访问。若主机C想访问主机A的mysql服务(3306端口)。 ...

July 11, 2020

Tampermonkey脚本开发入门-xx网课挂机插件开发

Tampermonkey简介 Tampermonkey(油猴子)是一款免费的浏览器扩展和用户脚本管理器,它适用于 Chrome, Microsoft Edge, Safari, Opera Next, 和 Firefox。 Tampermonkey最常见的用途就是网盘限速突破、视频网站下载工具、各类网课挂机等,文本通过实际开发示例介绍xx网课挂机插件的开发过程。 开发/运行环境搭建 Chrome浏览器因网络原因,安装插件不太方便,建议使用360极速浏览器。 安装360极速浏览器 下载地址:https://browser.360.cn/ee/ 安装Tampermonkey插件 使用360极速浏览器打开:插件链接 根据提示安装插件。 安装脚本 使用360极速浏览器打开 greasyfork 寻找自己喜欢的脚本,根据提示安装即可。 开发示例 本示例参考实际案例,介绍xx网课系统挂机脚本的开发过程。 实现原理 该网课系统防挂机机制很简单,每隔15分钟弹出一个alert确认框,用户需手动点击确认后才能继续学习。 查看js代码发现,15分钟间隔时间是由一个js全局变量dingshi控制的,于是我们利用脚本修改该变量的值即可实现挂机。 脚本编写 添加新脚本 浏览器右上角点击插件,选择添加新脚本 编辑器中编写脚本 // ==UserScript== // @name xx网课-挂机插件 // @namespace http://notes.stepin.cn // @version 0.2 // @description xx网课-免15分钟弹窗 // @author stepin // @include http://www.xx.com/* // @run-at document-end // ==/UserScript== (function () { if(typeof(dingshi) === 'undefined'){ }else{ var longDS = 3100 confirm("开始挂机,修改参数dingshi:弹窗时间由"+dingshi+"秒变为"+ longDS+"秒") dingshi = longDS } })(); @include http://www.xx.com/*需替换为实际的xx网课网站地址 ...

May 24, 2020

Vue+axios+Nginx接口地址多环境配置抽离

背景 新加入一个项目,发现前端同事每次打包Vue工程时都要检查调用后台的接口地址是否正确,不然部署到生产环境却调用测试环境接口就尴尬了。当时就好奇,难道前端不能自适应环境吗? 如果能做到一次打包所有环境通用,既提高了效率,也能满足测试的基本要求,测试过的物件就是发布所需的物件。 解决方案 相对路径 其实解决这个问题,使用相对路径是最直接的方法,vue项目使用axios组件进行http接口调用,忽略baseURL参数,url以/开头,实际的请求地址就是<scheme>://<host>:<port><url> axios({ //请求方式为post method: 'post', //绝对路径 url: '/pic/', //`baseURL`将自动加在`url`前面,除非`url`是一个绝对URL。 baseURL: '', //data等设置省略 ...... }); 例如后台登录接口地址为http://demo.com/api/login,无论前端部署在http://demo.com/还是http://demo.com/adm上,url参数指定为/api/login,最终请求的地址都是网站根路径+/api/login,即:http://demo.com/api/login proxyTable 前端同学很多实际开发过程中,调用接口是跨域调用后台接口,而且需要制定绝对的路径 这样配置url为相对路径就无法满足开发过程中的需求。 其实使用webpack的proxyTable即可 dev: { proxyTable: { '/api': { target: 'http://demo.com', //目标接口域名 changeOrigin: true, //是否跨域 pathRewrite: { '^/api': '/api' //重写接口 } }, } Nginx抽离配置 基于以上的了解,我们可以在nginx中定义所有后台接口都以某个固定的前缀开头,例如/api location ^~ /api/ { proxy_pass http://web/; } 项目中在static目录使用单独的配置文件config.js,定义这个前缀 config={apiBase:'/api'} 请求时使用前缀拼接接口地址 axios.post(config.apiBase + "/login", data); Nginx中根据需要自定义config.js的内容 #测试/生产环境 location = /static/config.js { default_type text/html; return 200 "config={apiBase:'/api'}"; } #后台开发独立自验环境,${name}为某个开发人员的姓名拼音 location = /static/config.js { default_type text/html; return 200 "config={apiBase:'/${name}/api'}"; } #代理开发人员本地的服务 location ^~ /${name}/api/ { proxy_pass http://${name}_web/; } 总结 使用相对路径+配置文件解决接口地址可配置的问题,使用nginx固定配置文件内容,通过nginx返回不同的内容适配不同的环境。 ...

May 1, 2020

NatApp+OpenResty+Redis实现内网穿透自主IP授权

使用NatApp均能解决内网IP+端口映射到公网IP+端口的问题,但是无法控制访问来源,如果你的系统比较敏感又需要控制访问源,则可以使用NATAPP+OpenResty+Redis的方案实现内网穿透的自主IP授权。 内网穿透简介 内网穿透简单来说就是将内网外网通过应用级的隧道打通,让内网的数据让外网可以获取。 有如下典型的使用场景 本地开发环境内网穿透 支付回调 单点登录回调 数据推送 公司内网穿透 访问内网的办公软件 临时web系统演示 内部gitlab私密共享 家庭内网穿透 远程控制放在家里的树莓派,服务器 NATAPP实现内网穿透 NATAPP使用参加官网文档即可,不是本文介绍重点 NATAPP教程/文档 OpenResty+Redis实现自主IP授权 以Centos 7为例,配置步骤如下 Redis安装 yum install redis -y OpenResty安装 #安装编译依赖 yum install pcre-devel openssl-devel gcc curl -y #下载openresty源码包 wget https://openresty.org/download/openresty-1.15.8.2.tar.gz #解压源码包 tar -xzvf openresty-1.15.8.2.tar.gz #编译安装 cd openresty-1.15.8.2 ./configure gmake && gmake install ln -sf /usr/local/openresty/nginx/sbin/nginx /sbin/nginx #设置配置文件软链接 ln -s /usr/local/openresty/nginx/conf/nginx.conf /etc/nginx.conf sockproc 安装 lua调用shell命令需要 git clone https://github.com/juce/sockproc cd sockproc make cp sockproc /sbin/sockproc sockproc /tmp/shell.sock chmod 0666 /tmp/shell.sock 下载lua-resty-shell组件 lua调用shell的组件 ...

April 19, 2020

MarkDown语法练习

MarkDown语法练习,可查询常用格式 强调 星号*与下划线_都可以,单是斜体,双是粗体,符号可跨行,符号可加空格 *单星号是斜体* 单星号是斜体 **双星号是粗体** 双星号是粗体 分割线 三个或更多_*,必须单独一行,可含空格 --- 引用 翻译成html就是<blockquote></blockquote>,符号>后的空格可不要 >引用 引用 注意使用空行结束 内层符号前的空格必须要 > 引用 >>引用中的引用 引用 引用中的引用 标题 SetText方式 三个=-或更多 大标题 === 小标题 --- 小标题 Atx方式 符号#后必须加空格 # 一级标题 ## 二级标题 ### 三级标题 #### 四级标题 ##### 五级标题 ###### 六级标题 # 一级标题 ## 二级标题 ### 三级标题 #### 四级标题 ##### 五级标题 ###### 六级标题 无序列表 符号之后的空格不能少,-+*效果一样,但不能混合使用,因混合是嵌套列表,内容可超长 + 无序列表 + 无序列表 + 无序列表 + 无序列表:我很长。我也很长!那比一比啊?比就比!我有这么长,你有我长吗?我有这么这么长!好吧,你赢了! 无序列表 无序列表 无序列表 无序列表:我很长。我也很长!那比一比啊?比就比!我有这么长,你有我长吗?我有这么这么长!好吧,你赢了! 有序列表 数字不能省略但可无序,点号.之后的空格不能少 ...

June 21, 2018