2020,面向下一个十年

今年是进入股市的第8年,多年来,没有认真盘点过自己的收益,只知道自己有挣钱,但并不知道挣了多少,因此想借这个机会,一是盘点当前的投资情况,二是合理制定面向未来的更合适的投资策略。

写在前面

回顾自己一年来的持仓变化,投资理念也发生了翻天覆地的变化,投资一些以前不会尝试的标的。一年以前,我更关注上市公司市净率、市盈率等财务指标,相信并等待他们价值回归。最近几年投资市场分化比较严重,深刻反思了自己的投资理念,完全是在财务指标是把自己困住了。不能拿着2013年的投资理念继续在2020年搞投资。应与时俱进,不断更新自己的知识面。
价值投资应该更关注企业的长期经营与盈利情况,好的管理层,对股东负责的管理层,不然财务数据再好也没有用,投资最重要的就是赚钱,先把钱赚了,再谈其他,不赚钱就是loser。

对基金的看法

过去我并不太看好主动混合基金,基金经理很难解决一个悖论,就是你这么牛逼,能挣这么多钱,你为什么要帮我挣,你怎么不自己搞投资,你为什么不去搞私募。2015年老鼠仓的情况非常严重,导致我对基金有非常不好的印象。这个印象回想起来,反而影响到了自己挣钱,不能因为被几个渣男欺骗了,就不相信爱情,不结婚了。
投资是一件很复杂的事情,需要一定的规模、优秀的投资策略、控制利润回撤的能力、高效的投研团队,是需要整个投资团队协作的。如果一个基金经理已经拥有5-10年的公募基金经历,并且有非常好的信誉、回撤控制与卓越的年化收益,我们有什么理由去怀疑他们。

对投资的思考

某一天,我统计了过往几年的盈利情况,发现我的年化收益远远不如头部基金的基金经理,我开始思索自己投资跑赢基金经理的可能性,毕竟人家是专业的,在这方面投入了大量的时间、精力,是拿钱干活的。
自2019年起,开始小幅增持了ETF、LOF,发现比起股票,ETF与LOF更加抗风险、不收印花税、分红不收税、手续费更低,拥有众多优势。也开始不断补充和完善自己的投资体系,提升自己的知识面,增加对多种投资工具的了解。深入研究和学习别人的投资策略与投资理念,取其精华,去其糟粕。
既然我过往几年的平均年化收益,都不能跑赢这些优秀的基金经理,我为什么不调整自己的思路,拥抱变化,选择优秀的基金经理,让专业的人去做专业的事情呢?
人的一生,就是不断认清现实的过程,认清自己自己的投资水平与能力,解放自己,不再幸苦做很多不必要的研究,我只需要研究好基金与基金经理,与优秀的基金经理为伍,基金经理们会让利润跑起来,会得到更大的投资回报。

转变

2020年3月开始,我陆续将重仓个股转换成ETF与LOF,调整增持了大批LOF,根据折价情况买入了若干封闭基金,7月份完成了所有仓位的转移。
伴随着较好的市场行情,投资收益也开始水涨船高,取得了不俗的成绩。
我将原先100%的个股仓位,转成100%仓位的LOF+封闭基金,封闭基金根据折价情况不断调仓轮动,LOF根据业绩表现与回撤情况不断优胜略汰,并不断去发掘一些优秀的基金,通过融资融券买入约30%的仓位沪深股票,作为申购新股的市值,一方面可以获得优质股票股价上涨的盈利,另一方面可以通过新股申购,达到一定年化收益,用于抵消融资的利息。

未来计划与执行

为了更好回顾投资计划的执行,定期调整分析、优化投资策略,我将按月记录自己投资的净值变化。
以2020年8月27日,作为基准日期,当日收盘权益净值计为1,后续增资、减资将影响持有份额,不影响净值,通过统计净值来回顾与优化当前的投资方案。

投资记录

2020.08.30,本月中签一只新股,在云南白药上获益颇丰,若干投资的基金都创下净值历史新高。支付宝的小额投资完成了50%以上的仓位调整,把亏损、盈利能力较差的基金淘汰,增持优秀的基金。
2020.09.18,本月所投资的基金遇到较为大幅的回撤,通过一些个股的波段操作,挽回了一些收益,总体的利润未产生大幅回撤。后续在个股的操作上考虑投入更大的资金,风格上可以更加激进。基金方面的投资保持现有策略不变,继续看好基金未来的收益
2020.10.15,本月再次中签一只新股,收益有了小幅增长,本月盈利较大的标有:兴全合宜、生物医药、富国天惠、兴全模式、鸿德丰泽、银华大盘、兴全趋势。大幅降低了仓位,移出富国天惠、银华大盘、兴全趋势,加仓地产与银行。
2020.10.31,本月仅实现了2个点的增长,月底最后一天有较大的回撤,在广州酒家操作上面过于谨慎,提前做了减仓处理,没有及时收盘时买回完成当日做T,导致错过了一次大涨。继续看好港股、银行、房地产行业,投入一定比例的资金,期望后续能带来可观的收益。11月的策略继续加仓基金与ETF,减少个股持有比例。11月美国大选结束,预期股市应该会解决当前的分化窘境,还是有希望走出箱体行情,更看好是上涨。保持中等仓位配置,做好大跌后加仓准备。
2020.11.14,本月投资较为顺利,利润回撤控制也挺好的,多个波段的仓位也获得了较理想的收益,净值再创新高。
反思今年个别股票亏损,成也仓位,败也仓位,一个是生物医药ETF追高了,后续仓位控制不够好,出现了较大幅的亏损,另一个是运营商,作为国企尽管事实上垄断,但受到政策面的压制以及工信部要求的普惠,故尽管业绩还行,但股价不涨,生物医药遇到暂时回撤,但相信还是有机会复苏,运营商由于仓位控制尚可,对部分仓位止损转移至恒生指数,及时计提损失并挽回了较大收益。回溯减仓操作,止损还是非常合理且有必要的,没必要把时间浪费在辣鸡的身上。
还有一个个股也是因仓位控制不好,加仓过早,目前还是-2%左右,预计下周可以回本后出掉一半,若无法回本也将出掉一半降低仓位。后续应该更加控制好仓位,不管是短期波段还是长期价值投资,都应当经过试验性仓位后,才会大幅加仓,不应在个股上押注过多。
看好港股内资银行与房地产行业未来发展,将适度扩大两行业的投资。

投资比例分配

固定仓位

fund percent(%) plan(%)
香港银行 18 25
兴全合宜 16 20
兴全模式 11 15
泓德丰泽 10 10
富国天惠 3 10
上海A股 8 12
深圳A股 5 8

动态/波段仓位

fund percent(%) plan(%)
恒生ETF 0 10
H股ETF 0 10
传媒ETF 24 20
运营商 8 8
地产ETF 0 5
500ETF / /

Investment Report

date mtd ytd net value share HS300 ytd
2020.01.01 / / 0.8707 0.6489 00.00%(4121.35)
2020.08.28 +8.52% +19.30% 1.0396 1 14.02%(4727.29)
2020.09.18 -1.87% +17.15% 1.0201 1 12.50%(4636.64)
2020.10.15 +3.55% +21.32% 1.0564 1 16.73%(4810.78)
2020.10.30 +2.12% +19.65% 1.0418 1 15.90%(4776.47)
2020.11.05 +4.24% +24.72% 1.0860 1 18.53%(4885.11)
2020.11.15 +5.25% +25.93% 1.0965 1 17.84%(4856.85)
2020.11.27 +3.59% +23.95% 1.0792 1 20.85%(4980.77)

proxy_next_upstream 对POST无效

需要添加non_idemponent,官方文档的说明是:
normally, requests with a non-idempotent method (POST, LOCK, PATCH) are not passed to the next server if a request has been sent to an upstream server (1.9.13); enabling this option explicitly allows retrying such requests;
意思是如果不加上non_idemponent,对POST这些不幂等的方法,出错是不会重试的。

猜测

按照严格RESTful设计,PUT和POST都是对资源的修改,比如新增一个用户、修改某个信息、增加积分等等。GET请求一般用于查询资源的情况,一般情况下都是幂等的,意味着不管执行多少次,结果都是一样的。当遇到5xx的时候,POST的请求如果没有使用事务,可能结束了,也可能没有结束,即便用了事务,也可能一定的几率会执行成功了,但没有正确返回。POST如果是用来新增一些资源,proxy_next_upstream可能会导致出现一些业务逻辑上的异常。
但很多场景下,出于加密或者其他考虑(如:前端不好支持等等),有些业务并不是按照严格的RESTful来实现的,如果后端做了一些充分的容错处理,支持接口幂等。那么是可以对POST等接口做一个失败重试,相应的配置要加上non_idemponent,就会生效。

爬虫的抓取错误

前言

一个网站在处理百度爬虫抓取情况的时候,遇到了大量的抓取错误,记录一下解决问题的过程和思路。
百度的解释是当百度spider访问服务器,进行tcp通信的时候,socket读写发生异常,导致数据不能正常返回。请检查服务器连接状况和防火墙设置是否符合预期
首先列出该网站的特征信息:香港机房,HTTPS请求,后端server为nginx。
先通过另外的域名,位于广州机房的网站,抓取若干次,发现还是偶发socket错误,同时将香港机房的请求由https改为http,抓取50次没有任何问题。故可以得出结论一:

  1. 不是因为机房位置问题导致的,而是因为HTTPS引起的,而具体是因为server配置不正确,还是受到GFW等干扰而引起的,不得而知。(内心os:某网站爬虫真垃圾)
    根据结论一,可以得出一个兜底策略,可以不用https,直接改用http,别搞这些幺蛾子了。

深入分析

本着继续挖掘https引起的问题,故还是深入研究一下问题所在。
首先怀疑Cipher suites与ocsp等问题,把这些安全配置逐个去掉,发现没有起到作用,还是有错误。
尝试过千万种可能之后,都开始怀疑人生了,最终得出一个结论,只有可能是百度爬虫自己的问题。于是把服务器的server直接关掉,抓取几次应该是得到443 Connection refused,但通过百度爬虫抓取,还是得到了socket 读写错误。(我服务器都关掉了,还是有这个错误,这只有可能是百度自己的错误了)
本以为能发现什么大的问题,找到服务器有什么常年存在的错误,结果是这个原因。

Vagrant 如何压缩空间

Vagrant

vagrant是个很好用的工具,可以用来工作流构建和管理虚拟机环境,常用的命令也很简单易用,可以配合VirtualBox来启动各类的虚拟机,简单易用。vagrant主要是负责镜像的管理,尤其是对于Mac系统来说,虚拟机比起Docker Desktop或者brew要更加方便管理、迁移、分享,可以更专注于linux做好事情,减少系统差异。另一个场景是当一个线上环境需要安装大量的依赖,如何对新人更加友好,如何使得所有的变更都能迅速同步给所有同学,vagrant也可以用于多环境的情况。
可以养成定期备份快照的好习惯,如果某些文件被误删,或者需要回滚一些操作的时候,就非常方便了。

如何压缩空间

随着使用时间的增长,vagrant的容器体积会不断变大,有一天可能会把宿主机的磁盘都占满。vagrant的容器类似docker,我认为他应该是会不断叠加层级,因为从观察来看,新建一个文件,再删除掉这个文件,镜像的大小并没有减少。他会有一些空间回收,但往往不是那么及时,那么从使用者的角度来说,如何让他更快回收空间,并且确保镜像尽可能小呢。
我罗列出我常用的几个步骤,其中//为注释的说明内容。

1
2
3
4
5
6
7
8
9
// 虚拟机内执行
// 清除所有的没用docker

docker system prune --volumes -a
// 对镜像做磁盘空间擦除

sudo dd if=/dev/zero of=/EMPTY bs=1M
sudo rm -f /EMPTY
// 宿主机执行
// 打包镜像

vagrant package --output package.box.20200214

这时候,已经压缩好的镜像就生成好了,也可以将该box分享给其他同学,作为一个内部测试环境等等。
把原虚拟机销毁(vagrant destory,不熟练的话,请谨慎使用),然后修改Vagrantfile,将文件名修改为刚刚打包好的box,通过vagrant up,虚拟机就又重新焕发新生了。

写在2020年代的开篇

2020年1月28日,2019-nCoV还在持续爆发与扩散,中国国务院延长了假期,阿富汗塔利班击落了美国CIA的客机(美国军方表示质疑),众多错综复杂的消息也预示着,2020年将是一个不平凡的一年。不平凡的一年,这句话套用到任何年份都可以,就像基金经理人总要在年终总结的时候,告诉所有投资人,他今年做了哪些正确的决定,帮助大家创造了多大的效益。

成长,过去一年学到的知识

拥抱不确定

学会拥抱不确定性,不确定性过去有、现在有、将来还会有,过去面对不确定性,我总是想着去避免,去对冲,君子不立于危墙之下,就是这个道理。但另一个道理又告诉我们,富贵险中求,人生得意须尽欢。不确定性中往往蕴含着机遇,如果一味消极对待,其实不利于我们个人或者财富增长。反而面对不确定性,应该更加积极、主动。说不定能有一番作为呢。

更加乐观、主动

无论悲观还是乐观,生活总是要继续的,不能说因为我悲观,接下来的日子我就不过了。既然悲观和乐观的结果都是一样的,那么何不乐观一点,至少自己可以开心一点。面对再大的困难和不确定性,可以做好最坏的准备,但要乐观应对。

用长期预测抵御短期预测风险

2019年,如果你在年初买入沪深300,到了年末卖出,她能赚40%以上。而多少聪明的基金管理人、分析师,都远远达不到这个收益,因为短期的预测会让他们认为沪深300已经高估了,到达了卖出点了。在证券市场中,把时间拉长看,现在永远都是低点。在漫漫人生路上,把时间拉长,现在永远都是起点。
短期预期其实往往会不准确,因为干扰因素太大了,就像我们很难预测下周一天气怎么样,温度如何,我参加考试能不能通过。但如果把时间拉长,一个地方的温度,晴天数量和阴天数量是确定的,只要我坚持考试,是一定会通过的,只是时间长短问题。股市是一定会涨的,只要你买了,不放弃,终有一日他会给你意想不到的回报。
所有的分析师中,最受欢迎的永远都是那些看涨的,如果涨了,说明他预测对了,如果不涨甚至跌了,至少他给了你安慰,给了你信心。反而是那些看跌的分析师,真的跌了,大部分人可能并不会感激他。
你的预期差,实现必然就差,反而不如对未来客观一点,最坏的可能你都想到了,还能再差到哪里呢?

未来,多一些思考

我想,我等,我期待,未来却不能因此安排。我遇见谁,会有怎样的对白,我等的人,他在多远的未来。
这是孙燕姿的遇见,面向2020年代,我们得明确自己想要达成什么目标,想成为一个怎样的人。现在就要开始着手准备应对下一个10年了。
TODO…

回顾与展望 2020

展望2020

这一年,看书还是没有看多少,去图书馆的次数还是下降了,背英语单词还是依旧只坚持了一小段时间,早睡早起也没有形成习惯。
这一年,还是继续贯彻了极简生活的理念,丢弃了很多不是必须的物品。
这一年,去了杨千嬅演唱会,去了很多地方旅游,去了香港多次露营与游玩。
这一年,离开了广告行业,重新出发。

回顾过往,继续制定2020年计划。

2020计划

家庭生活计划

  • 周三美食日
  • 周六活动日
  • 周日学习日

活动日:执行旅游计划中的短期任务、爬山、远足,因不可抗力可调整为其他积极向上的活动)
学习日:看书、学习

娱乐休闲计划

  • 日本旅游
  • 深圳游玩
  • 香港游玩
  • 澳门游玩
  • 短期旅游

每日时间表

  • 早睡早起,早起运动,早起喝一杯白开水
  • 每天一杯牛奶,吃早餐
  • 每天下午茶
  • 早晚刷牙洗脸洗澡洗头
  • 定期(每月两次)执行超市采购计划,保证家里任何时候都有吃的

财富增长计划

  • 扩大港股持股比例(目前20%,争取一年内扩大至50%)
  • 建立长效投资与长期投资策略(通过ETF+个股+低估行业股,盈利5%以上方可卖出)
  • 扩大可转债投入(尝试增持部分可转债)
  • 继续申购新股
  • 研究新的盈利渠道(含技术相关与非技术相关)

行为习惯提升计划

  • 不断跳出心理舒适区
  • 多聆听,多接纳多方意见
  • 多赞扬肯定对方
  • 严格要求自己,对他人要多宽容
  • 态度要温和
  • 增加对未来10年的思考,明确自己想要达成什么目标,想成为一个怎样的人

每周细分计划

  • 看2本书(细化书单:豆瓣托管
  • 看1部电影(豆瓣托管
  • 1次跑步运动
  • 1次爬山或远足
  • 看1集美剧(定期刷新剧目)
  • 看1次以上指定节目(细化列表)

2019计划与总结

总结语

过去的2019年,各项计划完成的还不错,实现了一定程度的自我提升,希望再接再厉。

家庭生活计划

  • 周三美食日 (吃顿大餐)✓(完成度80%)
  • 周四总结日 (回顾本周计划执行情况)✓(完成度20%)
  • 周六活动日 (执行旅游计划中的短期任务、爬山、远足,因不可抗力可调整为其他积极向上的活动) ✓(完成度20%)
  • 周日学习日 (看书、学习) ✓(完成度30%)

娱乐休闲计划

  • 日本旅游 ×
  • 深圳游玩 × (去了,但不是游玩)
  • 香港游玩 ✓
  • 澳门游玩 ✓
  • 短期旅游 ✓ (广西南宁、湖南郴州、四川成都与峨眉山、湖南长沙、湖北武汉)

每日时间表

  • 早起运动,不能起太晚 ✓ (30%)
  • 早起喝一杯白开水 ✓
  • 刷牙听歌洗澡洗头 ✓
  • 出门上班,希望能直接骑共享单车到地铁站,不要坐公交换乘 ×
  • 地铁上通过Telegram阅读过去一天技术领域的发展与新闻 ✓
  • 上班做好每天工作计划 ×
  • 睡前喝杯牛奶,刷牙洗脸洗澡上床 ✓

财富增长计划

  • 提升技术能力,不断学习新技术,并在生产环境中实践 ✓
  • 在证券投资方面,继续原有的持股策略,继续加大投入,并获取更大的财富 ✓
  • 开通港股通,通过上交所买卖香港地区上市的股票 ✓
  • 控制流动资金的比例,并做好流动资金增长计划 ✓

行为习惯提升计划

  • 不断跳出心理舒适区 ✓
  • 多聆听,多接纳多方意见 ✓
  • 多赞扬肯定对方 ✓
  • 严格要求自己,对他人要多宽容 ✓
  • 态度要温和 ✓

体重增长计划

  • 定期执行超市采购计划,保证家里任何时候都有吃的 ✓ (70%)
  • 每天下午增加一个下午茶 ✓ (50%)
  • 要多吃,辅助定期运动 ✓ (已完成体重目标,60KG)

每周细分计划

  • 看2本书(细化) ×
  • 看1部电影 ✓
  • 1次跑步运动 ×
  • 1次爬山或远足 ×
  • 看1集美剧(定期刷新剧目) ×
  • 看1次以上指定节目(细化列表)

Go的CICD、Module、构建、测试

前言

最近在做一些项目的重构,将一些服务和功能逻辑逐渐迁移到Go,或者通过来来重写一遍。一是优化相关逻辑,删减部分代码和逻辑功能,二是之前的架构和服务的部署方式,已经远远落后于时代,不论是开发效率、代码检查、部署与发布上线等等,都消耗了大量不必要的时间,严重影响开发效率。

CICD有很多方案,如Drone、Jenkins、Gitlab等,过往的经历告诉我,不能单纯为了Go而使用Go,除非Go编写的服务确实好用,足够成果。从简单实现的角度,通过Gitlab来完成,公司的部署环境有些特殊,有安全层面的考虑,是不能直接访问外网的,包括但不限于docker pull、go get、go mod download等等。如何加快CI的执行速度,如何使得构建后的镜像最小,减少传输带宽,如何复用Go相关的package,减少多次构建需要重新下载等等,就这些问题分享一下我的思路和做法。

基本概念

CI的全称是Continuous Integration,表示持续集成。
在常见的开发场景中,开发人员可能会频繁地提交自己的代码,这些提交的代码,先通过提交Merge Request,再经过Code Review合并到主分支,最终经过测试、灰度,然后上线。
CI主要作用是保障Code Review之前的代码,通过一系列自动化测试验证代码的质量和Bug,提升代码质量,并根据测试结果,向开发人员或团队进行预警。
CI同时也会检查Merge之后的代码是否正常,符合一切预期。

CI的好处

减少Review代码同学的压力与工作量

假若有一个同学经常写bug,他的代码每次都能测出一大堆问题,他自己也没有意识主动去检查自己的代码能不能跑起来,全靠你帮他发现,有些变量名写错了又不好发现。你每次提醒他,他都能快速意识到自己的问题,但下次还是可能会遇到。你作为负责Review代码的同事,面对大量的改动,是否有些担心?

制定并维护团队代码规范

你是一个技术管理,你为团队制定了一系列规范,如上线前自测、各个模块要有相应的设计与说明文档、数据库的DDL变更要及时维护在代码库中、代码的测试用例覆盖率要达到50%。经过一段时间,你发现这些问题并没有很好的改善,如何能够让一起定下规范严格执行呢?

快速迭代,小步快跑

在开发过程中,当你需要改到一些你不是非常熟悉的模块,总担心自己会不会改错一些逻辑,当初写这个逻辑的同学可能也不在了,那么如何确保自己修改的内容有效呢?产品经理又催得紧要求今晚上线?

持续改进与提升

在开发中,你写了一个模块,当时是有较高的QPS,有正常的结果返回,结果随着项目的迭代,你发现该模块变得越来越慢。你发现当初大家约定的Code Style,慢慢也开始变样了,一个方法里面有多种实现,怎么办?

怎么做

CI可以代码库设置了一个较高的标准,所有不符合规范,有语法错误的代码,都可以直接拒绝合并至主分支。
根据不同语言,可以检查语法错误、变量定义但未使用、重复引入package、未格式化、scheme定义不正确、测试用例等等。

  • 代码提交之后,将会根据分支进行不同的自动化处理:
    • 代码常规检查(Code Style、代码底层实现Code Lint等,检测所有语法错误、告警、未使用代码等)
    • 自动化单元测试(输出结果与测试覆盖率,对代码逻辑检测)
    • 复杂用例覆盖测试(构造MySQL、Redis等数据,对业务逻辑检测)
  • 代码Review阶段,由于上一阶段已经对代码质量做了基本检查,相关reviewer只需要review逻辑和实现方案即可。
  • 合并待发布阶段,会自动检查是否冲突,没有冲突自动合并,有冲突则通知相关同学人工介入进行git rebase等处理。
  • 构建阶段,CI可以根据Dockerfile自动构建并将分支推送至镜像仓库。
  • 部署阶段,发布到测试环境、预发环境、线上环境,灰度上线等。

根据上述的过程,我们其实已经将各类简单的脚本变成了一系列自动化执行的流程,也带来了标准化,可以直接一键应用到所有的项目。这就是持续集成,在我们背后无声无息的进行着,减少了大量极其浪费资源的人工执行,对研发团队来说,解决了大量的资源,对研发同学来说,可以减少review代码、测试的时间,也可以更加自信上线,快速迭代,更加专注产品、业务打磨。

在研发流程中,每个人只需要关注自己开发的功能,并编写测试用例来确保自己的功能(逻辑)不受其他人提交的代码影响,同时确保自己没有影响到别人功能(逻辑),其他代码质量、逻辑检验由CI完成。

Gitlab Runner

Gitlab其实是能够关联一些很好的工具,比如一些需求管理、Wiki等等。其中Gitlab CI可以做一些代码检查、跑测试用例、自动构建等等工作。我们使用的代码仓库也是Gitlab,那么使用Gitlab直接做CI,是最顺其自然,最方便的事情了。
常见的大项目下面会有很多微服务,各个服务之间存在交叉影响,能够完善测试各个数据库、缓存之间的交互,模拟流量和数据,在一个小型环境中跑通全部测试用例,这对开发效率与自动化是非常有意义的。
Gitlab CI依靠Gitlab Runner来运行,Gitlab Runner可以通过docker容器来运行,可以配置最大任务数限制,当有git commit的时候,他就会拉取最新的代码,对代码做一些CICD,这些配置命令,可以写在.gitlab-ci.yml,Gitlab Runner会按这个配置来执行这些计划任务。当测试不通过的时候,会发送邮件到对应的邮箱。
对应的Dockerfile其实可以很简单,只需要加入配置文件即可:

1
2
3
FROM gitlab/gitlab-runner:latest
WORKDIR /
COPY ./config.toml /etc/gitlab-runner

Gitlab Runner的注册其实也很简单,可以先起一个空镜像容器,运行sudo gitlab-ci-multi-runner register,输入gitlab的链接、Token、Runner的名字、类型等等,就会生成一个如下的配置,这个配置可以保存下来,将其提交到镜像中,这个容器就可以发布到线上了。其中concurrent表示同时运行的任务数,pull_policy等拉取规则可以定制化修改一下。
需要特别留意的是,docker的sock文件是需要挂载进镜像的,以便Gitlab Runner能够运行各类docker镜像来跑Gitlab的CI。
配置参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
concurrent = 6
check_interval = 0

[session_server]
session_timeout = 1800

[[runners]]
name = "CI-name"
url = "https://git.y.cn/"
token = "xxxx"
executor = "docker"
[runners.custom_build_dir]
[runners.docker]
tls_verify = false
image = "golang:lastest"
memory = "4g"
cpus = "4"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
pull_policy = "if-not-present"
shm_size = 0
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.custom]
run_exec = ""

通过docker命令可以将gitlab runner启动docker run --net=host --rm -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest,这时候任何满足规则的commit都会触发Gitlab CI。可以根据不同的语言来配置不同的.gitlab-ci.yml,根据项目的特性来编写一些定制化的检查。如if-not-present等配置可以在docker不存在的时候,才拉取相关分支。

镜像构建

docker对大家来讲,已经非常常见了,并不是什么新潮的东西。但经常能看到大家构建出来的镜像,并不是最佳实践,构建出来的镜像包含了很多不必要的内容,尤其是如果这个镜像作为基础镜像需要给其他人使用的,那么这就很不负责任了。我只是跑一个简单的服务,你却需要我docker pull一个4GB的镜像,要花大量时间等待,尤其是网络不好的时候,编译发布也不方便。
如何构建出一个体积最小的镜像,这是我们需要对自己的基本要求,也是一个基本良心。

合并多个RUN语句

看过大量的Dockerfile都会发现,很多Dockerfile都用了一些奇怪的技巧,用了大量的&&,来替代多个RUN,因为根据docker镜像构建的原理,其实就和git commit一样,是逐层叠加的,层数越多,占用的空间也就会越大。

多阶段构建

若多个阶段使用同个镜像,那么多阶段构建等于压缩了中间多个层,将他们合并到一个层。如我们在git中add了一个较大的文件,然后下一次commit的时候把他删除了,虽然我们拉取这个git代码时,这个文件不在了,但是他一直存在在这个仓库,还是会占用较大的体积。所以我们做的ADD data ./RUN rm ./data,其实就是个障眼法,掩耳盗铃,这个data还是存在于镜像中。所以可以在移除掉之后,通过多阶段构建,将结果COPY一份,不需要中间过程产生的文件等。

1
2
3
4
5
6
7
FROM golang:1.12 as build-env
WORKDIR /go/src/y.cn/some
ADD . ./
RUN CGO_ENABLED=0 GO111MODULE=on go build -ldflags -o some y.cn/some
FROM golang:1.12
WORKDIR /go
COPY --from=build-env /go/src/y.cn/some/some ./

上述脚本已经将所编译的some,copy到一个新的镜像,这就是一个简单合并各个镜像层。

distroless

这是Google提供的一个容器库,支持多个主流语言,仅包含该语言所需的环境、二进制文件和脚本,他甚至shell都没有,所以你想attach进去容器都是不行的,当docker相关的日志和监控比较完善的时候,才有条件使用。

Alpine

Alpine是各类镜像使用最多的基础镜像,Alpine是一个轻量级、面向安全的linux发行版,也是比较实用的镜像中最小的一个,如最新的3.10版本,仅有5MB大小。如Go开发的脚本、服务,可以将二进制文件构建好后,copy到一个干净的Alpine镜像中,这样得到的镜像大小就特别小,约等于Alpine镜像大小+Go二进制包大小。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM golang:1.12 as build-env
WORKDIR /go/src/y.cn
RUN git clone git@y.cn:some.git --depth 1 \
&& cd some \
&& CGO_ENABLED=0 GO111MODULE=on go build -ldflags -o some y.cn/some

FROM alpine:3.10
RUN apk update \
&& apk upgrade \
&& apk add --no-cache \
ca-certificates \
&& update-ca-certificates 2>/dev/null || true
WORKDIR /data
COPY --from=build-env /go/src/y.cn/some/some ./
ADD conf.yml ./etc
ENTRYPOINT ["./SERVICE_NAME"]

这样就是得到了一个非常小的镜像,通常10~30MB的一个镜像就能将服务容器跑起来。因为Alpine是非常小的,所以并不支持gcc环境等,需要CGO_ENABLED=0关闭CGO,不然你会发现编译后的二进制包无法运行。

Go Module

Go Mod是Go新引入的一个包管理工具,实际上谈不上成熟,官方也没有推荐使用,但go mod毕竟还是未来,提前做好准备还是十分必要的。原先的GoVendor/GoDep的包管理,并未在部署上尝试特别大的问题。但由于go mod下载的package其实是放在你的$GOPATH/pkg/mod中,并没有加载到你的项目中,go mod vendor我执行之后也并未成果,暂时没找到什么原因。
所以使用go mod也带来一个挑战,事实上很多服务器的环境,并没有一个放开的公网网络,若 package之前未层使用过,go build或者go mod download的时候,都需要下载对应的package。意味着给Go项目的CICD、测试带来了一些影响。
在如何解决这类问题方面,分享一下我的看法,无非就是线上环境的$GOPATH/pkg/mod需要提前准备好相关package的问题。想到了可以通过golang:latest作为基础镜像,把项目git仓库ADD进去,go mod download下载所有所需的package,然后删掉git库,commit这个镜像到线上,那线上就有pkg/mod了。然后这个镜像就可以作为线上构建的基础镜像,包含所需的库,并且提供一些必备的工具脚本。

1
2
3
4
5
6
7
8
9
10
11
FROM golang:1.13 as build-env
WORKDIR /go/src/y.cn/some
COPY go.mod ./
RUN GO111MODULE=on go mod download
FROM golang:1.13
WORKDIR /go
RUN apt-get update && apt-get install -y mariadb-client > /dev/null
RUN go get golang.org/x/tools/cmd/goimports \
&& go get honnef.co/go/tools/cmd/staticcheck \
&& go get github.com/golangci/golangci-lint/cmd/golangci-lint
COPY --from=build-env /go/ ./

最近一些感想

最近还是提了离职,其实这个想法考虑了很长时间,也确实是经过慎重思考之后才决定的。时间不长不短,不到两年的时间内,对广告行业有了长足的认识,在期间也通过实际行动做到了用科技改变生活、促进流水提升,能制定适宜业务的技术实现方案、架构调整等等,2年来最大的收获不仅仅是技术能力的提升,更重要的是业务思维。离开广告行业其实也是有些不舍的,但套用另一个同事的话来讲,哪有什么不舍的,都是打工,找到自己最舒适、最开心的活法才是更重要的。
临走之际,其实往往都会有很多谈话。譬如聊聊你在公司觉得有哪些让你觉得不舒服的地方、你觉得可以改进的问题、你对这个业务的看法、离职的原因是什么,甚至会问问你有没有挽回的余地。离职的理由往往千万种,上班太早,领导太傻逼,工作太无聊,没啥成就感,工作安排不合理,业务太烦,高层不靠谱,不懂管理、看不到希望、钱没给够等等都是理由。
一份工作,主要靠三点来维持,相处的人、工作内容、薪酬,三者缺二不可,只要任意两点崩塌,就没法继续待下去了。2年前的一个同事群,一共9个人,现在已经只剩下2个人。这个行业流动性还是很高的,没有谁离了谁就不行了。
薪酬、职级没调整,是比较直接的原因之一,不少同事在有米的时间已经很长很长了,对这些同事而言,有米是第一份工作,也是倾注了蛮多情感的家,有米的技术栈其实都比较前沿,技术同学的能力都还不错,广州的技术公司并不多,圈子就这么大,有米的同学们还是很有市场竞争力。早些年的有米还是很不错的,弹性的考勤制度、双休、少加班(至少不强制),这种制度比较考验主动性,是建立在一个互相信任的基础上的,相对扁平化的管理,自由的环境。近两年公司的一系列战略性错误,对业务、产品的短视,不接底气的制度,都是近两年人才流失、业务方向迷失的根本原因。一个公司堕落的开始,都是注重考勤,开始过分关注员工的上班时间,加班情况,又缺乏激励,缺乏对自身实力与定位的了解,没有关注员工的自身发展,没有一个适宜的绩效评定。不懂业务的高层,不接地气的考勤制度,不尊重市场规律,朝更暮改的战略方向。高层想法变得太快,过于随意,普通同学其实是很累的,并不是大家不够努力,而且方向不对。有米并不是一个大公司,但似乎所有大公司有的弊病,都不少。
其实不管是平时的工作,还是离职的例行问答,我的回答都是很直接、直率的,也会经常与leader交换一下看法和意见,沟通所有关键问题的想法和gap,并解决这类问题。这些都是中层能直接解决的问题,并且都解决的不错,我们也找到很好的协作方式,确实做出了较好的成绩。但高层其实很少聆听他人的意见,不管是业务方向上还是公司经营方式,反馈最多的考勤问题,就直接置之不理,带着这种傲慢与偏见。广州的早高峰特别挤,挤到你怀疑人生,挤到你根本就不想去上班,然而你去到公司楼下,还要多排队15-20分钟等电梯,可能你会怀疑早到这点时间能做什么,毕竟同学们付出了大于半个小时通勤时间的成本。
广告行业其实是一个非常朝气蓬勃的行业,当你自己遇到问题了,可以了解市场和同行的情况,你应该反思自己是不是哪里做得不对,而不是责怪行情、市场,往往不是这个市场不行了,而是你不行了。看看别家的年会与年终奖,不要责怪大环境,经济再差也有大把赚钱的公司,好的技术根本不愁工作。找不到工作的都是各个行业的底层。

技术的价值

以前我一直在思考技术的价值在哪里,有哪些别人所不具备的优势,最近似乎得到了一些答案。纵观很多技术经理、技术总监,有的很懂业务,但缺乏技术架构认识,很难管住其他的技术,想法、关注点与实际也有些差距,就很难维持一个高效稳定业务系统,有的技术可能很强,但缺乏对业务的理解,重心放在技术代码的提升,如何规范化,如何提升技术的能力等等,但其实会发现,如果你缺乏对业务的了解,你的技术再好,方向错了,业务场景发生了变化,再多的努力都是无用功。做技术、做架构,我认为都不应该脱离业务思维,脱离了业务思维的架构师,往往会给你一个很高大上的架构,他确实很好,技术也很厉害,但不适合这个业务。
作为技术负责,如何使用有限的资源,做更多、更大的事情,这才是我们的价值体现。资源永远都是稀缺的,如果有1000个bug和100个紧急的业务需求,你如何判断哪50个是最重要、最迫切的。这要求你需要具备行业敏锐度,有判断能力,了解你的业务场景是面向哪些用户,你的用户群体最在意的是什么?你要建造的是一栋大厦,而不仅仅是一堵墙,这面墙有些瑕疵,但并不影响你这栋大楼的按期交付,别把大量的时间耗费在这些小事上面。
靠谱的程序员很难得,你交给他一个需求,他会先判断这个需求的合理性、场景,有争议部分事先充分沟通,制定一个适宜的方案,跟需求方确认这个方案的预期,开发,并且做足测试,确认功能的完整性,和需求方一起验收需求,通过数据回顾功能的作用,并能主动做出一些优化、提出一些建议。能做到这些的,都是非常靠谱的技术,你会很信任他,愿意把更多更重要的事情,交给他负责。

对海外市场看法

海外其实是一个非常有前景的市场,毕竟除了中国,其他的都算海外,市场够大(😄),尤其是新兴市场,你可以照搬你以往的成功经验,适当改造,通过一些接地气的方案,就能在一个新的市场收割一波。再从汇率角度考虑,比如你在美国市场赚2美元,换回来就是14元了,比起外国公司,坐拥廉价的中国劳动力,还能在美国、欧洲等这些海外市场同台竞争,这就是一种网络倾销。
只要你的业务广,这就像是一个风投,多方押注,总会有一个成功的,东方不亮西方亮,这个市场不成功,换一个市场再尝试一次。成功一个就能收回一切成本,并且大赚一笔,而且没有什么市场准入、政策风险。

回顾与展望 2019

2018年总结

这一年的目标还是没有实现,看书没有看多少,甚至去图书馆的次数也少了很多,背英语单词坚持了小半年,但没有坚持下去。早睡早起也没有做好,还是比较迟才睡觉,这里有工作的原因,也有自己生活习惯的原因。但在极简方面做得还是不错,减少了很多不需要、多余的物品。演唱会没有去,这一年去了日本旅游,也去了成都旅游,在峨眉山看雪,也去了香港若干次,其中两次在香港徒步与露营,走了香港比较著名的麦理浩径,也去了香港最南的蒲台岛。
这一年也取得了一些不便公开的成果。这一年在业务上更加深入了,对广告行业也有了一定的了解,但感觉在技术上感觉没有特别大的进步、成就感,所以接下来要加强技术学习。

回顾过往,继续制定2019年计划。

2019计划

家庭生活计划

  • 周三美食日 (吃顿大餐)
  • 周四总结日 (回顾本周计划执行情况)
  • 周六活动日 (执行旅游计划中的短期任务、爬山、远足,因不可抗力可调整为其他积极向上的活动)
  • 周日学习日 (看书、学习)

娱乐休闲计划

  • 日本旅游
  • 深圳游玩
  • 香港游玩
  • 澳门游玩
  • 短期旅游(城市本地景点活动探索)

每日时间表

  • 早起运动,不能起太晚
  • 早起喝一杯白开水
  • 刷牙听歌洗澡洗头
  • 出门上班,希望能直接骑共享单车到地铁站,不要坐公交换乘
  • 地铁上通过Telegram阅读过去一天技术领域的发展与新闻
  • 上班做好每天工作计划
  • 睡前喝杯牛奶,刷牙洗脸洗澡上床

财富增长计划

  • 提升技术能力,不断学习新技术,并在生产环境中实践
  • 在证券投资方面,继续原有的持股策略,继续加大投入,并获取更大的财富
  • 开通港股通,通过上交所买卖香港地区上市的股票
  • 控制流动资金的比例,并做好流动资金增长计划

行为习惯提升计划

  • 不断跳出心理舒适区
  • 多聆听,多接纳多方意见
  • 多赞扬肯定对方
  • 严格要求自己,对他人要多宽容
  • 态度要温和

体重增长计划

  • 定期执行超市采购计划,保证家里任何时候都有吃的
  • 每天下午增加一个下午茶
  • 要多吃,辅助定期运动

每周细分计划

  • 看2本书(细化)
  • 看1部电影
  • 1次跑步运动
  • 1次爬山或远足
  • 看1集美剧(定期刷新剧目)
  • 看1次以上指定节目(细化列表)

写于证券投资5年之余

上大学时,学业之余总是特别的乏味。了解我的人都知道,我几乎不玩游戏,大学四年间,我拜读了图书馆所有我想看的书籍。出于对新书的偏好,我认为一本书如果真的有价值,那么一定会再版的,读旧书容易浪费时间。在每周五图书馆上新日,我都会把感兴趣的新书一扫而空。我的业余兴趣特别广泛、不论是数学、计算机、经济学、心理学等社科都有涉猎,从小就读了很多很杂很杂,可能对我一生都不会有什么作用的书。
真正接触证券投资还是从2013年开始,当我满足开立账户的条件,就去开立了证券账户。性格上,我是还是倾向于风险规避(risk aversion),我更倾向于承担更小的风险,实现相对没有那么大,但不那么低的年化收益率。证券投资领域,外行跟内行差距很大,认识的很多同事,虽然也买卖股票很多年,但其实并没有总结经验,也没有形成自己的投资策略或方法论。这种就像古代的中国人,只注重应用,却没有提炼出其基础理论,永远只能停留在“野蛮人”时代。
证券投资之前,投资过一段时间比特币,毕竟当时未成年,A股开户都开不了,这段经历,让我有了诸多体会,也快速进入了投资市场。大学期间参与了大学一个技术实验室,在技术之余,我们会聊及历史、人文、投资。通过一师兄了解到了比特币,当时比特币市值500元,于是我尝试性的投资了一笔小钱,最终涨到700元的时候,我就hold不住卖掉了,这也算是实现了40%的一次收益率,这段时间仅仅几个星期。多年后,比特币涨到了1万多美元,当年交易剩余的尾数,也价值几十元了。
投资证券这几年,倒是从未想到要通过证券实现财富自由,这也不现实,给自己的定位就是挣点小钱,争取不大亏,为后面的更多年,积累经验。我的操作频率很低,可能一年就买卖几次,一个月也不见得有一次买卖。操作手法也很简单,选一些市值高、市净率低、优质的上市公司,价格低的时候买,涨了就卖,套了就隔一段时间补一些,反正自己的钱又不急用,挣那么多钱也没处花。

转变

从2013年开立证券账户,转眼间5年时间已经过去,已经成为名副其实的老股民了。今年开始计划承担更大的风险,尝试一些过去没有做的操作。