May 12, 2025

[Home-K8S] #1 Ghost CMS 구성

[Home-K8S] #1  Ghost CMS 구성

HomeLab K8S 소개

업무도 있고, 개인 프로젝트도 진행하면서 블로그에 대한 필요성도 느끼고 있었습니다. 티스토리도 해보고 워드프레스도 해봤는데 영 손이 안가더라고요. 개인적으로 미니PC를 구성해서 HomeLab을 진행하고 있었기도 해서 직접 호스팅 하기로 했습니다. Ghost CMS를 시작으로 HomeLab에 구성된 인프라를 하나씩 포스팅하겠습니다.

Why Ghost CMS?

ChatGPT한테 추천을 받았어요. WordPress, Wagtail 같은 동적 호스팅 CMS 와 Hugo/Jekyll 같은 정적 호스팅 플랫폼 중에서 고민을 했어요.
"그냥 md 파일로 만들어서 Hugo 로 띄워주기만 하면 되지 않을까....?" 하고 있었는데, md 파일을 만들어서 넣어줄 때마다 다시 빌드해서 넣어줘야 한다고 하더라고요.

물론 commit만 하면 자동으로 빌드하고 배포하게 할 수 있지만, md파일을 다른 곳에서 만들어서 넣어주는 것도 그렇고 귀찮아서 동적 호스팅인 Ghost CMS를 사용하기로 했습니다. 이제 구성 방법을 알아봅시다.

Ghost docker image: https://hub.docker.com/_/ghost

-1.Helm 으로 구성하는 법

Ghost CMS를 Helm으로 구성하는 것은 가장 편한 방법입니다. 하지만 기본적으로 mysql로 설정되어 같이 설치되기도 하고 별로 맘에 드는 방법은 아닙니다.

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install my-ghost bitnami/ghost --namespace ghost --create-namespace

0.구성 환경

  1. PV or StorageClass (PVC를 생성하기 위해 필요합니다. )
  2. nginx-ingress, cert-manager (https로 접근하기 위해 필요합니다. )
    NodePort를 이용한 http 접근도 가능합니다.

1.Namespace, PVC

PVC를 해놓아야 Pod가 꺼졌다 켜져도 데이터가 유지됩니다. Post 날아가면 너무 슬플듯...
저는 NFS에 연결해서 사용 중입니다.

apiVersion: v1
kind: Namespace
metadata:
  name: ghost
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ghost-content-pvc
  namespace: ghost
spec:
  storageClassName: ""
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

ghost Namespace, PVC

2.Deployment, Service

Ghost의 공식 이미지를 사용하면 편리합니다. 몇개 설정만 해주면 됩니다.
환경변수 설정:

1. url: "https://blog.dogring.kr"
blog의 url을 지정합니다. tls가 없으면 http로 해야 합니다. https로 넣으면 http에서 login 창이 열리지 않습니다.

2. database__client: "sqlite3"
사용할 db를 선택합니다. 기본적으로 Ghost CMS는 MYSQL을 사용하지만, 사용량이 많은 것도 아니고 리소스가 아깝습니다. db는 sqlite로 하고 content는 PVC와 NFS로 저장하겠습니다.

3. database__connection__filename: "/var/lib/ghost/content/data/ghost.db"
db의 정보를 저장할 위치입니다. content의 저장 위치에 db를 저장해서 pvc에 같이 있도록 합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ghost
  namespace: ghost
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ghost
  template:
    metadata:
      labels:
        app: ghost
    spec:
      containers:
        - name: ghost
          image: ghost:latest
          ports:
            - containerPort: 2368
          env:
            - name: url
              value: "https://blog.dogring.kr"
            - name: database__client
              value: "sqlite3"
            - name: database__connection__filename
              value: "/var/lib/ghost/content/data/ghost.db"
          volumeMounts:
            - name: ghost-content
              mountPath: /var/lib/ghost/content
      volumes:
        - name: ghost-content
          persistentVolumeClaim:
            claimName: ghost-content-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: ghost-service
  namespace: ghost
spec:
  type: ClusterIP
  selector:
    app: ghost
  ports:
    - protocol: TCP
      port: 80
      targetPort: 2368

ghost Deployment, Service

3.Ingress

nginx-ingress를 예시로 보여드립니다.
참고로 porxy-body-size가 기본으로는 작게 되어 있습니다. 이 값을 설정해주지 않으면 이 크기 이상의 파일(사진 등)은 넣을 수 없습니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ghost-ingress
  namespace: ghost
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/proxy-body-size: "1G"   # size 설정
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - blog.dogring.kr
    secretName: blog-cert
  rules:
    - host: blog.dogring.kr
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: ghost-service
                port:
                  number: 80

Ghost Ingress

4.사용

/ghost 의 경로로 접근하면 관리자 계정을 만들 수 있는 페이지가 나올 겁니다.
여기서 관리자 계정을 만들고 나서 /ghost에서 글을 작성할 수 있습니다.
ex) blog.dogring.kr/ghost

5.이메일 설정

Ghost CMS 5버전 이상에서는 관리자로 로그인 하기 위해서는 Email로 코드를 받아야 합니다. 하지만 메일을 보내기 위한 SMTP 설정을 안해서 로그인이 다시 안됩니다.

나중에 SMTP서버를 구성할 생각은 있지만, 지금은 아닙니다. 그래서 2차 인증 방법을 꺼야 합니다. config에 다음 설정 값을 넣어줘야 합니다.
security": {  "staffDeviceVerification": false }

  1. Pod에서 현재 설정된 config 파일을 가져옵니다.
    1. kubectl cp -n ghost ghost-7686957632-dgfdx:/var/lib/ghost/config.production.json ./config.json
  2. config 파일로 configMap을 만들어서 설정을 추가합니다.
apiVersion: v1
kind: ConfigMap
metadata:
  name: ghost-config
  namespace: ghost
data:
  config.production.json: |
    {
      "url": "http://localhost:2368",
      "server": {
        "port": 2368,
        "host": "::"
      },
      "mail": {
        "transport": "Direct"
      },
      "logging": {
        "transports": [
          "file",
          "stdout"
        ]
      },
      "process": "systemd",
      "paths": {
        "contentPath": "/var/lib/ghost/content"
      },
      "security": {            # 추가할 부분
        "staffDeviceVerification": false
      }
    }
  1. config 설정
    1. pod에 ConfigMap을 넣어줍니다.
          volumeMounts:
            - name: ghost-content
              mountPath: /var/lib/ghost/content
            - name: config-volume                # 추가할 부분
              mountPath: /var/lib/ghost/config.production.json
              subPath: config.production.json
      volumes:
        - name: ghost-content
          persistentVolumeClaim:
            claimName: ghost-content-pvc
        - name: config-volume                    # 추가할 부분
          configMap:
            name: ghost-config

6.Ghost 설정

Ghost의 테마를 설정하거나 댓글 시스템을 추가하거나 하는 방법은 차후에 따로 포스팅을 하겠습니다.


Comments