6 min read

한 서버(Linux)에서 여러개의 사이트를 운영하기-1

한 서버(Linux)에서 여러개의 사이트를 운영하기-1
Photo by Scott Webb / Unsplash
💡
사전 작업- 도커 스웜 설치

도커(스웜)을 설치하는 이유

  1. 설정이 간편하다
    yml 문서만 생성해 주면 복잡한 명령어 없이도 한번에 설치가 간단하다
  2. 운영이 간편한다
    위에 한 설정으로 도커 볼륨과 DB를 백업하기가 편하다
  3. 오류가 적다
    도커 없이 설치하면 꼬이거나 에러생기는 일이 많고 생겨도 처리하기 어려운데 도커운영하면 오류가 적어 운영적인 면에서도 좋다.
도커 스웜 명령어
매니저 노드에서 일단 확인 docker node ls 워커에서 조인할 꺼 알려줌 docker swarm join-token worker 나는 미리 zero-tier로 vpn으로 다 묶어놓음. 그러면 스웜포트 별도 개방 안해도 됨. 그리고 zertier를 대역대로 방화벽도 개방해 놓음 (이건 별도 설명 필요함~) 그래서 이렇게 나옴. 요걸 복사해서 1. 도커 설치 2. 워커노드에 그대로 복사 그러면
💡
DB만들기- 한 yml에 해도 되는데 관리(백업)의 이유로...

DB는 RSYNC 백업시 오류가 생길 수 있다. 그래서 DUMP로 해줘야 한다. 그리고 각 컨테이너마다 별도로 만들어도 되겠지만 관리차원에서는 한개의 컨테이너에 DB를 넣고 관리 하면 용량면에서 효율적이다. 물론, 통합관리는 SPOF의 위험이 있지만 2중, 3중으로 백업해서 관리하면 그 리스크가 오히려 낮아진다.

mariaDB 복제(를 해야 하는 이유)
mariaDB는 운영중에 백업을 하려면 덤프를 뜨거나 DB를 멈추고 백업을 해야 한다. 1. 덤프를 백업을 하는데 백업받은 DB데이터를 실시간으로 덤프를 뜨기 어렵다 2. DB를 멈추고 백업을 한다는 것은 운영을 중단한다는 거랑 같다 그래서 replica를 해서 master-slave로 하여 slave에서 멈춤-백업을 일정기간 할 수 있다. mysql, mariaDB에서 하는 개념이다. 도커 스웜에서 master와 slave를
version: "3.8"

services:
  muas-ghost:
    #image: ghost:5.80.1       # 이 버전으로 하면 gmail app 비밀번호를 몰라도 됨
    image: ghost:latest        # 보안강화를 위해 메일 인증 필요
    environment:
      url: https://muas.facil.co.kr

      database__client: mysql                 
      database__connection__host: db_mysql           #dbnet이라는 도커 네트워크안에 격리
      database__connection__port: 3306               #외부에 열려있지 않음
      database__connection__user: ragoni             #DB 도커를 만들때
      database__connection__database: muas           #만든DB에서 생성해준DB명

    secrets:
      - mysql_password             #도커스웜으로 하면 비밀번호를 따로 관리해준다.

    # ✅ secret 파일을 읽어 password 문자열로 주입
    command: >
      sh -lc '
        export database__connection__password="$$(cat /run/secrets/mysql_password)";
        cp /custom/config.production.json /var/lib/ghost/config.production.json;
        docker-entrypoint.sh node current/index.js
      '

    volumes:
      - /docker/muas/content:/var/lib/ghost/content
      - /docker/muas/config.production.json:/custom/config.production.json
# 네임드 볼륨보다는 바인드 볼륨으로 해야 백업이 편하다.
    networks:
      - proxy           # caddy가 실행되는 네트워크
      - dbnet           # DB 컨테이너가 있는 네트워크

    deploy:
      replicas: 1
      placement:
        constraints:
          #- node.hostname == gram
          - node.role == worker

      labels:
        - "caddy=muas.facil.co.kr"
        - "caddy.reverse_proxy={{upstreams 2368}}"
        - "caddy.tls.dns=cloudflare"
        - "caddy.tls.ca=https://acme-v02.api.letsencrypt.org/directory"

  tech-ghost:          # 두번째 ghost CMS 컨테이너이다.
    image: ghost:latest

    environment:
      url: https://tech.facil.co.kr

      database__client: mysql
      database__connection__host: db_mysql
      database__connection__port: 3306
      database__connection__user: ragoni
      database__connection__database: tech

    secrets:
      - mysql_password

    command: >
      sh -lc '
        export database__connection__password="$$(cat /run/secrets/mysql_password)";
        cp /custom/config.production.json /var/lib/ghost/config.production.json;
        docker-entrypoint.sh node current/index.js
      '

    volumes:
      - /docker/tech/content:/var/lib/ghost/content
      - /docker/tech/config.production.json:/custom/config.production.json

    networks:
      - proxy
      - dbnet

    deploy:
      placement:
        constraints:
          #- node.hostname == gram
          - node.role == worker

      labels:
        - "caddy=tech.facil.co.kr"
        - "caddy.reverse_proxy={{upstreams 2368}}"
        - "caddy.tls.dns=cloudflare"
        - "caddy.tls.ca=https://acme-v02.api.letsencrypt.org/directory"

  blog-wordpress:                 # 워드프레스도 설치 가능하다.
    image: wordpress:latest
    environment:
      WORDPRESS_DB_HOST: db_mysql:3306
      WORDPRESS_DB_USER: ragoni
      WORDPRESS_DB_PASSWORD_FILE: /run/secrets/mysql_password
      WORDPRESS_DB_NAME: blog
      WORDPRESS_CONFIG_EXTRA: |
        define('WP_REDIS_HOST', 'redis');
        define('WP_REDIS_PORT', 6379);
        define('WP_REDIS_PREFIX', 'blog_');   # key prefix로 충돌 방지

    secrets:
      - mysql_password
    networks:
      - proxy
      - dbnet
    deploy:
      placement:
        constraints:
          - node.role == worker
          #- node.hostname == gram
      labels:
        - "traefik.enable=true"
        - "caddy=blog.facil.co.kr"
        - "caddy.reverse_proxy={{upstreams 80}}"
        - "caddy.tls.dns=cloudflare"
        - "caddy.tls.ca=https://acme-v02.api.letsencrypt.org/directory"

    volumes:
      - /docker/blog/:/var/www/html
      - /docker/word/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini


networks:
  proxy:
    external: true
  dbnet:
    external: true

secrets:
  mysql_password:
    external: true

이렇게 나는 2개의 고스트 1개의 워드프레스를 이용하고 있다. 둘다 CMS인데도 ghost backup 솔루션이 워낙 유명해서 CMS라고 표시하는 것이다.

이제 아래와 같이 DB를 만들어 주고 3306 포트를 열어 DBeaver로 데이터 베이스를 생성하는 방법과 CLI로 들어가는 방법이 있는데
나는 컨테이너로 DBeaver를 만들어 3306포트를 열지 않고 dbnet에서 작동하게 만들었다.

mariaDB 복제(를 해야 하는 이유)
mariaDB는 운영중에 백업을 하려면 덤프를 뜨거나 DB를 멈추고 백업을 해야 한다. 1. 덤프를 백업을 하는데 백업받은 DB데이터를 실시간으로 덤프를 뜨기 어렵다 2. DB를 멈추고 백업을 한다는 것은 운영을 중단한다는 거랑 같다 그래서 replica를 해서 master-slave로 하여 slave에서 멈춤-백업을 일정기간 할 수 있다. mysql, mariaDB에서 하는 개념이다. 도커 스웜에서 master와 slave를
version: "3.8"

services:
  cloudbeaver:
    image: dbeaver/cloudbeaver:latest
    networks:
      - proxy
      - dbnet
    volumes:
      # CloudBeaver workspace (설정/유저/연결정보 등 저장)
      - /docker/cloudb:/opt/cloudbeaver/workspace

    environment:
      - TZ=Asia/Seoul

    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          #- node.role == worker
          - node.hostname == gram
          #- node.hostname == pubuntu
      labels:
        # ✅ Caddy (caddy-docker-proxy) 라벨
        - "caddy=db.facil.co.kr"
        - "caddy.reverse_proxy={{upstreams 8978}}"

        # (선택) 압축
        - "caddy.encode=zstd gzip"

        # (선택) 기본 인증 걸고 싶으면 아래 1줄만 활성화
        - "caddy.basicauth=/* admin $2a$14$gGEAkKDX0IKljp./D0ySxOkbdwJoaI5KAx5BQbR1dS0XMtOYHqzVC"

networks:
  proxy:
    external: true
  dbnet:
    external: true

secrets:
  mysql_password:
    external: true
  mysql_root_password:
    external: true

위 mysql과 같은 네트워크에만 있으면 작동 되며 caddy로 연동만 해주면 DB관리가 용이하다.