5.2.1 Docker 基础与使用 #
Docker 是目前最流行的容器化平台,它通过操作系统级虚拟化技术,将应用程序及其依赖项打包到轻量级、可移植的容器中。对于 Go 开发者而言,Docker 提供了一致的运行环境和简化的部署流程。
Docker 核心概念 #
容器与虚拟机的区别 #
容器和虚拟机都是虚拟化技术,但它们的实现方式和资源消耗有显著差异:
虚拟机特点:
- 完整的操作系统
- 硬件级虚拟化
- 资源消耗大
- 启动时间长
容器特点:
- 共享宿主机内核
- 操作系统级虚拟化
- 资源消耗小
- 启动时间短
Docker 架构组件 #
Docker 采用客户端-服务器架构,主要组件包括:
- Docker Client:用户交互界面,发送命令到 Docker Daemon
- Docker Daemon:后台服务,管理镜像、容器、网络和存储
- Docker Registry:镜像仓库,存储和分发 Docker 镜像
- Docker Images:只读模板,用于创建容器
- 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 提供多种网络模式:
- bridge:默认网络模式,容器通过虚拟网桥通信
- host:容器直接使用宿主机网络
- none:容器没有网络接口
- 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 ./
最佳实践总结 #
- 使用多阶段构建:减少最终镜像大小
- 选择合适的基础镜像:优先使用 Alpine 等轻量级镜像
- 合理使用缓存:优化 Dockerfile 层顺序
- 设置健康检查:确保容器运行状态
- 使用非 root 用户:提高安全性
- 合理配置资源限制:避免资源争用
- 使用 .dockerignore:减少构建上下文大小
- 标签管理:使用语义化版本标签
通过掌握这些 Docker 基础知识和实践技巧,您可以有效地将 Go 应用程序容器化,为后续的微服务部署和云原生开发奠定坚实基础。