Mail-in-a-Box 기업 환경 완전 가이드: 도커화와 쿠버네티스 운영
⏱️ 예상 읽기 시간: 18분
기업에서 자체 메일 서버 운영을 고려하고 있나요? Mail-in-a-Box는 14.6k 스타를 받은 오픈소스 메일 서버 솔루션으로, 복잡한 메일 서버 구축을 원클릭으로 간소화합니다. 이 가이드에서는 Mail-in-a-Box를 기업 환경에 최적화하여 도커화하고 쿠버네티스에서 운영하는 실전 방법을 다룹니다.
Mail-in-a-Box 개요
핵심 특징
- 완전 통합 솔루션: SMTP, IMAP, 웹메일, DNS, 인증서 관리 등 모든 기능 포함
- 원클릭 설치: Ubuntu 22.04 LTS에서 완전 자동 설치
- 보안 내장: SPF, DKIM, DMARC, DNSSEC, Let’s Encrypt 자동 구성
- 관리 대시보드: 웹 기반 제어 패널과 REST API
- 개인정보 보호: 자체 호스팅으로 완전한 데이터 제어
구성 요소
서비스 | 소프트웨어 | 기능 |
---|---|---|
SMTP | Postfix | 메일 송신/수신 |
IMAP | Dovecot | 메일함 관리 |
웹메일 | Roundcube | 브라우저 메일 클라이언트 |
캘린더/연락처 | Nextcloud | CardDAV/CalDAV |
모바일 동기화 | Z-Push | Exchange ActiveSync |
DNS | NSD4 | 도메인 네임 서버 |
웹서버 | Nginx | 웹 인터페이스 |
데이터베이스 | SQLite | 사용자/설정 관리 |
기업 환경 고려사항
1. 기존 Mail-in-a-Box의 한계
단일 서버 구조
- 고가용성 부족
- 스케일링 제한
- 장애 시 전체 서비스 중단
제한된 커스터마이징
- 기업 정책 적용 어려움
- 복잡한 라우팅 규칙 미지원
- 외부 시스템 통합 제한
2. 기업 요구사항
고가용성 (HA)
- 99.9% 이상 가용성 목표
- 자동 장애 복구
- 무중단 업데이트
보안 강화
- 다단계 인증 (MFA)
- 메일 암호화
- 감사 로그
- 규정 준수 (GDPR, SOX 등)
확장성
- 수천 명의 사용자 지원
- 대용량 메일 처리
- 성능 모니터링
도커화 전략
1. 마이크로서비스 분해
# docker-compose.yml
version: '3.8'
services:
postfix:
build: ./containers/postfix
volumes:
- postfix-data:/var/lib/postfix
- mail-data:/var/mail
environment:
- DOMAIN=${MAIL_DOMAIN}
- HOSTNAME=${MAIL_HOSTNAME}
networks:
- mail-network
dovecot:
build: ./containers/dovecot
volumes:
- dovecot-data:/var/lib/dovecot
- mail-data:/var/mail
networks:
- mail-network
roundcube:
build: ./containers/roundcube
environment:
- DB_HOST=mysql
- DB_USER=${DB_USER}
- DB_PASS=${DB_PASS}
networks:
- mail-network
- web-network
nginx:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ssl-certs:/etc/ssl/certs
ports:
- "80:80"
- "443:443"
networks:
- web-network
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASS}
- MYSQL_DATABASE=roundcube
volumes:
- mysql-data:/var/lib/mysql
networks:
- mail-network
networks:
mail-network:
driver: bridge
web-network:
driver: bridge
volumes:
postfix-data:
dovecot-data:
mail-data:
mysql-data:
ssl-certs:
2. 컨테이너 이미지 구성
Postfix 컨테이너
# containers/postfix/Dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
postfix \
postfix-mysql \
opendkim \
opendkim-tools \
&& rm -rf /var/lib/apt/lists/*
COPY configs/postfix/ /etc/postfix/
COPY configs/opendkim/ /etc/opendkim/
COPY scripts/postfix-start.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/postfix-start.sh
EXPOSE 25 587 465
CMD ["/usr/local/bin/postfix-start.sh"]
Dovecot 컨테이너
# containers/dovecot/Dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
dovecot-core \
dovecot-imapd \
dovecot-pop3d \
dovecot-lmtpd \
dovecot-mysql \
&& rm -rf /var/lib/apt/lists/*
COPY configs/dovecot/ /etc/dovecot/
COPY scripts/dovecot-start.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/dovecot-start.sh
EXPOSE 143 993 110 995 24
CMD ["/usr/local/bin/dovecot-start.sh"]
3. 설정 관리
ConfigMap 기반 설정
# k8s/configmaps/postfix-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: postfix-config
data:
main.cf: |
smtpd_banner = $myhostname ESMTP $mail_name
biff = no
append_dot_mydomain = no
readme_directory = no
compatibility_level = 2
# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/mail.crt
smtpd_tls_key_file=/etc/ssl/private/mail.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# Authentication
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
# Restrictions
smtpd_recipient_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination
# Virtual domains
virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf
master.cf: |
smtp inet n - y - - smtpd
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_reject_unlisted_recipient=no
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
쿠버네티스 배포 구성
1. 네임스페이스 및 보안
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: mail-system
labels:
name: mail-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: mail-service-account
namespace: mail-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: mail-system
name: mail-role
rules:
- apiGroups: [""]
resources: ["secrets", "configmaps"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mail-role-binding
namespace: mail-system
subjects:
- kind: ServiceAccount
name: mail-service-account
namespace: mail-system
roleRef:
kind: Role
name: mail-role
apiGroup: rbac.authorization.k8s.io
2. 스토리지 구성
# k8s/storage.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: mail-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mail-data-pvc
namespace: mail-system
spec:
accessModes:
- ReadWriteOnce
storageClassName: mail-storage
resources:
requests:
storage: 100Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data-pvc
namespace: mail-system
spec:
accessModes:
- ReadWriteOnce
storageClassName: mail-storage
resources:
requests:
storage: 50Gi
3. MySQL 데이터베이스
# k8s/mysql.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: mail-system
spec:
serviceName: mysql
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
serviceAccountName: mail-service-account
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
- name: MYSQL_DATABASE
value: "mailserver"
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
exec:
command:
- mysqladmin
- ping
- -h
- localhost
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command:
- mysql
- -h
- localhost
- -e
- "SELECT 1"
initialDelaySeconds: 5
periodSeconds: 5
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: mail-storage
resources:
requests:
storage: 50Gi
---
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: mail-system
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
clusterIP: None
4. Postfix 배포
# k8s/postfix.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: postfix
namespace: mail-system
spec:
replicas: 2
selector:
matchLabels:
app: postfix
template:
metadata:
labels:
app: postfix
spec:
serviceAccountName: mail-service-account
containers:
- name: postfix
image: your-registry/mail-postfix:latest
env:
- name: MAIL_DOMAIN
value: "your-company.com"
- name: DB_HOST
value: "mysql"
- name: DB_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: mail-user
- name: DB_PASS
valueFrom:
secretKeyRef:
name: mysql-secret
key: mail-password
ports:
- containerPort: 25
- containerPort: 587
- containerPort: 465
volumeMounts:
- name: postfix-config
mountPath: /etc/postfix
- name: mail-data
mountPath: /var/mail
- name: ssl-certs
mountPath: /etc/ssl/certs
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
tcpSocket:
port: 25
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 25
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: postfix-config
configMap:
name: postfix-config
- name: mail-data
persistentVolumeClaim:
claimName: mail-data-pvc
- name: ssl-certs
secret:
secretName: mail-tls-secret
---
apiVersion: v1
kind: Service
metadata:
name: postfix
namespace: mail-system
spec:
selector:
app: postfix
ports:
- name: smtp
port: 25
targetPort: 25
- name: submission
port: 587
targetPort: 587
- name: smtps
port: 465
targetPort: 465
type: LoadBalancer
5. Dovecot 배포
# k8s/dovecot.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dovecot
namespace: mail-system
spec:
replicas: 2
selector:
matchLabels:
app: dovecot
template:
metadata:
labels:
app: dovecot
spec:
serviceAccountName: mail-service-account
containers:
- name: dovecot
image: your-registry/mail-dovecot:latest
env:
- name: DB_HOST
value: "mysql"
- name: DB_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: mail-user
- name: DB_PASS
valueFrom:
secretKeyRef:
name: mysql-secret
key: mail-password
ports:
- containerPort: 143
- containerPort: 993
- containerPort: 110
- containerPort: 995
- containerPort: 24
volumeMounts:
- name: dovecot-config
mountPath: /etc/dovecot
- name: mail-data
mountPath: /var/mail
- name: ssl-certs
mountPath: /etc/ssl/certs
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
tcpSocket:
port: 143
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 143
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: dovecot-config
configMap:
name: dovecot-config
- name: mail-data
persistentVolumeClaim:
claimName: mail-data-pvc
- name: ssl-certs
secret:
secretName: mail-tls-secret
---
apiVersion: v1
kind: Service
metadata:
name: dovecot
namespace: mail-system
spec:
selector:
app: dovecot
ports:
- name: imap
port: 143
targetPort: 143
- name: imaps
port: 993
targetPort: 993
- name: pop3
port: 110
targetPort: 110
- name: pop3s
port: 995
targetPort: 995
- name: lmtp
port: 24
targetPort: 24
type: LoadBalancer
고가용성 구성
1. 데이터베이스 클러스터
# k8s/mysql-cluster.yaml
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
name: mysql-cluster
namespace: mail-system
spec:
secretName: mysql-secret
tlsUseSelfSigned: true
instances: 3
router:
instances: 2
datadirVolumeClaimTemplate:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
storageClassName: mail-storage
mycnf: |
[mysqld]
max_connections = 200
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
2. 로드 밸런서 구성
# k8s/load-balancer.yaml
apiVersion: v1
kind: Service
metadata:
name: mail-lb
namespace: mail-system
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
type: LoadBalancer
selector:
app: postfix
ports:
- name: smtp
port: 25
targetPort: 25
protocol: TCP
- name: submission
port: 587
targetPort: 587
protocol: TCP
- name: smtps
port: 465
targetPort: 465
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mail-web-ingress
namespace: mail-system
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- mail.your-company.com
secretName: mail-web-tls
rules:
- host: mail.your-company.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: roundcube
port:
number: 80
3. 자동 스케일링
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: postfix-hpa
namespace: mail-system
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: postfix
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: dovecot-hpa
namespace: mail-system
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: dovecot
minReplicas: 2
maxReplicas: 8
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
보안 강화
1. 네트워크 정책
# k8s/network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mail-network-policy
namespace: mail-system
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: mail-system
ports:
- protocol: TCP
port: 25
- protocol: TCP
port: 587
- protocol: TCP
port: 465
- protocol: TCP
port: 143
- protocol: TCP
port: 993
- from: []
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
egress:
- to: []
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
- protocol: TCP
port: 80
- protocol: TCP
port: 443
- protocol: TCP
port: 3306
2. Pod Security Standards
# k8s/pod-security.yaml
apiVersion: v1
kind: Namespace
metadata:
name: mail-system
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: postfix-pdb
namespace: mail-system
spec:
minAvailable: 1
selector:
matchLabels:
app: postfix
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: dovecot-pdb
namespace: mail-system
spec:
minAvailable: 1
selector:
matchLabels:
app: dovecot
3. 시크릿 관리
# k8s/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: mail-system
type: Opaque
data:
root-password: <base64-encoded-password>
mail-user: <base64-encoded-username>
mail-password: <base64-encoded-password>
---
apiVersion: v1
kind: Secret
metadata:
name: mail-admin-secret
namespace: mail-system
type: Opaque
data:
admin-user: <base64-encoded-admin-user>
admin-password: <base64-encoded-admin-password>
---
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-secret-store
namespace: mail-system
spec:
provider:
vault:
server: "https://vault.your-company.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "mail-role"
모니터링 및 로깅
1. Prometheus 모니터링
# k8s/monitoring.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: mail-services
namespace: mail-system
spec:
selector:
matchLabels:
monitoring: "true"
endpoints:
- port: metrics
interval: 30s
path: /metrics
---
apiVersion: v1
kind: Service
metadata:
name: postfix-metrics
namespace: mail-system
labels:
monitoring: "true"
spec:
selector:
app: postfix
ports:
- name: metrics
port: 9154
targetPort: 9154
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: mail-alerts
namespace: mail-system
spec:
groups:
- name: mail.rules
rules:
- alert: PostfixDown
expr: up{job="postfix"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Postfix service is down"
description: "Postfix has been down for more than 5 minutes"
- alert: HighMailQueueSize
expr: postfix_showq_messages_total > 1000
for: 10m
labels:
severity: warning
annotations:
summary: "High mail queue size"
description: "Mail queue size is messages"
- alert: DovecotDown
expr: up{job="dovecot"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Dovecot service is down"
description: "Dovecot has been down for more than 5 minutes"
2. 로그 집계
# k8s/logging.yaml
apiVersion: logging.coreos.com/v1
kind: ClusterLogForwarder
metadata:
name: mail-log-forwarder
namespace: openshift-logging
spec:
outputs:
- name: mail-elasticsearch
type: elasticsearch
url: "https://elasticsearch.your-company.com:9200"
secret:
name: elasticsearch-secret
pipelines:
- name: mail-logs
inputRefs:
- application
filterRefs:
- mail-filter
outputRefs:
- mail-elasticsearch
---
apiVersion: logging.coreos.com/v1
kind: ClusterLogFilter
metadata:
name: mail-filter
spec:
type: json
json:
javascript: |
const log = record.log;
if (log.kubernetes && log.kubernetes.namespace_name === 'mail-system') {
return record;
}
return null;
백업 및 재해 복구
1. 백업 전략
# k8s/backup.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: mail-backup
namespace: mail-system
spec:
schedule: "0 2 * * *" # 매일 새벽 2시
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: your-registry/mail-backup:latest
env:
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: backup-secret
key: access-key
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: backup-secret
key: secret-key
- name: S3_BUCKET
value: "your-company-mail-backup"
command:
- /bin/bash
- -c
- |
# MySQL 백업
mysqldump -h mysql -u root -p${MYSQL_ROOT_PASSWORD} --all-databases > /tmp/mysql-backup.sql
# 메일 데이터 백업
tar -czf /tmp/mail-data-backup.tar.gz /var/mail
# S3에 업로드
aws s3 cp /tmp/mysql-backup.sql s3://${S3_BUCKET}/$(date +%Y%m%d)/
aws s3 cp /tmp/mail-data-backup.tar.gz s3://${S3_BUCKET}/$(date +%Y%m%d)/
volumeMounts:
- name: mail-data
mountPath: /var/mail
volumes:
- name: mail-data
persistentVolumeClaim:
claimName: mail-data-pvc
restartPolicy: OnFailure
2. 재해 복구 절차
#!/bin/bash
# disaster-recovery.sh
# 1. 네임스페이스 생성
kubectl create namespace mail-system-dr
# 2. 백업에서 데이터 복원
aws s3 cp s3://your-company-mail-backup/latest/mysql-backup.sql /tmp/
aws s3 cp s3://your-company-mail-backup/latest/mail-data-backup.tar.gz /tmp/
# 3. MySQL 복원
kubectl exec -n mail-system-dr mysql-0 -- mysql -u root -p${MYSQL_ROOT_PASSWORD} < /tmp/mysql-backup.sql
# 4. 메일 데이터 복원
kubectl cp /tmp/mail-data-backup.tar.gz mail-system-dr/postfix-0:/tmp/
kubectl exec -n mail-system-dr postfix-0 -- tar -xzf /tmp/mail-data-backup.tar.gz -C /
# 5. 서비스 확인
kubectl get pods -n mail-system-dr
kubectl get svc -n mail-system-dr
성능 최적화
1. 리소스 튜닝
# k8s/performance-tuning.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: postfix-performance
namespace: mail-system
data:
main.cf: |
# 성능 최적화 설정
default_process_limit = 100
smtpd_client_connection_count_limit = 50
smtpd_client_connection_rate_limit = 30
anvil_rate_time_unit = 60s
anvil_status_update_time = 600s
# 큐 관리 최적화
maximal_queue_lifetime = 5d
bounce_queue_lifetime = 5d
maximal_backoff_time = 4000s
minimal_backoff_time = 300s
queue_run_delay = 300s
---
apiVersion: v1
kind: ConfigMap
metadata:
name: dovecot-performance
namespace: mail-system
data:
dovecot.conf: |
# 성능 최적화 설정
login_max_processes_count = 128
login_max_connections = 256
default_process_limit = 100
default_client_limit = 1000
# 메일박스 캐시 설정
mailbox_list_index = yes
maildir_very_dirty_syncs = yes
# IMAP 성능 최적화
imap_capability = +IDLE +COMPRESS=DEFLATE
imap_client_workarounds = delay-newmail tb-extra-mailbox-sep
2. 캐싱 전략
# k8s/redis-cache.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: mail-system
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
command:
- redis-server
- --maxmemory
- 512mb
- --maxmemory-policy
- allkeys-lru
ports:
- containerPort: 6379
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "200m"
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: mail-system
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
운영 자동화
1. Helm 차트
# helm/mail-in-a-box/Chart.yaml
apiVersion: v2
name: mail-in-a-box
description: Enterprise Mail-in-a-Box Helm Chart
type: application
version: 1.0.0
appVersion: "v72"
dependencies:
- name: mysql
version: 9.4.0
repository: https://charts.bitnami.com/bitnami
- name: redis
version: 17.3.0
repository: https://charts.bitnami.com/bitnami
# helm/mail-in-a-box/values.yaml
global:
storageClass: "mail-storage"
mail:
domain: "your-company.com"
hostname: "mail.your-company.com"
postfix:
replicaCount: 2
image:
repository: your-registry/mail-postfix
tag: "latest"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
dovecot:
replicaCount: 2
image:
repository: your-registry/mail-dovecot
tag: "latest"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
mysql:
enabled: true
auth:
rootPassword: "secure-root-password"
database: "mailserver"
primary:
persistence:
size: 50Gi
redis:
enabled: true
auth:
enabled: false
master:
persistence:
size: 8Gi
2. GitOps 워크플로우
# .github/workflows/deploy.yml
name: Deploy Mail-in-a-Box
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Helm
uses: azure/setup-helm@v3
with:
version: '3.10.0'
- name: Lint Helm Chart
run: helm lint helm/mail-in-a-box
- name: Template Helm Chart
run: helm template mail-in-a-box helm/mail-in-a-box --values helm/mail-in-a-box/values-test.yaml
deploy:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: $
- name: Deploy to Production
run: |
helm upgrade --install mail-in-a-box helm/mail-in-a-box \
--namespace mail-system \
--create-namespace \
--values helm/mail-in-a-box/values-prod.yaml \
--wait --timeout=10m
운영 모니터링 대시보드
1. Grafana 대시보드
{
"dashboard": {
"title": "Mail-in-a-Box Enterprise Dashboard",
"panels": [
{
"title": "Mail Queue Size",
"type": "graph",
"targets": [
{
"expr": "postfix_showq_messages_total",
"legendFormat": " - Queue Size"
}
]
},
{
"title": "SMTP Connections",
"type": "graph",
"targets": [
{
"expr": "rate(postfix_smtpd_connects_total[5m])",
"legendFormat": " - Connections/sec"
}
]
},
{
"title": "IMAP Sessions",
"type": "stat",
"targets": [
{
"expr": "dovecot_imap_logged_in_users"
}
]
},
{
"title": "Disk Usage",
"type": "graph",
"targets": [
{
"expr": "100 - (node_filesystem_avail_bytes{mountpoint=\"/var/mail\"} / node_filesystem_size_bytes{mountpoint=\"/var/mail\"} * 100)",
"legendFormat": "Mail Storage Usage %"
}
]
}
]
}
}
2. 알림 규칙
# k8s/alerting-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: mail-critical-alerts
namespace: mail-system
spec:
groups:
- name: mail.critical
rules:
- alert: MailServiceDown
expr: up{job=~"postfix|dovecot"} == 0
for: 2m
labels:
severity: critical
team: infrastructure
annotations:
summary: "Critical mail service is down"
description: " service has been down for more than 2 minutes"
runbook_url: "https://wiki.company.com/mail-service-down"
- alert: MailQueueBacklog
expr: postfix_showq_messages_total > 10000
for: 5m
labels:
severity: warning
team: infrastructure
annotations:
summary: "Mail queue backlog detected"
description: "Mail queue has messages pending"
runbook_url: "https://wiki.company.com/mail-queue-backlog"
- alert: DiskSpaceMailStorage
expr: 100 - (node_filesystem_avail_bytes{mountpoint="/var/mail"} / node_filesystem_size_bytes{mountpoint="/var/mail"} * 100) > 85
for: 10m
labels:
severity: warning
team: infrastructure
annotations:
summary: "Mail storage disk space running low"
description: "Mail storage is % full"
runbook_url: "https://wiki.company.com/disk-cleanup"
결론
Mail-in-a-Box를 기업 환경에서 성공적으로 운영하기 위해서는 단순한 컨테이너화를 넘어 종합적인 엔터프라이즈 아키텍처 접근이 필요합니다.
핵심 성공 요소
- 마이크로서비스 분해: 각 구성 요소를 독립적으로 스케일링
- 고가용성 설계: 다중화와 자동 장애 복구
- 보안 강화: 네트워크 정책, 시크릿 관리, 정기 보안 감사
- 운영 자동화: GitOps 기반 배포와 모니터링
기대 효과
비용 절감: 외부 메일 서비스 대비 70% 비용 절약 데이터 주권: 완전한 메일 데이터 제어 맞춤 설정: 기업 정책에 맞는 유연한 구성 확장성: 수천 명 규모까지 지원
Mail-in-a-Box의 14.6k 스타가 증명하는 안정성과 이 가이드의 엔터프라이즈 최적화를 통해 기업용 메일 서버의 새로운 기준을 만들어보세요!
추가 리소스: