13. systemd 심화

13. systemd 심화

ν•™μŠ΅ λͺ©ν‘œ

  • systemd μ•„ν‚€ν…μ²˜μ™€ λ™μž‘ 원리 이해
  • μ»€μŠ€ν…€ μ„œλΉ„μŠ€ μœ λ‹› μž‘μ„±
  • 타이머 μœ λ‹›μœΌλ‘œ μŠ€μΌ€μ€„λ§
  • μ†ŒμΌ“ ν™œμ„±ν™”μ™€ μ˜μ‘΄μ„± 관리

λͺ©μ°¨

  1. systemd μ•„ν‚€ν…μ²˜
  2. μ„œλΉ„μŠ€ μœ λ‹› μž‘μ„±
  3. 타이머 μœ λ‹›
  4. μ†ŒμΌ“ ν™œμ„±ν™”
  5. μ˜μ‘΄μ„±κ³Ό μˆœμ„œ
  6. journald λ‘œκΉ…
  7. μ—°μŠ΅ 문제

1. systemd μ•„ν‚€ν…μ²˜

1.1 systemd κ°œμš”

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    systemd μ•„ν‚€ν…μ²˜                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                  systemd (PID 1)                     β”‚   β”‚
β”‚  β”‚  β€’ μ‹œμŠ€ν…œ μ΄ˆκΈ°ν™” 및 μ„œλΉ„μŠ€ 관리                      β”‚   β”‚
β”‚  β”‚  β€’ 병렬 μ„œλΉ„μŠ€ μ‹œμž‘                                  β”‚   β”‚
β”‚  β”‚  β€’ μ†ŒμΌ“/D-Bus ν™œμ„±ν™”                                 β”‚   β”‚
β”‚  β”‚  β€’ cgroups 기반 λ¦¬μ†ŒμŠ€ 관리                          β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚           β”‚                                                 β”‚
β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚
β”‚    β”‚      β”‚                              β”‚                  β”‚
β”‚    β–Ό      β–Ό                              β–Ό                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”             β”‚
β”‚  β”‚udevd β”‚ β”‚logindβ”‚ β”‚journaldβ”‚ β”‚networkdβ”‚ β”‚resolvedβ”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜             β”‚
β”‚                                                             β”‚
β”‚  μœ λ‹› νƒ€μž…:                                                 β”‚
β”‚  β€’ .service  - μ„œλΉ„μŠ€/데λͺ¬                                  β”‚
β”‚  β€’ .socket   - μ†ŒμΌ“                                         β”‚
β”‚  β€’ .timer    - 타이머 (cron λŒ€μ²΄)                          β”‚
β”‚  β€’ .target   - κ·Έλ£Ή (런레벨 λŒ€μ²΄)                          β”‚
β”‚  β€’ .mount    - 마운트 포인트                                β”‚
β”‚  β€’ .device   - μž₯치                                         β”‚
β”‚  β€’ .path     - 경둜 λͺ¨λ‹ˆν„°λ§                                β”‚
β”‚  β€’ .slice    - λ¦¬μ†ŒμŠ€ κ·Έλ£Ή                                  β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.2 μœ λ‹› 파일 μœ„μΉ˜

# μœ λ‹› 파일 μœ„μΉ˜ (μš°μ„ μˆœμœ„ 순)
/etc/systemd/system/        # μ‹œμŠ€ν…œ κ΄€λ¦¬μž μ„€μ • (μ΅œμš°μ„ )
/run/systemd/system/        # λŸ°νƒ€μž„ 생성 μœ λ‹›
/usr/lib/systemd/system/    # νŒ¨ν‚€μ§€ μ„€μΉ˜ μœ λ‹›

# μ‚¬μš©μž μœ λ‹›
~/.config/systemd/user/     # μ‚¬μš©μž μ„€μ •
/etc/systemd/user/          # μ „μ—­ μ‚¬μš©μž μ„€μ •
/usr/lib/systemd/user/      # νŒ¨ν‚€μ§€ μ„€μΉ˜ μ‚¬μš©μž μœ λ‹›

# μœ λ‹› 파일 μœ„μΉ˜ 확인
systemctl show -p FragmentPath nginx.service
systemctl cat nginx.service

1.3 κΈ°λ³Έ systemctl λͺ…λ Ή

# μ„œλΉ„μŠ€ 관리
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl reload nginx       # μ„€μ •λ§Œ λ¦¬λ‘œλ“œ
systemctl status nginx

# λΆ€νŒ… μ‹œ μžλ™ μ‹œμž‘
systemctl enable nginx
systemctl disable nginx
systemctl is-enabled nginx

# μ„œλΉ„μŠ€ λ§ˆμŠ€ν‚Ή (μ‹œμž‘ μ™„μ „ λ°©μ§€)
systemctl mask nginx
systemctl unmask nginx

# μœ λ‹› λͺ©λ‘
systemctl list-units
systemctl list-units --type=service
systemctl list-units --state=failed

# μœ λ‹› 파일 λͺ©λ‘
systemctl list-unit-files

# μ˜μ‘΄μ„± 확인
systemctl list-dependencies nginx.service
systemctl list-dependencies --reverse nginx.service

# 데λͺ¬ λ¦¬λ‘œλ“œ (μœ λ‹› 파일 μˆ˜μ • ν›„)
systemctl daemon-reload

2. μ„œλΉ„μŠ€ μœ λ‹› μž‘μ„±

2.1 κΈ°λ³Έ μ„œλΉ„μŠ€ μœ λ‹›

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Service
Documentation=https://myapp.example.com/docs
After=network.target
Wants=network-online.target

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5

# ν™˜κ²½ λ³€μˆ˜
Environment=NODE_ENV=production
EnvironmentFile=-/etc/myapp/env  # - λŠ” 파일 없어도 μ—λŸ¬ μ•ˆ 남

# λ‘œκΉ…
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target

2.2 μ„œλΉ„μŠ€ Type

# Type=simple (κΈ°λ³Έκ°’)
# ν”„λ‘œμ„ΈμŠ€κ°€ λ°”λ‘œ 메인 ν”„λ‘œμ„ΈμŠ€
[Service]
Type=simple
ExecStart=/usr/bin/myapp

# Type=forking
# ν”„λ‘œμ„ΈμŠ€κ°€ forkν•˜κ³  λΆ€λͺ¨κ°€ μ’…λ£Œ
[Service]
Type=forking
PIDFile=/var/run/myapp.pid
ExecStart=/usr/bin/myapp --daemon

# Type=oneshot
# μ‹œμž‘ ν›„ μ’…λ£Œλ˜λŠ” μΌνšŒμ„± μž‘μ—…
[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup-script.sh
RemainAfterExit=yes  # μ’…λ£Œ 후에도 active μƒνƒœ μœ μ§€

# Type=notify
# ν”„λ‘œμ„ΈμŠ€κ°€ sd_notify()둜 μ€€λΉ„ μ™„λ£Œ μ•Œλ¦Ό
[Service]
Type=notify
ExecStart=/usr/bin/myapp-with-notify

# Type=dbus
# D-Bus 이름 νšλ“ μ‹œ μ€€λΉ„ μ™„λ£Œ
[Service]
Type=dbus
BusName=org.example.MyApp
ExecStart=/usr/bin/myapp

# Type=idle
# λ‹€λ₯Έ μž‘μ—… μ™„λ£Œ ν›„ μ‹€ν–‰ (λΆ€νŒ… λ©”μ‹œμ§€ 좜λ ₯ λ“±)
[Service]
Type=idle
ExecStart=/usr/bin/welcome-message

2.3 Exec μ˜΅μ…˜

[Service]
# μ‹œμž‘/μ’…λ£Œ λͺ…λ Ή
ExecStartPre=/usr/bin/myapp-check    # μ‹œμž‘ μ „ μ‹€ν–‰
ExecStart=/usr/bin/myapp             # 메인 λͺ…λ Ή
ExecStartPost=/usr/bin/myapp-notify  # μ‹œμž‘ ν›„ μ‹€ν–‰
ExecReload=/bin/kill -HUP $MAINPID   # reload μ‹œ μ‹€ν–‰
ExecStop=/usr/bin/myapp stop         # μ’…λ£Œ λͺ…λ Ή
ExecStopPost=/usr/bin/cleanup        # μ’…λ£Œ ν›„ μ‹€ν–‰

# μ‹€νŒ¨ν•΄λ„ 계속 (-접두사)
ExecStartPre=-/usr/bin/optional-check

# μ‰˜ μ‚¬μš© (;λŠ” μ—¬λŸ¬ λͺ…λ Ή)
ExecStart=/bin/sh -c 'echo start && /usr/bin/myapp'

# νƒ€μž„μ•„μ›ƒ
TimeoutStartSec=30
TimeoutStopSec=30
TimeoutSec=30  # λ‘˜ λ‹€ μ„€μ •

# μž¬μ‹œμž‘ 쑰건
Restart=no              # μž¬μ‹œμž‘ μ•ˆ 함
Restart=on-success      # 정상 μ’…λ£Œ μ‹œλ§Œ
Restart=on-failure      # 비정상 μ’…λ£Œ μ‹œλ§Œ
Restart=on-abnormal     # μ‹œκ·Έλ„/νƒ€μž„μ•„μ›ƒ μ‹œ
Restart=on-watchdog     # watchdog νƒ€μž„μ•„μ›ƒ μ‹œ
Restart=on-abort        # 캐치 μ•ˆ 된 μ‹œκ·Έλ„ μ‹œ
Restart=always          # 항상 μž¬μ‹œμž‘

RestartSec=5            # μž¬μ‹œμž‘ μ „ λŒ€κΈ° μ‹œκ°„
RestartPreventExitStatus=1 23  # 이 μ’…λ£Œμ½”λ“œλ©΄ μž¬μ‹œμž‘ μ•ˆ 함

2.4 λ³΄μ•ˆ μ˜΅μ…˜

[Service]
# μ‚¬μš©μž/κ·Έλ£Ή
User=myapp
Group=myapp
DynamicUser=yes  # 동적 μ‚¬μš©μž 생성 (μž„μ‹œ)

# νŒŒμΌμ‹œμŠ€ν…œ μ ‘κ·Ό μ œν•œ
ProtectSystem=strict     # /usr, /boot 읽기 μ „μš©
ProtectHome=yes          # /home μ ‘κ·Ό λΆˆκ°€
PrivateTmp=yes           # 격리된 /tmp
ReadWritePaths=/var/lib/myapp
ReadOnlyPaths=/etc/myapp

# λ„€νŠΈμ›Œν¬ μ œν•œ
PrivateNetwork=yes       # 격리된 λ„€νŠΈμ›Œν¬
RestrictAddressFamilies=AF_INET AF_INET6

# μ‹œμŠ€ν…œ 콜 필터링
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources

# 기타 λ³΄μ•ˆ
NoNewPrivileges=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE

# λ¦¬μ†ŒμŠ€ μ œν•œ
LimitNOFILE=65536
LimitNPROC=4096
MemoryMax=1G
CPUQuota=50%

2.5 μ™„μ „ν•œ 예제: Node.js μ•±

# /etc/systemd/system/nodeapp.service
[Unit]
Description=Node.js Application
Documentation=https://example.com/docs
After=network.target mongodb.service
Wants=mongodb.service

[Service]
Type=simple
User=nodeapp
Group=nodeapp
WorkingDirectory=/opt/nodeapp

# Node.js 경둜
Environment=PATH=/opt/nodeapp/node/bin:/usr/bin
Environment=NODE_ENV=production
EnvironmentFile=/etc/nodeapp/env

# μ‹€ν–‰
ExecStart=/opt/nodeapp/node/bin/node /opt/nodeapp/app.js
ExecReload=/bin/kill -HUP $MAINPID

# μž¬μ‹œμž‘ μ •μ±…
Restart=always
RestartSec=10
WatchdogSec=30

# λ‘œκΉ…
StandardOutput=journal
StandardError=journal
SyslogIdentifier=nodeapp

# λ³΄μ•ˆ
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/nodeapp /var/log/nodeapp

# λ¦¬μ†ŒμŠ€ μ œν•œ
LimitNOFILE=65536
MemoryMax=2G

[Install]
WantedBy=multi-user.target

3. 타이머 μœ λ‹›

3.1 타이머 κΈ°λ³Έ

# /etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer

[Timer]
# μ‹€μ‹œκ°„ (wallclock) 타이머
OnCalendar=*-*-* 02:00:00  # 맀일 μƒˆλ²½ 2μ‹œ

# λ˜λŠ” λͺ¨λ…Έν† λ‹‰ 타이머
# OnBootSec=15min           # λΆ€νŒ… 15λΆ„ ν›„
# OnUnitActiveSec=1h        # λ§ˆμ§€λ§‰ ν™œμ„±ν™” ν›„ 1μ‹œκ°„

# 정확도 (배터리 μ ˆμ•½)
AccuracySec=1min

# λ†“μΉœ μ‹€ν–‰ 처리
Persistent=yes  # μ‹œμŠ€ν…œ 꺼져있던 λ™μ•ˆ λ†“μΉœ μ‹€ν–‰ 보정

[Install]
WantedBy=timers.target

---
# /etc/systemd/system/backup.service
[Unit]
Description=Backup Service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

3.2 OnCalendar 문법

# OnCalendar ν˜•μ‹: DayOfWeek Year-Month-Day Hour:Minute:Second

# 맀일 μžμ •
OnCalendar=daily
OnCalendar=*-*-* 00:00:00

# λ§€μ‹œκ°„
OnCalendar=hourly
OnCalendar=*-*-* *:00:00

# λ§€μ£Ό μ›”μš”μΌ μ˜€μ „ 6μ‹œ
OnCalendar=Mon *-*-* 06:00:00
OnCalendar=weekly

# λ§€μ›” 1일
OnCalendar=monthly
OnCalendar=*-*-01 00:00:00

# λ§€λ…„ 1μ›” 1일
OnCalendar=yearly
OnCalendar=*-01-01 00:00:00

# 5λΆ„λ§ˆλ‹€
OnCalendar=*:0/5
OnCalendar=*-*-* *:00/5:00

# 평일 μ˜€μ „ 9μ‹œ
OnCalendar=Mon..Fri *-*-* 09:00:00

# νŠΉμ • λ‚ μ§œ
OnCalendar=2024-12-25 00:00:00

# λ²”μœ„
OnCalendar=*-*-* 08..18:00:00  # λ§€μ‹œκ°„ (8μ‹œ-18μ‹œ)

# 타이머 ν…ŒμŠ€νŠΈ
systemd-analyze calendar "Mon *-*-* 09:00:00"
systemd-analyze calendar --iterations=5 "daily"

3.3 타이머 관리

# 타이머 μ‹œμž‘/ν™œμ„±ν™”
systemctl start backup.timer
systemctl enable backup.timer

# 타이머 λͺ©λ‘
systemctl list-timers
systemctl list-timers --all

# 타이머 μƒνƒœ
systemctl status backup.timer

# μ¦‰μ‹œ μ‹€ν–‰ (타이머 λ¬΄μ‹œ)
systemctl start backup.service

# cronμ—μ„œ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜
# crontab: 0 2 * * * /usr/local/bin/backup.sh
# β†’ systemd timer: OnCalendar=*-*-* 02:00:00

3.4 μ—¬λŸ¬ μŠ€μΌ€μ€„

# /etc/systemd/system/multi-schedule.timer
[Unit]
Description=Multiple Schedule Timer

[Timer]
# μ—¬λŸ¬ μ‹œκ°„ μ§€μ • κ°€λŠ₯
OnCalendar=Mon *-*-* 06:00:00
OnCalendar=Wed *-*-* 06:00:00
OnCalendar=Fri *-*-* 06:00:00

# λ˜λŠ”
OnCalendar=Mon,Wed,Fri *-*-* 06:00:00

[Install]
WantedBy=timers.target

4. μ†ŒμΌ“ ν™œμ„±ν™”

4.1 μ†ŒμΌ“ ν™œμ„±ν™” κ°œλ…

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    μ†ŒμΌ“ ν™œμ„±ν™” 흐름                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  1. λΆ€νŒ… μ‹œ                                                 β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                            β”‚
β”‚     β”‚ systemd │──▢ μ†ŒμΌ“ μ—΄κΈ° (μ„œλΉ„μŠ€ μ‹œμž‘ μ•ˆ 함)           β”‚
β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                            β”‚
β”‚          β”‚                                                  β”‚
β”‚          β–Ό                                                  β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                            β”‚
β”‚     β”‚ socket  β”‚ (λŒ€κΈ° 쀑)                                  β”‚
β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                            β”‚
β”‚                                                             β”‚
β”‚  2. μ—°κ²° μš”μ²­ μ‹œ                                            β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚     β”‚ Client  │─────▢│ socket  │─────▢│ systemd β”‚         β”‚
β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚                                              β”‚              β”‚
β”‚                                              β–Ό              β”‚
β”‚                                        μ„œλΉ„μŠ€ μ‹œμž‘          β”‚
β”‚                                              β”‚              β”‚
β”‚                                              β–Ό              β”‚
β”‚                                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚                                        β”‚ service β”‚         β”‚
β”‚                                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚                                                             β”‚
β”‚  μž₯점:                                                      β”‚
β”‚  β€’ λΆ€νŒ… μ‹œκ°„ 단좕                                           β”‚
β”‚  β€’ μš”μ²­ μ‹œμ—λ§Œ μ„œλΉ„μŠ€ μ‹œμž‘                                  β”‚
β”‚  β€’ μ„œλΉ„μŠ€ μž¬μ‹œμž‘ 쀑에도 μ—°κ²° μœ μ§€                           β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

4.2 μ†ŒμΌ“ μœ λ‹›

# /etc/systemd/system/myapp.socket
[Unit]
Description=My App Socket

[Socket]
# TCP μ†ŒμΌ“
ListenStream=8080
# λ˜λŠ” IP μ§€μ •
# ListenStream=127.0.0.1:8080
# λ˜λŠ” IPv6
# ListenStream=[::1]:8080

# Unix μ†ŒμΌ“
# ListenStream=/run/myapp/myapp.sock
# SocketUser=myapp
# SocketGroup=myapp
# SocketMode=0660

# UDP μ†ŒμΌ“
# ListenDatagram=8081

# μ—°κ²° λŒ€κΈ°μ—΄
Backlog=128

# μ—°κ²°λ‹Ή ν•˜λ‚˜μ˜ μ„œλΉ„μŠ€ μΈμŠ€ν„΄μŠ€
Accept=no  # κΈ°λ³Έκ°’, ν•˜λ‚˜μ˜ μ„œλΉ„μŠ€κ°€ λͺ¨λ“  μ—°κ²° 처리
# Accept=yes  # μ—°κ²°λ§ˆλ‹€ μƒˆ μΈμŠ€ν„΄μŠ€ (inetd μŠ€νƒ€μΌ)

# μ„œλΉ„μŠ€μ™€ μ—°κ²° (κΈ°λ³Έ: 같은 이름.service)
# Service=myapp.service

[Install]
WantedBy=sockets.target

---
# /etc/systemd/system/myapp.service
[Unit]
Description=My App Service
Requires=myapp.socket

[Service]
Type=simple
ExecStart=/opt/myapp/bin/myapp
# μ†ŒμΌ“μ€ fd 3으둜 전달됨 (λ˜λŠ” $LISTEN_FDS)

[Install]
WantedBy=multi-user.target

4.3 μ†ŒμΌ“ ν™œμ„±ν™” μ„œλΉ„μŠ€ 예제

#!/usr/bin/env python3
# /opt/myapp/bin/myapp.py
# μ†ŒμΌ“ ν™œμ„±ν™”λ₯Ό μ§€μ›ν•˜λŠ” Python μ„œλ²„

import socket
import os
import sys

def get_systemd_socket():
    """systemdμ—μ„œ μ „λ‹¬λœ μ†ŒμΌ“ κ°€μ Έμ˜€κΈ°"""
    # LISTEN_FDS: μ „λ‹¬λœ fd 개수
    # LISTEN_PID: λŒ€μƒ ν”„λ‘œμ„ΈμŠ€ PID
    listen_fds = int(os.environ.get('LISTEN_FDS', 0))
    listen_pid = int(os.environ.get('LISTEN_PID', 0))

    if listen_pid != os.getpid():
        return None

    if listen_fds >= 1:
        # fd 3λΆ€ν„° μ‹œμž‘ (0=stdin, 1=stdout, 2=stderr)
        return socket.fromfd(3, socket.AF_INET, socket.SOCK_STREAM)

    return None

def main():
    # systemd μ†ŒμΌ“ λ˜λŠ” μƒˆ μ†ŒμΌ“ 생성
    sock = get_systemd_socket()
    if sock is None:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('0.0.0.0', 8080))
        sock.listen(128)
        print("Listening on port 8080")
    else:
        print("Using systemd socket")

    while True:
        conn, addr = sock.accept()
        with conn:
            print(f"Connection from {addr}")
            conn.sendall(b"Hello from socket-activated service!\n")

if __name__ == '__main__':
    main()

5. μ˜μ‘΄μ„±κ³Ό μˆœμ„œ

5.1 μ˜μ‘΄μ„± μ§€μ‹œμž

[Unit]
# Requires: ν•„μˆ˜ μ˜μ‘΄μ„± (의쑴 μœ λ‹› μ‹€νŒ¨ μ‹œ 이 μœ λ‹›λ„ μ‹€νŒ¨)
Requires=postgresql.service

# Wants: 선택적 μ˜μ‘΄μ„± (의쑴 μœ λ‹› μ‹€νŒ¨ν•΄λ„ 계속)
Wants=redis.service

# Requisite: 이미 ν™œμ„±ν™”λœ μœ λ‹›μ—λ§Œ 의쑴
Requisite=network.target

# BindsTo: κ°•ν•œ μ˜μ‘΄μ„± (의쑴 μœ λ‹› μ’…λ£Œ μ‹œ 이 μœ λ‹›λ„ μ’…λ£Œ)
BindsTo=libvirtd.service

# PartOf: 의쑴 μœ λ‹› μž¬μ‹œμž‘/μ’…λ£Œ μ‹œ 같이 μž¬μ‹œμž‘/μ’…λ£Œ
PartOf=docker.service

# Conflicts: λ™μ‹œ μ‹€ν–‰ λΆˆκ°€
Conflicts=shutdown.target

5.2 μˆœμ„œ μ§€μ‹œμž

[Unit]
# After: μ§€μ • μœ λ‹› μ‹œμž‘ 후에 μ‹œμž‘
After=network.target postgresql.service

# Before: μ§€μ • μœ λ‹› μ‹œμž‘ 전에 μ‹œμž‘
Before=httpd.service

# μ˜μ‘΄μ„±κ³Ό μˆœμ„œλŠ” λ³„κ°œ!
# Wants=postgresql.service β†’ postgresqlκ³Ό ν•¨κ»˜ μ‹œμž‘ μ‹œλ„
# After=postgresql.service β†’ postgresql μ‹œμž‘ μ™„λ£Œ ν›„ μ‹œμž‘

# μ˜¬λ°”λ₯Έ μ‘°ν•©
Wants=postgresql.service
After=postgresql.service

5.3 Target μœ λ‹›

# /etc/systemd/system/myapp.target
[Unit]
Description=My Application Stack
Requires=myapp-web.service myapp-worker.service
After=myapp-web.service myapp-worker.service

[Install]
WantedBy=multi-user.target

# μ‚¬μš©
systemctl start myapp.target    # λͺ¨λ“  κ΄€λ ¨ μ„œλΉ„μŠ€ μ‹œμž‘
systemctl stop myapp.target     # λͺ¨λ“  κ΄€λ ¨ μ„œλΉ„μŠ€ μ’…λ£Œ
systemctl restart myapp.target

5.4 μ˜μ‘΄μ„± μ‹œκ°ν™”

# μ˜μ‘΄μ„± 트리
systemctl list-dependencies nginx.service
systemctl list-dependencies --reverse nginx.service

# λΆ€νŒ… μˆœμ„œ 뢄석
systemd-analyze
systemd-analyze blame
systemd-analyze critical-chain
systemd-analyze critical-chain nginx.service

# κ·Έλž˜ν”„ 생성 (SVG)
systemd-analyze dot | dot -Tsvg > systemd.svg
systemd-analyze dot "nginx.service" | dot -Tsvg > nginx-deps.svg

6. journald λ‘œκΉ…

6.1 journalctl κΈ°λ³Έ

# 전체 둜그
journalctl

# νŠΉμ • μœ λ‹› 둜그
journalctl -u nginx.service

# μ‹€μ‹œκ°„ 둜그 (tail -f)
journalctl -f
journalctl -fu nginx.service

# λΆ€νŒ… 둜그
journalctl -b          # ν˜„μž¬ λΆ€νŒ…
journalctl -b -1       # 이전 λΆ€νŒ…
journalctl --list-boots

# μ‹œκ°„ λ²”μœ„
journalctl --since "2024-01-01"
journalctl --since "1 hour ago"
journalctl --since "2024-01-01" --until "2024-01-02"
journalctl --since yesterday

# μš°μ„ μˆœμœ„ ν•„ν„°
journalctl -p err      # error 이상
journalctl -p warning  # warning 이상
# 0=emerg, 1=alert, 2=crit, 3=err, 4=warning, 5=notice, 6=info, 7=debug

# 컀널 λ©”μ‹œμ§€
journalctl -k
journalctl --dmesg

# JSON 좜λ ₯
journalctl -o json
journalctl -o json-pretty

# λ””μŠ€ν¬ μ‚¬μš©λŸ‰
journalctl --disk-usage

# 둜그 정리
journalctl --vacuum-size=500M
journalctl --vacuum-time=7d

6.2 journald μ„€μ •

# /etc/systemd/journald.conf
[Journal]
# μ €μž₯ 방식
Storage=persistent     # 영ꡬ μ €μž₯ (/var/log/journal)
# Storage=volatile     # λ©”λͺ¨λ¦¬λ§Œ (/run/log/journal)
# Storage=auto         # /var/log/journal 있으면 persistent

# 크기 μ œν•œ
SystemMaxUse=500M      # μ΅œλŒ€ λ””μŠ€ν¬ μ‚¬μš©λŸ‰
SystemMaxFileSize=50M  # κ°œλ³„ 파일 μ΅œλŒ€ 크기
RuntimeMaxUse=100M     # λŸ°νƒ€μž„(λ©”λͺ¨λ¦¬) μ΅œλŒ€

# 보쑴 κΈ°κ°„
MaxRetentionSec=1month

# μ••μΆ•
Compress=yes

# syslog 전달
ForwardToSyslog=no

# μ½˜μ†” 좜λ ₯
ForwardToConsole=no

# 속도 μ œν•œ
RateLimitIntervalSec=30s
RateLimitBurst=10000
# μ„€μ • 적용
systemctl restart systemd-journald

# 영ꡬ μ €μž₯ 디렉토리 생성
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal

6.3 κ΅¬μ‘°ν™”λœ λ‘œκΉ…

# systemd-cat으둜 둜그 전솑
echo "Hello" | systemd-cat -t myapp -p info

# μŠ€ν¬λ¦½νŠΈμ—μ„œ
#!/bin/bash
exec 1> >(systemd-cat -t myscript -p info)
exec 2> >(systemd-cat -t myscript -p err)
echo "This goes to journal"
# Pythonμ—μ„œ (systemd.journal)
from systemd import journal

journal.send('Hello from Python',
             PRIORITY=journal.LOG_INFO,
             SYSLOG_IDENTIFIER='myapp',
             MYFIELD='custom_value')

6.4 둜그 필터링 κ³ κΈ‰

# ν•„λ“œ 기반 필터링
journalctl _SYSTEMD_UNIT=nginx.service
journalctl _UID=1000
journalctl _PID=1234
journalctl _COMM=nginx

# μ—¬λŸ¬ 쑰건 (AND)
journalctl _SYSTEMD_UNIT=nginx.service _PID=1234

# OR 쑰건
journalctl _SYSTEMD_UNIT=nginx.service + _SYSTEMD_UNIT=php-fpm.service

# νŠΉμ • ν•„λ“œ 좜λ ₯
journalctl -o verbose
journalctl -u nginx --output-fields=MESSAGE,_PID

# grepκ³Ό ν•¨κ»˜
journalctl -u nginx | grep -i error
journalctl -u nginx -g "error|warning"  # -gλŠ” grep νŒ¨ν„΄

7. μ—°μŠ΅ 문제

μ—°μŠ΅ 1: μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€

# μš”κ΅¬μ‚¬ν•­:
# 1. Python/Node.js μ›Ή 앱을 μ„œλΉ„μŠ€λ‘œ 등둝
# 2. μž¬μ‹œμž‘ μ •μ±… μ„€μ • (on-failure)
# 3. ν™˜κ²½ λ³€μˆ˜ 파일 μ‚¬μš©
# 4. λ³΄μ•ˆ μ˜΅μ…˜ 적용

# μ„œλΉ„μŠ€ μœ λ‹› μž‘μ„±:

μ—°μŠ΅ 2: λ°±μ—… 타이머

# μš”κ΅¬μ‚¬ν•­:
# 1. 맀일 μƒˆλ²½ 3μ‹œ λ°±μ—… μ‹€ν–‰
# 2. λ§€μ£Ό μΌμš”μΌ 전체 λ°±μ—…
# 3. λ†“μΉœ μ‹€ν–‰ 보정
# 4. 둜그λ₯Ό journal에 기둝

# 타이머 및 μ„œλΉ„μŠ€ μœ λ‹› μž‘μ„±:

μ—°μŠ΅ 3: μ†ŒμΌ“ ν™œμ„±ν™” μ„œλΉ„μŠ€

# μš”κ΅¬μ‚¬ν•­:
# 1. 포트 9000μ—μ„œ λŒ€κΈ°
# 2. μ—°κ²° μ‹œμ—λ§Œ μ„œλΉ„μŠ€ μ‹œμž‘
# 3. 유휴 μ‹œ μžλ™ μ’…λ£Œ (IdleTimeout)

# μ†ŒμΌ“ 및 μ„œλΉ„μŠ€ μœ λ‹› μž‘μ„±:

μ—°μŠ΅ 4: λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€ μŠ€νƒ

# μš”κ΅¬μ‚¬ν•­:
# 1. API μ„œλΉ„μŠ€ (api.service)
# 2. μ›Œμ»€ μ„œλΉ„μŠ€ (worker.service)
# 3. λ°μ΄ν„°λ² μ΄μŠ€ μ˜μ‘΄μ„±
# 4. 전체 μŠ€νƒ target

# λͺ¨λ“  μœ λ‹› 파일 μž‘μ„±:

λ‹€μŒ 단계

참고 자료


← 이전: λ³΄μ•ˆκ³Ό λ°©ν™”λ²½ | λ‹€μŒ: μ„±λŠ₯ νŠœλ‹ β†’ | λͺ©μ°¨

to navigate between lessons