From 0cfd55af7bb1b37c737498ece4667b09cb6d7051 Mon Sep 17 00:00:00 2001 From: yeasy Date: Wed, 10 Jun 2026 12:06:57 -0700 Subject: [PATCH] fix(content): repair Django tutorial crash and complete the hardening sweep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-ups to the 9 secret-hardening commits (each verified against docs.docker.com / vendor docs; one outright new bug found and fixed): - 11.6: eb5e4397 changed settings.py to a hard os.environ lookup but the web service never receives POSTGRES_PASSWORD (only db does; DATABASE_URL is set but never read) -> step-6 'docker compose up' crashed with KeyError. Pass the var to web; harden the leftover literal password in the 配置详解 snippet that contradicted the new guidance two lines down; blank line after the inserted sentence (bold heading merged into the paragraph); dev/prod table no longer claims dev uses 明文 passwords - 11.8: FAQ still told readers to check passwords in .env after the same file banned passwords in .env -> point at secrets/db_password.txt; backup sidecar env vars updated to tiredofit/db-backup 4.x interface (DB01_* + DB01_PASS_FILE + DB01_BACKUP_INTERVAL - the unprefixed DB_* names are ignored by current :latest; verified against upstream README) - demo/wordpress: compose now references secrets files that ship nowhere -> add README with the creation commands from 11.8; demo/django: align with the hardened 11.6 (env-injected password, passed to web too) - 04_image multistage demos: go build without go.mod fails on module-mode Go (reproduced by reviewer on go1.26) -> add 'go mod init helloworld' matching the 7.17 doc pattern in all three Dockerfiles - 21.7: init script reworked init-db.sql -> init-db.sh per the official image's env-reading .sh hook - removes the baked 'secure_password' AND the CREATE DATABASE myappdb collision with POSTGRES_DB that aborted first-boot init (ON_ERROR_STOP); compose passes APP_DB_PASSWORD; microservices init.sql mount annotated schema-only (POSTGRES_USER: appuser would collide with CREATE USER); Dockerfile-redis healthcheck now authenticates via REDISCLI_AUTH read from redis.conf (plain redis-cli ping gets NOAUTH against requirepass - same class as the compose sibling eb5e4397 already fixed); dev-container dev/dev creds annotated local-only - 19.3: Grafana admin password 'admin' sat directly under the newly added security warning -> env-injected like the rest of the stack --- 04_image/demo/multistage-builds/Dockerfile | 3 +- .../demo/multistage-builds/Dockerfile.build | 3 +- .../demo/multistage-builds/Dockerfile.one | 3 +- 11_compose/11.6_django.md | 7 +- 11_compose/11.8_wordpress.md | 16 ++--- 11_compose/demo/django/docker-compose.yml | 5 +- 11_compose/demo/wordpress/README.md | 18 +++++ .../19.3_performance_optimization.md | 2 +- 21_case_devops/21.7_practical_examples.md | 70 +++++++++++-------- 9 files changed, 81 insertions(+), 46 deletions(-) create mode 100644 11_compose/demo/wordpress/README.md diff --git a/04_image/demo/multistage-builds/Dockerfile b/04_image/demo/multistage-builds/Dockerfile index 9dcf57e..f3e2206 100644 --- a/04_image/demo/multistage-builds/Dockerfile +++ b/04_image/demo/multistage-builds/Dockerfile @@ -4,7 +4,8 @@ WORKDIR /go/src/github.com/go/helloworld/ COPY app.go . -RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . +RUN go mod init helloworld \ + && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest as prod diff --git a/04_image/demo/multistage-builds/Dockerfile.build b/04_image/demo/multistage-builds/Dockerfile.build index 112595b..5b96600 100644 --- a/04_image/demo/multistage-builds/Dockerfile.build +++ b/04_image/demo/multistage-builds/Dockerfile.build @@ -4,4 +4,5 @@ WORKDIR /go/src/github.com/go/helloworld COPY app.go . -RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . +RUN go mod init helloworld \ + && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . diff --git a/04_image/demo/multistage-builds/Dockerfile.one b/04_image/demo/multistage-builds/Dockerfile.one index e2fe420..690ee5e 100644 --- a/04_image/demo/multistage-builds/Dockerfile.one +++ b/04_image/demo/multistage-builds/Dockerfile.one @@ -6,7 +6,8 @@ WORKDIR /go/src/github.com/go/helloworld/ COPY app.go . -RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \ +RUN go mod init helloworld \ + && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \ && cp /go/src/github.com/go/helloworld/app /root WORKDIR /root/ diff --git a/11_compose/11.6_django.md b/11_compose/11.6_django.md index 7ef004a..c27eab4 100644 --- a/11_compose/11.6_django.md +++ b/11_compose/11.6_django.md @@ -136,6 +136,8 @@ services: db: condition: service_healthy environment: + # settings.py 直接读取 POSTGRES_PASSWORD,必须传入 web 容器 + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} DATABASE_URL: postgres://django_user:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}@db:5432/django_db volumes: @@ -153,7 +155,7 @@ db: environment: POSTGRES_DB: django_db # 创建的数据库名 POSTGRES_USER: django_user # 数据库用户 - POSTGRES_PASSWORD: django_password # 数据库密码 + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} # 数据库密码经环境变量注入,不写明文 volumes: - postgres_data:/var/lib/postgresql/data # 持久化数据 healthcheck: # 健康检查,确保数据库就绪 @@ -241,6 +243,7 @@ ALLOWED_HOSTS = ['*'] ``` 生产环境优先使用 Docker Compose secrets 或外部密钥管理;即使是本地示例,也不要把固定密码写进 Compose 文件或 Django 默认值中。 + **为什么 HOST 是 `db` 而不是 `localhost`?** 在 Docker Compose 中,各服务通过服务名相互访问。Docker 内置的 DNS 会将 `db` 解析为 db 服务容器的 IP 地址。这是 Docker Compose 的核心功能之一。 @@ -330,7 +333,7 @@ $ sudo chown -R $USER:$USER . |--------|---------|---------| | **Web 服务器** | `runserver` | `gunicorn` + Nginx | | **DEBUG** | `True` | `False` | -| **密码管理** | 明文写在配置 | 使用 Docker Secrets 或环境变量 | +| **密码管理** | 环境变量注入(如本节 `${POSTGRES_PASSWORD:?}`) | 使用 Docker Secrets 或外部密钥管理 | | **Volume** | 挂载代码目录 | 代码直接 COPY 进镜像 | | **ALLOWED_HOSTS** | `['*']` | 具体域名 | diff --git a/11_compose/11.8_wordpress.md b/11_compose/11.8_wordpress.md index 371e27d..a47ce3e 100644 --- a/11_compose/11.8_wordpress.md +++ b/11_compose/11.8_wordpress.md @@ -167,13 +167,13 @@ $ docker compose exec -T db sh -c 'tmp=$(mktemp) && printf "[client]\nuser=wordp volumes: - ./backups:/backup environment: - - DB_TYPE=mysql - - DB_HOST=db - - DB_NAME=wordpress - - DB_USER=wordpress - # 选用支持从文件读取密码的备份镜像,或用自定义 entrypoint 从 secret 文件注入。 - - DB_PASS_FILE=/run/secrets/db_password - - DB_DUMP_FREQ=1440 # 每天备份一次 + # tiredofit/db-backup 4.x 起按 DB01_ 前缀配置备份任务,并原生支持 _FILE 读密 + - DB01_TYPE=mysql + - DB01_HOST=db + - DB01_NAME=wordpress + - DB01_USER=wordpress + - DB01_PASS_FILE=/run/secrets/db_password + - DB01_BACKUP_INTERVAL=1440 # 每天备份一次(单位:分钟) secrets: - db_password depends_on: @@ -212,7 +212,7 @@ WordPress 支持 Redis 缓存以提高性能。 **现象**:访问页面显示 “Error establishing a database connection”。**排查**: 1. 检查 `docker compose logs wordpress` -2. 确认 `.env` 中的密码与 YAML 文件引用一致 +2. 确认 `secrets/db_password.txt` 的内容正确,且与数据库初始化时使用的密码一致(改密码后需要重建 db 数据卷) 3. 确认 `WORDPRESS_DB_HOST` 也是 `db` (服务名) 4. MySQL 8.4 可能需要几秒钟启动,WordPress 会自动重试,稍等片刻即可。 diff --git a/11_compose/demo/django/docker-compose.yml b/11_compose/demo/django/docker-compose.yml index 7219cb6..9871503 100644 --- a/11_compose/demo/django/docker-compose.yml +++ b/11_compose/demo/django/docker-compose.yml @@ -4,7 +4,7 @@ services: db: image: postgres environment: - POSTGRES_PASSWORD: 'postgres' + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} web: build: . @@ -13,3 +13,6 @@ services: - .:/code ports: - "8000:8000" + environment: + # 与书中 11.6 节一致:settings.py 从环境变量读取数据库密码 + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} diff --git a/11_compose/demo/wordpress/README.md b/11_compose/demo/wordpress/README.md new file mode 100644 index 0000000..d446b8f --- /dev/null +++ b/11_compose/demo/wordpress/README.md @@ -0,0 +1,18 @@ +# WordPress Compose 示例 + +本示例使用 Docker Compose secrets 管理数据库密码,启动前需要先创建密钥文件(参见书中 11.8 节): + +```bash +mkdir -p secrets +printf '%s\n' 'somestrongrootpassword' > secrets/db_root_password.txt +printf '%s\n' 'somestronguserpassword' > secrets/db_password.txt +chmod 600 secrets/*.txt +``` + +然后启动: + +```bash +docker compose up -d +``` + +注意:`secrets/` 目录不要提交到版本库;生产环境应改用平台的密钥管理能力。 diff --git a/19_observability/19.3_performance_optimization.md b/19_observability/19.3_performance_optimization.md index f62fe34..7e4f4cf 100644 --- a/19_observability/19.3_performance_optimization.md +++ b/19_observability/19.3_performance_optimization.md @@ -229,7 +229,7 @@ services: ports: - "3000:3000" environment: - - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:?set GRAFANA_ADMIN_PASSWORD} - GF_INSTALL_PLUGINS=grafana-piechart-panel volumes: - grafana_data:/var/lib/grafana diff --git a/21_case_devops/21.7_practical_examples.md b/21_case_devops/21.7_practical_examples.md index 617713b..23e4d4b 100644 --- a/21_case_devops/21.7_practical_examples.md +++ b/21_case_devops/21.7_practical_examples.md @@ -291,8 +291,8 @@ RUN apk add --no-cache \ pg-stat-monitor \ curl -# 复制初始化脚本 -COPY init-db.sql /docker-entrypoint-initdb.d/ +# 复制初始化脚本(.sh 形式可从环境变量读取密码,避免在 SQL 中写明文) +COPY init-db.sh /docker-entrypoint-initdb.d/ COPY health-check.sh / RUN chmod +x /health-check.sh @@ -302,40 +302,42 @@ HEALTHCHECK --interval=10s --timeout=5s --start-period=40s --retries=3 \ EXPOSE 5432 ``` -**初始化脚本(init-db.sql):** +**初始化脚本(init-db.sh):** -```sql --- 创建自定义用户 -CREATE USER appuser WITH PASSWORD 'secure_password'; +官方镜像会执行 `/docker-entrypoint-initdb.d/` 下的 `.sh` 脚本,因此应用用户的密码可以从环境变量注入,而不必写进 SQL;数据库 `myappdb` 已由入口脚本按 `POSTGRES_DB` 创建,初始化脚本中不要重复 `CREATE DATABASE`(否则首次初始化会因冲突而中止)。 --- 创建数据库 -CREATE DATABASE myappdb OWNER appuser; +```bash +#!/bin/bash +set -e --- 创建扩展 -\c myappdb +psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" <<-EOSQL + -- 创建应用用户(密码来自环境变量 APP_DB_PASSWORD) + CREATE USER appuser WITH PASSWORD '$APP_DB_PASSWORD'; -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -CREATE EXTENSION IF NOT EXISTS hstore; -CREATE EXTENSION IF NOT EXISTS pg_trgm; + -- 创建扩展 + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + CREATE EXTENSION IF NOT EXISTS hstore; + CREATE EXTENSION IF NOT EXISTS pg_trgm; --- 创建表 -CREATE TABLE users ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - username VARCHAR(255) NOT NULL UNIQUE, - email VARCHAR(255) NOT NULL UNIQUE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); + -- 创建表 + CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + username VARCHAR(255) NOT NULL UNIQUE, + email VARCHAR(255) NOT NULL UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); --- 创建索引 -CREATE INDEX idx_users_username ON users (username); -CREATE INDEX idx_users_email ON users (email); + -- 创建索引 + CREATE INDEX idx_users_username ON users (username); + CREATE INDEX idx_users_email ON users (email); --- 授予权限 -GRANT CONNECT ON DATABASE myappdb TO appuser; -GRANT USAGE ON SCHEMA public TO appuser; -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO appuser; -GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO appuser; + -- 授予权限 + GRANT CONNECT ON DATABASE $POSTGRES_DB TO appuser; + GRANT USAGE ON SCHEMA public TO appuser; + GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO appuser; + GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO appuser; +EOSQL ``` **健康检查脚本(health-check.sh):** @@ -363,6 +365,7 @@ services: POSTGRES_DB: myappdb POSTGRES_USER: postgres POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env} + APP_DB_PASSWORD: ${APP_DB_PASSWORD:?set APP_DB_PASSWORD in .env} POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=en_US.UTF-8" volumes: - postgres_data:/var/lib/postgresql/data @@ -510,8 +513,10 @@ CMD ["redis-server", "/usr/local/etc/redis/redis.conf"] EXPOSE 6379 +# redis.conf 启用了 requirepass,健康检查必须带认证,否则只会收到 NOAUTH 错误 +# REDISCLI_AUTH 可避免把密码出现在进程参数中 HEALTHCHECK --interval=5s --timeout=3s --retries=5 \ - CMD redis-cli ping || exit 1 + CMD sh -c 'REDISCLI_AUTH="$(awk "/^requirepass /{print \$2}" /usr/local/etc/redis/redis.conf)" redis-cli ping | grep -q PONG' ``` **redis.conf 配置:** @@ -522,7 +527,7 @@ bind 0.0.0.0 # 端口 port 6379 -# 密码保护 +# 密码保护(示例占位值,部署时应替换并避免提交到版本库) requirepass your_secure_password # 内存管理 @@ -621,6 +626,8 @@ services: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data + # 此处的 init.sql 只放建表/索引等 schema 语句; + # 用户与数据库已由 POSTGRES_USER/POSTGRES_DB 创建,脚本中不要重复 CREATE USER/DATABASE - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql networks: - backend-network @@ -844,6 +851,7 @@ services: postgres: image: postgres:16-alpine environment: + # 仅限本机 Dev Container 的一次性开发凭证,不要在任何共享/联网环境复用 POSTGRES_USER: dev POSTGRES_PASSWORD: dev POSTGRES_DB: myapp