Home » Как создавать и устанавливать программы на Go
Как создавать и устанавливать программы на Go

Как создавать и устанавливать программы на Go

Работать с Go сегодня — это не только писать код, но и понимать всю цепочку от разработки до продакшена. Если вы администрируете серверы, то наверняка сталкивались с ситуацией, когда нужно быстро собрать и развернуть Go-приложение, настроить его как сервис или интегрировать в существующую инфраструктуру. Эта статья поможет разобраться с тем, как правильно создавать, компилировать и устанавливать Go-программы на серверах — от простых утилит до полноценных веб-сервисов.

Основы работы с Go: от исходников до бинарников

Go отличается от интерпретируемых языков тем, что компилируется в статические бинарные файлы. Это означает, что на целевом сервере не нужен рантайм — только скомпилированный файл. Звучит просто, но есть нюансы.

Процесс выглядит так:

  • Исходный код на Go → компиляция → статический бинарник
  • Бинарник содержит всё необходимое для работы (включая runtime)
  • Кросс-компиляция позволяет собирать под разные платформы с одной машины

Установка Go на сервер:

wget https://golang.org/dl/go1.21.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
go version

Создание простой Go-программы

Начнём с классического примера — веб-сервера. Создаём структуру проекта:

mkdir myapp
cd myapp
go mod init myapp
touch main.go

Содержимое main.go:

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello from Go! PID: %d", os.Getpid())
    })
    
    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        fmt.Fprint(w, "OK")
    })
    
    log.Printf("Server starting on port %s", port)
    log.Fatal(http.ListenAndServe(":"+port, nil))
}

Компиляция и оптимизация

Базовая компиляция:

go build -o myapp main.go

Но для продакшена лучше использовать оптимизированную сборку:

# Компиляция с оптимизацией размера и отключением debug-информации
go build -ldflags="-s -w" -o myapp main.go

# Для статической линковки (если используются C-библиотеки)
CGO_ENABLED=0 go build -ldflags="-s -w" -o myapp main.go

# Кросс-компиляция для разных платформ
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o myapp-linux main.go
GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o myapp-mac main.go
Флаг Назначение Эффект на размер
-s Убирает symbol table -20-30%
-w Убирает DWARF debug info -40-50%
CGO_ENABLED=0 Отключает CGO Статическая сборка

Установка и настройка как systemd-сервиса

Для нормального деплоя нужно настроить приложение как системный сервис. Создаём пользователя для приложения:

sudo useradd --system --shell /bin/false myapp
sudo mkdir -p /opt/myapp
sudo chown myapp:myapp /opt/myapp

Копируем бинарник и создаём systemd unit:

sudo cp myapp /opt/myapp/
sudo chmod +x /opt/myapp/myapp

# Создаём systemd service
sudo tee /etc/systemd/system/myapp.service > /dev/null < < EOF
(Unit)
Description=My Go Application
After=network.target

# Service
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/myapp
Restart=always
RestartSec=5
Environment=PORT=8080

# Безопасность
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/myapp
ProtectHome=true

# Install
WantedBy=multi-user.target
EOF

Запускаем и включаем автозапуск:

sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
sudo systemctl status myapp

Мониторинг и логирование

Добавляем структурированное логирование в приложение:

go get github.com/sirupsen/logrus

Обновляем main.go:

package main

import (
    "context"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
    
    "github.com/sirupsen/logrus"
)

func main() {
    log := logrus.New()
    log.SetFormatter(&logrus.JSONFormatter{})
    
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        log.WithFields(logrus.Fields{
            "method": r.Method,
            "path":   r.URL.Path,
            "ip":     r.RemoteAddr,
        }).Info("Request received")
        
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("Hello from Go!"))
    })
    
    srv := &http.Server{
        Addr:         ":" + port,
        Handler:      mux,
        ReadTimeout:  15 * time.Second,
        WriteTimeout: 15 * time.Second,
    }
    
    // Graceful shutdown
    go func() {
        log.Info("Starting server on port " + port)
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatal("Server failed to start: ", err)
        }
    }()
    
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    log.Info("Shutting down server...")
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatal("Server forced to shutdown: ", err)
    }
    
    log.Info("Server exited")
}

Настройка reverse proxy с nginx

Обычно Go-приложения размещают за reverse proxy. Конфигурация nginx:

server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Для WebSocket (если нужно)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
    
    location /health {
        proxy_pass http://127.0.0.1:8080;
        access_log off;
    }
}

Автоматизация деплоя

Создаём простой скрипт для деплоя:

#!/bin/bash
# deploy.sh

set -e

APP_NAME="myapp"
APP_DIR="/opt/$APP_NAME"
BINARY_NAME="$APP_NAME"

echo "Building application..."
CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o $BINARY_NAME main.go

echo "Stopping service..."
sudo systemctl stop $APP_NAME

echo "Backing up current binary..."
sudo cp $APP_DIR/$BINARY_NAME $APP_DIR/$BINARY_NAME.bak 2>/dev/null || true

echo "Copying new binary..."
sudo cp $BINARY_NAME $APP_DIR/
sudo chown $APP_NAME:$APP_NAME $APP_DIR/$BINARY_NAME
sudo chmod +x $APP_DIR/$BINARY_NAME

echo "Starting service..."
sudo systemctl start $APP_NAME

echo "Checking service status..."
sleep 2
sudo systemctl status $APP_NAME --no-pager

echo "Deployment completed!"

Использование Docker

Многие предпочитают контейнеризацию. Создаём 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 -ldflags="-s -w" -o myapp main.go

# Финальный образ
FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/

COPY --from=builder /app/myapp .
EXPOSE 8080

CMD ["./myapp"]

Сборка и запуск:

docker build -t myapp .
docker run -d -p 8080:8080 --name myapp myapp

Сравнение с другими языками

Критерий Go Python Node.js Java
Размер бинарника 5-15 MB Интерпретируемый Интерпретируемый Большой с JVM
Время старта Мгновенно Быстро Быстро Медленно
Потребление памяти Низкое Среднее Высокое Очень высокое
Деплой Один файл Зависимости node_modules JAR + JVM

Полезные утилиты и пакеты

Для работы с Go-приложениями на серверах пригодятся:

  • air — автоперезагрузка во время разработки
  • upx — дополнительное сжатие бинарников
  • delve — отладчик для Go
  • pprof — профилирование производительности
  • cobra — для создания CLI-приложений

Установка дополнительных инструментов:

go install github.com/cosmtrek/air@latest
go install github.com/go-delve/delve/cmd/dlv@latest
go install github.com/spf13/cobra-cli@latest

# UPX для сжатия бинарников
sudo apt-get install upx-ucl
upx --best myapp

Мониторинг и метрики

Интеграция с Prometheus для мониторинга:

go get github.com/prometheus/client_golang

Добавляем метрики в приложение:

package main

import (
    "net/http"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    requests = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total HTTP requests",
        },
        []string{"method", "path", "status"},
    )
)

func init() {
    prometheus.MustRegister(requests)
}

func main() {
    http.Handle("/metrics", promhttp.Handler())
    // остальной код...
}

Нестандартные способы использования

Go отлично подходит для:

  • Системные утилиты — замена bash-скриптов на Go для сложной логики
  • Сетевые прокси — высокая производительность и простота
  • Микросервисы — быстрый старт и низкое потребление ресурсов
  • CLI-инструменты — распространение одним файлом

Пример системной утилиты для мониторинга дисков:

package main

import (
    "fmt"
    "os"
    "syscall"
)

func diskUsage(path string) (uint64, uint64, error) {
    var stat syscall.Statfs_t
    err := syscall.Statfs(path, &stat)
    if err != nil {
        return 0, 0, err
    }
    
    total := stat.Blocks * uint64(stat.Bsize)
    free := stat.Bavail * uint64(stat.Bsize)
    
    return total, free, nil
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("Usage: diskcheck ")
        os.Exit(1)
    }
    
    total, free, err := diskUsage(os.Args[1])
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        os.Exit(1)
    }
    
    used := total - free
    usedPercent := float64(used) / float64(total) * 100
    
    fmt.Printf("Total: %.2f GB\n", float64(total)/1024/1024/1024)
    fmt.Printf("Used: %.2f GB (%.1f%%)\n", float64(used)/1024/1024/1024, usedPercent)
    fmt.Printf("Free: %.2f GB\n", float64(free)/1024/1024/1024)
    
    if usedPercent > 90 {
        os.Exit(2) // Critical
    } else if usedPercent > 80 {
        os.Exit(1) // Warning
    }
}

Автоматизация и интеграция

Go-приложения легко интегрируются в CI/CD пайплайны. Пример для GitHub Actions:

name: Deploy Go App
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Go
      uses: actions/setup-go@v3
      with:
        go-version: 1.21
    
    - name: Build
      run: CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o myapp main.go
    
    - name: Deploy
      run: |
        scp myapp user@server:/tmp/
        ssh user@server 'sudo systemctl stop myapp && sudo cp /tmp/myapp /opt/myapp/ && sudo systemctl start myapp'

Тестирование и отладка

Не забываем про тесты. Создаём main_test.go:

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestHealthEndpoint(t *testing.T) {
    req, err := http.NewRequest("GET", "/health", nil)
    if err != nil {
        t.Fatal(err)
    }
    
    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(healthHandler)
    handler.ServeHTTP(rr, req)
    
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("Wrong status code: got %v want %v", status, http.StatusOK)
    }
    
    expected := "OK"
    if rr.Body.String() != expected {
        t.Errorf("Wrong body: got %v want %v", rr.Body.String(), expected)
    }
}

func BenchmarkHealthEndpoint(b *testing.B) {
    for i := 0; i < b.N; i++ {
        req, _ := http.NewRequest("GET", "/health", nil)
        rr := httptest.NewRecorder()
        handler := http.HandlerFunc(healthHandler)
        handler.ServeHTTP(rr, req)
    }
}

Запуск тестов:

go test -v
go test -bench=.
go test -race

Для серверных задач может понадобиться VPS или выделенный сервер с достаточными ресурсами для компиляции и запуска приложений.

Выводы и рекомендации

Go — отличный выбор для серверных приложений благодаря:

  • Простоте деплоя — один статический бинарник
  • Производительности — низкое потребление ресурсов
  • Надёжности — встроенная поддержка concurrency
  • Экосистеме — богатая стандартная библиотека

Рекомендации по использованию:

  • Используйте Go для API-серверов, микросервисов и CLI-утилит
  • Всегда настраивайте graceful shutdown
  • Не забывайте про мониторинг и логирование
  • Применяйте контейнеризацию для сложных deployment'ов
  • Используйте кросс-компиляцию для разных архитектур

Go показывает отличные результаты в продакшене — от небольших утилит до высоконагруженных систем. Основные преимущества проявляются именно на этапе деплоя и эксплуатации, что делает его идеальным выбором для системных администраторов и DevOps-инженеров.


В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.

Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.

Leave a reply

Your email address will not be published. Required fields are marked