Docker container容器内访问宿主机host服务

问题:docker的container内,本身是一个微小的主机,那么请求127.0.0.1或者localhost,自然是请求到了container本身的网络,而无法抵达宿主机。

在以往,需要各位去手动寻找docker创建的docker0 bridge来访问宿主机网络,但在今天,docker已经提供了非常优雅的解决办法了

Linux:

Docker版本高于v20.10(2020年12月4日更新)

在启动docker时,加入如下语句
--add-host=host.docker.internal:host-gateway
而在container内,可以直接请求host.docker.internal:PORT,来获取宿主机上提供的各种服务

如果使用了Docker Compose,则应该将下面的句子加入container的声明中:

extra_hosts:
- "host.docker.internal:host-gateway"

Mac和Windows:

Docker版本高于v18.03(2018年3月21日更新)

直接在container内使用host.docker.internal:PORT来访问宿主机服务即可

对于Mac上Docker版本低于上述版本的:

Mac Docker版本v17.12到v18.02: 使用docker.for.mac.host.internal

Mac Docker版本v17.06到v18.11: 使用docker.for.mac.localhost

对于更低版本的docker,只能使用老旧的方法了,这里不再多做赘述

参考:https://stackoverflow.com/a/43541732

使用SSH来连接远程服务器的MySQL

本文要解决什么问题:当远程服务器无法开放MySQL端口或者无权限操作远程服务器的安全组时,我们应该如何在本地连接到远程的MySQL并进行操作

本文基础要求:远程服务器至少开放了SSH端口(通常默认情况下端口号为22),如果未开放22端口,则本文所述方法并不可用。

本文方法总结:利用SSH自带的端口映射功能,建立一个tunnel,将远程服务器上的MySQL端口映射到本地的端口上,从而使得本地客户端可以直接连接

关于如何使用SSH,以及如何更简单的使用SSH,可以参考另一篇文章

当SSH已经准备就绪,打开Terminal,输入如下指令


ssh -L [local port]:[database host]:[remote port] [username]@[remote host]

ssh -L [本地端口]:[数据库所在地址]:[数据库远程端口] [远程服务器用户名]@[远程服务器地址]

*如果你不用同时登陆远程服务器进行操作,可以加上-N指令

以上指令唯一需要解释的就是数据库所在地址,如果远程服务器本身就是MySQL服务器,那么可以直接填127.0.0.1, 而如果远程服务器本身还是一个跳板,则应该输入在远程服务器内可以访问到MySQL的地址。

示例如下:

ssh -N -L 3310:127.0.0.1:3306 root@MyRemote

以上的命令会将远程服务器监听3306端口的MySQL服务,映射到本地的3310端口上,之后可以直接使用MySQL客户端在命令行里登录,或者使用pma等数据库操作工具

mysql -u root -h 127.0.0.1 -P 3310 -p

利用context和kubectx快速切换kubernetes集群

在使用kubectl操作kubernetes集群的过程中,可能会遇到需要操作不同集群的问题,例如本地的minikube集群和线上的部署集群,或是线上的测试集群和线上的部署集群,如果集群之间切换不频繁,也可以使用笨办法,比如准备多个config文件在/.kube文件夹下,要切换的时候就改config的名字。不过对于需要频繁切换操作集群的人来说,这个方法显然太麻烦了,当然也不够cool。

当然,k8s是非常完善的集群解决方案,肯定是考虑到了这个问题的,这里就用到了k8s的config文件的context(上下文)了。

如果你是从零开始配置两个集群的cluster, user等信息的,那么可以参照官方的这个文档

Define-clusters-users-and-contexts

这里就不详细说以上这种方法了,我们来说说已经存在了两个集群的config文件时如何简单的生成context,当然,在上述文档的后半部分,也有我要说明的这些,也可以去看英文原文,我这里是因为遇到了一个坑,所以详细解释一下。

首先说方法,我们都知道kubectl如果没有设置config相关位置的环境变量,那么其会默认去一个地方寻找,对于Mac来说,就是

$HOME/.kube/config

如果要自己指定一个config文件所在的位置,那么就要设置KUBECONFIG环境变量,例如:

export KUBECONFIG=$KUBECONFIG:$HOME/.kube/config

而如果有两个集群的配置文件呢,其实就可以非常简单的直接加在环境变量的后面,然后让kubectl自动为我们生成context,例如:

export KUBECONFIG=$KUBECONFIG:$HOME/.kube/config:$HOME/.kube/config-2

注意:可以使用

kubectl config view

来查看目前的context的情况

那么坑在哪里呢,由于两个集群的配置文件可能是管理员只分配给你自己使用的,那么用户名就可能是相同的,在用户名相同的情况下,如果集群的连接启用了TLS验证,那么kubectl自己融合的config文件就会使用相同的client-certificate-data和client-key-data,如果两个集群在同一服务商的同一片网络下还好,可是如果是在不同的网络下,那么就会出现只能连接上其中一个,而另一个集群无法连接的情况,这种时候,我的解决办法是修改用户名,但要注意,在config里的context下,可以看到user和name两个字段,我们要修改的是user字段,这里只是一个名称,不影响集群侧的用户验证,修改完user字段,记得翻到users字段下,那里面有一个name,这个name的值应该和刚刚修改之前的user字段中的值一样,把这里也修改成同样的新值,保存,重新配置环境变量使之生效,这时候就可以使用了。

至于官方提供的切换集群的方法,在上述文档中也能找到,这里也不在赘述,这里主要介绍一个第三方工具来帮我们更快捷的做这个事:
kubectx

这个工具可以给context指定alias并快速的切换他们,简单的介绍一下

kubectx                     : 列出目前所有的context
kubectx <NAME>              : 切换到<NAME>对应的context
kubectx -                   : 切换到上一次的context
kubectx <NEW NAME>=<NAME>   : 给<NAME>context命名一个新名字
kubectx <NAME>=.            : 给当前context命名一个新名字

可以看出,在完成了上述config文件自动融合之后,使用这个工具就可以快速的切换不同的操作集群,同时这个工具还支持bash/zsh的tab自动补全,以及fzf这个快速选择工具,可以说从此以后切换集群就可以如丝般顺滑了,不过缺点就是kubectx这个工具会影响对kubectl的自动补全,全看各位的习惯了
 

Mysql更新排名的取巧实现

最近的项目连续遇到了对用户排名的需求,一个要求不用并列,一个要求同分并列,因为用户体量的考虑,拒绝了实时更新(即每个用户查询排名时获取排名),改为定时更新,在网上查了大量的MySQL排名算法之后,发现大部分的文章是以查询时获取来实现,而非定时更新所有人的排名,所以经过一番研究之后自己写了一个算法,欢迎斧正

首先我们假设有一张user表,其中有四个字段,score,rank,score_rank,join_time分别代表用户的分数,排名,获取排名时的分数,及用户加入时间

首先是不考虑并列的情况

SET @r = 0
UPDATE user
SET `rank` = @r := (@r + 1), `score_rank` = `score`
WHERE `score` != 0
ORDER BY `score` DESC, `join_time` DESC // 因为没有并列,所以增加一个排序

然后是考虑并列的情况

SET @r = 0;
SET @l = 0;
SET @s = 1;
UPDATE `user` SET `rank` = (
CASE
WHEN `score` = @l THEN @r := @r + (@s := @s + 1) - @s
ELSE @r := (@r + @s) + (@s := 1) - 1 END), `score_rank` = @l := (`score`)
WHERE `score` != 0
ORDER BY `score` DESC

这里唯一需要注意的事情就是MySQL中的变量问题,变量只能在两个地方赋值,SET或SELECT时,其余的地方只允许在变量赋值给其他字段时才允许被修改,所以就是在这里我们开始取巧,来解释一下并列时的思想:

r,l,s分别是当前排名,当前排名的分数,和当前同分的人数

当某一行的数据与上一行的分数相同时,即 score = @l,那么这时应该给rank赋值为当前的排名,但同时应该更新同分人数,所以我们这样来实现:@r := @r + (@s := @s + 1) – @s,这样s实现了自增,但对返回结果r没有影响

当某一行的数据与上一行的分数不同时,则应该给rank赋值r+1,但同时我们希望给l赋值当前的分数,并把同分人数s归为1,则选择这样实现:@r := (@r + @s) + (@s := 1) – 1

关于速度

这个算法在我这边的模拟测试中,三万条的数据排序用时0.4秒,所以肯定不能作为实时处理,计划是每天定时更新一回,操作时间也算是可以接受了。不知道其他算法的时间是会是多少,欢迎提供下对比。