- Home »

Как создавать и устанавливать программы на 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-инженеров.
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.