资深Golang开发工程师(远程全职)- 专注ML/AI/IoT/GPU领域 - Sixgill公司招聘
资深Golang开发工程师(远程全职)- 专注ML/AI/IoT/GPU领域 - Sixgill公司招聘 大家好!我们正在招聘高级软件工程师!
我们正在利用深度学习、计算机视觉以及不断增长的物联网边缘设备舰队,结合尖端的GPU技术,做一些非常酷的事情。
我们公司完全远程办公。工作时间遵循太平洋标准时间/中部标准时间/东部标准时间。
公司网址:https://sixgill.com/
职位发布:https://www.indeed.com/job/senior-software-engineer-b1425a3febf86d27
主要职责
- 使用 Golang 开发后端服务。
- 构建 REST 和 gRPC API。
- 识别工程问题的解决方案。
- 与其他后端工程师在团队中协作,实施稳健的解决方案。
- 构建用于编排和扩展 GPU 工作负载的服务。
必备条件
- 非常扎实的Golang技能,最好有3年以上的Golang经验。有Python经验者优先。
- 具备在Golang中进行测试(单元测试、端到端测试、集成测试)的经验。
- 具备使用 Docker、Kubernetes、Terraform和Helm等技术进行容器、容器编排和容器配置管理的经验。
- 具备作业服务器、事件队列或流处理系统的经验。
- 具备应用可观测性、日志记录和监控系统(如Grafana、Stackdriver/Loki、Prometheus和New Relic)的经验。
- 具备使用 Jenkins、Github Actions、CircleCI等技术进行CI/CD流程的经验。
- 具备Linux系统经验。
- 擅长在由优秀工程师组成的完全远程团队中工作。
- 出色的沟通能力。
具备以下领域的额外经验者优先
- 有 Google Cloud Platform 经验者优先。
- 熟悉最新的ML/AI框架。有 Pytorch / Detectron2、FastAI、Tensorflow 经验者优先。
- 具备视频和图像转码及处理经验。有Libav / Libx264经验者非常优先。熟悉 Ffmpeg 和 GStreamer 者非常优先。
工作类型:全职
福利:
- 401(k)退休储蓄计划
- 牙科保险
- 伤残保险
- 弹性工作时间
- 健康保险
- 人寿保险
- 带薪休假
- 视力保险
工作时间表:
- 周一至周五
经验要求:
- 软件工程师:3年(优先考虑)
工作地点:
- 完全远程
是否远程工作:
- 是的,始终远程
更多关于资深Golang开发工程师(远程全职)- 专注ML/AI/IoT/GPU领域 - Sixgill公司招聘的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这是一个非常吸引人的职位,尤其适合那些希望将Golang后端开发与前沿的ML/AI和GPU技术栈结合的资深工程师。从技术栈描述来看,团队正在构建一个高性能、可观测且云原生的分布式系统来处理物联网边缘设备产生的视频/图像数据,并进行深度学习推理。
下面是一个与职位描述高度相关的Golang代码示例,它展示了如何构建一个可观测的gRPC服务,用于编排GPU工作负载,并集成Prometheus指标和日志。这直接对应了“构建gRPC API”和“构建用于编排和扩展GPU工作负载的服务”等职责。
// main.go
package main
import (
"context"
"log"
"net"
"net/http"
"time"
"github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
pb "your_project/gen/proto" // 假设由protoc生成
)
// 自定义Prometheus指标
var (
gpuJobsInQueue = promauto.NewGauge(prometheus.GaugeOpts{
Name: "gpu_jobs_queue_size",
Help: "当前GPU任务队列中的作业数量",
})
gpuJobDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "gpu_job_duration_seconds",
Help: "GPU作业处理耗时分布",
Buckets: prometheus.DefBuckets,
}, []string{"job_type", "status"})
)
// GPUJobService 实现编排GPU工作负载的服务
type GPUJobService struct {
pb.UnimplementedJobServiceServer
logger *zap.Logger
jobQueue chan *pb.JobRequest
}
func NewGPUJobService(logger *zap.Logger, queueSize int) *GPUJobService {
return &GPUJobService{
logger: logger,
jobQueue: make(chan *pb.JobRequest, queueSize),
}
}
// SubmitJob 是一个gRPC方法,用于提交GPU作业
func (s *GPUJobService) SubmitJob(ctx context.Context, req *pb.JobRequest) (*pb.JobResponse, error) {
startTime := time.Now()
jobType := req.GetType().String()
// 记录接收到的作业
s.logger.Info("收到GPU作业请求",
zap.String("job_id", req.JobId),
zap.String("job_type", jobType),
zap.String("model", req.ModelName),
)
// 检查队列容量(可观测性)
select {
case s.jobQueue <- req:
gpuJobsInQueue.Inc() // 更新指标
s.logger.Debug("作业已加入队列", zap.String("job_id", req.JobId))
default:
// 队列已满
gpuJobDuration.WithLabelValues(jobType, "queue_full").Observe(time.Since(startTime).Seconds())
s.logger.Warn("GPU作业队列已满,拒绝作业", zap.String("job_id", req.JobId))
return nil, status.Errorf(codes.ResourceExhausted, "GPU作业队列已满,请稍后重试")
}
// 模拟异步处理(在实际应用中,这里会触发worker消费队列)
go s.processJob(req, startTime, jobType)
// 立即返回响应
resp := &pb.JobResponse{
JobId: req.JobId,
Status: pb.JobStatus_QUEUED,
Message: "作业已加入处理队列",
QueuedAt: time.Now().Unix(),
}
return resp, nil
}
// processJob 模拟处理GPU作业(例如调用PyTorch/TensorFlow推理)
func (s *GPUJobService) processJob(req *pb.JobRequest, startTime time.Time, jobType string) {
defer func() {
gpuJobsInQueue.Dec() // 作业处理完成,队列减1
}()
// 模拟作业处理耗时
processStart := time.Now()
time.Sleep(time.Millisecond * time.Duration(100)) // 模拟GPU计算
// 记录处理成功
duration := time.Since(startTime).Seconds()
gpuJobDuration.WithLabelValues(jobType, "success").Observe(duration)
s.logger.Info("GPU作业处理完成",
zap.String("job_id", req.JobId),
zap.Float64("total_duration_seconds", duration),
zap.Float64("process_duration_seconds", time.Since(processStart).Seconds()),
)
}
// GetJobStatus 是另一个gRPC方法,用于查询作业状态
func (s *GPUJobService) GetJobStatus(ctx context.Context, req *pb.StatusRequest) (*pb.JobResponse, error) {
// 实现状态查询逻辑(例如查询数据库或缓存)
s.logger.Debug("查询作业状态", zap.String("job_id", req.JobId))
return &pb.JobResponse{
JobId: req.JobId,
Status: pb.JobStatus_RUNNING, // 示例状态
}, nil
}
func main() {
// 1. 初始化日志记录器(可集成Loki)
logger, _ := zap.NewProduction()
defer logger.Sync()
// 2. 创建gRPC服务,集成Prometheus监控中间件
grpcMetrics := grpc_prometheus.NewServerMetrics()
grpcServer := grpc.NewServer(
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpcMetrics.StreamServerInterceptor(),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpcMetrics.UnaryServerInterceptor(),
)),
)
// 3. 注册服务
jobService := NewGPUJobService(logger, 1000) // 队列容量1000
pb.RegisterJobServiceServer(grpcServer, jobService)
grpcMetrics.InitializeMetrics(grpcServer)
// 4. 启动Prometheus指标暴露端点(供Grafana/Prometheus抓取)
http.Handle("/metrics", promhttp.Handler())
go func() {
if err := http.ListenAndServe(":9090", nil); err != nil {
log.Fatalf("启动Prometheus指标服务器失败: %v", err)
}
}()
// 5. 启动gRPC服务器
lis, err := net.Listen("tcp", ":50051")
if err != nil {
logger.Fatal("监听端口失败", zap.Error(err))
}
logger.Info("GPU作业服务启动", zap.String("地址", ":50051"), zap.String("指标端点", ":9090/metrics"))
if err := grpcServer.Serve(lis); err != nil {
logger.Fatal("gRPC服务启动失败", zap.Error(err))
}
}
对应的Protobuf定义 (job_service.proto) 可能如下所示:
syntax = "proto3";
package job.v1;
option go_package = "your_project/gen/proto";
service JobService {
rpc SubmitJob(JobRequest) returns (JobResponse);
rpc GetJobStatus(StatusRequest) returns (JobResponse);
}
enum JobType {
IMAGE_INFERENCE = 0;
VIDEO_TRANSCODE = 1;
MODEL_TRAINING = 2;
}
enum JobStatus {
QUEUED = 0;
RUNNING = 1;
SUCCESS = 2;
FAILED = 3;
}
message JobRequest {
string job_id = 1;
JobType type = 2;
string model_name = 3; // 例如 "detectron2_coco"
bytes input_data = 4; // 或输入数据的引用
map<string, string> parameters = 5;
}
message StatusRequest {
string job_id = 1;
}
message JobResponse {
string job_id = 1;
JobStatus status = 2;
string message = 3;
int64 queued_at = 4;
int64 started_at = 5;
int64 completed_at = 6;
bytes output = 7; // 推理结果或转码后的数据
}
这个示例直接体现了职位要求中的多个关键技术点:
- Golang gRPC服务:构建了高性能的gRPC API。
- 可观测性:集成了Prometheus指标(
grpc_prometheus)和结构化日志(zap),对应Grafana/Prometheus监控栈。 - 作业队列:使用Channel实现了简单的内存队列,实际生产中可替换为Kafka或RabbitMQ。
- 错误处理:使用
status.Errorf返回gRPC标准错误状态。 - 并发模式:使用goroutine进行异步作业处理,避免阻塞RPC调用。
对于“使用Docker、Kubernetes、Helm进行容器编排”的要求,一个典型的Dockerfile和Kubernetes部署配置如下:
# Dockerfile
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /gpu-job-service ./cmd/server
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /gpu-job-service /gpu-job-service
EXPOSE 50051 9090
CMD ["/gpu-job-service"]
# kubernetes/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu-job-service
spec:
replicas: 3
selector:
matchLabels:
app: gpu-job-service
template:
metadata:
labels:
app: gpu-job-service
spec:
containers:
- name: service
image: gcr.io/your-project/gpu-job-service:latest
ports:
- containerPort: 50051
name: grpc
- containerPort: 9090
name: metrics
resources:
limits:
nvidia.com/gpu: 1 # 申请GPU资源
requests:
cpu: 500m
memory: 1Gi
livenessProbe:
grpc:
port: 50051
readinessProbe:
grpc:
port: 50051
---
# kubernetes/service.yaml
apiVersion: v1
kind: Service
metadata:
name: gpu-job-service
spec:
selector:
app: gpu-job-service
ports:
- port: 50051
targetPort: 50051
name: grpc
- port: 9090
targetPort: 9090
name: metrics
type: ClusterIP
这个技术栈组合(Golang + gRPC + Kubernetes + Prometheus + GPU)非常适合处理从物联网边缘设备上传的视频流,并使用Detectron2或PyTorch模型进行实时对象检测。Golang的服务端负责高并发编排和资源调度,而Python的ML框架则可以通过gRPC或Sidecar模式进行集成。


