# Golang运行环境
# 运行环境说明
# 工作流程
Golang运行环境是新浪云推出的本地可写的Golang语言运行环境,通过Git hook触发自动构建逻辑,将提交的代码打包为Docker镜像,然后由新浪云的容器管理引擎启动生成的镜像实例运行。
# 工作流程示意图
总体的流程分为以下步骤:
- 从本地通过Git客户端提交代码到仓库,如何部署请参考容器环境Git管理示例;
- 新浪云的Git仓库通过触发器启动一个同步构建镜像的任务,解析代码中指定的需要安装的Golang版本、下载依赖的包等,制作一个Docker的镜像;
- 如果以上过程没有错误,则将镜像推送到新浪云的镜像中心,并通知托管服务管理系统;如果出错,从Git的回显通知具体的错误;
- 调度新建或者替换正在运行的镜像实例,启动后将更新后的运行环境更新到负载均衡;
- 所有的实例更新或则启动完成后,通过Git回显告知提交成功。
# 创建应用
登录云应用管理平台https://sae.sinacloud.com (opens new window),如果没有账号请先注册;
点击“+创建应用”进入应用的创建页面;
开发语言选择“Go”;
运行环境选择“容器环境”;
选择单实例的配置;
选择实例个数,开发阶段可以选择1,可以随时调整;
输入应用的二级域名前缀,二级域名的组成规则请参考应用名,应用名称(应用的中文介绍);
点击“确认创建”完成创建。
说明
- 每个实例中将会拥有相同的代码,每个实例的资源配额为创建时指定的单实例配置;
- 实例的个数可以随时修改;
- 计费的规则为:单实例价格 * 实例总数 * 实例的运行时长,按分钟计费。
# 计费说明
# 计费规则
计费的规则为:单实例价格 * 实例总数 * 实例的运行时长,按分钟计费。
其中单实例的价格可以从价格中心 (opens new window)查询。
例如单实例的配置为标准型,单价为0.06元每实例每小时,开启的实例个数为2,则一天的价格为:
0.06 * 2 * 24 = 2.88元
# 流量计费
当前Golang运行环境的流量不计费。
# 构建和运行说明
# 如何识别应用是Go应用
Go通过应用根目录下的 Godeps/Godeps.json
文件来判断一个应用是否是Go应用,所以即使应用没有任何依赖,您也必须执行 godep save
来生成这个文件,该命令也会记录下本地使用的Go版本,服务端会使用对应版本的Go来完成应用的构建。
# 指定Golang版本
您可以使用 Godeps/Godeps.json
中的 GoVersion
来指定服务端构建和运行的时候使用的Go版本。
{
"ImportPath": "...",
"GoVersion": "go1.6"
}
注意
建议使用 1.5.3 以上的版本。
# 构建说明
服务端使用下面的命令来编译你的Go应用程序:
godep go install ./...
# 静态文件处理
所有通过git提交的文件在应用运行的时候都可以直接访问,您可以直接使用Go的 http.FileServer
来处理这些静态文件的访问请求。
# Hello world
# 创建Golang应用
先在新浪云创建一个Go类型的应用,如果不知道如何创建,请参考创建应用章节。
# 准备工作
从Github上clone一个go示例程序https://github.com/sinacloud/go-getting-started (opens new window)。
$ git clone https://github.com/sinacloud/go-getting-started.git
$ cd go-getting-started
$ $ ls -al
total 19
drwxr-xr-x 1 37708 197609 0 10月 12 13:25 ./
drwxr-xr-x 1 37708 197609 0 9月 28 14:09 ../
drwxr-xr-x 1 37708 197609 0 10月 12 13:28 .git/
-rw-r--r-- 1 37708 197609 7 10月 12 13:24 1.txt
drwxr-xr-x 1 37708 197609 0 10月 12 13:25 files/
drwxr-xr-x 1 37708 197609 0 11月 17 2017 Godeps/
-rw-r--r-- 1 37708 197609 491 10月 12 13:27 main.go
-rw-r--r-- 1 37708 197609 24 11月 17 2017 Procfile
-rw-r--r-- 1 37708 197609 733 11月 17 2017 README.md
drwxr-xr-x 1 37708 197609 0 11月 17 2017 static/
drwxr-xr-x 1 37708 197609 0 11月 17 2017 templates/
drwxr-xr-x 1 37708 197609 0 11月 17 2017 vendor/
这个示例程序是一个使用Gin Web (opens new window)框架写的HTTP Web服务器。我们依次来看下每个文件的作用,首先,main.go文件:
package main
import (
"net/http"
"os"
"github.com/gin-gonic/gin"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "5050"
}
r := gin.Default()
r.StaticFile("/favicon.ico", "./static/favicon.ico")
r.Static("/files", "./files")
r.LoadHTMLGlob("templates/*.tmpl.html")
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, World")
})
r.GET("/ping", func(c *gin.Context) {
c.HTML(http.StatusOK, "ping.tmpl.html", nil)
})
r.Run(":" + port)
}
这段程序就是设置了一些HTTP Handler
,最后启动一个Web服务器,监听在环境变量 PORT
指定的端口上。
上面这段程序依赖于 Gin
这个包, Godeps
目录下就是通过 godep save -r ./...
保存下来的所有依赖包。
最后通过 Procfile
文件指定了如何启动这个应用的程序:
web: go-getting-started
# 通过Git提交代码
通过Git将代码提交到应用的仓库中,系统会自动构建运行环境并创建或者重启应用的容器,如何部署应用请参考容器环境git管理示例。
# 访问应用
部署完成后即可从浏览器访问应用:
# 支持的Golang版本
当前支持1.11及以下的所有版本,建议使用1.6及以上的版本。
# 使用gomod
Golang 1.11加入了gomod,可以使用其更好的管理依赖,目前,我们已经支持。
# 在本地的非$GOPATH路径初始化项目
go mod init github.com/you/hello
# 编写main.go
package main
import (
"net/http"
"os"
"github.com/gin-gonic/gin"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "5050"
}
r := gin.Default()
r.StaticFile("/favicon.ico", "./static/favicon.ico")
r.Static("/files", "./files")
r.LoadHTMLGlob("templates/*.tmpl.html")
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, World")
})
r.GET("/ping", func(c *gin.Context) {
c.HTML(http.StatusOK, "ping.tmpl.html", nil)
})
r.Run(":" + port)
}
# 编译
go build
看到输出如下:
root@38930c89bb89:/tmp/test/src# go build main.go
go: finding github.com/gin-gonic/gin v1.3.0
go: downloading github.com/gin-gonic/gin v1.3.0
go: finding github.com/mattn/go-isatty v0.0.4
go: downloading github.com/mattn/go-isatty v0.0.4
go: finding github.com/gin-contrib/sse latest
go: finding github.com/golang/protobuf/proto latest
go: finding github.com/ugorji/go/codec latest
go: finding gopkg.in/yaml.v2 v2.2.1
go: finding gopkg.in/go-playground/validator.v8 v8.18.2
go: downloading github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7
go: downloading gopkg.in/go-playground/validator.v8 v8.18.2
go: downloading github.com/ugorji/go/codec v0.0.0-20181012064053-8333dd449516
go: downloading gopkg.in/yaml.v2 v2.2.1
go: finding github.com/golang/protobuf v1.2.0
go: downloading github.com/golang/protobuf v1.2.0
go: finding gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
其中go.mod的文件中会自动加入依赖的信息:
root@38930c89bb89:/tmp/test/src# cat go.mod
module github.com/you/hello
require (
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect
github.com/gin-gonic/gin v1.3.0 // indirect
github.com/golang/protobuf v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/ugorji/go/codec v0.0.0-20181012064053-8333dd449516 // indirect
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/yaml.v2 v2.2.1 // indirect
)
可以看到版本和依赖已经自动写入了。
同时多了一个go.sum
文件:
root@38930c89bb89:/tmp/test/src# cat go.sum
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/ugorji/go/codec v0.0.0-20181012064053-8333dd449516 h1:tYsnVMTj4SrtarTPEquseLh3QgR7mEY3WSPW7x2c9hk=
github.com/ugorji/go/codec v0.0.0-20181012064053-8333dd449516/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
# 编译完成后的文件列表
go build
完成后的文件里列表如下:
root@38930c89bb89:/tmp/test/src# ls -al
total 15044
drwxr-xr-x 3 root root 4096 Oct 15 02:26 .
drwxr-xr-x 3 root root 4096 Oct 15 02:09 ..
-rw-r--r-- 1 root root 424 Oct 15 02:20 go.mod
-rw-r--r-- 1 root root 1413 Oct 15 02:20 go.sum
-rwxr-xr-x 1 root root 15358854 Oct 15 02:20 main
-rw-r--r-- 1 root root 491 Oct 15 02:10 main.go
drwxr-xr-x 2 root root 4096 Oct 15 02:27 templates
注意:加了一个template/ping.tmpl.html文件,内容是:
<strong>{{ "Pong!" }}</strong>
# 启动进程
root@38930c89bb89:/tmp/test/src# ./main
[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /favicon.ico --> github.com/gin-gonic/gin.(*RouterGroup).StaticFile.func1 (3 handlers)
[GIN-debug] HEAD /favicon.ico --> github.com/gin-gonic/gin.(*RouterGroup).StaticFile.func1 (3 handlers)
[GIN-debug] GET /files/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] HEAD /files/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] Loaded HTML Templates (2):
- ping.tmpl.html
-
[GIN-debug] GET / --> main.main.func1 (3 handlers)
[GIN-debug] GET /ping --> main.main.func2 (3 handlers)
[GIN-debug] Listening and serving HTTP on :5050
可以看到进程已经启动在5050端口。
说明
更多关于Go mod的说明请参考Go 1.11 Modules (opens new window)
# 提交代码
提交非编译后的文件到Git仓库即可,代码部署请参考通过Git提交代码。
# 协议支持
应用支持http、https和websocket协议(ws、wss)。
# 端口映射
# 工作原理
应用的负载均衡(新浪云提供)监听80和443端口,然后将请求转发到容器中的5050端口,示意图如下:
# 支持的端口
容器中可以启动多个端口,但是仅5050端口会映射到负载均衡上,其他的端口只能在本地访问。可以从环境变量中获取支持的端口,环境变量的key为:
PORT
可以通过:
port := os.Getenv("PORT")
请注意
- 另外HTTP等服务器应该监听在0.0.0.0,不要监听在127.0.0.1,如果监听在127.0.0.1,仅在容器上运行的程序才可以访问,外部无法访问;
- 如果使用HTTPS访问,在负载均衡到容器间会转变为HTTP协议,可以从HTTP的header头中判断是否有
HTTP-X-PROTO
头,如果这个header头的值为SSL
表示本次请求为HTTPS访问; - 如上所述,HTTPS证书应该传到负载均衡处(从管理面板的域名绑定处上传),不应该放到容器中。
# 查询容器stdout日志
- 进入应用的列表页面https://sae.sinacloud.com (opens new window)
- 点击操作处的管理进入应用的管理中心
- 左侧导航选择“日志及监控”,选择“日志中心”进入日志管理
- 选择“容器日志”的“stdout”分类
# 查询容器stderr日志
- 进入应用的列表页面https://sae.sinacloud.com (opens new window)
- 点击操作处的管理进入应用的管理中心
- 左侧导航选择“日志及监控”,选择“日志中心”进入日志管理
- 选择“容器日志”的“stderr”分类
# SSH登录
# 说明
Golang容器支持通过在线SSH终端登录到容器中查看进程的状态和系统的信息。
# 如何登录
- 进入应用的列表页面https://sae.sinacloud.com (opens new window)
- 点击操作处的管理进入应用的管理中心
- 左侧导航选择“运行环境管理”,选择“容器管理”进入应用的容器列表页面
- 点击需要管理的容器示例的“操作”区域的“终端”即可进入web版本的终端
- 输入Linux命令可以查询进程的状态、执行其他的操作
# 文件系统
# 本地文件系统
实例在创建时将会分配5GB的本地磁盘,用于存放程序的代码和系统文件等,你可以从程序往本地写文件,但本地的文件不是持久化存储,在重启后文件会删除。
# 挂载共享存储
共享存储服务是为容器云提供的分布式存储服务,利用多副本写入实现数据高可靠性,并提升读取性能,可以提供数十M/S的峰值读写性能。
您可以通过挂载,将对应的共享存储卷映射进容器中,是容器获得本地文件读写和数据持久化功能。
通过共享存储,还可以在不同的容器之间共享数据,实现跨容器的数据共享和交互。
有关共享存储的更多介绍,请参考共享存储章节,请参考以下步骤挂载共享存储:
- 从共享存储 (opens new window)管理面板创建一个共享存储;
- 从云应用管理平台 (opens new window)应用列表页面点击“管理”进入应用的管理中心;
- 左侧导航选择“运行环境管理”,“容器管理”进入容器管理页面;
- 点击“挂载管理”进入挂载管理页面;
- 点击“+新增挂载”,存储的分类选择“共享存储”,从下拉列表选择刚刚创建的存储,输入一个挂载路径。
说明
挂载路径是系统的绝对路径,如果你挂载的路径在本地文件系统已经存在,则挂载后本地的文件系统将被覆盖。
- 选择“立即重启容器”后挂载会立即生效,挂载完成后可以看到挂载的详情:
挂载完成后,从容器的实例中可以看到挂载的路径:
共享存储中的文件支持FTP管理,详情请参考共享存储的FTP管理章节。
挂载完共享存储到本地的路径后,就可以在程序中将需要存储的文件写到挂载的路径下了。
# 挂载本地存储
本地存储服务是为容器云提供的本地问年间存储服务,可以和本地磁盘读写一样速度的磁盘。
您可以通过挂载,将对应的本地存储卷映射进容器中,是容器获得本地文件读写和数据持久化功能。
有关共享存储的更多介绍,请参考本地存储章节,请参考以下步骤挂载本地存储:
- 从本地存储 (opens new window)管理面板创建一个本地存储;
- 从云应用管理平台 (opens new window)应用列表页面点击“管理”进入应用的管理中心;
- 左侧导航选择“运行环境管理”,“容器管理”进入容器管理页面;
- 点击“挂载管理”进入挂载管理页面;
- 点击“+新增挂载”,存储的分类选择“本地存储”,从下拉列表选择刚刚创建的存储,输入一个挂载路径。
说明
挂载路径是系统的绝对路径,如果你挂载的路径在本地文件系统已经存在,则挂载后本地的文件系统将被覆盖。
- 选择“立即重启容器”后挂载会立即生效,挂载完成后可以看到挂载的详情:
挂载完成后,从容器的实例中可以看到挂载的路径:
本地存储中的文件支持FTP管理,详情请参考本地存储的FTP管理章节。
挂载完地存储到本地的路径后,就可以在程序中将需要存储的文件写到挂载的路径了。
← NodeJS运行环境 容器虚拟机 →