5.2.1 Docker 基础与使用

5.2.1 Docker 基础与使用 #

Docker 是目前最流行的容器化平台,它通过操作系统级虚拟化技术,将应用程序及其依赖项打包到轻量级、可移植的容器中。对于 Go 开发者而言,Docker 提供了一致的运行环境和简化的部署流程。

Docker 核心概念 #

容器与虚拟机的区别 #

容器和虚拟机都是虚拟化技术,但它们的实现方式和资源消耗有显著差异:

虚拟机特点

  • 完整的操作系统
  • 硬件级虚拟化
  • 资源消耗大
  • 启动时间长

容器特点

  • 共享宿主机内核
  • 操作系统级虚拟化
  • 资源消耗小
  • 启动时间短

Docker 架构组件 #

Docker 采用客户端-服务器架构,主要组件包括:

  1. Docker Client:用户交互界面,发送命令到 Docker Daemon
  2. Docker Daemon:后台服务,管理镜像、容器、网络和存储
  3. Docker Registry:镜像仓库,存储和分发 Docker 镜像
  4. Docker Images:只读模板,用于创建容器
  5. Docker Containers:镜像的运行实例

Docker 基本操作 #

镜像管理 #

# 拉取镜像
docker pull golang:1.19-alpine

# 查看本地镜像
docker images

# 删除镜像
docker rmi golang:1.19-alpine

# 构建镜像
docker build -t myapp:v1.0 .

# 推送镜像到仓库
docker push myregistry/myapp:v1.0

容器生命周期管理 #

# 运行容器
docker run -d --name myapp -p 8080:8080 myapp:v1.0

# 查看运行中的容器
docker ps

# 查看所有容器(包括停止的)
docker ps -a

# 停止容器
docker stop myapp

# 启动已停止的容器
docker start myapp

# 重启容器
docker restart myapp

# 删除容器
docker rm myapp

# 强制删除运行中的容器
docker rm -f myapp

容器交互操作 #

# 进入运行中的容器
docker exec -it myapp /bin/sh

# 查看容器日志
docker logs myapp

# 实时查看日志
docker logs -f myapp

# 查看容器资源使用情况
docker stats myapp

# 查看容器详细信息
docker inspect myapp

Go 应用容器化实践 #

简单的 Go Web 应用 #

首先创建一个简单的 Go Web 应用:

// 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 container! Host: %s\n",
            os.Getenv("HOSTNAME"))
    })

    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.mod
module myapp

go 1.19

基础 Dockerfile #

# Dockerfile
FROM golang:1.19-alpine AS builder

# 设置工作目录
WORKDIR /app

# 复制 go mod 文件
COPY go.mod go.sum ./

# 下载依赖
RUN go mod download

# 复制源代码
COPY . .

# 构建应用
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# 运行阶段
FROM alpine:latest

# 安装 ca-certificates 用于 HTTPS 请求
RUN apk --no-cache add ca-certificates

WORKDIR /root/

# 从构建阶段复制二进制文件
COPY --from=builder /app/main .

# 暴露端口
EXPOSE 8080

# 运行应用
CMD ["./main"]

构建和运行容器 #

# 构建镜像
docker build -t go-web-app:v1.0 .

# 运行容器
docker run -d --name go-app -p 8080:8080 go-web-app:v1.0

# 测试应用
curl http://localhost:8080
curl http://localhost:8080/health

Docker 网络 #

网络模式 #

Docker 提供多种网络模式:

  1. bridge:默认网络模式,容器通过虚拟网桥通信
  2. host:容器直接使用宿主机网络
  3. none:容器没有网络接口
  4. overlay:跨主机容器通信(Swarm 模式)

自定义网络 #

# 创建自定义网络
docker network create --driver bridge mynetwork

# 查看网络列表
docker network ls

# 在指定网络中运行容器
docker run -d --name app1 --network mynetwork go-web-app:v1.0
docker run -d --name app2 --network mynetwork go-web-app:v1.0

# 容器间可以通过容器名通信
docker exec app1 ping app2

端口映射 #

# 映射单个端口
docker run -p 8080:8080 go-web-app:v1.0

# 映射多个端口
docker run -p 8080:8080 -p 9090:9090 go-web-app:v1.0

# 映射到随机端口
docker run -P go-web-app:v1.0

# 查看端口映射
docker port container_name

Docker 存储 #

数据卷(Volumes) #

数据卷是 Docker 推荐的数据持久化方式:

# 创建数据卷
docker volume create mydata

# 查看数据卷
docker volume ls

# 使用数据卷
docker run -d --name app -v mydata:/app/data go-web-app:v1.0

# 查看数据卷详情
docker volume inspect mydata

绑定挂载(Bind Mounts) #

将宿主机目录挂载到容器:

# 挂载当前目录
docker run -d --name app -v $(pwd):/app go-web-app:v1.0

# 只读挂载
docker run -d --name app -v $(pwd):/app:ro go-web-app:v1.0

临时文件系统(tmpfs) #

在内存中创建临时存储:

docker run -d --name app --tmpfs /tmp go-web-app:v1.0

实际应用示例 #

带数据库的 Go 应用 #

创建一个连接 PostgreSQL 数据库的 Go 应用:

// main.go
package main

import (
    "database/sql"
    "fmt"
    "log"
    "net/http"
    "os"

    _ "github.com/lib/pq"
)

func main() {
    dbHost := os.Getenv("DB_HOST")
    dbUser := os.Getenv("DB_USER")
    dbPassword := os.Getenv("DB_PASSWORD")
    dbName := os.Getenv("DB_NAME")

    if dbHost == "" {
        dbHost = "localhost"
    }

    connStr := fmt.Sprintf("host=%s user=%s password=%s dbname=%s sslmode=disable",
        dbHost, dbUser, dbPassword, dbName)

    db, err := sql.Open("postgres", connStr)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // 测试数据库连接
    if err := db.Ping(); err != nil {
        log.Fatal("Cannot connect to database:", err)
    }

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        var count int
        err := db.QueryRow("SELECT COUNT(*) FROM information_schema.tables").Scan(&count)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        fmt.Fprintf(w, "Database connected! Table count: %d\n", count)
    })

    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Docker Compose 配置 #

# docker-compose.yml
version: "3.8"

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=postgres
      - DB_USER=myuser
      - DB_PASSWORD=mypassword
      - DB_NAME=mydb
    depends_on:
      - postgres
    networks:
      - app-network

  postgres:
    image: postgres:13-alpine
    environment:
      - POSTGRES_USER=myuser
      - POSTGRES_PASSWORD=mypassword
      - POSTGRES_DB=mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge

运行多容器应用 #

# 启动所有服务
docker-compose up -d

# 查看服务状态
docker-compose ps

# 查看日志
docker-compose logs app

# 停止所有服务
docker-compose down

# 停止并删除数据卷
docker-compose down -v

容器监控和调试 #

资源监控 #

# 查看容器资源使用情况
docker stats

# 查看特定容器的资源使用
docker stats container_name

# 限制容器资源使用
docker run -d --name app --memory=512m --cpus=1.0 go-web-app:v1.0

日志管理 #

# 查看容器日志
docker logs container_name

# 实时跟踪日志
docker logs -f container_name

# 查看最近的日志
docker logs --tail 100 container_name

# 查看特定时间段的日志
docker logs --since 2023-01-01T00:00:00 container_name

容器调试 #

# 进入容器进行调试
docker exec -it container_name /bin/sh

# 在容器中运行命令
docker exec container_name ps aux

# 复制文件到容器
docker cp file.txt container_name:/path/to/destination

# 从容器复制文件
docker cp container_name:/path/to/file ./

最佳实践总结 #

  1. 使用多阶段构建:减少最终镜像大小
  2. 选择合适的基础镜像:优先使用 Alpine 等轻量级镜像
  3. 合理使用缓存:优化 Dockerfile 层顺序
  4. 设置健康检查:确保容器运行状态
  5. 使用非 root 用户:提高安全性
  6. 合理配置资源限制:避免资源争用
  7. 使用 .dockerignore:减少构建上下文大小
  8. 标签管理:使用语义化版本标签

通过掌握这些 Docker 基础知识和实践技巧,您可以有效地将 Go 应用程序容器化,为后续的微服务部署和云原生开发奠定坚实基础。