Docker_grafana 启动失败

Docker grafana 启动失败 今天在使用 Docker 启动 grafana 的时候,遇到了一个问题,问题如图: 为什么会出现这个问题 根据错误日志的提示,打开 http://docs.grafana.org/installation/docker/#migrate-to-v51-or-later 网址,根据官方的描述,grafana Docker 镜像在版本 5.1 及以后有了重大改变: * 在容器启动过程中不会改变文件的权限 * 默认用户 ID 由 104 变为 472 * 删除了以下的隐式卷: * /var/lib/grafana * /etc/grafana * /var/log/grafana 解决 根据官方文档的提示,我仔细查看了我的 compose 文件,发现我加了 user: ‘104’ , 应该是此行的原因导致 grafana 没有启动成功,删除此行,顺利启动。

八月 12, 2021 · overstarry

Nginx 搭建静态图片资源服务器

Nginx 搭建静态图片资源服务器 本文介绍使用 Docker Nginx 搭建静态图片资源服务器的过程和搭建中间遇到的问题。 我使用 Docker compose 搭建静态图片资源服务器,我使用的 compose 文件内容如下: version: '3.1' services: nginx: restart: always image: nginx container_name: asset ports: - 80:80 volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./uploads:/usr/share/nginx/uploads - ./conf.d:/etc/nginx/conf.d /srv/msg/storage/uploads:/usr/share/nginx/uploads 将本地的图片文件夹挂载到容器内的 /usr/share/nginx/uploads 文件夹 nginx.conf 是 Nginx 的主配置文件,conf.d 是各个网站配置的文件夹。我的 nginx.conf 内容如下: user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; server { listen 80 default_server; listen [::]:80 default_server; server_name 192.168.1.117; root /usr/share/nginx/uploads/; location /{ autoindex on; } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } } 静态资源图片服务器的配置文件如下: ...

八月 6, 2021 · overstarry

Traefik 入门使用

Traefik 入门使用 简介 Traefik(发音为 traffic)是一个现代 HTTP 反向代理和负载均衡设施,使部署微服务变得容易。Traefik 与你现有的基础设施组件(Docker、Swarm 模式、Kubernetes、Consul、Etcd、…)集成,并自动和动态地配置自己。将 Traefik 指向你的基础设施组件应该是你唯一需要的配置步骤。 快速入门 在这里我使用 Docker 来快速使用 Traefik。 先创建一个 docker-compose.yml 文件,写入如下内容: version: '3' services: reverse-proxy: # 使用的 traefik2.4 Docker 镜像版本 image: traefik:v2.4 # 通过命令行参数启动 traefik,启用不安全模式以使用 dashboard,配置发现使用了 docker command: --api.insecure=true --providers.docker ports: # The HTTP port - "80:80" # dashboard 端口 - "8080:8080" volumes: # 监听 docker 事件 - /var/run/docker.sock:/var/run/docker.sock 执行 docker-compose up -d reverse-proxy 启动 docker-compose up -d reverse-proxy 打开浏览器输入 http://localhost:8080/api/rawdata 访问看见 Traefik’s API rawdata,就表示安装成功了。 ...

八月 6, 2021 · overstarry

Go_Modules 指南和常见问题

Go Modules 简明使用 Go Modules 是从 go1.11 开始初步支持,到现在的 1.16、1.17 默认开启,是目前 go 项目的推荐包管理方式。接下来让我们看一下 go modules 的简单使用方法。 使用 Go Modules 初始化 # go mod init [包路径] go mod init github.com/overstarry/go-mod-example 执行上述命令会生成 go.mod 文件 module github.com/overstarry/kratos-demo go 1.16 添加依赖 在当前项目的目录中执行 go get 命令,会添加相应的库到 mod 文件中 go get github.com/go-kratos/kratos/v2 这个命令会在 mod 文件里添加以下信息 require ( github.com/go-kratos/kratos/v2 v2.0.1 ) 在 go get 的时候如果不手动指定版本信息,会自动拉取最新的版本的包 如果想要拉取指定版本可以通过 go get github.com/go-kratos/kratos/v2 v2.0.1 的方式,支持 @版本号 例如 @v2.0.1 @分支名 例如 @master @commit tag 例如 @6cff360233dc4457f1536e4f3df4e4e740fd3410 // indirect 表示,我们在代码中没有直接应用这个包 执行完 go get 命令之后还会在目录下生成一个 go.sum 文件 github.com/go-kratos/kratos/v2 v2.0.0-rc7 h1:Qvpz07BefgMFQycSDb57NlWhtYGz4me3wh8E1naI9/k= github.com/go-kratos/kratos/v2 v2.0.0-rc7/go.mod h1:/2bGobqE+/F9kKOe4Re0OO5X2NWNGt+7n2e8Y5DHFRc= github.com/go-kratos/kratos/v2 v2.0.1 h1:iFteVlcLWnAQu5n4I5bTN63svW+0YylGwhNTYO2MkOQ= github.com/go-kratos/kratos/v2 v2.0.1/go.mod h1:Jz6fuJFF2SLczQ7Y8ocKieVGgstQKa+R9NX09bCHekU= 这个文件主要包含当前依赖的所有的包。 ...

七月 30, 2021 · overstarry

Gitlab CI 构建 docker 镜像

Gitlab CI 构建 docker 镜像 利用 Gitlab CI 结合 Docker 构建 docker 镜像主要有三种方法。 将 Docker 执行器与 Docker 镜像一起使用。 使用 shell 执行器 Docker socket 绑定 现在来讲讲我的具体使用过程和遇到的一些问题,由于我的 gitlab-runners 使用了 docker 执行器,所以我主要使用了 1 和 3 两种方法。 由于网上的相关文章主要是采用 Docker in Docker 的方式,所以最开始我也是采用这种方式。 我的 ci 脚本是: services: - docker:dind variables: OUTPUT_NAME: bot DOCKER_HOST: tcp://localhost:2375 DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "" build_docker_image_and_push_to_nexus: stage: build image: docker:stable extends: .go-cache script: - docker info - docker build -t docker.overtsarry.vip/bot:1.0.1 . - docker login --username=$username docker.overtsarry.vip --password $pwd - docker push docker.overtsarry.vip/bot:1.0.1 运行 CI,发现 CI 运行失败,具体报错是 ...

七月 22, 2021 · overstarry

Hugo Github_page 自定义域名

部署好 GithubPage 后,觉得提供的域名不好,想要换成自己的域名,该如何操作呢。下面来介绍下给 Github Page 设置自定义域的方式。 在 Github 上,打开 GithubPage 所在仓库。 在仓库名称下,单击 Settings(设置)。 在左侧边栏中,单击 Pages(页面)。 在 “Custom domain(自定义域)“下,输入自定义域,然后单击 Save(保存)。这将创建一个在发布源根目录中添加 CNAME 文件的提交。 导航到您的 DNS 提供程序并创建 CNAME 记录,使子域指向您站点的默认域。就是在你域名的提供商进行相应域名的解析,添加对应的 CNAME 记录集。 6.(可选)要为您的站点实施 HTTPS 加密,请选择 Enforce HTTPS(实施 HTTPS)。可能要过 24 小时才能使用此选项。 上面这是常规的标准做法,由于我是采用 Github actions 构建生成站点,每次 CI 运行时,都会重新生成仓库文件,CNAME 文件都需要重新创建,十分不方便。于是在 Hugo site static 文件夹创建 CNAME 文件,这样每次运行 CI 时,都不需要重复上面步骤。 参考链接 https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site

七月 15, 2021 · overstarry

Go 错误实践

panic 在程序启动的时候,如果有强依赖的服务出现故障时 panic 退出 在程序启动的时候,如果发现有配置明显不符合要求,可以 panic 退出(防御编程) 其他情况下只要不是不可恢复的程序错误,都不应该直接 panic 应该返回 error 在程序入口处,例如 gin 中间件需要使用 recover 预防 panic 程序退出 在程序中我们应该避免使用野生的 goroutine 如果是在请求中需要执行异步任务,应该使用异步 worker,消息通知的方式进行处理,避免请求量大时大量 goroutine 创建 如果需要使用 goroutine 时,应该使用同一的 Go 函数进行创建,这个函数中会进行 recover,避免因为野生 goroutine panic 导致主进程退出 func fn(f func()){ go func(){ defer func(){ if err := recover(); err != nil { log.Printf("panic: %+v", err) } }() f() }() } error 我们在应用程序中使用 github.com/pkg/errors 处理应用错误,注意在公共库当中,我们一般不使用这个 error 应该是函数的最后一个返回值,当 error 不为 nil 时,函数的其他返回值是不可用的状态,不应该对其他返回值做任何期待 func f() (io.Reader, *S1, error) 在这里,我们不知道 io.Reader 中是否有数据,可能有,也有可能有一部分 错误处理的时候应该先判断错误,if err != nil 出现错误及时返回,使代码是一条流畅的直线,避免过多的嵌套。 在应用程序中出现错误时,使用 errors.New 或者 errors.Errorf 返回错误 如果是调用应用程序的其他函数出现错误,请直接返回,如果需要携带信息,请使用 errors.WithMessage 如果是调用其他库(标准库、企业公共库、开源第三方库等)获取到错误时,请使用 errors.Wrap 添加堆栈信息 切记,不要每个地方都是用 errors.Wrap 只需要在错误第一次出现时进行 errors.Wrap 即可 根据场景进行判断是否需要将其他库的原始错误吞掉,例如可以把 repository 层的数据库相关错误吞掉,返回业务错误码,避免后续我们分割微服务或者更换 ORM 库时需要去修改上层代码 注意我们在基础库,被大量引入的第三方库编写时一般不使用 errors.Wrap 避免堆栈信息重复 func f() error { err := json.Unmashal(&a, data) if err != nil { return errors.Wrap(err, "其他附加信息") } // 其他逻辑 return nil } 禁止每个出错的地方都打日志,只需要在进程的最开始的地方使用 %+v 进行统一打印,例如 http/rpc 服务的中间件 错误判断使用 errors.Is 进行比较 func f() error { err := A() if errors.Is(err, io.EOF){ return nil } // 其他逻辑 return nil } 错误类型判断,使用 errors.As 进行赋值 func f() error { err := A() var errA errorA if errors.As(err, &errA){ // ... } // 其他逻辑 return nil } 如何判定错误的信息是否足够,想一想当你的代码出现问题需要排查的时候你的错误信息是否可以帮助你快速的定位问题,例如我们在请求中一般会输出参数信息,用于辅助判断错误 对于业务错误,推荐在一个统一的地方创建一个错误字典,错误字典里面应该包含错误的 code,并且在日志中作为独立字段打印,方便做业务告警的判断,错误必须有清晰的错误文档 不需要返回,被忽略的错误必须输出日志信息 同一个地方不停的报错,最好不要不停输出错误日志,这样可能会导致被大量的错误日志信息淹没,无法排查问题,比较好的做法是打印一次错误详情,然后打印出错误出现的次数 对同一个类型的错误,采用相同的模式,例如参数错误,不要有的返回 404 有的返回 200 处理错误的时候,需要处理已分配的资源,使用 defer 进行清理,例如文件句柄

七月 9, 2021 · overstarry

使用 Redis 实现消息队列

使用 Redis 实现消息队列主要有三种方法: List 队列 发布/订阅模型 Pub/Sub Stream (Redis5+) 下面分别对这三种方法进行介绍,并编写简单例子。 List 队列 看到队列,你会想到 Redis 有个数据类型 List,List 能很好符合队列的要求。List 的底层是一个链表,在头部和尾部进行操作的时间复杂度都是 O(1)。 使用 List 进行队列操作,你可以这样使用。 生产者使用 LPUSH 进行消息发布 消费者使用 RPOP 对消息进行消费 这里存在着一个问题,如果 LIST 没有消息时,消费者执行 RPOP 时,会返回 null(nil)。 我们在编写消费者逻辑时,一般是循环不断从队列中消费数据进行处理,如果此时队列为空,那消费者依旧会频繁拉取消息,这会造成「CPU 空转」,不仅浪费 CPU 资源,还会对 Redis 造成压力。Redis 提供了阻塞式拉起命令 BRPOP / BLPOP,使用 BRPOP 这种阻塞式方式拉取消息时,还支持传入一个「超时时间」,如果设置为 0,则表示不设置超时,直到有新消息才返回,否则会在指定的超时时间后返回 null,既兼顾了效率还避免了 CPU 空转问题。 这是 List 队列的代码例子: // 生产者 package main import ( "context" "github.com/go-redis/redis/v8" ) func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 1, }) data :=[]string{`{"name": "jinzhu11221212321", "age": 18, "tags": ["tag211", "tag2"], "orgs": {"orga": "orga"}`,`{"name": "asd", "age": 18, "tags": ["tag211", "tag2"], "orgs": {"orga": "orga"}`} rdb.LPush(context.Background(), "queue", data) } // 消费者 package main import ( "context" "fmt" "log" "time" "github.com/go-redis/redis/v8" ) func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 1, }) for true { //result, err := rdb.RPop(context.Background(), "queue").Result() result, err := rdb.BRPop(context.Background(), 0, "queue").Result() if err == redis.Nil { continue } else if err != nil { log.Println(err) } fmt.Printf("consumer msg %v\n", result) } } List 队列的缺点: ...

七月 7, 2021 · overstarry

My First Post

Hello Hugo!

七月 5, 2021 · overstarry