K8S|我写Dockerfile的一些心得


其实之前的时候,就写过一篇K8S|通过Dockerfile定制自己的镜像,简单演示了,怎样通过Dockerfile生成镜像.当时也想弄篇Dockerfile最佳实践来着.正好起个什么名字好呢搞稿了,也很干货.今天就聊一下Dockerfile的实操心得吧.

选择基础镜像

镜像自然是越小越好,毕竟如果太大的话,对存储和传输都是不小的挑战,所以选择基础镜像是基础中的基础.
首先我会中意alpine这个系统,原版才5M.实在是居家旅行的利器.
其次选择官方提供的alpine,比如,python的看相应的官方版本有没有提供alpine的镜像,java的别管openjdk还是oracle选择完版本都要看一下有没有alpine的提供.
最后,如果选择不了alpine除了自己做一个外,一定要选择和主机系统一样的容器系统.比如主机是ubuntu16.04,基础镜像也建议用ubuntu16.04;主机是centeos,自然也要选择相应的官方基础镜像
ok,这步就是上一节所说的FROM那行

修改依赖源

这一步调整相应的源,包括系统源,npm源,python源等,主要解决打包速度问题
像apk源

sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories

npm源

npm config get registry
https://registry.npm.taobao.org

python的pip源

pip config set global.trusted-host mirrors.aliyun.com
pip config set global.index-url https://mirrors.aliyun.com/simple

调整系统时区

一般的基础镜像用的时区都是0时区,这样的带来的问题就是跑在里面的应用还有我们进入系统查看日志时发现时间都差8个小时.例如alpine调整镜像

ENV TZ=Asia/Shanghai
RUN apk add --no-cache --update tzdata; \
    ln -snf /usr/share/zoneinfo/$TZ /etc/localtime; \
    echo $TZ > /etc/timezone; \
    && rm -rf /tmp/* /var/cache/apk/*

看到没,上节的最佳实践,apk的更新和安装写在一行,最后还不忘删除一下缓存文件

做自己的基础应用镜像

上面说的都是基础镜像的调整,这时候没有跟实际要部署的应用有交集.上面这些调整建议打成一个自己内部使用的基础镜像.这样简单的封一层,后面在做应用镜像时都是FROM自己调整过的镜像,无论是从管理角度看,还是实际打包效率都是最佳实践

依赖包外挂

比如在打包nodejs应用时,依赖包会放在项目目录下的node_modules目录,我们在做镜像时,最后将这个目录通过docker -v 挂在主机上,这样只有第一次时完全是空目录,后面再次打包,除了有更新,速度飞快.

docker run --rm -it \
-w /opt/app_dir \
-v $PWD:/opt/app_dir \
npm install

当然这一步在Dockerfile之前,下一步是将当前目录添加到容器

ADD app_dir /opt/app_dir
Posted in K8S | Tagged , , , , , , , , , , , , | Leave a comment

K8S|Dockerfile最佳实践

K8S负责维护容器状态,容器来自于镜像,此篇就是生成镜像的最佳实践.虽然比较基础,但是满满的干货的诚意.

Dockerfile指令介绍

  • FROM
  • MAINTAINER(作者信息,老版本,抛弃)
  • LABEL(添加标签,键值对)
LABEL version="1.0"
LABEL description="test"

我们可以给镜像添加多个 LABEL
注意:
1. 每条 LABEL 指令都会生成一个新的层。所以最好是把添加的多个 LABEL 合并为一条命令:
LABEL multi.label1="value1" multi.label2="value2" other="value3"
2. 如果新添加的 LABEL 和已有的 LABEL 同名,则新值会覆盖掉旧值。

我们可以使用 docker inspect 命令查看镜像的 LABEL 信息
  • RUN
    RUN command
RUN echo hello
RUN ["echo","hello"]
  • EXPOSE
    EXPOSE 80/tcp
告诉 Docker 服务,容器需要暴露的端口号
docker run -P(Publish all exposed ports to random ports)
docker run -p 18080:8080 
  • ENV
    格式为 ENV 指定一个环境变量,并在容器运行时保持
  • ADD 与 COPY
格式为 ADD/COPY <src>  <dest>
区别:
ADD可以是Dockerfile所在目录的一个相对路径;也可以是一个URL;还可以是一个tar文件(自动解压为目录)
COPY本地主机的(为Dockerfile所在目录的相对路径,文件或目录) 

建议: 优先使用COPY,比ADD更简单明了
ADD 命令功能相对 COPY 更复杂,包含从internet下载,解压等
不推荐使用 ADD 命令从远程下载一个软件包解压到镜像内
推荐使用curl/wget下载到本地,然后再解压到镜像内,并将原始文件删除
  • WORKDIR
    格式为 WORKDIR /path/to/workdir
    为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录
  • USER
    设置启动容器的用户,可以是用户名或UID
USER meiqia
USER 1002

注意:如果设置了容器以某用去运行,那么RUN, CMD 和 ENTRYPOINT 都会以这个用户去运行,
使用这个指令一定要确认容器中拥有这个用户,并且拥有足够权限
  • CMD 与 ENTRYPOINT
    • shell形式
      command
    • exec形式
      ["executa ble", "parameter"]
例子:
CMD echo “Hello World” (shell格式)
CMD [“echo”, “Hello World”] (exec格式 )
ENTRYPOINT echo “Hello World” (shell格 式)
ENTRYPOINT [“echo”, “Hello World”] (exec格式 )

FROM busybox
ENTRYPOINT ["echo","hello"]
CMD ["echo"]

尽量都采用 exec 形式,防止有潜在问题

  • Tips

    • 缓存
      在开发周期中,构建Docker映像,更改代码或步骤然后重新构建时,利用缓存很重要。缓存有助于避免在不
      需要时再次运行构建步骤

      • 构建顺序
        从最少到最频繁更改的步骤对您的步骤进行排序,以优化缓存
      • 确定可缓存单元,例如安装包和更新
        每个RUN指令都可以看作是可缓存的执行单元。它们过多可能是不必要的,从程序包管理器安装程序包时,始终希望更新索引并以相同的RUN模式安装程序包:它们一起形成一个可缓存单元
    • 缩小 image size

      • 删除不必要的依赖包
      • 删除软件包管理器缓存
      • 删除源码安装包
    • 高维护性

      • 尽量采用官方image,已有应用减少自己构建
        生式image可以节省大量维护时间,因为所有安装步骤都已完成,并且采用了最佳实践
      • 使用更具体的标签,避免不同版本差异
    • 依赖性
      多阶段构建

参考链接
https://blog.csdn.net/atlansi/article/details/87892016
https://www.docker.com/blog/intro-guide-to-dockerfile-best-practices/

转载请注明: 转自Rainbird的个人博客
   本文链接: K8S|Dockerfile最佳实践

Posted in K8S | Tagged , , , , , , , , , , , | Leave a comment

Android|error: device unauthorized.


Android手机,调试时报错

error: device unauthorized.
This adb server's $ADB_VENDOR_KEYS is not set
Try 'adb kill-server' if that seems wrong.
Otherwise check for a confirmation dialog on your device.

因为是批量调试,以为脚本有问题,重试了几次,发现一直报同样的错.
喵了一眼,手机发现,其中一个提示是否允许调度对话框,点完确认就一切ok了.

网上搜了一下这个问题的解决办法:
1.重新插拔USB
2.Rom升级,需要再次打开允许USB调试
3.按提示执行adb kill-server
4.换根儿USB线

Posted in Android | Tagged , , , , , , , , , | Leave a comment

ELK|filebeat采集日志野蛮占用资源

filebeat是日志采集的agent,每个采集点都要部署一份.它作为golang实现的客户端,采集效率确实比较高,如果你认为它占用资源也很小的话就有点儿大错特错了.当然这里的占用资源是指CPU和内存两种,网络的话内网可以忽略不计.

CPU抢占

去年某日压测,发现某一核心网关性能特别差,通过主机监控观察发现,负载特别高.网关繁忙可以理解,高到何种情况呢?进程监控明细拉出来一看,有点儿大跌眼镜:50%的cpu资源被filebeat抢走了.网关只是可怜巴巴的占了20%.原来故事是这样的:大量请求传递到了网关,网关事无巨细的记录了大量的日志,filebeat一看,哇,活儿来了,然后玩命采集,结果喧宾夺主,把资源都占走了.好在是测试环境压测,发现这个问题解决很简单了:filebeat加一条配置max_procs: 1 限制cpu使用核数.

close_inactive: 5m
close_timeout: 30m
clean_inactive: 1h
ignore_older: 2h
max_procs: 1 
queue.mem.events: 256
queue.mem.flush.min_events: 128
queue.mem.flush.timeout: 2s
filebeat.prospectors:
....

内存抢占

本来故事到这里应该就结束了,可是没想到事隔一年,准备618的压测过程中,再次遇到了filebeat抢占资源造成节点不稳定的情况.

如图,最顶上那哥们儿,很敬业的将内存占到了11GB,然后直接引起节点内存压力,然后kubelet就开始强制驱逐了.

May 21 01:55:13 k8s-168-25 kubelet[828]: W0521 01:55:13.612640     828 eviction_manager.go:344] eviction manager: attempting to reclaim memory
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.612687     828 eviction_manager.go:358] eviction manager: must evict pod(s) to reclaim   memory
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.612739     828 eviction_manager.go:376] eviction manager: pods ranked for eviction:      filebeat-6w2n8_pro(825f41cc-9a77-11ea-a0c9-00163e10e901), nagios-p9fv8_kube-system(091440e7-9a0c-11ea-a0c9-00163e10e901), error-mail-py-         b4nt5_zkt-base(098d70ba-9a0c-11ea-a0c9-00163e10e901), ticket-api-7cc55747f9-278jh_pro(f6b8eb04-990f-11ea-a0c9-00163e10e901), node-exporter-      j2f6r_monitoring(ba795520-9a02-11ea-a0c9-00163e10e901), bg-api-gateway-6b949d665b-6tj4n_pro(2b6661ff-9910-11ea-a0c9-00163e10e901)
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.612881     828 kuberuntime_container.go:547] Killing container "docker://                30ff3f4d027467ce7e0e479bc1f0597851a5f08084c673e27cc9db786e846cde" with 30 second grace period
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.644719     828 kubelet.go:1877] SyncLoop (DELETE, "api"): "filebeat-6w2n8_pro(825f41cc-  9a77-11ea-a0c9-00163e10e901)"
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.649208     828 kubelet.go:1871] SyncLoop (REMOVE, "api"): "filebeat-6w2n8_pro(825f41cc-  9a77-11ea-a0c9-00163e10e901)"
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.658878     828 kubelet_pods.go:1106] Killing unwanted pod "filebeat-6w2n8"
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.658922     828 kuberuntime_container.go:547] Killing container "docker://                30ff3f4d027467ce7e0e479bc1f0597851a5f08084c673e27cc9db786e846cde" with 0 second grace period
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.672992     828 kubelet.go:1861] SyncLoop (ADD, "api"): "filebeat-r4fbc_pro(0e40b5e0-9ac3-11ea-a0c9-00163e10e901)"
May 21 01:55:13 k8s-168-25 kubelet[828]: W0521 01:55:13.673050     828 eviction_manager.go:144] Failed to admit pod filebeat-r4fbc_pro(0e40b5e0- 9ac3-11ea-a0c9-00163e10e901) - node has conditions: [MemoryPressure]
May 21 01:55:13 k8s-168-25 dockerd[770]: time="2020-05-21T01:55:13+08:00" level=info msg="shim reaped"                                           id=30ff3f4d027467ce7e0e479bc1f0597851a5f08084c673e27cc9db786e846cde module="containerd/tasks"
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.720046     828 kubelet.go:1877] SyncLoop (DELETE, "api"): "filebeat-r4fbc_pro(0e40b5e0-  9ac3-11ea-a0c9-00163e10e901)"
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.720486     828 kubelet.go:1871] SyncLoop (REMOVE, "api"): "filebeat-r4fbc_pro(0e40b5e0-  9ac3-11ea-a0c9-00163e10e901)"
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.720536     828 kubelet.go:2065] Failed to delete pod "filebeat-r4fbc_pro(0e40b5e0-9ac3-  11ea-a0c9-00163e10e901)", err: pod not found
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.738615     828 kubelet.go:1861] SyncLoop (ADD, "api"): "filebeat-lsl46_pro(0e4b24c0-9ac3-11ea-a0c9-00163e10e901)"
May 21 01:55:13 k8s-168-25 kubelet[828]: W0521 01:55:13.738704     828 eviction_manager.go:144] Failed to admit pod filebeat-lsl46_pro(0e4b24c0- 9ac3-11ea-a0c9-00163e10e901) - node has conditions: [MemoryPressure]
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.779795     828 kubelet.go:1877] SyncLoop (DELETE, "api"): "filebeat-lsl46_pro(0e4b24c0-  9ac3-11ea-a0c9-00163e10e901)"
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.782575     828 kubelet.go:1871] SyncLoop (REMOVE, "api"): "filebeat-lsl46_pro(0e4b24c0-  9ac3-11ea-a0c9-00163e10e901)"
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.782623     828 kubelet.go:2065] Failed to delete pod "filebeat-lsl46_pro(0e4b24c0-9ac3-  11ea-a0c9-00163e10e901)", err: pod not found
May 21 01:55:13 k8s-168-25 kubelet[828]: I0521 01:55:13.799802     828 kubelet.go:1861] SyncLoop (ADD, "api"): "filebeat-2dh8b_pro(0e54e8a8-9ac3-11ea-a0c9-00163e10e901)"

可以看到在驱逐filebeat的过程中,节点内存压力了
node has conditions: [MemoryPressure]
结果filebeat是DaemonSet跑在节点上了,于是就出现了:扁担要绑在板凳上,板凳偏不让扁担绑在板凳上的死循环,有几分钟,直接其它的服务也因为稍微有些内存超过限制直接oom-killer.

限制资源

既然发现了问题,解决办法也就很简单了:限制pod内存使用.不过filebeat的配置文件可没有内存相关的配置,看来只能借助k8s的能力了.想到这里问题来了:filebeat限制的cpu使用1核和k8s的cpu限制1核一样吗?

看了一下cpu的使用情况,看起来是有出入的.
所以直接也把cpu的限制给加上吧

"resources": {
  "limits": {
    "cpu": "1",
    "memory": "2Gi"
  },
  "requests": {
    "cpu": "100m",
    "memory": "1Gi"
  }
}

后续再观察一下日志采集情况,想来应该是问题不大.

Posted in ELK, K8S | Tagged , , , , , , , , , , , , | Leave a comment

K8S|线上压测之nf_conntrack: table full, dropping

又是一年6.18
公司已经开启了密集压测模式,分析系统瓶颈,提升抗并发能力.
几轮压测过后,系统容量已经有了大幅度的增加.在分析的过程中,发现一个重要的核心网关,三个实例中的一个,在某个时间点陡然请求数为0.另外两个实例,出现了压力不均的情况.正常情况下,k8s中的实例的资源使用和请求数基本是一致的.

分析

立马祭出pod的资源数据,发现CPU和内存的使用比较稳定,没有明显波动.
后怀疑,是否因为同主机上的其它节点有资源抢占,结果只有一个后端服务与其运行在一起,同一时间段资源使用也不高.
一时间分析陷入了僵局.
突然间灵机一动,想着因为压测结果越来越好,后端网关的压力也越来越大,是不是系统层面的参数影响到了服务的运行?随即登陆有问题的节点查看系统日志/var/log/syslog.果然发现了重要信息

16788 May 20 02:03:28 k8s-168-35 kernel: [122013.114610] net_ratelimit: 1 callbacks suppressed
16789 May 20 02:03:28 k8s-168-35 kernel: [122013.114614] nf_conntrack: table full, dropping packet
16790 May 20 02:03:28 k8s-168-35 kernel: [122013.121276] nf_conntrack: table full, dropping packet
16791 May 20 02:03:29 k8s-168-35 kernel: [122014.151307] nf_conntrack: table full, dropping packet
16792 May 20 02:03:29 k8s-168-35 kernel: [122014.694497] nf_conntrack: table full, dropping packet
16793 May 20 02:03:29 k8s-168-35 kernel: [122014.732505] nf_conntrack: table full, dropping packet
16794 May 20 02:03:30 k8s-168-35 kernel: [122015.528920] nf_conntrack: table full, dropping packet
16795 May 20 02:03:30 k8s-168-35 kernel: [122015.804467] nf_conntrack: table full, dropping packet
16796 May 20 02:03:31 k8s-168-35 kernel: [122016.201035] nf_conntrack: table full, dropping packet
16797 May 20 02:03:31 k8s-168-35 kernel: [122016.292206] nf_conntrack: table full, dropping packet
16798 May 20 02:03:31 k8s-168-35 kernel: [122016.305049] nf_conntrack: table full, dropping packet
16799 May 20 02:03:33 k8s-168-35 kernel: [122018.216549] net_ratelimit: 3 callbacks suppressed

看到这里,差点儿爆粗口了,同样的坑去年踩过一遍了!!!

原因

错误是:kernel: nf_conntrack: table full, dropping packet
意思是:tcp连接过多,nf_conntrack表无法记录,直接丢弃的新连接.
原因是:
nf_conntrack 模块在 kernel 2.6.15(2006-01-03 发布) 被引入,支持 IPv4 和 IPv6,取代只支持 IPv4 的 ip_connktrack,用于跟踪连接的状态,供其他模块使用。
需要 NAT 的服务都会用到它,例如防火墙、Docker 等。以 iptables 的 nat 和 state 模块为例:

ipv4 2 icmp 1 29 src=127.0.0.1 dst=127.0.0.1 type=8 code=0 id=26067 src=127.0.0.1 dst=127.0.0.1 type=0 code=0 id=26067 mark=0 use=1

nat:根据转发规则修改 IP 包的源/目标地址,靠 conntrack 记录才能让返回的包能路由到发请求的机器。
state:直接用 conntrack 记录的连接状态(NEW/ESTABLISHED/RELATED/INVALID 等)来匹配防火墙过滤规则。
nf_conntrack 跟踪所有网络连接,记录存储在 1 个哈希表里。首先根据五元组算出哈希值,分配一个桶,如果有冲突就在链表上遍历,直到找到一个精确匹配的。如果没有匹配的则新建。

即使来自客户端的访问量不多,内部请求多的话照样会塞满哈希表,例如 ping 本机也会留下这么一条记录:

ipv4 2 icmp 1 29 src=127.0.0.1 dst=127.0.0.1 type=8 code=0 id=26067 src=127.0.0.1 dst=127.0.0.1 type=0 code=0 id=26067 mark=0 use=1

连接记录会在哈希表里保留一段时间,根据协议和状态有所不同,直到超时都没有收发包就会清除记录。如果服务器比较繁忙,新连接进来的速度远高于释放的速度,把哈希表塞满了,新连接的数据包就会被丢掉。此时 netfilter 变成了一个黑洞, 这发生在3层(网络层),应用程序毫无办法。

解决

解决办法

#检查配置
cat /proc/sys/net/netfilter/nf_conntrack_max
262144
#更改配置
sysctl -w net.netfilter.nf_conntrack_max=1048576

#验证配置
cat /proc/sys/net/netfilter/nf_conntrack_max  
1048576
#开机加载配置
echo 'net.netfilter.nf_conntrack_max=1048576' >>/etc/sysctl.conf

总结

之所有遇到这样的问题,说明系统的压力已经比较大了,连接数比较多,又因为网络层用的iptables模式,不知道如果切到ipvs会不会有同样的问题,留待未来kubernetes版本升级以后再看吧

Posted in K8S, linux | Tagged , , , , , , , , | Leave a comment

Android|MIUI10 当前设备已被临时限制3-1


MIUI10在开启USB调试的过程中,提示:当前设备已被临时限制3-1

网上查的方案:
方案一:
http://bbs.zol.com.cn/sjbbs/d34002_84582.html

  1. 先登出你的 小米账号
  2. 再去点击 USB 安装选项
  3. 系统让你登入时候,选上同意条款

结果:不好使

方案二:
https://zhidao.baidu.com/question/181823929106110604.html

  1. 退出账号
  2. 重新打开,提示登录,尝试打开,失败
  3. 把开发者所有选项关闭,把开发者模式关闭,再重新usb调试
  4. 开发usb安装

结果:不好使

我自己的方案:
重新注册一个小米号,立马好使了,缺点是需要一个手机号

Posted in Android | Tagged , , , , , , , | Leave a comment

Linux|lnmp运行时间久了504 bad gateway

lnmp环境,一键安装,上面只跑了一个wordpress,压力也不大,每天几百个pv,但是基本五六天就会504,重启后又正常.
忍不住了,看了一下日志

大量php-fpm segfault错误

 kernel: [4383249.904122] php-fpm[29952]: segfault at 0 ip 00000000008213e0 sp 00007ffe2821e098 error 6 in php-fpm[400000+7b9000]

网上查问题,说有可能是php-fpm的backlog设置-1的问题

[global]
pid = /usr/local/php/var/run/php-fpm.pid
error_log = /usr/local/php/var/log/php-fpm.log
log_level = notice

[www]
listen = /tmp/php-cgi.sock
listen.backlog = -1
listen.allowed_clients = 127.0.0.1
listen.owner = www-data
listen.group = www-data
listen.mode = 0666
user = www-data
group = www-data
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 6
request_terminate_timeout = 100
request_slowlog_timeout = 0
slowlog = var/log/slow.log

看了一下自己的配置,果断改成1024

listen.backlog = 1024

其实还有个参数可以添加

pm.max_requests = 200

一个php进程响应多少个请求自动退出,用来防止内存泄露问题

Posted in linux | Tagged , , , , , , , , , | Leave a comment

MIUI 8 的4种截图功能使用方法

MIUI8 的4种截图功能使用方法,对于刚刚入手的新用户来说,可能并不了解,一起来看下。

方法一:音量下键+任务键
这种方法非常简单,并且是依靠硬件按键进行截屏,不需要触碰屏幕。

方法二:通知栏下滑,选择截屏开关
此种方法是依靠通知栏,点击截屏开关后就会触发截屏,如果你觉得开关的位置不够醒目,还可以自定义的设置开关位置。

方法三:开启悬浮球,选择悬浮球中截屏开关
悬浮球中,截屏开关是默认处在中下方的位置,如果感觉点起来不方便,还可自定义设置其位置。

方法四:三指下滑快捷截屏
这是 MIUI 8 最新的功能,不需要任何设置,只需在屏幕任何处,三指下滑即可出发截屏。目前此功能已经上线体验版,在本周将会登录开发版。

方法 一二四都在miui10上测试了.好用

转自:
https://www.xiaomi.cn/post/2178695

Posted in Android | Tagged , , , , | Leave a comment

OSX|更换brew源解决下载慢问题


在Mac下,brew install xxx的时候,经常卡在Updating Homebrew...这步很长时间.
这是因为brew每次执行的时候,都会去github检查有没有更新造成很慢.

通过brew udpate -v命令可以观察到,影响brew速度的主要是三个git地址+一个下载:


https://github.com/Homebrew/brew.git
https://github.com/Homebrew/homebrew-core.git
https://github.com/Homebrew/homebrew-cask.git

HOMEBREW_BOTTLE_DOMAIN

查看配置

$ brew config
HOMEBREW_VERSION: 2.2.11-110-g0859115
ORIGIN: https://github.com/Homebrew/brew.git
HEAD: 0859115a2400b4548a52a8b67656601c46c64db5
Last commit: 17 hours ago
Core tap ORIGIN: https://github.com/Homebrew/homebrew-core.git
Core tap HEAD: e0f4187b4e9aebf91624730aadc121a5510fa450
Core tap last commit: 3 hours ago
HOMEBREW_PREFIX: /usr/local
HOMEBREW_DEV_CMD_RUN: 1
CPU: octa-core 64-bit kabylake
Homebrew Ruby: 2.6.3 => /usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/2.6.3/bin/ruby
Clang: 11.0 build 1103
Git: 2.24.1 => /Applications/Xcode.app/Contents/Developer/usr/bin/git
Curl: 7.64.1 => /usr/bin/curl
Java: 1.8.0_60
macOS: 10.15.3-x86_64
CLT: 11.0.33.17
Xcode: 11.4

换成淘宝源

git -C "$(brew --repo)" remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin  https://mirrors.aliyun.com/homebrew/homebrew-core.git

export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles

换成官方

git -C "$(brew --repo)" remote set-url origin https://github.com/Homebrew/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin https://github.com/Homebrew/homebrew-core.git
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://github.com/Homebrew/homebrew-cask.git

换成中科大

git -C "$(brew --repo)" remote set-url origin https://mirrors.ustc.edu.cn/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.ustc.edu.cn/homebrew-cask.git
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git

HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles

换成清华源

git -C "$(brew --repo)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git

export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles

参考: https://mirrors.tuna.tsinghua.edu.cn/help/homebrew/

  • 问题处理

    • rm -fr ".git/rebase-apply"
    fatal: It seems that there is already a rebase-apply directory, and
    I wonder if you are in the middle of another rebase.  If that is the
    case, please try
    git rebase (--continue | --abort | --skip)
    If that is not the case, please
    rm -fr ".git/rebase-apply"
    and run me again.  I am stopping in case you still have something
    valuable there.
    Error: homebrew/cask/tcl-tk 8.6.9_1 is already installed
    To upgrade to 8.6.10, run brew upgrade homebrew/cask/tcl-tk

    解决

    $ cd $(brew --repo)
    $ brew -v install tcl-tk
    Updating Homebrew...
    ==> Auto-updated Homebrew!
    Updated Homebrew from e94fff894 to 0859115a2.
    No changes to formulae.
    Error: homebrew/cask/tcl-tk 8.6.9_1 is already installed
    To upgrade to 8.6.10, run brew upgrade homebrew/cask/tcl-tkbrew -v install tcl-tk
    Updating Homebrew...
    ==> Auto-updated Homebrew!
    Updated Homebrew from e94fff894 to 0859115a2.
    No changes to formulae.
    Error: homebrew/cask/tcl-tk 8.6.9_1 is already installed
    To upgrade to 8.6.10, run brew upgrade homebrew/cask/tcl-tk
Posted in Mac | Tagged , , , , , , , , , | Leave a comment

Python|统计函数执行时间的优雅用法


当应用规模大到一定程度,我们会开始关心,整体的响应时间,而整体的响应时间又是由单个函数的执行时间决定的,怎样统计函数执行时间呢?
很简单,开始的时候记录一次时间,结束的时候记录一次时间,两个时间相减,得到的就是函数执行时间,例子如下:

import time
def fun_run_time():
    time_start = time.time()
    print('sleep 3 start')
    time.sleep(3)
    print('sleep 3 end')
    time_end = time.time()
    print(f'fun_run_time use {(time_end - time_start):.2f} secends')

if __name__ == "__main__":
    fun_run_time()

执行这段代码,得到运行结果:

sleep 3 start
sleep 3 end
fun_run_time use 3.00 secends

time.sleep(3)就是我们正常的函数代码部分.至此,统计函数执行时间的目的是达到了,怎么优雅呢?
这里就要用到装饰器这个神奇的东西了.所谓装饰器就是在代码运行期间动态增加功能,英文名:Decorator

改进后的代码是这样的:

import time
import functools

def fun_run_time(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        time_start = time.time()
        res = func(*args, **kw)
        time_end = time.time()
        print(f'{func.__name__} use {(time_end - time_start):.2f} secends')
        return res
    return wrapper

@fun_run_time
def long_time_fun():
    print('sleep 3 start')
    time.sleep(3)
    print('sleep 3 end')
if __name__ == "__main__":
    long_time_fun()

执行结果:

sleep 3 start
sleep 3 end
long_time_fun use 3.00 secends

没错,执行结果是一样的,以后,如果想知道哪个函数的执行时间,直接在它的上面写上

@fun_run_time

就可以了.
装饰器的原理,网上搜索吧,装饰器的好处就是:在不修改原有函数的情况下,达到了统计原有函数使用时间的目的.

Posted in Python | Tagged , , , , , , | Leave a comment