Go 初学者的一些琐碎问题

写了一个 Go 语言的 hello world 来练手。可以在 github 上查看 https://github.com/kelvinblood/ping-pong-go

这个 hello world 主要是 server-client 形式的应用。 功能很简单, client 给 server 发送 ping/pong 的消息,server 回复 pong/ping 消息。

这篇文章记录一些写这个hello world 时遇到的小问题,初学者可以看一看。文末附上源代码。

单双引号

字符串由双引号包裹。单引号内只能有一个字符,例如’c’,输出会返回这个字符的ascii码

字符串相同却不相等

一开始特别奇怪,明明两个变量一摸一样,字符串判断时候却认为他们不一样。。。颇有真假孙悟空的感觉。

接下来就是寻找解决办法的过程

  • 是不是类型不一样:

    • 使用 reflect 包的 reflect.TypeOf 方法,发现都是 string 类型
    • switch 下用Go的空接口: // 建一个函数t 设置参数i 的类型为空接口,空接口可以接受任何数据类型 func t(i interface{}) { //函数t 有一个参数i switch i.(type) { //多选语句switch case string: //是字符时做的事情 case int: //是整数时做的事情 } return }

        // _i.(type)_ 只能在switch中使用
      
  • 是不是像 c 一样数组还有 \n 的诡异操作:

    查看str的长度:

    终于发现了问题所在。

所以解决的办法是只将前面一部分拿来比较:

if strings.EqualFold(recieve[0:len(recieveDefault)],recieveDefault) {
	go send(conn,sendDefault)
  	}else if strings.EqualFold(recieve[0:len(sendDefault)],sendDefault) {
	go send(conn,recieveDefault)
  	}

这里有一篇对比 golang 的 string和[]byte 的区别,写的很好:golang string和[]byte的对比

既然string就是一系列字节,而[]byte也可以表达一系列字节,那么实际运用中应当如何取舍?

  • string可以直接比较,而[]byte不可以,所以[]byte不可以当map的key值。
  • 因为无法修改string中的某个字符,需要粒度小到操作一个字符时,用[]byte。
  • string值不可为nil,所以如果你想要通过返回nil表达额外的含义,就用[]byte。
  • []byte切片这么灵活,想要用切片的特性就用[]byte。
  • 需要大量字符串处理的时候用[]byte,性能好很多。

用到的包

可以直接查看官方文档: https://golang.org/pkg/

  • “fmt”
  • “net”
  • “strings”
  • “time”
  • “math/rand”
  • “reflect”

一些参考资料:

Go语言fmt包Printf方法详解

附源代码

package main

import (
    "fmt"
    "net"
    "strings"
    "time"
    "math/rand"
//    "reflect"
)

func main(){
	_, isClient,err := clientPre()
	
        if err != nil {
		server()
		return
	}

	if isClient {
		client()
		return
	}

	server()
}

func clientPre()(conn net.Conn, isClient bool, err error) {
    //打开连接:
    conn, err = net.Dial("tcp", "localhost:18080")
    if err != nil {
	isClient = false
        fmt.Println(err.Error())
        return
    }

    isClient = true

    conn.Close()
    return
}

func client(){
	sendContent := [2]string{"ping","pong"}
    conn, err := net.Dial("tcp", "localhost:18080")

    fmt.Println("client: ",conn.LocalAddr(),"server: ",conn.RemoteAddr()," ",rand.Intn(100)," ",sendContent[rand.Intn(100)%2])
	
	_,err = send(conn,sendContent[rand.Intn(100)%2])

	if err != nil {
        	fmt.Println(err.Error())
		return
	}
	
    doServerStuff(conn)
    return
}

func server() {
    listener, err := net.Listen("tcp", "0.0.0.0:18080")
    fmt.Println("server listen 18080")
    if err != nil {
        fmt.Println("开启端口错误,终止进程", err.Error())
        return //终止程序
    }
    // 监听并接受来自客户端的连接
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting", err.Error())
            return // 终止程序
        }

        go doServerStuff(conn)
    }
}

func doServerStuff(conn net.Conn) {
    count := 0
    recieveDefault := "ping"
    sendDefault := "pong"

    for {
	count += 1
	recieve,err := rev(conn)

        if err != nil {
            return 
        }

  	fmt.Printf("%d ", count)
  	if strings.EqualFold(recieve[0:len(recieveDefault)],recieveDefault) {
		go send(conn,sendDefault)
  	}else if strings.EqualFold(recieve[0:len(sendDefault)],sendDefault) {
		go send(conn,recieveDefault)
  	}
    }
}

func send(conn net.Conn, content string)(send string, err error){
	time.Sleep(100 * time.Millisecond)
        _, err = conn.Write([]byte(content))
	if err != nil {
		fmt.Println("发送错误.", err.Error())
		return
	}
	send = content

	fmt.Println("send", send)

	return
}

func rev(conn net.Conn)(recieve string, err error){
        buf := make([]byte, 512)
        _, err = conn.Read(buf)
        if err != nil {
	    fmt.Println("接收停止,原因:", err.Error())
            return 
        }

	recieve = string(buf)
  	fmt.Printf("recieving: %v ", recieve) 

	return
}

Rancher 上线应用商店的基本流程

在 Rancher 中,编写好 docker-commpose.yaml 和 rancher-compose.yaml 后,我们就可以部署应用了。 然而当我们给客户提供容器模板时候,更简单的方式还是走应用商店。

Rancher提供了一个应用商店,通过商店中的应用程序模版的可以简化部署复杂应用的过程。这篇文章简单记录一下创建私有应用商店的步骤。

可以查看 github 上的社区商店帮助理解:https://github.com/rancher/community-catalog

  • Cattle 调度引擎:templates文件夹
  • Swarm 调度引擎: swarm-templates文件夹
  • Mesos 调度引擎: mesos-templates文件夹

私有商店项目

-- templates (Or any of templates folder)
  |-- cloudflare
  |   |-- 0
  |   |   |-- docker-compose.yml
  |   |   |-- rancher-compose.yml
  |   |-- 1
  |   |   |-- docker-compose.yml
  |   |   |-- rancher-compose.yml
  |   |-- catalogIcon-cloudflare.svg
  |   |-- config.yml
...
  • 私有仓库至少要包含一个模板目录,明明为前文描述的那几个templates。这个目录确定 Rancher 的调用引擎。
  • 在这个templates目录下就放置我们的应用模板了。假设是 cloudflare
  • cloudflare文件夹中包含0、1、2等文件夹。第一个版本为0,后续每个版本加1。每增加一个新版本的文件夹,你就可以使用这个新版本的应用模版来升级应用了。另外,你也可直接更新0文件夹中的内容并重新部署应用。
  • cloudflare文件夹中还包含两个文件,一个是 config.yml,包含了应用模板的详细信息。另一个是模板的logo,以 catalogIcon- 开头。

以我的 hello world 为例,config.yml 的内容如下,单词的解释也蛮清楚得了:

name: Pingpong
description: kelu's ping pong hello world
version: v0.01
category: Entertainment
maintainer: kelvin blood <xxx@xxx.org>

将这个项目部署到 Rancher 可以访问的 git 服务器上,在 Rancher 设置中添加好就可以使用了。可以查看我的: https://github.com/kelvinblood/community-catalog/tree/init

Rancher 添加

在 Rancher 中一共有四种应用商店:

  • 个人应用商店
  • 共享应用商店
  • 社区应用商店
  • 官方应用商店

作为管理员可以将社区和官方应用商店关闭掉。如果以管理员身份添加的的应用商店,即为共享应用商店:

个人用户添加的应用商店入口在这:

参考资料


rancher-compose.yml

查了很多官方的文档,也没有见到 rancher-compose.yml reference. 可以从官方和社区的 rancher-compose.yml 里找到一些案例来学习一下。https://github.com/rancher/community-catalog

这个项目实际上是 Rancher Catelog 的项目。每个应用基本上就是 docker-compose.yml 和 rancher-compose.yml 构成。这两个文件定义了整个应用。同时我们也可以从老环境的应用中导出这两个文件,新环境直接部署即可使用,非常的便利。

询问了 Rancher 官方的开发者,他们的建议是理解概念以后使用 UI 界面进行配置后导出,不建议直接编写。

最简单的例子如下:

# Reference the service that you want to extend
version: '2'
services:
  web:
    scale: 2
  db:
    scale: 1

参考资料


美团点评业务之技术解密,日均请求数十亿次的容器平台 - infoq

转载自 InfoQ

本文介绍美团点评的 Docker 容器集群管理平台(以下简称容器平台)。该平台始于 2015 年,基于美团云的基础架构和组件而开发的 Docker 容器集群管理平台。目前该平台为美团点评的外卖、酒店、到店、猫眼等十几个事业部提供容器计算服务,承载线上业务数百个,容器实例超过 3 万个,日均线上请求超过 45 亿次,业务类型涵盖 Web、数据库、缓存、消息队列等等。

容器到来之前

在容器平台实施之前,美团点评的所有业务都是运行在美团私有云提供的虚拟机之上。美团点评大部分的线上业务都是面向消费者和商家的,业务类型多样,弹性的时间、频度也不尽相同,这对弹性服务提出了很高的要求。

在这一点上,虚拟机已经难以满足需求,主要体现以下两点:

  • 虚拟机弹性能力较弱,部署效率低,认为干预较多,可靠性差。

  • IT 成本高。由于虚拟机弹性能力较弱,业务部门为了应对流量高峰和突发流量,普遍采用预留大量机器和服务实例的做法。资源没有得到充分使用产生浪费。

容器时代

架构设计

美团点评将容器管理平台视作一种云计算模式,因此云计算的架构同样适用于容器。

如前所述,容器平台的架构依托于美团私有云现有架构,其中私有云的大部分组件可以直接复用或者经过少量扩展开发,如图所示。


360 的容器化之路

【编者的话】容器化技术作为“搅局者”,势必面临适配公司已有架构的挑战,本文将为大家介绍360如何让Docker落地。主要包括三方面内容:一,结合公司业务特点,如何使Docker适配现有技术架构 ,完成线上环境快速部署扩容;二,“让产品失败的更廉价”,使用Docker构建PaaS环境加速中小业务快速孵化上线;三,使用Docker技术,在构建持续集成环境方面的一些积累。

容器化技术作为“搅局者”,势必面临适配公司已有架构的挑战,本文将为大家介绍360如何让Docker落地。主要包括三方面内容:一,结合公司业务特点,如何使Docker适配现有技术架构 ,完成线上环境快速部署扩容;二,“让产品失败的更廉价”,使用Docker构建PaaS环境加速中小业务快速孵化上线;三,使用Docker技术,在构建持续集成环境方面的一些积累。

以Docker为主的容器化技术现在可谓风生水起,大家都觉得它可能会颠覆整个IT格局。我们刚开始接触到Docker的时候也觉得它非常好,有很多优点吸引我们。因为它的颠覆性我们称它为“搅局者”。

改造“搅局者”Docker

我们先来看看这位搅局者的优点:

  1. Namespace、CGroups虚拟化, 相比传统虚拟化会有更好性能,反映在生产环境中就是能更大程度的利用资源。
  2. 启动速度快,虚拟机最快也得30秒-1分钟,它的启动创建都是秒级。
  3. 镜像分层技术,解决了快速变更环境的问题。

这些优点很吸引我们,我们非常希望把它用在生产环境中,但是我们发现理想很美好,现实很残酷。我们之前基础架构都是使用传统虚拟机化技术就是虚拟机。我们要使用Docker就会面临这几个问题 :

  1. 不能SSH,紧急问题怎么排查?
  2. 怎么监控?
  3. 基础服务如何对接?
  4. 最重要的问题: 这东西稳定么,线上业务当然不能出问题。

所以,在应用Docker的时候,我们犯了犹豫,因为按照它推荐的方式,我们无法直接立马就在线上业务使用。因为Docker本身也对业务的架构设计有一定要求,比如我们常说的容器无状态,容器中不要留中间数据。我们发现公司的业务架构改造起来困难很大,涉及到方方面面,所以我们决定要Docker去适应公司的架构。

接下来我们就是要解决Docker技术”落地”的问题。

我们对Docker改造点主要有:

  1. 容器内部绑定独立IP。
  2. 容器内部开启多进程服务。
  3. 自动添加监控。
  4. CPU配额硬限制。
  5. 容器绑定独立IP这样外部可直接SSH了。

我们考虑在容器内部运行多个进程服务,因为默认容器只开启一个进程,这无法满足我们要求,所以我们大胆进行了改造。我们甚至在镜像里实现了chkconfig让以前的RPM包原生可用。

自动添加监控让创建的容器自动添加到Zabbix中。CPU配额硬限制 Docker 1.7版本已经支持了,我们在这之前自己实现了一套。

改造Docker支持这些功能后,我们又开发了一套调度系统,负责管理调度在集群上如何创建容器,我们也调研了一些开源的调度系统,发现都不满足需求,所以自己开发了一套。

通过这些手段我们就可以让Docker技术“落地”了,而带来的好处是,之前的体系我们要上线新的业务大约需要40分钟,使用Docker缩短到了5分钟。

这是分享的第一部分因为“搅局者”Docker使用遇到了困境,所以我们对它进行了一些改造,更好适配公司场景,让技术“落地”。

基于Docker做一个内部PaaS平台

紧接着我们基于Docker做了一个内部PaaS平台。公司每天会上线很多业务,这些业务有些是体量很大的重要业务,有些是带有试错性质的小业务。

传统业务上线的步骤会非常得严谨,流程会比较长,这些流程其实也对业务稳定性会有保障。有些试错性质的小业务,使用同样的流程变得不太合适,所以我们就想加速小业务上线流程,让他们可以快速上线,验证自己得价值。基于这种考虑,而且Docker天生的特点就特别适合干这个。

这是界面的一个截图,主要是前端Web UI去访问一个调度层 ,调度层通过调用Docker API来创建容器。目前PaaS平台支持PHP、Node.js、Python、Java等语言。

除了创建容器,我们还需要,创建Git仓库、配置访问代理等,总之研发一键就可以让业务进入待上线状态,只要他传完代码就可以上线了。

目前这个平台跑了300+业务,让很多研发只要有一个idea,就可以快速实施上线,很受他们欢迎。

这也是我们应用Docker的第二部分,通过私有PaaS平台,加速业务孵化。

关于持续集成

第三部分是关于持续集成。

持续集成当然是Docker最纯粹的玩法了,通过『Dockerfile-构建镜像-创建新容器』来完成环境的变更。

这块比较复杂,我们大致分了9个模块,比如调度模块、监控模块、存储模块等。

首先我们做了一个配置转换模块来转换Dockerfile,这样即可以统一镜像构建标准,同时也降低了编写Dockerfile的学习成本。

调度模块就直接用的Mesos和Marathon,镜像Registry直接使用了 Registry V2因为它性能更好对高并发支持也很好,最后是镜像构建模块,使用的是Jenkins CI。

但是我们发现一个问题:镜像构建在高并发下其实并不快。 比如装一个RPM包,SSH肯定会比重新build快得多。所以我们做了很多优化在镜像构建这一块,现在结果是100个任务同时构建我们也能达到和传统集群管理如Puppet一样的效率。

Q&A

问:开发自己的调度系统大概花了多久,有遇到特别的技术难点吗? 答:大约2个工程师一个月样子,没有太多得困难。因为调度逻辑比较简单。

问:您刚才说,通过绑定独立的IP就可以直接使用SSH了。 答:通过绑定独立的IP就可以直接使用SSH了,官方关于network那篇文档有介绍实现方案。

问:一般Docker的服务封装是no daemon的,这时如果重启服务,容器也会退出的,如何debug? 答:可使用supvirsod或者monit等将no daemon封装一下。

问:你们服务的注册发现用的是什么? 答:我们基础架构组开发了一个,名字叫QConf,已经开源在GitHub上

问:你说Zabbix做的一个容器监控,那有没有一个基于宿主机的监控方式?因为据我所知你这样的话每个容器都要运行一个代理吧。 答:我们就是使用宿主机里安装Zabbix代理,通过Zabbix自发现来动态获取容器列表,再基于自定义的监控脚本获得每个容器的监控数值。

问:你们的那些业务跑在Docker里了? 答:目前360的很多Web2.0业务已经跑在了上面,像影视、新闻、免费WIFI等。

问:Docker建议无状态,那么是否意味着不建议存放数据?比如MySQL,还是说通过-v来解决? 答: 这其实是数据存储问题,你可以使用分布式存储来存储数据,只要数据和逻辑分离容器就无状态了。

问:Docker建议无状态,那么是否意味着不建议存放数据?比如MySQL,还是说通过-v来解决? 答:我理解就是容器无状态就是基于镜像创建的马上就能线上使用。

问:线上Docker的稳定性如何? 答:目前运行都很稳定,没有出现容器异常崩溃等情况。

问:Container中跑多个进程,那么PID为1的进程你们是由什么控制的,直接由对应的应用程序还是其他什么? 答:之前用了supervisord 现在使用S6。

问:Registry面对大量的并发,有测试出大致的性能占比吗,整个registry是mirror还是其他架构? 答:Registry目前我们更新到了V2,我们测试V2在高并发pull和push上性能非常好,镜像存储使用共享存储,这样Registry也可以横向扩展。

问:如果容器配置用户可以直接访问的IP,在宿主虚拟机中是否可以基于Open vSwitch实现,否则会太依赖虚拟机网络? 答:这个可以的,实际上我们也测试过没问题,当时基于稳定性考虑没有使用。

问:奇虎的CPU配额管理是如何实现的? 答:这个Docker 1.7已经实现了,我们和官方的实现思路是一致的。

问:关于容器中数据存储是怎么做的,如果是共享存储如何进行对应? 答: 可以试试GlusterFS或者Ceph。

问:容器绑IP,容器重启后IP要重新绑吧,IP会变吗? 答:需要重新绑,可做成自动化脚本。


Linux 下的 TCPing

以前写了一篇 《禁ping也能ping的工具:tcping》,介绍了由 Eli Fulkerson 编写的 Windows 下的 tcping 这个工具, ping 那些不允许 ping 的主机。

今天介绍 Linux 下的 TCPing,来自 richard。

什么是 TCPing

tcping 是模仿 icmp 协议下的 ping 命令,不一样的是 tcping 走的是 tcp 协议。所以 TCPing 还支持监听具体某个端口的状态。

因此,即使服务器禁止 Ping,也可以通过 TCPing 来测试与服务器的连接。

安装

使用 richard 的 tcpping 首先要安装好 tcptraceroute:

sudo apt-get install tcptraceroute bc
cd /usr/local/bin
sudo wget http://www.vdberg.org/~richard/tcpping
sudo chmod 755 tcpping

我给 tcping 也做了一个备份:https://cdn.kelu.org/blog/2017/11/tcpping,可以在我网站下载。

用法

tcpping v1.7 Richard van den Berg richard@vdberg.org

Usage: tcpping [-d] [-c] [-C] [-w sec] [-q num] [-x count] ipaddress [port]

    -d   print timestamp before every result
    -c   print a columned result line
    -C   print in the same format as fping's -C option
    -w   wait time in seconds (defaults to 3)
    -r   repeat every n seconds (defaults to 1)
    -x   repeat n times (defaults to unlimited)

也可以使用man tcptraceroute查看用法。

例如:

tcpping xxx.com

tcpping xxx.com 12345

参考资料