第十一章 Docker 集成
第十一章:Docker 集成
11.1 概述
Guix 与 Docker 的集成是将 Guix 的函数式包管理优势带入容器化部署工作流的重要方式。通过 guix pack,你可以将 Guix 管理的包集合导出为 Docker 镜像、OCI 镜像或其他格式。
11.1.1 Guix + Docker 的优势
| 优势 | 说明 |
|---|---|
| 可重现镜像 | 相同 manifest 总是产出相同镜像 |
| 最小镜像 | 只包含声明的包,无冗余 |
| 确定性 | 哈希标识的包路径,可审计 |
| 安全 | 沙箱构建,减少供应链风险 |
| 兼容性 | 标准 Docker/OCI 格式,任何容器运行时可用 |
11.1.2 工作流概览
manifest.scm → guix pack -f docker → image.tar.gz → docker load → 运行容器
(声明) (构建命令) (镜像文件) (导入) (部署)
11.2 基本镜像构建
11.2.1 最简单的 Docker 镜像
# 创建包含 Python 的 Docker 镜像
guix pack -f docker -o python-image.tar.gz \
python python-requests python-flask
# 导入到 Docker
docker load < python-image.tar.gz
# 输出:Loaded image: ...
# 运行
docker run --rm -it <image-id> python3 -c "print('Hello from Guix!')"
11.2.2 指定入口点
# 设置默认入口点
guix pack -f docker \
--entry-point=/bin/python3 \
-o python-app.tar.gz \
python python-flask gunicorn
# 运行时直接执行
docker run --rm -it <image-id> -c "print('hello')"
11.2.3 指定镜像名称和标签
# 指定名称和标签
guix pack -f docker \
--image-tag="myapp:1.0" \
-o myapp-1.0.tar.gz \
python python-flask
# 导入后镜像名为 myapp:1.0
docker load < myapp-1.0.tar.gz
docker images | grep myapp
11.3 使用 Manifest 构建镜像
11.3.1 应用镜像 Manifest
;; webapp-manifest.scm
(specifications->manifest
'("python"
"python-flask"
"python-gunicorn"
"python-redis"
"python-psycopg2"
"openssl"
"ca-certificates"
"tzdata"))
guix pack -f docker \
--image-tag="webapp:latest" \
--manifest=webapp-manifest.scm \
--entry-point=/bin/gunicorn \
-o webapp.tar.gz
11.3.2 多阶段 Manifest
;; manifest.scm — 完整的 Web 应用环境
(use-modules (guix gexp))
(specifications->manifest
'(;; 运行时
"python"
"python-flask"
"python-gunicorn"
"python-redis"
"python-sqlalchemy"
"python-psycopg2"
;; 工具
"openssl"
"ca-certificates"
"tzdata"
"coreutils" ; ls, cat 等
"bash" ; Shell
"curl")) ; 健康检查
11.3.3 复杂的 Manifest 配置
(use-modules (gnu packages)
(gnu packages python)
(gnu packages python-web)
(gnu packages databases))
;; 使用 Scheme 表达式精细控制
(packages->manifest
(list
;; Python 运行时
python-3.11
;; Web 框架
python-flask
python-gunicorn
;; 数据库
python-sqlalchemy
python-alembic
python-psycopg2
;; 缓存
python-redis
;; 工具
openssl
(specification->package "ca-certificates")))
11.4 应用打包实战
11.4.1 Flask 应用 Docker 镜像
项目结构:
my-flask-app/
├── app/
│ ├── __init__.py
│ ├── routes.py
│ └── models.py
├── requirements.txt
├── guix-manifest.scm
├── Dockerfile.guix
└── wsgi.py
Guix Manifest:
;; guix-manifest.scm
(specifications->manifest
'("python"
"python-flask"
"python-gunicorn"
"python-sqlalchemy"
"python-psycopg2"
"python-redis"
"openssl"
"ca-certificates"))
构建并打包:
# 使用 Guix 构建镜像
guix pack -f docker \
--image-tag="my-flask-app:1.0" \
--manifest=guix-manifest.scm \
--entry-point=/bin/gunicorn \
-o app-image.tar.gz
# 或者使用 Dockerfile 混合模式
混合 Dockerfile:
# Dockerfile.guix — 使用 Guix 构建的镜像作为基础
FROM guix-pack:latest as builder
# 复制应用代码
COPY app/ /app/
COPY wsgi.py /app/
# 设置工作目录
WORKDIR /app
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s \
CMD curl -f http://localhost:8000/health || exit 1
# 暴露端口
EXPOSE 8000
# 启动应用
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "wsgi:app"]
11.4.2 静态网站镜像
;; nginx-manifest.scm
(specifications->manifest
'("nginx"
"openssl"))
guix pack -f docker \
--image-tag="static-site:latest" \
--manifest=nginx-manifest.scm \
--entry-point=/bin/nginx \
-o static-site.tar.gz
FROM static-site:latest
# 复制网站文件
COPY dist/ /var/www/html/
# 使用自定义 nginx 配置
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
11.4.3 微服务镜像
;; api-gateway-manifest.scm
(specifications->manifest
'("nginx"
"openssl"
"curl"))
;; user-service-manifest.scm
(specifications->manifest
'("python"
"python-flask"
"python-sqlalchemy"
"python-jwt"))
;; order-service-manifest.scm
(specifications->manifest
'("python"
"python-flask"
"python-redis"
"python-celery"))
# 构建多个服务镜像
for service in api-gateway user-service order-service; do
guix pack -f docker \
--image-tag="${service}:latest" \
--manifest=${service}-manifest.scm \
--entry-point=/bin/python3 \
-o ${service}.tar.gz
done
11.5 高级打包选项
11.5.1 可重定位包(Relocatable)
# 创建可重定位的包
guix pack --relocatable --relocatable-name=myapp \
-f tarball \
python python-flask
# 这个包可以在任意位置解压使用
tar xf myapp.tar.gz
./myapp/bin/python3
11.5.2 SquashFS 镜像
# SquashFS 是只读压缩文件系统
guix pack -f squashfs \
--manifest=manifest.scm \
-o app.squashfs
# 挂载使用
sudo mount app.squashfs /mnt/app
/mnt/app/bin/python3
11.5.3 自定义镜像配置
# 完整的 guix pack 选项
guix pack -f docker \
--image-tag="myapp:1.0" \
--manifest=manifest.scm \
--entry-point=/bin/python3 \
--max-jobs=4 \
--no-grafts \
--save-provenance \
-o myapp.tar.gz
| 选项 | 说明 |
|---|---|
--image-tag | 镜像名称和标签 |
--manifest | 使用 manifest 文件 |
--entry-point | 容器入口点 |
--max-jobs | 并行构建数 |
--no-grafts | 跳过 graft(安全补丁应用) |
--save-provenance | 保存包定义到镜像中 |
--relocatable | 创建可重定位包 |
-o | 输出文件名 |
11.6 Docker Compose 集成
11.6.1 多服务部署
# docker-compose.yml
version: "3.8"
services:
web:
image: my-flask-app:1.0
build:
context: .
dockerfile: Dockerfile.guix
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
db:
image: guix-postgres:15
build:
context: .
dockerfile: Dockerfile.postgres.guix
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
redis:
image: guix-redis:7
build:
context: .
dockerfile: Dockerfile.redis.guix
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
11.6.2 构建脚本
#!/bin/bash
# build-images.sh — 构建所有 Guix Docker 镜像
set -e
echo "Building Guix Docker images..."
# 构建 Web 应用镜像
echo "Building web app..."
guix pack -f docker \
--image-tag="my-flask-app:1.0" \
--manifest=webapp-manifest.scm \
--entry-point=/bin/gunicorn \
-o webapp.tar.gz
# 构建 PostgreSQL 镜像
echo "Building PostgreSQL..."
guix pack -f docker \
--image-tag="guix-postgres:15" \
--manifest=postgres-manifest.scm \
--entry-point=/bin/postgres \
-o postgres.tar.gz
# 构建 Redis 镜像
echo "Building Redis..."
guix pack -f docker \
--image-tag="guix-redis:7" \
--manifest=redis-manifest.scm \
--entry-point=/bin/redis-server \
-o redis.tar.gz
# 导入所有镜像
echo "Loading images..."
docker load < webapp.tar.gz
docker load < postgres.tar.gz
docker load < redis.tar.gz
echo "Done! All images loaded."
docker images | grep guix
11.7 CI/CD 中的镜像构建
11.7.1 GitLab CI
# .gitlab-ci.yml
stages:
- build
- test
- deploy
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
build-image:
stage: build
image: guix
script:
- guix pull
- guix pack -f docker
--image-tag="$IMAGE_TAG"
--manifest=manifest.scm
--entry-point=/bin/python3
-o app.tar.gz
- docker load < app.tar.gz
- docker push $IMAGE_TAG
artifacts:
paths:
- app.tar.gz
test-image:
stage: test
image: docker:latest
services:
- docker:dind
script:
- docker load < app.tar.gz
- docker run --rm $IMAGE_TAG python3 -m pytest /app/tests/
deploy:
stage: deploy
only:
- main
script:
- docker load < app.tar.gz
- docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
11.7.2 GitHub Actions
# .github/workflows/docker.yml
name: Build Docker Image
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Guix
run: |
wget https://git.savannah.gnu.org/cgit/guix.git/plain/etc/guix-install.sh
chmod +x guix-install.sh
sudo ./guix-install.sh
guix pull
- name: Build Docker Image
run: |
guix pack -f docker \
--image-tag="myapp:${{ github.sha }}" \
--manifest=manifest.scm \
-o app.tar.gz
- name: Load and Tag
run: |
docker load < app.tar.gz
docker tag myapp:${{ github.sha }} ghcr.io/${{ github.repository }}:latest
docker tag myapp:${{ github.sha }} ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Push to Registry
if: github.event_name == 'push'
run: |
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/${{ github.repository }}:latest
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
11.8 镜像优化
11.8.1 减小镜像体积
| 技巧 | 说明 |
|---|---|
| 精简依赖 | 只在 manifest 中声明必需的包 |
| 使用输出 | package:out 只包含运行时文件 |
| 移除文档 | 排除 :doc 输出 |
| 最小基础包 | 使用 coreutils 替代完整 GNU 工具集 |
;; 精简 manifest
(specifications->manifest
'("python:minimal" ; 最小 Python
"python-flask"
"ca-certificates"))
11.8.2 多层构建策略
;; 运行时最小集
(define %runtime-packages
(specifications->packages
'("python:minimal"
"ca-certificates"
"tzdata")))
;; 开发工具(不在最终镜像中)
(define %dev-packages
(specifications->packages
'("gcc-toolchain"
"python-pytest"
"python-mypy")))
11.9 安全考虑
11.9.1 镜像安全检查
# 验证镜像内容
guix pack -f docker --save-provenance ...
# 镜像中会包含 /gnu/store 中所有包的定义
# 可以审计每个包的构建来源
11.9.2 安全最佳实践
| 实践 | 说明 |
|---|---|
| 固定版本 | 使用 channels.scm 锁定版本 |
| 签名验证 | 验证通道签名 |
| 最小权限 | 容器内使用非 root 用户 |
| 只读文件系统 | 使用 --read-only 运行 |
| 健康检查 | 添加 HEALTHCHECK |
| 定期更新 | 定期重建镜像获取安全补丁 |
11.9.3 非 root 容器
FROM guix-app:latest
# 创建应用用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
# 设置目录权限
RUN mkdir -p /app/data && chown -R appuser:appuser /app
# 切换到非 root 用户
USER appuser
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
11.10 Guix System 的 Docker/OCI 导出
11.10.1 导出完整系统容器
;; system-container.scm
(use-modules (gnu)
(gnu system linux-container))
(operating-system
(host-name "guix-container")
(timezone "Asia/Shanghai")
(locale "zh_CN.utf8")
(bootloader (bootloader-configuration
(bootloader grub-bootloader)
(targets '("/dev/null"))))
(file-systems
(cons* (file-system
(device "tmpfs")
(mount-point "/")
(type "tmpfs"))
%base-file-systems))
(packages (list python nginx))
(services
(cons* (service nginx-service-type
(nginx-configuration
(server-blocks
(list (nginx-server-configuration
(server-name '("localhost"))
(listen '("80"))
(root "/var/www/html"))))))
%base-services)))
# 构建系统容器
guix system container system-container.scm
11.11 Docker 命令速查表
| 操作 | 命令 |
|---|---|
| 构建镜像 | guix pack -f docker -o image.tar.gz <packages> |
| 使用 Manifest | guix pack -f docker --manifest=m.scm -o image.tar.gz |
| 指定标签 | guix pack -f docker --image-tag="name:tag" |
| 设置入口 | guix pack -f docker --entry-point=/bin/app |
| 导入镜像 | docker load < image.tar.gz |
| 运行容器 | docker run --rm -it <image> /bin/bash |
| 保存溯源 | guix pack -f docker --save-provenance |
| SquashFS | guix pack -f squashfs |
| Tarball | guix pack -f tarball |
| 可重定位 | guix pack --relocatable |
11.12 总结
本章讲解了 Guix 与 Docker 的集成:
- 基本概念——Guix pack 生成 Docker/OCI 镜像
- 镜像构建——从简单到复杂的各种场景
- Manifest 管理——声明式镜像依赖管理
- 应用打包——Flask、静态网站、微服务
- CI/CD 集成——自动化镜像构建和发布
- 镜像优化——减小体积和提升安全性
- 最佳实践——安全、可重现的镜像管理
下一章我们将总结最佳实践和工作流。