Immutable OS และ Fedora CoreOS คืออะไร
Immutable OS คือระบบปฏิบัติการที่ root filesystem เป็น read-only ไม่สามารถแก้ไขได้ขณะรัน การเปลี่ยนแปลงทำผ่าน atomic updates ที่สร้าง filesystem snapshot ใหม่ทั้งหมด ถ้า update มีปัญหาสามารถ rollback ไป version ก่อนหน้าได้ทันที
Fedora CoreOS (FCOS) เป็น immutable Linux distribution ที่ออกแบบมาสำหรับรัน containerized workloads เป็นการรวม Fedora Atomic Host กับ Container Linux (CoreOS) เข้าด้วยกัน ให้ minimal OS ที่มี automatic updates, container runtime (Podman, Docker) และ provisioning ผ่าน Ignition
ข้อดีของ Immutable OS ได้แก่ Security ที่ดีขึ้นเพราะ filesystem ถูก lock ลด attack surface, Reliability จาก atomic updates ที่ rollback ได้, Consistency ทุกเครื่องเหมือนกัน 100% ไม่มี configuration drift, Reproducibility สร้าง environment เดิมได้ทุกครั้ง และ Simplicity ลด management overhead
Immutable OS อื่นๆ ได้แก่ Flatcar Container Linux, Bottlerocket (AWS), Talos Linux (Kubernetes), NixOS (functional package management) และ openSUSE MicroOS แต่ละตัวมี target use case ต่างกัน Fedora CoreOS เหมาะสำหรับ general-purpose container hosting
ติดตั้ง Fedora CoreOS ด้วย Ignition
Provision Fedora CoreOS ด้วย Ignition configuration
# === Fedora CoreOS Installation ===
# 1. ติดตั้ง Butane (config transpiler)
# Butane แปลง YAML config เป็น Ignition JSON
# Linux
curl -LO https://github.com/coreos/butane/releases/latest/download/butane-x86_64-unknown-linux-gnu
chmod +x butane-x86_64-unknown-linux-gnu
sudo mv butane-x86_64-unknown-linux-gnu /usr/local/bin/butane
# macOS
brew install butane
# ตรวจสอบ
butane --version
# 2. สร้าง Butane Config
cat > config.bu << 'YAML'
variant: fcos
version: "1.5.0"
passwd:
users:
- name: core
ssh_authorized_keys:
- ssh-rsa AAAA...your-public-key...
groups:
- sudo
- docker
- name: admin
ssh_authorized_keys:
- ssh-rsa AAAA...admin-key...
groups:
- sudo
storage:
files:
- path: /etc/hostname
mode: 0644
contents:
inline: fcos-node-1
- path: /etc/sysctl.d/90-custom.conf
mode: 0644
contents:
inline: |
net.ipv4.ip_forward = 1
net.core.somaxconn = 65535
vm.max_map_count = 262144
- path: /etc/zincati/config.d/55-updates-strategy.toml
mode: 0644
contents:
inline: |
[updates]
strategy = "periodic"
[[updates.periodic.window]]
days = [ "Sun" ]
start_time = "03:00"
length_minutes = 120
- path: /opt/scripts/health-check.sh
mode: 0755
contents:
inline: |
#!/bin/bash
echo "=== System Health Check ==="
echo "Hostname: $(hostname)"
echo "Uptime: $(uptime -p)"
echo "OS: $(rpm-ostree status --json | jq -r '.deployments[0].version')"
echo "Containers: $(podman ps -q | wc -l) running"
echo "Disk: $(df -h / | tail -1 | awk '{print $5}') used"
echo "Memory: $(free -h | grep Mem | awk '{print $3"/"$2}')"
directories:
- path: /opt/containers
mode: 0755
- path: /opt/data
mode: 0755
systemd:
units:
- name: podman-auto-update.timer
enabled: true
- name: health-check.timer
enabled: true
contents: |
[Unit]
Description=Periodic health check
[Timer]
OnCalendar=*:0/15
[Install]
WantedBy=timers.target
- name: health-check.service
contents: |
[Unit]
Description=System health check
[Service]
Type=oneshot
ExecStart=/opt/scripts/health-check.sh
YAML
# 3. Transpile to Ignition
butane --pretty --strict config.bu > config.ign
# 4. ติดตั้ง (bare metal)
# Download Fedora CoreOS ISO
curl -LO https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/latest/x86_64/fedora-coreos-live.x86_64.iso
# Install to disk
sudo coreos-installer install /dev/sda \
--ignition-file config.ign \
--stream stable
# 5. ติดตั้ง (QEMU/KVM)
IGNITION_CONFIG="$(pwd)/config.ign"
IMAGE="fedora-coreos-qemu.x86_64.qcow2"
qemu-img create -f qcow2 -b "$IMAGE" -F qcow2 fcos-node.qcow2 20G
virt-install \
--name fcos-node-1 \
--ram 4096 --vcpus 2 \
--import \
--disk fcos-node.qcow2 \
--os-variant fedora-coreos-stable \
--network network=default \
--qemu-commandline="-fw_cfg name=opt/com.coreos/config, file=$IGNITION_CONFIG" \
--noautoconsole
echo "Fedora CoreOS installed"
จัดการ Container Workloads บน CoreOS
รัน containers ด้วย Podman Quadlet
# === Podman Quadlet (systemd-native containers) ===
# Quadlet = declarative container management ผ่าน systemd
# สร้าง container unit files
# /etc/containers/systemd/nginx.container
cat > /etc/containers/systemd/nginx.container << 'UNIT'
[Unit]
Description=Nginx Web Server
After=network-online.target
[Container]
Image=docker.io/library/nginx:alpine
PublishPort=80:80
PublishPort=443:443
Volume=/opt/data/nginx/html:/usr/share/nginx/html:ro,Z
Volume=/opt/data/nginx/conf:/etc/nginx/conf.d:ro,Z
AutoUpdate=registry
HealthCmd=curl -f http://localhost/ || exit 1
HealthInterval=30s
[Service]
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
UNIT
# สร้าง PostgreSQL container
cat > /etc/containers/systemd/postgres.container << 'UNIT'
[Unit]
Description=PostgreSQL Database
After=network-online.target
[Container]
Image=docker.io/library/postgres:16-alpine
PublishPort=5432:5432
Volume=postgres-data.volume:/var/lib/postgresql/data:Z
Environment=POSTGRES_PASSWORD=secure_password
Environment=POSTGRES_DB=appdb
Environment=POSTGRES_USER=appuser
HealthCmd=pg_isready -U appuser
HealthInterval=30s
[Service]
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
UNIT
# สร้าง Volume
cat > /etc/containers/systemd/postgres-data.volume << 'UNIT'
[Volume]
Driver=local
UNIT
# สร้าง Network
cat > /etc/containers/systemd/app.network << 'UNIT'
[Network]
Subnet=172.20.0.0/16
Gateway=172.20.0.1
UNIT
# Reload และ start
systemctl daemon-reload
systemctl start nginx
systemctl start postgres
# ตรวจสอบ status
systemctl status nginx
systemctl status postgres
podman ps
# === Podman Auto-Update ===
# ตรวจสอบ updates
podman auto-update --dry-run
# Apply updates
podman auto-update
# === Podman Pod (group containers) ===
# สร้าง pod สำหรับ application stack
podman pod create --name app-stack \
-p 8080:8080 \
-p 5432:5432
# Add containers to pod
podman run -d --pod app-stack \
--name app-web \
-e DATABASE_URL=postgresql://appuser:password@localhost:5432/appdb \
my-app:latest
podman run -d --pod app-stack \
--name app-db \
-e POSTGRES_PASSWORD=password \
-v pgdata:/var/lib/postgresql/data \
postgres:16-alpine
# Generate systemd units from pod
podman generate systemd --new --files --name app-stack
sudo mv *.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now pod-app-stack
echo "Container workloads configured"
Automatic Updates และ rpm-ostree
จัดการ updates และ package layering
#!/usr/bin/env python3
# fcos_manager.py — Fedora CoreOS Management Tool
import subprocess
import json
import logging
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("fcos_manager")
class FCOSManager:
def __init__(self):
self.deployments = []
def get_status(self):
result = subprocess.run(
["rpm-ostree", "status", "--json"],
capture_output=True, text=True,
)
data = json.loads(result.stdout)
self.deployments = []
for dep in data.get("deployments", []):
self.deployments.append({
"version": dep.get("version", "unknown"),
"timestamp": dep.get("timestamp", 0),
"booted": dep.get("booted", False),
"staged": dep.get("staged", False),
"checksum": dep.get("checksum", "")[:12],
"packages_layered": dep.get("requested-packages", []),
"pinned": dep.get("pinned", False),
})
return self.deployments
def check_updates(self):
result = subprocess.run(
["rpm-ostree", "upgrade", "--check"],
capture_output=True, text=True,
)
if "AvailableUpdate" in result.stdout:
logger.info("Update available")
return True
else:
logger.info("System is up to date")
return False
def stage_update(self):
logger.info("Staging update...")
result = subprocess.run(
["rpm-ostree", "upgrade"],
capture_output=True, text=True,
)
if result.returncode == 0:
logger.info("Update staged. Reboot to apply.")
return True
else:
logger.error(f"Update failed: {result.stderr}")
return False
def rollback(self):
logger.info("Rolling back to previous deployment...")
result = subprocess.run(
["rpm-ostree", "rollback"],
capture_output=True, text=True,
)
if result.returncode == 0:
logger.info("Rollback staged. Reboot to apply.")
return True
return False
def install_package(self, packages):
if isinstance(packages, str):
packages = [packages]
logger.info(f"Layering packages: {packages}")
cmd = ["rpm-ostree", "install", "--apply-live"] + packages
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
logger.info("Packages layered successfully")
return True
else:
logger.error(f"Failed: {result.stderr}")
return False
def remove_package(self, packages):
if isinstance(packages, str):
packages = [packages]
cmd = ["rpm-ostree", "uninstall"] + packages
result = subprocess.run(cmd, capture_output=True, text=True)
return result.returncode == 0
def pin_deployment(self, index=0):
subprocess.run(
["ostree", "admin", "pin", str(index)],
capture_output=True, text=True,
)
logger.info(f"Pinned deployment {index}")
def get_zincati_status(self):
result = subprocess.run(
["systemctl", "status", "zincati", "--no-pager"],
capture_output=True, text=True,
)
return {
"active": "active (running)" in result.stdout,
"output": result.stdout[:500],
}
def generate_report(self):
deployments = self.get_status()
zincati = self.get_zincati_status()
report = {
"timestamp": datetime.utcnow().isoformat(),
"hostname": subprocess.getoutput("hostname"),
"current_version": next(
(d["version"] for d in deployments if d["booted"]), "unknown"
),
"deployments": deployments,
"auto_updates": zincati["active"],
"containers_running": int(
subprocess.getoutput("podman ps -q | wc -l")
),
}
return report
# manager = FCOSManager()
# status = manager.get_status()
# for dep in status:
# marker = " (booted)" if dep["booted"] else ""
# print(f" {dep['version']}{marker} [{dep['checksum']}]")
#
# if manager.check_updates():
# manager.stage_update()
#
# report = manager.generate_report()
# print(json.dumps(report, indent=2))
สร้าง CoreOS Cluster ด้วย Butane Config
Provision multi-node cluster
#!/bin/bash
# provision_cluster.sh — Provision Fedora CoreOS Cluster
set -euo pipefail
NODES=("node1:10.0.0.11" "node2:10.0.0.12" "node3:10.0.0.13")
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
CLUSTER_TOKEN="$(openssl rand -hex 16)"
generate_butane() {
local hostname=$1
local ip=$2
local role=$3
cat << EOF
variant: fcos
version: "1.5.0"
passwd:
users:
- name: core
ssh_authorized_keys:
-
groups: [sudo, docker]
storage:
files:
- path: /etc/hostname
mode: 0644
contents:
inline:
- path: /etc/NetworkManager/system-connections/static.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=static
type=ethernet
[ipv4]
method=manual
addresses=/24
gateway=10.0.0.1
dns=8.8.8.8;8.8.4.4
- path: /etc/sysctl.d/99-kubernetes.conf
mode: 0644
contents:
inline: |
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
vm.max_map_count = 262144
- path: /etc/modules-load.d/kubernetes.conf
mode: 0644
contents:
inline: |
overlay
br_netfilter
- path: /opt/cluster/role
mode: 0644
contents:
inline:
- path: /opt/cluster/token
mode: 0600
contents:
inline:
- path: /opt/scripts/setup-node.sh
mode: 0755
contents:
inline: |
#!/bin/bash
echo "Setting up \$(hostname) as \$(cat /opt/cluster/role)"
# Install k3s
if [ "\$(cat /opt/cluster/role)" = "controller" ]; then
curl -sfL https://get.k3s.io | sh -s - server \\
--token \$(cat /opt/cluster/token) \\
--tls-san
else
curl -sfL https://get.k3s.io | sh -s - agent \\
--server https://10.0.0.11:6443 \\
--token \$(cat /opt/cluster/token)
fi
- path: /etc/zincati/config.d/55-updates.toml
mode: 0644
contents:
inline: |
[updates]
strategy = "periodic"
[[updates.periodic.window]]
days = ["Sun"]
start_time = "04:00"
length_minutes = 60
systemd:
units:
- name: setup-node.service
enabled: true
contents: |
[Unit]
Description=Initial node setup
After=network-online.target
ConditionPathExists=!/opt/cluster/.setup-done
[Service]
Type=oneshot
ExecStart=/opt/scripts/setup-node.sh
ExecStartPost=/usr/bin/touch /opt/cluster/.setup-done
[Install]
WantedBy=multi-user.target
EOF
}
# Generate configs for each node
echo "=== Generating cluster configs ==="
for node_entry in ""; do
hostname=""
ip=""
if [ "$hostname" = "node1" ]; then
role="controller"
else
role="worker"
fi
echo "Generating config for () as "
generate_butane "$hostname" "$ip" "$role" > ".bu"
butane --pretty --strict ".bu" > ".ign"
echo " Created .ign"
done
echo ""
echo "=== Cluster Configuration ==="
echo "Token: "
echo "Controller: node1 (10.0.0.11)"
echo "Workers: node2 (10.0.0.12), node3 (10.0.0.13)"
echo ""
echo "Deploy each node with its .ign file"
echo "After boot, k3s will auto-configure the cluster"
Community Building สำหรับ Open Source Projects
แนวทางสร้าง community สำหรับ open source
# === Open Source Community Building Strategies ===
# 1. Documentation First
# ===================================
# README.md ที่ดีต้องมี:
# - Clear project description (1-2 sentences)
# - Quick start (< 5 minutes to first success)
# - Installation instructions (all platforms)
# - Usage examples with code
# - Contributing guidelines
# - License information
# - Badges (CI status, version, downloads)
# CONTRIBUTING.md template:
# - How to report bugs (issue template)
# - How to suggest features
# - Development setup instructions
# - Code style guidelines
# - Pull request process
# - Code of conduct reference
# 2. Issue Management
# ===================================
# Label system:
# - good-first-issue: สำหรับ newcomers
# - help-wanted: ต้องการความช่วยเหลือ
# - bug: confirmed bugs
# - enhancement: feature requests
# - documentation: docs improvements
# - priority-high/medium/low: prioritization
# Issue templates (.github/ISSUE_TEMPLATE/):
# bug_report.md:
# ---
# name: Bug Report
# about: Report a bug
# labels: bug
# ---
# **Describe the bug**
# **Steps to reproduce**
# **Expected behavior**
# **Environment** (OS, version, etc.)
# **Screenshots/Logs**
# 3. Contributor Experience
# ===================================
# Make first contribution easy:
# - Pre-configured dev container (.devcontainer/)
# - Makefile with common commands
# - CI that runs on PRs automatically
# - Automated code formatting (pre-commit hooks)
# - Clear PR template
# .github/pull_request_template.md:
# ## Description
# ## Type of change
# - [ ] Bug fix
# - [ ] New feature
# - [ ] Breaking change
# - [ ] Documentation update
# ## Testing
# - [ ] Unit tests pass
# - [ ] Manual testing done
# ## Checklist
# - [ ] Code follows project style
# - [ ] Self-review completed
# - [ ] Documentation updated
# 4. Communication Channels
# ===================================
# - GitHub Discussions: Q&A, ideas, show-and-tell
# - Discord/Matrix: real-time chat
# - Blog/Newsletter: project updates
# - Social media: announcements, community highlights
# - Monthly community calls: roadmap, demos
# 5. Recognition
# ===================================
# - All Contributors bot (recognize non-code contributions)
# - Contributor spotlight in release notes
# - Swag for significant contributors
# - Maintainer pathway for active contributors
# 6. Governance
# ===================================
# - Clear decision-making process
# - RFC process for major changes
# - Maintainer guidelines
# - Transparent roadmap
# - Regular releases with changelogs
# 7. Metrics to Track
# ===================================
# - Stars/Forks growth rate
# - Issue response time (target: < 48h)
# - PR review time (target: < 1 week)
# - Contributor count (new per month)
# - Bus factor (knowledge distribution)
# - Time to first contribution
echo "Community building strategies documented"
FAQ คำถามที่พบบ่อย
Q: Immutable OS ติดตั้ง packages เพิ่มได้ไหม?
A: ได้ด้วย rpm-ostree install ซึ่งเรียกว่า package layering จะสร้าง new deployment ที่รวม base OS กับ packages ที่เพิ่ม แต่แนะนำให้ใช้ containers แทนเมื่อเป็นไปได้ เพราะ layered packages ทำให้ update ช้าลงและเพิ่ม complexity ใช้ rpm-ostree สำหรับ system-level tools เท่านั้น เช่น htop, tmux, strace
Q: Fedora CoreOS กับ Flatcar Container Linux ต่างกันอย่างไร?
A: FCOS ใช้ rpm-ostree package manager อ้างอิง Fedora ecosystem ใช้ Ignition สำหรับ provisioning มี Zincati สำหรับ auto-updates Flatcar ใช้ A/B partition scheme สำหรับ updates ไม่มี package manager (immutable 100%) ใช้ Container Linux Config สำหรับ provisioning FCOS flexible กว่า (layer packages ได้) Flatcar minimal กว่าและ proven ใน production (ex-CoreOS)
Q: ใช้ Immutable OS กับ Kubernetes อย่างไร?
A: เหมาะมากเพราะ Kubernetes nodes ไม่ควรมี state ทั้งหมดอยู่ใน containers FCOS ใช้กับ k3s, k0s หรือ kubeadm ได้ Talos Linux ออกแบบมาเฉพาะสำหรับ Kubernetes nodes (API-managed, no SSH) Bottlerocket ของ AWS ออกแบบสำหรับ EKS nodes auto-updates ทำให้ nodes ได้ security patches อัตโนมัติ
Q: จะเริ่มสร้าง open source community อย่างไร?
A: เริ่มจาก documentation ที่ดี (README, CONTRIBUTING, CODE_OF_CONDUCT) สร้าง good-first-issues สำหรับ newcomers ตั้ง communication channels (GitHub Discussions, Discord) respond ทุก issue/PR ภายใน 48 ชั่วโมง recognize contributors ทุกู้คืน (ไม่ใช่แค่ code) เขียน blog posts เกี่ยวกับ project และ share ใน communities ที่เกี่ยวข้อง ความสม่ำเสมอสำคัญกว่า viral moment
