Ghost on Docker Compose
3줄 요약
- 라즈베리파이4B 위에 Ghost CMS 올려서 블로그 운영중임.
- Nginx + Let’s Encrypt + Ghost ( Ghost 엔진 + MySQL)으로 https 블로그 만들었음.
- Docker Compose로 올렸음.
들어가며
지난 글에서는 그냥 간략하게 개요만 적은 거 같아, 어떻게 이 서비스를 올렸는지 좀 더 상세하게 적어보고 싶어서, 다시 한번 글을 쓰게되었다.
구축할 때 가장 메인 테스크는 다음과 같았다:
- 라즈베리파이 OS 설치 및 초기 설정
- 도메인 구매 및 연결
- 공유기 포트포워딩 설정
- Ghost CMS 및 Nginx 리버스 프록시 기반 서버 구축 (Docker Compose 활용)
1번부터 3번은 다른 블로그들에서도 잘 나와있어, 꼭 작성을 하지 않아도 될거 같다. 하지만, 이 Ghost를 Docker Compose로 올리는 방법에 대해서는 조금 설명을 하면 좋을 것 같아 이 글을 작성한다.
기존 예제
Ghost 공식 Docker 이미지는 Docker Hub에 잘 설명되어 있고, 기본적인 docker-compose.yml
예제도 제공하고 있다:
version: '3.1'
services:
ghost:
image: ghost:5-alpine
restart: always
ports:
- 8080:2368
environment:
# see <https://ghost.org/docs/config/#configuration-options>
database__client: mysql
database__connection__host: db
database__connection__user: root
database__connection__password: example
database__connection__database: ghost
# this url value is just an example, and is likely wrong for your environment!
url: <http://localhost:8080>
# contrary to the default mentioned in the linked documentation, this image defaults to NODE_ENV=production (so development mode needs to be explicitly specified if desired)
#NODE_ENV: development
volumes:
- ghost:/var/lib/ghost/content
db:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: example
volumes:
- db:/var/lib/mysql
volumes:
ghost:
db:
하지만, 이 예제를 그대로 실행하면 테스트가 불가능하다. Ghost 컨테이너가 MySQL 컨테이너가 완전히 준비되기 전에 시작되면서 접속이 실패가 된다.
또한, 실제 서비스를 운영하려면 HTTPS 설정도 해줘야하고, URL 세팅도 해줘야한다.
수정 방식
그렇기 때문에, 나는 다음과 같은 절차들을 수행했다:
- MySQL 컨테이너에 healthcheck를 설정해 Ghost가 DB 준비 이후에 실행되도록 설정
- 외부 접속을 위한 URL과 도메인 환경 변수 설정
- Nginx 리버스 프록시 + Let’s Encrypt 인증서를 사용해 HTTPS 접속 지원
그렇게 만들어진 결과는 다음과 같다:
version: '3.8'
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- certs:/etc/nginx/certs
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
networks:
- ghost-net
letsencrypt:
image: nginxproxy/acme-companion
container_name: nginx-letsencrypt
restart: always
depends_on:
- nginx-proxy
environment:
DEFAULT_EMAIL: ${DEFAULT_EMAIL}
NGINX_PROXY_CONTAINER: nginx-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- acme:/etc/acme.sh
networks:
- ghost-net
ghost:
image: ghost:5-alpine
container_name: ghost
restart: always
environment:
database__client: mysql
database__connection__host: ${GHOST_DB_HOST}
database__connection__user: ${GHOST_DB_USER}
database__connection__password: ${GHOST_DB_PASSWORD}
database__connection__database: ${GHOST_DB_NAME}
url: ${GHOST_URL}
VIRTUAL_HOST: ${VIRTUAL_HOST}
LETSENCRYPT_HOST: ${LETSENCRYPT_HOST}
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
volumes:
- ghost:/var/lib/ghost/content
- ./config.production.json:/var/lib/ghost/config.production.json
networks:
- ghost-net
db:
image: mysql:8.0
container_name: ghost-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
volumes:
- db:/var/lib/mysql
networks:
- ghost-net
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
ghost:
db:
certs:
vhost:
html:
acme:
networks:
ghost-net:
환경 변수들은 .env
에 처리해놨는데, 이런 식으로 사용하면 된다.
GHOST_DB_HOST=ghost-db
GHOST_DB_USER=root
GHOST_DB_PASSWORD=example
GHOST_DB_NAME=ghost
MYSQL_ROOT_PASSWORD=example
MYSQL_DATABASE=ghost
GHOST_URL=https://yourdomain.com
VIRTUAL_HOST=yourdomain.com
LETSENCRYPT_HOST=yourdomain.com
LETSENCRYPT_EMAIL=your@email.com
DEFAULT_EMAIL=your@email.com