Linux: 卒業プロジェクト — ゼロからWeb開発サーバーを構築
コース最後のプロジェクトです。前の25レッスンすべてのスキルを組み合わせ — 裸のマシンから始めて、完全に機能するWeb開発サーバーを構築します。
📋 前提条件:まず以下を完了していること
- レッスン1:Linux入門
- レッスン2:Linuxのインストール
- レッスン3:ターミナルの基礎
- レッスン4:ファイルシステムナビゲーション
- レッスン5:ファイルとディレクトリ操作
- レッスン6:開発環境の構築
- レッスン7:テキストエディタ
- レッスン8:ファイル権限
- レッスン9:ユーザーとグループ管理
- レッスン10:環境変数とPATH
- レッスン11:マルチユーザー開発環境
- レッスン12:標準ストリームとリダイレクト
- レッスン13:パイプとフィルター
- レッスン14:テキスト検索
- レッスン15:テキスト処理
- レッスン16:サーバーログ分析
- レッスン17:プロセス管理
- レッスン18:パッケージ管理
- レッスン19:アーカイブと圧縮
- レッスン20:Bashスクリプト基礎
- レッスン21:Bashスクリプト応用
- レッスン22:自動バックアップスクリプト
- レッスン23:ネットワーク基礎
- レッスン24:ファイアウォールとセキュリティ
- レッスン25:タスク自動化
1. このレッスンで学ぶこと
- サーバー初期化フルワークフロー
- LEMPスタックの構築
- Node.js/Pythonアプリケーションのデプロイ
- HTTPS証明書の設定
- 監視とアラートシステム
2. 小明の「明日本番稼働」チャレンジ
(1) 悩み:新サーバー、明日本番稼働せよ
小明の会社がクラウドサーバーを購入しました。ボスが言いました:「明日本番稼働だ。」IPとrootパスワードだけの裸のマシンを前に、小明は学んだすべてを適用する必要がありました。
(2) 裸のマシンから本番までの10ステップ
小明は計画を立てました:
1. SSH login → 6. Deploy Node.js app
2. User creation + SSH hardening → 7. Configure HTTPS certificate
3. Firewall config → 8. Configure PM2 process manager
4. Install Nginx → 9. Automated backup
5. Configure reverse proxy → 10. Health check + alerting
(3) 得られるもの:3時間で納品
小明は午後2時に開始し、計画をステップごとに実行し、午後5時までに全デプロイを完了。ボスがテストして返信しました:「よし、本番稼働OK。」
3. 知識ポイント
(1) サーバー初期化チェックリスト
□ SSH login (root password)
□ Update system (apt update && apt upgrade)
□ Create regular user (useradd -m -s /bin/bash)
□ Configure sudo (usermod -aG sudo)
□ SSH key authentication
□ Disable root + password login
□ Firewall (ufw: 22/80/443)
□ fail2ban protection
□ Automatic security updates
(2) LEMPスタック(Linux + Nginx + MySQL + PHP/Node.js/Python)
# Nginx
sudo apt install nginx
sudo systemctl enable --now nginx
# If using MySQL
sudo apt install mysql-server
sudo mysql_secure_installation
# Application deployment comparison
| 方式 | 最適な用途 | メリット | デメリット |
|---|---|---|---|
| systemdサービス | 任意の言語 | ネイティブ、成熟、安定 | 設定がやや複雑 |
| PM2 | Node.js | ゼロ設定、組み込みモニタリング | Node.jsのみ |
| Gunicorn/uWSGI | Python | 高パフォーマンス、WSGI標準 | Nginxが必要 |
| Docker Compose | 複雑なアプリ | 環境統一、スケール容易 | Docker知識が必要 |
(3) ▶ サンプル:Node.jsアプリをNginxにデプロイ
#!/bin/bash
# Deploy a Node.js app to production
# 1. Create app directory
sudo mkdir -p /opt/webapp
sudo chown -R alice:alice /opt/webapp
# 2. Clone app files (assuming already developed)
git clone https://github.com/example/webapp.git /opt/webapp
# 3. Install dependencies and build
cd /opt/webapp
npm install
npm run build
# 4. Configure PM2
npm install -g pm2
pm2 start server.js --name webapp -i max
pm2 save
pm2 startup
# 5. Configure Nginx reverse proxy
sudo tee /etc/nginx/sites-available/webapp << 'EOF'
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/ {
alias /opt/webapp/public/;
expires 30d;
}
}
EOF
sudo ln -sf /etc/nginx/sites-available/webapp /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
出力:
TEXTnpm WARN deprecated package@1.0.0 added 245 packages in 12s Building production bundle... [PM2] Process webapp started ┌────┬──────────┬─────────┬─────────┐ │ id │ name │ status │ cpu │ ├────┼──────────┼─────────┼─────────┤ │ 0 │ webapp │ online │ 0% │ └────┴──────────┴─────────┴─────────┘ nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
(4) ▶ サンプル:HTTPS(Let's Encrypt + certbot)
sudo certbot renew --dry-runで更新プロセスをテストしてください。また、設定変更で自動更新が壊れた場合に備え、有効期限の約30日前(60日目頃)にカレンダーリマインダーを設定し、手動で更新成功を確認することをお勧めします。
# Install certbot
sudo apt install certbot python3-certbot-nginx -y
# Obtain certificate (automatically modifies Nginx config)
sudo certbot --nginx -d example.com -d www.example.com
# View certificates
sudo certbot certificates
# Verify auto-renewal
sudo certbot renew --dry-run
# Certificate auto-renews (certbot adds a systemd timer)
sudo systemctl list-timers | grep certbot
出力:
TEXTSaving debug log to /var/log/letsencrypt/letsencrypt.log Requesting a certificate for example.com and www.example.com Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem Found the following certs: Certificate Name: example.com Expiry Date: 2026-10-05 10:23:45+00:00 (VALID: 89 days) Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/example.com/fullchain.pem certbot.timer certbot.service Sat 2026-07-08 00:00:00 UTC 13h left
(5) ▶ サンプル:ヘルスチェックスクリプト
#!/bin/bash
# health-check.sh - Multi-service health check
check_service() {
local name="$1"
local url="$2"
local expect="${3:-200}"
local status=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null)
if [ "$status" = "$expect" ]; then
echo "[OK] $name: $status (healthy)"
return 0
else
echo "[FAIL] $name: $status (unhealthy)"
return 1
fi
}
echo "=== System Health Check ==="
echo "Time: $(date)"
echo ""
# System resources
echo "CPU: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}')%"
echo "Memory: $(free -h | grep Mem | awk '{print $3 "/" $2}')"
echo "Disk: $(df -h / | tail -1 | awk '{print $3 "/" $2 " (" $5 ")"}')"
echo ""
# Web services
check_service "Nginx" "http://localhost/nginx_status" 200
check_service "WebApp" "http://localhost:3000/health" 200
check_service "HTTPS" "https://example.com/" 200
echo ""
echo "=== Check Complete ==="
出力:
TEXT=== System Health Check === Time: Tue Jul 7 10:30:00 UTC 2026 CPU: 2.3% Memory: 1.2Gi/3.9Gi Disk: 12G/50G (24%) [OK] Nginx: 200 (healthy) [OK] WebApp: 200 (healthy) [OK] HTTPS: 200 (healthy) === Check Complete ===
(6) ▶ サンプル:完全なデプロイスクリプト
curl -fsSL URL | bash -パターンはリモートスクリプトをマシン上で直接実行し、サプライチェーン攻撃リスクがあります — ソースが侵害されたりDNSがハイジャックされた場合、悪意のあるコードが実行される可能性があります。本番環境では、スクリプトを先にダウンロードして内容を確認してから実行する方が安全です:curl -fsSL URL -o setup.sh && less setup.sh && bash setup.sh。
#!/bin/bash
# setup-server.sh - Build a Web dev server from scratch
# Usage: sudo ./setup-server.sh example.com
set -euo pipefail
DOMAIN="${1:-example.com}"
APP_DIR="/opt/webapp"
APP_PORT=3000
ADMIN_USER="admin"
echo "========================================"
echo " Build Web Dev Server from Scratch"
echo " Domain: $DOMAIN"
echo "========================================"
# 1. System update
echo ""
echo ">>> 1/10 Updating system..."
apt update && apt upgrade -y
# 2. Create regular user
echo ""
echo ">>> 2/10 Creating admin user..."
if ! id "$ADMIN_USER" &>/dev/null; then
useradd -m -s /bin/bash "$ADMIN_USER"
usermod -aG sudo "$ADMIN_USER"
echo "Set password for $ADMIN_USER:"
passwd "$ADMIN_USER"
# Note: Manually add your SSH public key to /home/$ADMIN_USER/.ssh/authorized_keys
mkdir -p /home/$ADMIN_USER/.ssh
cp /root/.ssh/authorized_keys /home/$ADMIN_USER/.ssh/ 2>/dev/null || true
chown -R $ADMIN_USER:$ADMIN_USER /home/$ADMIN_USER/.ssh
chmod 700 /home/$ADMIN_USER/.ssh
chmod 600 /home/$ADMIN_USER/.ssh/authorized_keys
fi
# 3. SSH hardening
echo ""
echo ">>> 3/10 Hardening SSH..."
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config
systemctl restart sshd
# 4. Firewall
echo ""
echo ">>> 4/10 Configuring firewall..."
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
# 5. fail2ban
echo ""
echo ">>> 5/10 Installing fail2ban..."
apt install -y fail2ban
cat > /etc/fail2ban/jail.d/sshd.conf << EOF
[sshd]
enabled = true
maxretry = 3
bantime = 86400
findtime = 600
EOF
systemctl enable --now fail2ban
# 6. Install Nginx
echo ""
echo ">>> 6/10 Installing Nginx..."
apt install -y nginx
systemctl enable --now nginx
# 7. Install Node.js
echo ""
echo ">>> 7/10 Installing Node.js..."
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
npm install -g pm2
# 8. Configure Nginx reverse proxy
echo ""
echo ">>> 8/10 Configuring Nginx..."
cat > /etc/nginx/sites-available/$DOMAIN << EOF
server {
listen 80;
server_name $DOMAIN www.$DOMAIN;
location / {
proxy_pass http://127.0.0.1:$APP_PORT;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_cache_bypass \$http_upgrade;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
ln -sf /etc/nginx/sites-available/$DOMAIN /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
# 9. HTTPS certificate
echo ""
echo ">>> 9/10 Configuring HTTPS..."
apt install -y certbot python3-certbot-nginx
certbot --nginx --non-interactive --agree-tos \
--email admin@$DOMAIN \
-d $DOMAIN -d www.$DOMAIN \
|| echo "Warning: certbot failed, run manually: sudo certbot --nginx -d $DOMAIN"
# 10. Health check script
echo ""
echo ">>> 10/10 Setting up health check..."
mkdir -p /opt/scripts
cat > /opt/scripts/health-check.sh << 'EOF'
#!/bin/bash
WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK"
for svc in nginx sshd fail2ban; do
if ! systemctl is-active --quiet "$svc"; then
echo "[$(date)] FAIL: $svc is down" >> /var/log/health.log
fi
done
DISK=$(df / | tail-1 | awk '{print $5}' | tr -d '%')
[ "$DISK" -gt 85 ] && echo "[$(date)] WARNING: Disk usage ${DISK}%" >> /var/log/health.log
EOF
chmod +x /opt/scripts/health-check.sh
# Add to cron
(crontab -l 2>/dev/null; echo "*/10 * * * * /opt/scripts/health-check.sh > /dev/null 2>&1") | crontab -
echo ""
echo "========================================"
echo "Server setup complete!"
echo ""
echo "Deployment info:"
echo " Admin user: $ADMIN_USER"
echo " Nginx: http://$DOMAIN"
echo " HTTPS: https://$DOMAIN (if certbot succeeded)"
echo " Node.js: $(node --version)"
echo " PM2: $(pm2 --version)"
echo ""
echo "Next steps:"
echo " 1. Deploy your app code to $APP_DIR"
echo " 2. Start the app: pm2 start $APP_DIR/server.js --name webapp"
echo " 3. Log in as $ADMIN_USER (root login is disabled)"
echo "========================================"
出力:
TEXT======================================== Build Web Dev Server from Scratch Domain: example.com ======================================== >>> 1/10 Updating system... ... ======================================== Server setup complete! Admin user: admin Nginx: http://example.com Node.js: v20.11.0 ========================================
(7) ▶ 総合例:完全なデプロイ検証
#!/bin/bash
# verify-deploy.sh - Post-deployment verification
echo "========================================"
echo " Deployment Verification"
echo " Time: $(date)"
echo "========================================"
echo ""
errors=0
check() {
local desc="$1"
shift
if "$@" &>/dev/null; then
echo " [OK] $desc"
else
echo " [FAIL] $desc"
errors=$((errors + 1))
fi
}
echo "1. System Services"
echo "-----------"
check "Nginx running" systemctl is-active nginx
check "SSH running" systemctl is-active sshd
check "fail2ban running" systemctl is-active fail2ban
echo ""
echo "2. Network"
echo "--------"
check "Port 80 listening" ss -tlnp grep -q ":80 "
check "Port 443 listening" ss -tlnp grep -q ":443 "
check "ufw enabled" ufw status grep -q active
echo ""
echo "3. HTTP Service"
echo "-------------"
check "Local Nginx responding" curl -s -o /dev/null -w "%{http_code}" http://localhost grep -q 200
check "App process running" pm2 list 2>/dev/null grep -q "online" || pgrep -f "node server" > /dev/null
echo ""
echo "4. Security"
echo "--------"
check "Root SSH disabled" grep "^PermitRootLogin" /etc/ssh/sshd_config grep -q "no"
check "Password login disabled" grep "^PasswordAuthentication" /etc/ssh/sshd_config grep -q "no"
echo ""
echo "5. Resources"
echo "--------"
echo " CPU: $(top -bn1 | grep 'Cpu(s)' | awk '{print $2"%"}' )"
echo " Memory: $(free -h | grep Mem | awk '{print $3"/"$2}')"
echo " Disk: $(df -h / | tail -1 | awk '{print $3"/"$2"("$5")"}')"
echo ""
echo "========================================"
if [ $errors -eq 0 ]; then
echo "All verifications passed!"
else
echo "$errors check(s) failed — please investigate"
fi
echo "========================================"
出力:
TEXT======================================== Deployment Verification Time: Tue Jul 7 10:45:00 UTC 2026 ======================================== 1. System Services ----------- [OK] Nginx running [OK] SSH running [OK] fail2ban running 2. Network -------- [OK] Port 80 listening [OK] Port 443 listening [OK] ufw enabled 3. HTTP Service ------------- [OK] Local Nginx responding [OK] App process running 4. Security -------- [OK] Root SSH disabled [OK] Password login disabled 5. Resources -------- CPU: 2.3% Memory: 1.2Gi/3.9Gi Disk: 12G/50G(24%) ======================================== All verifications passed! ========================================
❓ よくある質問
Q: サーバーデプロイで最初にすべきことは? A: システム更新(
apt update && apt upgrade)、次に一般ユーザー作成とSSH鍵認証の設定。rootで長期間作業してはいけません。
Q: Nginxリバースプロキシとアプリケーションの関係は? A: Nginxが「玄関」でアプリケーションが「中の人」。ユーザーはポート80/443でNginxにアクセス → Nginxがリクエストをどのバックエンドアプリに転送するか決定。Nginxは静的ファイル、SSL終端、負荷分散を処理。アプリはビジネスロジックに集中。
Q: HTTPS証明書は定期的に更新が必要? A: Let's Encryptの証明書は90日間有効。certbotがsystemdタイマーで自動更新を追加します。確認:
systemctl list-timers | grep certbot。手動更新:sudo certbot renew。
Q: サーバーで監視すべきメトリクスは? A: コアメトリクス:CPU使用率、メモリ消費量、ディスク容量(>80%でアラート)、ディスクI/O、ネットワークトラフィック、サービス稼働状況、SSL証明書の有効期限。
Q: デプロイ後のセキュリティ検証方法は? A: セキュリティスキャンツールを実行:1)
sudo ufw statusでポートを確認。2)ss -tlnpでサービスのリスニングアドレスを確認。3)grep "Failed password" /var/log/auth.log | wc -lで攻撃量を確認。4) https://www.ssllabs.com/ssltest/ でSSL設定をテスト。
📖 まとめ
- サーバー初期化10ステップフロー:システム更新 → ユーザー → SSH硬化 → ファイアウォール → fail2ban → Nginx → アプリ → HTTPS → バックアップ → 監視
- Nginxリバースプロキシ:
proxy_pass http://127.0.0.1:3000 - certbot:
--nginx -d example.comで自動HTTPS - PM2:
pm2 start app.js -i maxでマルチプロセス管理 - ヘルスチェック:
curl -o /dev/null -w "%{http_code}" http://localhost/health
📝 練習問題
- 基本(難易度 ⭐):VMまたはクラウドサーバーで
setup-server.shをゼロから実行し、シンプルなNode.jsまたはPython Webアプリをデプロイし、Nginxリバースプロキシ経由でアクセスする - 中級(難易度 ⭐⭐):Let's Encrypt HTTPS証明書を設定(テストには
--dry-runを使用)、ヘルスチェックスクリプトを書いてデプロイし、アラート(メールまたはwebhook)を設定する - 上級(難易度 ⭐⭐⭐⭐⭐):完全なバックアップ戦略(cron + rsyncリモートバックアップ)を設定し、バックアップが復元可能であることを検証し、全サービスステータス、セキュリティ設定、パフォーマンスメトリクスをチェックしてフォーマット済み検収レポートを出力する自動デプロイ検証スクリプトを書く