<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Production on Commentary of Takao</title><link>https://takao.blog/zh-tw/tags/production/</link><description>Recent content in Production on Commentary of Takao</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Commentary of Takao</copyright><lastBuildDate>Tue, 12 Nov 2024 00:00:00 +0900</lastBuildDate><atom:link href="https://takao.blog/zh-tw/tags/production/index.xml" rel="self" type="application/rss+xml"/><item><title>Docker Compose for Production: Deployment 最佳 Practices</title><link>https://takao.blog/zh-tw/web/docker-compose-production/</link><pubDate>Tue, 12 Nov 2024 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/docker-compose-production/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/docker-compose-production-zh-tw.png" alt="Featured image of post Docker Compose for Production: Deployment 最佳 Practices" /&gt;&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;Docker Compose is often relegated to 本地 development, but it is increasingly used for single-host production deployments of small-to-medium applications. The common criticism that &amp;ldquo;Compose is not for production&amp;rdquo; overlooks a key point: for many workloads, a well-configured Compose stack on a single VM provides the right balance of simplicity and reliability.&lt;/p&gt;
&lt;p&gt;This guide covers production-grade Compose practices — treating your compose files as infrastructure-as-code with proper version control, CI/CD integration, and production-specific hardening.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-compose-file-structure-and-versioning"&gt;1. Compose File Structure and Versioning
&lt;/h2&gt;&lt;p&gt;Use the Compose Specification (format &lt;code&gt;3.8+&lt;/code&gt;) and adopt a multi-file strategy for separating concerns:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;compose.yml # Base configuration (shared across environments)
compose.override.yml # Local development overrides (not used in production)
compose.prod.yml # Production-specific overrides
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Leverage YAML anchors to reduce duplication across services:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;x-logging&lt;/span&gt;: &lt;span style="color:#75715e"&gt;&amp;amp;logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;driver&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;json-file&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;options&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;max-size&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;10m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;max-file&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;myapp:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;logging&lt;/span&gt;: &lt;span style="color:#75715e"&gt;*logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;restart&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;unless-stopped&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Use &lt;code&gt;--env-file&lt;/code&gt; to inject environment-specific variables and avoid hardcoding secrets. Prefer &lt;code&gt;docker compose&lt;/code&gt; (v2) over the deprecated &lt;code&gt;docker-compose&lt;/code&gt; (v1).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="2-service-dependencies-and-health-checks"&gt;2. Service Dependencies and Health Checks
&lt;/h2&gt;&lt;p&gt;Relying on &lt;code&gt;depends_on&lt;/code&gt; alone is insufficient for production. Use &lt;code&gt;condition: service_healthy&lt;/code&gt; to ensure services wait for dependencies to pass health checks before starting.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;postgres&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;postgres:16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;healthcheck&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;test&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;CMD-SHELL&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;pg_isready -U $POSTGRES_USER&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;interval&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;timeout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;retries&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;start_period&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;depends_on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;postgres&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;condition&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service_healthy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;healthcheck&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;test&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;CMD&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;curl&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;-f&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;http://localhost:3000/healthz&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;interval&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;timeout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;retries&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This cascading health check pattern — Nginx → App → Redis → PostgreSQL — ensures each service starts only when its dependencies are truly ready.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="3-networking-configuration"&gt;3. Networking Configuration
&lt;/h2&gt;&lt;p&gt;Use custom networks with &lt;code&gt;driver: bridge&lt;/code&gt; for service isolation. Avoid the default &lt;code&gt;bridge&lt;/code&gt; network, which lacks service discovery via DNS.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;frontend&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;driver&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bridge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;backend&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;driver&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bridge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;internal&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;monitoring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;driver&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bridge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;frontend&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;backend&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;monitoring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A three-network topology provides clean separation: &lt;code&gt;frontend&lt;/code&gt; for the reverse proxy and app, &lt;code&gt;backend&lt;/code&gt; (internal) for the app and database, and &lt;code&gt;monitoring&lt;/code&gt; for observability tools. Use network aliases for stable DNS resolution across restarts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="4-resource-constraints-and-limits"&gt;4. Resource Constraints and Limits
&lt;/h2&gt;&lt;p&gt;Production containers must have resource boundaries to prevent a single service from starving the host.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;deploy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;limits&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cpus&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;0.5&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;memory&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;256M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;pids&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;reservations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cpus&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;0.25&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;memory&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;128M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Directive&lt;/th&gt;
					&lt;th&gt;Purpose&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;limits.cpus&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Maximum CPU cores (e.g., &lt;code&gt;'0.5'&lt;/code&gt; = half a core)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;limits.memory&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Maximum memory before OOM kill&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reservations&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Guaranteed minimum resources&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ulimits&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;File descriptor and process limits (e.g., &lt;code&gt;nofile: 65536&lt;/code&gt;)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For Node.js apps, align &lt;code&gt;--max-old-space-size&lt;/code&gt; with the memory limit to avoid garbage collection thrashing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="5-logging-and-observability"&gt;5. Logging and Observability
&lt;/h2&gt;&lt;p&gt;Centralized logging is critical for debugging production issues. Configure the &lt;code&gt;json-file&lt;/code&gt; driver with rotation to prevent disk exhaustion:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;x-logging&lt;/span&gt;: &lt;span style="color:#75715e"&gt;&amp;amp;logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;driver&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;json-file&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;options&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;max-size&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;10m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;max-file&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alternative drivers for centralized aggregation:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Driver&lt;/th&gt;
					&lt;th&gt;Destination&lt;/th&gt;
					&lt;th&gt;Use Case&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;fluentd&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Fluentd → Elasticsearch&lt;/td&gt;
					&lt;td&gt;Full-text search and analytics&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;gelf&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Graylog&lt;/td&gt;
					&lt;td&gt;Structured log management&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;awslogs&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Amazon CloudWatch&lt;/td&gt;
					&lt;td&gt;AWS-native deployments&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Output structured JSON from your application so Docker can forward it efficiently. Use &lt;code&gt;docker compose logs --tail 100 --follow&lt;/code&gt; for real-time debugging.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="6-secrets-and-environment-management"&gt;6. Secrets and Environment Management
&lt;/h2&gt;&lt;p&gt;Never hardcode secrets in compose files. For Swarm mode, use Docker Secrets which are encrypted at rest and mounted as files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;secrets&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;db_password&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;file&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;./secrets/db_password.txt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;secrets&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;db_password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For non-Swarm Compose, use &lt;code&gt;.env&lt;/code&gt; files (listed in &lt;code&gt;.gitignore&lt;/code&gt;), encrypted with &lt;code&gt;sops&lt;/code&gt; or &lt;code&gt;age&lt;/code&gt;. Reference variables via `## Introduction&lt;/p&gt;
&lt;p&gt;Docker Compose is often relegated to 本地 development, but it is increasingly used for single-host production deployments of small-to-medium applications. The common criticism that &amp;ldquo;Compose is not for production&amp;rdquo; overlooks a key point: for many workloads, a well-configured Compose stack on a single VM provides the right balance of simplicity and reliability.&lt;/p&gt;
&lt;p&gt;This guide covers production-grade Compose practices — treating your compose files as infrastructure-as-code with proper version control, CI/CD integration, and production-specific hardening.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-compose-file-structure-and-versioning-1"&gt;1. Compose File Structure and Versioning
&lt;/h2&gt;&lt;p&gt;Use the Compose Specification (format &lt;code&gt;3.8+&lt;/code&gt;) and adopt a multi-file strategy for separating concerns:&lt;/p&gt;
&lt;p&gt;%%BLOCK_0%%&lt;/p&gt;
&lt;p&gt;Leverage YAML anchors to reduce duplication across services:&lt;/p&gt;
&lt;p&gt;%%BLOCK_1%%&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;--env-file&lt;/code&gt; to inject environment-specific variables and avoid hardcoding secrets. Prefer &lt;code&gt;docker compose&lt;/code&gt; (v2) over the deprecated &lt;code&gt;docker-compose&lt;/code&gt; (v1).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="2-service-dependencies-and-health-checks-1"&gt;2. Service Dependencies and Health Checks
&lt;/h2&gt;&lt;p&gt;Relying on &lt;code&gt;depends_on&lt;/code&gt; alone is insufficient for production. Use &lt;code&gt;condition: service_healthy&lt;/code&gt; to ensure services wait for dependencies to pass health checks before starting.&lt;/p&gt;
&lt;p&gt;%%BLOCK_2%%&lt;/p&gt;
&lt;p&gt;This cascading health check pattern — Nginx → App → Redis → PostgreSQL — ensures each service starts only when its dependencies are truly ready.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="3-networking-configuration-1"&gt;3. Networking Configuration
&lt;/h2&gt;&lt;p&gt;Use custom networks with &lt;code&gt;driver: bridge&lt;/code&gt; for service isolation. Avoid the default &lt;code&gt;bridge&lt;/code&gt; network, which lacks service discovery via DNS.&lt;/p&gt;
&lt;p&gt;%%BLOCK_3%%&lt;/p&gt;
&lt;p&gt;A three-network topology provides clean separation: &lt;code&gt;frontend&lt;/code&gt; for the reverse proxy and app, &lt;code&gt;backend&lt;/code&gt; (internal) for the app and database, and &lt;code&gt;monitoring&lt;/code&gt; for observability tools. Use network aliases for stable DNS resolution across restarts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="4-resource-constraints-and-limits-1"&gt;4. Resource Constraints and Limits
&lt;/h2&gt;&lt;p&gt;Production containers must have resource boundaries to prevent a single service from starving the host.&lt;/p&gt;
&lt;p&gt;%%BLOCK_4%%&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Directive&lt;/th&gt;
					&lt;th&gt;Purpose&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;limits.cpus&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Maximum CPU cores (e.g., &lt;code&gt;'0.5'&lt;/code&gt; = half a core)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;limits.memory&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Maximum memory before OOM kill&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reservations&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Guaranteed minimum resources&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ulimits&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;File descriptor and process limits (e.g., &lt;code&gt;nofile: 65536&lt;/code&gt;)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For Node.js apps, align &lt;code&gt;--max-old-space-size&lt;/code&gt; with the memory limit to avoid garbage collection thrashing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="5-logging-and-observability-1"&gt;5. Logging and Observability
&lt;/h2&gt;&lt;p&gt;Centralized logging is critical for debugging production issues. Configure the &lt;code&gt;json-file&lt;/code&gt; driver with rotation to prevent disk exhaustion:&lt;/p&gt;
&lt;p&gt;%%BLOCK_5%%&lt;/p&gt;
&lt;p&gt;Alternative drivers for centralized aggregation:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Driver&lt;/th&gt;
					&lt;th&gt;Destination&lt;/th&gt;
					&lt;th&gt;Use Case&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;fluentd&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Fluentd → Elasticsearch&lt;/td&gt;
					&lt;td&gt;Full-text search and analytics&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;gelf&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Graylog&lt;/td&gt;
					&lt;td&gt;Structured log management&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;awslogs&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Amazon CloudWatch&lt;/td&gt;
					&lt;td&gt;AWS-native deployments&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Output structured JSON from your application so Docker can forward it efficiently. Use &lt;code&gt;docker compose logs --tail 100 --follow&lt;/code&gt; for real-time debugging.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="6-secrets-and-environment-management-1"&gt;6. Secrets and Environment Management
&lt;/h2&gt;&lt;p&gt;Never hardcode secrets in compose files. For Swarm mode, use Docker Secrets which are encrypted at rest and mounted as files:&lt;/p&gt;
&lt;p&gt;%%BLOCK_6%%&lt;/p&gt;
&lt;p&gt;For non-Swarm Compose, use &lt;code&gt;.env&lt;/code&gt; files (listed in &lt;code&gt;.gitignore&lt;/code&gt;), encrypted with &lt;code&gt;sops&lt;/code&gt; or &lt;code&gt;age&lt;/code&gt;. Reference variables via syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;environment&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;DATABASE_URL=postgres://user:${DB_PASSWORD}@postgres:5432/myapp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Rotate secrets regularly and use a naming convention like &lt;code&gt;SERVICE_NAME_SECRET&lt;/code&gt; for clarity.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="7-rolling-updates-and-zero-downtime-deployments"&gt;7. Rolling Updates and Zero-Downtime Deployments
&lt;/h2&gt;&lt;p&gt;Use &lt;code&gt;deploy.update_config&lt;/code&gt; to orchestrate rolling updates with health check gating:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;deploy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;replicas&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;update_config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;parallelism&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;delay&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;order&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;start-first&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;failure_action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;rollback&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rollback_config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;parallelism&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;order&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;stop-first&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;parallelism&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Number of containers updated at once&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;delay&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Pause between update groups&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;order&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;start-first&lt;/code&gt; (blue-green) or &lt;code&gt;stop-first&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;failure_action&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;pause&lt;/code&gt;, &lt;code&gt;continue&lt;/code&gt;, or &lt;code&gt;rollback&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Run &lt;code&gt;docker compose up --detach --wait&lt;/code&gt; for orchestrated updates that wait for health checks to pass before considering the deployment complete.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="8-reverse-proxy-integration"&gt;8. Reverse Proxy Integration
&lt;/h2&gt;&lt;p&gt;Traefik is the recommended reverse proxy for Compose deployments. It discovers services dynamically via Docker labels and automates SSL certificate management with Let&amp;rsquo;s Encrypt.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;traefik&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;traefik:v3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;--providers.docker=true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;--entrypoints.websecure.address=:443&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;--certificatesresolvers.le.acme.tlschallenge=true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;443:443&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;/var/run/docker.sock:/var/run/docker.sock&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;labels&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;traefik.http.routers.app.rule=Host(`example.com`)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;traefik.http.services.app.loadbalancer.server.port=3000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alternatives: Nginx requires manual upstream definitions and SSL termination configuration. Caddy offers automatic HTTPS similar to Traefik but with less dynamic reconfiguration.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="9-monitoring-and-備用"&gt;9. Monitoring and 備用
&lt;/h2&gt;&lt;p&gt;Define a separate &lt;code&gt;monitoring.yml&lt;/code&gt; compose file for the observability stack (cAdvisor + Prometheus + Grafana). This keeps concerns separated — monitoring can be updated independently of the application.&lt;/p&gt;
&lt;p&gt;For database backups, schedule &lt;code&gt;docker exec&lt;/code&gt; dumps via cron:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; * * * docker exec postgres pg_dump -U user mydb &amp;gt; /backups/db_&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;date +&lt;span style="color:#ae81ff"&gt;\%&lt;/span&gt;Y&lt;span style="color:#ae81ff"&gt;\%&lt;/span&gt;m&lt;span style="color:#ae81ff"&gt;\%&lt;/span&gt;d&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;.sql
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Use &lt;code&gt;restart: unless-stopped&lt;/code&gt; for production services. Unlike &lt;code&gt;restart: always&lt;/code&gt;, this prevents containers from restarting after a manual &lt;code&gt;docker stop&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="結論"&gt;結論
&lt;/h2&gt;&lt;p&gt;Docker Compose is production-viable for the right scale of deployments. Treat compose files as code, design cascading health checks, set resource limits, centralize logs, manage secrets properly, and implement rolling updates. These practices transform Compose from a development convenience into a robust deployment tool.&lt;/p&gt;
&lt;p&gt;When scaling needs grow beyond a single host, the same compose file provides a smooth migration path to Docker Swarm or Kubernetes, making Compose a pragmatic starting point for container orchestration.&lt;/p&gt;</description></item></channel></rss>