安装ctr
1、添加yum源
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repoyum install -y yum-utils
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
2、安装containerd
yum -y install containerd.io-1.6.6
3、生成containerd配置文件
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
4、修改配置文件,修改以下两项配置即可。
vim /etc/containerd/config.tomlSystemdCgroup = true
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.7"
SystemdCgroup:使用SystemdCgroup驱动
sandbox_image:配置国内的沙箱镜像地址
沙箱镜像是一个特殊的镜像,其中包含了容器运行时所需的文件和工具,以及用于隔离容器的命名空间和cgroups等资源控制机制。当容器运行时需要创建新的沙箱时,会使用该配置项指定的镜像作为基础镜像来创建沙箱。
5、启动 && 设置自启动
systemctl enable containerd --now
6、配置镜像源
编辑Containerd的配置文件 /etc/containerd/config.toml, 在 [plugins."io.containerd.grpc.v1.cri".registry] 下方添加 config_path
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
创建目录 /etc/containerd/certs.d/docker.io,在其中添加包含下面内容的 hosts.toml 文件
server = "https://docker.io" # 源镜像地址
[host."https://xxxxxx.mirror.aliyuncs.com"] # 镜像加速地址
重启 Containerd
systemctl restart containerd
使用ctr 部署 skywalking
skywalking-oap-server
ctr run \
--net-host \
--env TZ=Asia/Shanghai \
--env SW_ES_USER="elastic" \
--env SW_ES_PASSWORD="xxxxxxxxxxxx" \
--env SW_STORAGE=elasticsearch \
--env SW_STORAGE_ES_CLUSTER_NODES=192.168.1xx.xx:3xx00 \
--label "io.containerd.runtime.name=skywalking-oap-server" \
--detach \
docker.io/apache/skywalking-oap-server:9.3.1 skywalking-oap-server
skywalking-ui
ctr run \
-d \
--net-host \
--env TZ=Asia/Shanghai \
--env SW_OAP_ADDRESS=http://192.168.1xx.xx:12800 \
docker.io/apache/skywalking-ui:9.3.1 \
skywalking-ui
启用nginx 身份验证给skywalking web端
yum install epel-release -y
yum install nginx -y
yum install httpd-tools -y
vim /etc/nginx/nginx.conf #添加以下配置:
server {
listen 80;
server_name example.com; # 替换成自己的域名或 IP 地址
location / {
proxy_pass http://localhost:8080; # 将请求转发到 SkyWalking 服务器
auth_basic "Restricted"; # 启用基本身份验证,显示提示信息
auth_basic_user_file /etc/nginx/htpasswd; # 指定存储用户名和密码的文件路径
}
}
设置admin密码
touch /etc/nginx/htpasswd。
#执行以下命令:
htpasswd -c /etc/nginx/htpasswd admin
gin 接入 skywalking
这里使用第三方库 go2sky 以及 go2sky 的 gin 中间件
go get -u github.com/SkyAPM/go2sky
go get github.com/SkyAPM/go2sky-plugins/gin/v3
代码示例
两个 gin 服务 demo-server1 和 demo-server2,demo-server1 调用 demo-server2 demo-server2 提供 POST /user/info 接口 demo-server1 提供 GET /tracer 接口
demo-server2 代码如下
package main import ( "fmt" "time" "github.com/SkyAPM/go2sky" "github.com/SkyAPM/go2sky/reporter" "github.com/gin-gonic/gin" v3 "github.com/SkyAPM/go2sky-plugins/gin/v3" ) const ( serverName = "demo-server2" serverPort = 8082 ) var skyAddr = "localhost:11800" type Params struct { Name string } func panicErr(err error) { if err != nil { panic(err) } } func main() { r := gin.Default() // skyAddr 是 skywaling 的 grpc 地址,默认是 localhost:11800, 默认心跳检测时间是 1s rp, err := reporter.NewGRPCReporter(skyAddr, reporter.WithCheckInterval(5*time.Second)) panicErr(err) // 初始化一个 tracer,一个服务只需要一个 tracer,其含义是这个服务名称 tracer, err := go2sky.NewTracer(serverName, go2sky.WithReporter(rp)) panicErr(err) // gin 使用 sky 自带的 middleware r.Use(v3.Middleware(r, tracer)) // 自定义一个接口 r.POST("/user/info", func(context *gin.Context) { // LocalSpan 可以理解为本地日志的 tracer,一般用户当前应用 span, ctx, err := tracer.CreateLocalSpan(context.Request.Context()) panicErr(err) // 每一个 span 都有一个名字去标实操作的名称! span.SetOperationName("UserInfo") // 记住重新设置一个 ctx,再其次这个 ctx 不是 gin 的 ctx,而是 http request 的 ctx context.Request = context.Request.WithContext(ctx) params := new(Params) err = context.BindJSON(params) panicErr(err) // 记录日志信息 span.Log(time.Now(), "[UserInfo]", fmt.Sprintf(serverName+" satrt, req : %+v", params)) local := gin.H{ "msg": fmt.Sprintf(serverName+" time : %s", time.Now().Format("15:04:05.9999")), } context.JSON(200, local) span.Log(time.Now(), "[UserInfo]", fmt.Sprintf(serverName+" end, resp : %s", local)) // 切记最后要设置 span - end,不然就是一个非闭环的 span.End() }) r.Run(fmt.Sprintf(":%d", serverPort)) }
demo-server1 代码如下
package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "log" "net/http" "time" "github.com/SkyAPM/go2sky" "github.com/SkyAPM/go2sky/reporter" "github.com/gin-gonic/gin" v3 "github.com/SkyAPM/go2sky-plugins/gin/v3" agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3" ) const ( serverName = "demo-server1" serverPort = 8081 remoteServerName = "demo-server2" remoteServerAddr = "localhost:8082" remotePath = "/user/info" ) var skyAddr = "localhost:11800" func panicErr(err error) { if err != nil { log.Fatal(err.Error()) } } type Params struct { Name string } var tracer *go2sky.Tracer func skyMiddleware(r *gin.Engine) { var err error rp, err := reporter.NewGRPCReporter(skyAddr, reporter.WithCheckInterval(5*time.Second)) panicErr(err) tracer, err = go2sky.NewTracer(serverName, go2sky.WithReporter(rp)) panicErr(err) r.Use(v3.Middleware(r, tracer)) } func trace(context *gin.Context) { span, ctx, err := tracer.CreateLocalSpan(context.Request.Context()) panicErr(err) span.SetOperationName("Trace") context.Request = context.Request.WithContext(ctx) span.Log(time.Now(), "[Trace]", fmt.Sprintf(serverName+" satrt, params : %s", time.Now().Format("15:04:05.9999"))) result := make([]map[string]interface{}, 0) //1、请求一次 { url := fmt.Sprintf("http://%s%s", remoteServerAddr, remotePath) params := Params{ Name: serverName + time.Now().Format("15:04:05.9999"), } buffer := &bytes.Buffer{} _ = json.NewEncoder(buffer).Encode(params) req, err := http.NewRequest(http.MethodPost, url, buffer) panicErr(err) // op_name 是每一个操作的名称 reqSpan, err := tracer.CreateExitSpan(context.Request.Context(), "invoke - "+remoteServerName, "localhost:8082/user/info", func(headerKey, headerValue string) error { req.Header.Set(headerKey, headerValue) return nil }) panicErr(err) reqSpan.SetComponent(2) reqSpan.SetSpanLayer(agentv3.SpanLayer_RPCFramework) // rpc 调用 resp, err := http.DefaultClient.Do(req) panicErr(err) defer resp.Body.Close() reqSpan.Log(time.Now(), "[HttpRequest]", fmt.Sprintf("开始请求,请求服务:%s, 请求地址:%s, 请求参数:%+v", remoteServerName, url, params)) body, err := ioutil.ReadAll(resp.Body) panicErr(err) fmt.Printf("接受到消息: %s\n", body) reqSpan.Tag(go2sky.TagHTTPMethod, http.MethodPost) reqSpan.Tag(go2sky.TagURL, url) reqSpan.Log(time.Now(), "[HttpRequest]", fmt.Sprintf("结束请求,响应结果:%s", body)) reqSpan.End() res := map[string]interface{}{} err = json.Unmarshal(body, &res) panicErr(err) result = append(result, res) } //2 、再请求一次 { url := fmt.Sprintf("http://%s%s", remoteServerAddr, remotePath) params := Params{ Name: serverName + time.Now().Format("15:04:05.9999"), } buffer := &bytes.Buffer{} _ = json.NewEncoder(buffer).Encode(params) req, err := http.NewRequest(http.MethodPost, url, buffer) panicErr(err) // 出去必须用这个携带 header reqSpan, err := tracer.CreateExitSpan(context.Request.Context(), "invoke - "+remoteServerName, "localhost:8082/user/info", func(headerKey, headerValue string) error { req.Header.Set(headerKey, headerValue) return nil }) panicErr(err) reqSpan.SetComponent(2) reqSpan.SetSpanLayer(agentv3.SpanLayer_RPCFramework) // rpc 调用 resp, err := http.DefaultClient.Do(req) panicErr(err) defer resp.Body.Close() reqSpan.Log(time.Now(), "[HttpRequest]", fmt.Sprintf("开始请求,请求服务:%s, 请求地址:%s, 请求参数:%+v", remoteServerName, url, params)) body, err := ioutil.ReadAll(resp.Body) panicErr(err) fmt.Printf("接受到消息: %s\n", body) reqSpan.Tag(go2sky.TagHTTPMethod, http.MethodPost) reqSpan.Tag(go2sky.TagURL, url) reqSpan.Log(time.Now(), "[HttpRequest]", fmt.Sprintf("结束请求,响应结果:%s", body)) reqSpan.End() res := map[string]interface{}{} err = json.Unmarshal(body, &res) panicErr(err) result = append(result, res) } // 设置响应结果 local := gin.H{ "msg": result, } context.JSON(200, local) span.Log(time.Now(), "[Trace]", fmt.Sprintf(serverName+" end, resp : %s", local)) span.End() { span, ctx, err := tracer.CreateEntrySpan(context.Request.Context(), "Send", func(s string) (string, error) { return "", nil }) context.Request = context.Request.WithContext(ctx) panicErr(err) span.SetOperationName("Send") span.Log(time.Now(), "[Info]", "send resp") span.End() } } func main() { // 这些都一样 r := gin.Default() // 使用 go2sky gin 中间件 skyMiddleware(r) // 调用接口 r.GET("/trace", trace) r.Run(fmt.Sprintf(":%d", serverPort)) }
调用测试
浏览器 访问 刷新 http://localhost:8081/trace
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。