Linux: 卒業プロジェクト — ゼロからWeb開発サーバーを構築

コース最後のプロジェクトです。前の25レッスンすべてのスキルを組み合わせ — 裸のマシンから始めて、完全に機能するWeb開発サーバーを構築します。

📋 前提条件:まず以下を完了していること

1. このレッスンで学ぶこと


2. 小明の「明日本番稼働」チャレンジ

(1) 悩み:新サーバー、明日本番稼働せよ

小明の会社がクラウドサーバーを購入しました。ボスが言いました:「明日本番稼働だ。」IPとrootパスワードだけの裸のマシンを前に、小明は学んだすべてを適用する必要がありました。

(2) 裸のマシンから本番までの10ステップ

小明は計画を立てました:

BASH

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) サーバー初期化チェックリスト

BASH
□ 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
⚠️ 警告:パスワードログインを無効化する前に、SSH鍵が正しく設定されテストされていることを確認してください。そうしないとサーバーから締め出されます。推奨順序:1) 鍵を設定;2) 鍵でログイン成功;3) すべて動作することを確認してからパスワードログインを無効化。常にアクティブなSSHセッションを安全策として維持してください。

(2) LEMPスタック(Linux + Nginx + MySQL + PHP/Node.js/Python)

BASH
# 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にデプロイ

BASH
#!/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

出力:

TEXT
npm 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)

💡 ヒント:certbotは証明書更新のためにsystemdタイマーまたはcronジョブを自動的に追加します。設定後、sudo certbot renew --dry-runで更新プロセスをテストしてください。また、設定変更で自動更新が壊れた場合に備え、有効期限の約30日前(60日目頃)にカレンダーリマインダーを設定し、手動で更新成功を確認することをお勧めします。

BASH
# 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

出力:

TEXT
Saving 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) ▶ サンプル:ヘルスチェックスクリプト

BASH
#!/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

BASH
#!/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) ▶ 総合例:完全なデプロイ検証

BASH
#!/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設定をテスト。


📖 まとめ


📝 練習問題

  1. 基本(難易度 ⭐):VMまたはクラウドサーバーでsetup-server.shをゼロから実行し、シンプルなNode.jsまたはPython Webアプリをデプロイし、Nginxリバースプロキシ経由でアクセスする
  2. 中級(難易度 ⭐⭐):Let's Encrypt HTTPS証明書を設定(テストには--dry-runを使用)、ヘルスチェックスクリプトを書いてデプロイし、アラート(メールまたはwebhook)を設定する
  3. 上級(難易度 ⭐⭐⭐⭐⭐):完全なバックアップ戦略(cron + rsyncリモートバックアップ)を設定し、バックアップが復元可能であることを検証し、全サービスステータス、セキュリティ設定、パフォーマンスメトリクスをチェックしてフォーマット済み検収レポートを出力する自動デプロイ検証スクリプトを書く
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%