웹사이트 검색

Ubuntu 22.04 LTS에서 Docker를 사용하여 Mastodon 소셜 네트워크를 설치하는 방법


이 튜토리얼은 다음 OS 버전에 대해 존재합니다.

  • Ubuntu 22.04(Jammy Jellyfish)
  • Ubuntu 18.04(Bionic Beaver)

이 페이지에서

  1. 전제 조건
  2. 1단계 - 방화벽 구성\n
  3. 2단계 - Docker 및 Docker Compose 설치
  4. 3단계 - 설치 준비
  5. 4단계 - Mastodon 설치
    1. 디렉토리 생성 및 소유권 설정
    2. 환경 생성 및 docker compose 파일\n
    3. 응용 프로그램 비밀 만들기\n
    4. Mastodon 환경 파일\n
    5. 마스토돈 준비

    1. Tootctl CLI 도구
    2. Mastodon 서비스 파일
    3. 검색 초기화\n
    4. 추가 도우미 서비스
    5. 마스토돈 액세스

    Mastodon은 무료 분산형 오픈 소스 소셜 네트워크입니다. Twitter의 대안으로 만들어졌습니다. Twitter와 마찬가지로 사람들은 서로를 팔로우하고 메시지, 이미지 및 비디오를 게시할 수 있습니다. 그러나 Twitter와 달리 콘텐츠에 대한 중앙 저장소나 권한이 없습니다.

    대신 Mastodon은 커뮤니티의 다양한 구성원이 각각 실행하는 수천 개의 서로 다른 서버에서 작동합니다. 한 서버에 등록된 사용자는 다른 네트워크의 사용자에게 쉽게 연결하고 인스턴스 간에 서로를 따를 수 있습니다.

    누구나 마스토돈 서버 인스턴스를 설치할 수 있습니다. 이 튜토리얼에서는 Docker를 사용하여 Ubuntu 22.04가 설치된 서버에서 Mastodon 인스턴스를 설정하는 방법을 설명합니다. Docker를 사용하면 컨테이너에 필요한 모든 패키지와 서비스를 포함하여 Mastodon을 쉽게 설치할 수 있습니다.

    전제 조건

    • A server running Ubuntu 22.04 with a minimum of 2 CPU cores and 2GB of memory. You will need to upgrade the server as per requirements.

    • A non-root user with sudo privileges.

    • A fully qualified domain name (FQDN) pointing to your server. For our purposes, we will use mastodon.example.com as the domain name.

    • Mastodon sends email notifications to users. We recommend you use a 3rd party Transactional mail service like Mailgun, Sendgrid, Amazon SES, or Sparkpost. The instructions in the guide will be using Amazon SES.

    • Make sure everything is updated.

      $ sudo apt update
      
    • Install basic utility packages. Some of them may already be installed.

      $ sudo apt install wget curl nano software-properties-common dirmngr apt-transport-https gnupg gnupg2 ca-certificates lsb-release ubuntu-keyring unzip -y
      

    1단계 - 방화벽 구성

    첫 번째 단계는 방화벽을 구성하는 것입니다. 우분투는 기본적으로 ufw(복잡하지 않은 방화벽)와 함께 제공됩니다.

    방화벽이 실행 중인지 확인하십시오.

    $ sudo ufw status
    

    다음 출력을 얻어야 합니다.

    Status: inactive
    

    방화벽이 활성화 시 현재 연결을 끊지 않도록 SSH 포트를 허용합니다.

    $ sudo ufw allow OpenSSH
    

    HTTP 및 HTTPS 포트도 허용합니다.

    $ sudo ufw allow http
    $ sudo ufw allow https
    

    방화벽 활성화

    $ sudo ufw enable
    Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
    Firewall is active and enabled on system startup
    

    방화벽의 상태를 다시 확인하십시오.

    $ sudo ufw status
    

    비슷한 출력이 표시되어야 합니다.

    Status: active
    
    To                         Action      From
    --                         ------      ----
    OpenSSH                    ALLOW       Anywhere
    80/tcp                     ALLOW       Anywhere
    443                        ALLOW       Anywhere
    OpenSSH (v6)               ALLOW       Anywhere (v6)
    80/tcp (v6)                ALLOW       Anywhere (v6)
    443 (v6)                   ALLOW       Anywhere (v6)
    

    2단계 - Docker 및 Docker Compose 설치

    Ubuntu 22.04는 이전 버전의 Docker와 함께 제공됩니다. 최신 버전을 설치하려면 먼저 Docker GPG 키를 가져옵니다.

    $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    

    Docker 리포지토리 파일을 만듭니다.

    $ echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    

    시스템 저장소 목록을 업데이트하십시오.

    $ sudo apt update
    

    최신 버전의 Docker를 설치합니다.

    $ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
    

    실행 중인지 확인합니다.

    $ sudo systemctl status docker
    ? docker.service - Docker Application Container Engine
         Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
         Active: active (running) since Fri 2022-12-30 15:55:17 UTC; 29min ago
    TriggeredBy: ? docker.socket
           Docs: https://docs.docker.com
       Main PID: 1966 (dockerd)
          Tasks: 8
         Memory: 20.8M
            CPU: 740ms
         CGroup: /system.slice/docker.service
                 ??1966 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
    

    기본적으로 Docker에는 루트 권한이 필요합니다. sudo 명령을 실행할 때마다 sudo를 사용하지 않으려면 docker 명령에 사용자 이름을 추가하세요. > 그룹.

    $ sudo usermod -aG docker $(whoami)
    

    이 변경 사항을 활성화하려면 서버에서 로그아웃하고 동일한 사용자로 다시 로그인하거나 다음 명령을 사용해야 합니다.

    $ su - ${USER}
    

    사용자가 Docker 그룹에 추가되었는지 확인합니다.

    $ groups
    navjot wheel docker
    

    3단계 - 설치 준비

    mmap 수의 기본 제한은 Elasticsearch에서 매우 낮습니다. 다음 명령을 실행하여 기본값을 확인하십시오.

    $ sysctl vm.max_map_count
    

    다음과 같은 결과가 표시됩니다.

    vm.max_map_count = 65530
    

    다음 명령을 사용하여 값을 늘립니다.

    $ echo "vm.max_map_count=262144" | sudo tee /etc/sysctl.d/90-max_map_count.conf
    $ sudo sysctl --load /etc/sysctl.d/90-max_map_count.conf
    

    4단계 - 마스토돈 설치

    디렉토리 생성 및 소유권 설정

    Mastodon 및 관련 서비스에 대한 디렉토리를 생성합니다.

    $ sudo mkdir -p /opt/mastodon/database/{postgresql,pgbackups,redis,elasticsearch}
    $ sudo mkdir -p /opt/mastodon/web/{public,system}
    $ sudo mkdir -p /opt/mastodon/branding
    

    Elasticsearch, 웹 및 백업 디렉터리에 적절한 소유권을 설정합니다.

    $ sudo chown 991:991 /opt/mastodon/web/{public,system}
    $ sudo chown 1000 /opt/mastodon/database/elasticsearch
    $ sudo chown 70:70 /opt/mastodon/database/pgbackups
    

    Mastodon 디렉터리로 전환합니다.

    $ cd /opt/mastodon
    

    환경 및 docker 작성 파일 만들기

    애플리케이션 및 데이터베이스에 대한 환경 파일을 생성합니다.

    $ sudo touch application.env database.env
    

    편집을 위해 Docker 작성 파일을 만들고 엽니다.

    $ sudo nano docker-compose.yml
    

    다음 코드를 붙여넣습니다.

    version: '3'
    
    services:
      postgresql:
        image: postgres:15-alpine
        env_file: database.env
        restart: always
        shm_size: 512mb
        healthcheck:
          test: ['CMD', 'pg_isready', '-U', 'postgres']
        volumes:
          - postgresql:/var/lib/postgresql/data
          - pgbackups:/backups
        networks:
          - internal_network
    
      redis:
        image: redis:7-alpine
        restart: always
        healthcheck:
          test: ['CMD', 'redis-cli', 'ping']
        volumes:
          - redis:/data
        networks:
          - internal_network
    
      redis-volatile:
        image: redis:7-alpine
        restart: always
        healthcheck:
          test: ['CMD', 'redis-cli', 'ping']
        networks:
          - internal_network
    
      elasticsearch:
        image: docker.elastic.co/elasticsearch/elasticsearch:7.17.7
        restart: always
        env_file: database.env
        environment:
          - cluster.name=elasticsearch-mastodon
          - discovery.type=single-node
          - bootstrap.memory_lock=true
          - xpack.security.enabled=true
          - ingest.geoip.downloader.enabled=false
          - "ES_JAVA_OPTS=-Xms512m -Xmx512m -Des.enforce.bootstrap.checks=true"
          - xpack.license.self_generated.type=basic
          - xpack.watcher.enabled=false
          - xpack.graph.enabled=false
          - xpack.ml.enabled=false
          - thread_pool.write.queue_size=1000
        ulimits:
          memlock:
            soft: -1
            hard: -1
          nofile:
            soft: 65536
            hard: 65536
        healthcheck:
          test: ["CMD-SHELL", "nc -z elasticsearch 9200"]
        volumes:
          - elasticsearch:/usr/share/elasticsearch/data
        networks:
          - internal_network
        ports:
          - '127.0.0.1:9200:9200'
    
      website:
        image: tootsuite/mastodon:v4.0.2
        env_file:
          - application.env
          - database.env
        command: bash -c "bundle exec rails s -p 3000"
        restart: always
        depends_on:
          - postgresql
          - redis
          - redis-volatile
          - elasticsearch
        ports:
          - '127.0.0.1:3000:3000'
        networks:
          - internal_network
          - external_network
        healthcheck:
          test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1']
        volumes:
          - uploads:/mastodon/public/system
    
      shell:
        image: tootsuite/mastodon:v4.0.2
        env_file:
          - application.env
          - database.env
        command: /bin/bash
        restart: "no"
        networks:
          - internal_network
          - external_network
        volumes:
          - uploads:/mastodon/public/system
          - static:/static
    
      streaming:
        image: tootsuite/mastodon:v4.0.2
        env_file:
          - application.env
          - database.env
        command: node ./streaming
        restart: always
        depends_on:
          - postgresql
          - redis
          - redis-volatile
          - elasticsearch
        ports:
          - '127.0.0.1:4000:4000'
        networks:
          - internal_network
          - external_network
        healthcheck:
          test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1']
    
      sidekiq:
        image: tootsuite/mastodon:v4.0.2
        env_file:
          - application.env
          - database.env
        command: bundle exec sidekiq
        restart: always
        depends_on:
          - postgresql
          - redis
          - redis-volatile
          - website
        networks:
          - internal_network
          - external_network
        healthcheck:
          test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"]
        volumes:
          - uploads:/mastodon/public/system
    
    networks:
      external_network:
      internal_network:
      	 internal:true
    
    volumes:
      postgresql:
        driver_opts:
          type: none
          device: /opt/mastodon/database/postgresql
          o: bind
      pgbackups:
        driver_opts:
          type: none
          device: /opt/mastodon/database/pgbackups
          o: bind
      redis:
        driver_opts:
          type: none
          device: /opt/mastodon/database/redis
          o: bind
      elasticsearch:
        driver_opts:
          type: none
          device: /opt/mastodon/database/elasticsearch
          o: bind
      uploads:
        driver_opts:
          type: none
          device: /opt/mastodon/web/system
          o: bind
      static:
        driver_opts:
          type: none
          device: /opt/mastodon/web/public
          o: bind
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    튜토리얼 작성 시점에 사용 가능한 Mastodon의 최신 버전은 v4.0.2입니다. Mastodon GitHub Releases 페이지를 확인하고 Docker compose 파일의 버전을 적절하게 조정하십시오. 최신 버전의 PostgreSQL과 Redis도 사용하고 있습니다. 요구 사항에 따라 조정할 수 있습니다. 현재 Elasticsearch 7.x를 사용하고 있습니다. Docker Hub 페이지에서 따를 수 있는 Elasticsearch의 주 버전이 없으므로 Java와 관련된 보안 업데이트를 위해 수동으로 계속 업데이트해야 합니다.

    애플리케이션 비밀 만들기

    다음 단계는 애플리케이션 비밀 값을 만드는 것입니다.

    다음 명령을 두 번 실행하여 SECRET_KEY_BASEOTP_SECRET 값을 생성합니다. 처음에는 이미지를 가져오기 때문에 시간이 좀 걸립니다.

    $ docker compose run --rm shell bundle exec rake secret
    

    동일한 용도로 openssl 유틸리티를 사용할 수도 있습니다.

    $ openssl rand -hex 64
    

    다음 명령을 사용하여 VAPID_PRIVATE_KEYVAPID_PUBLIC_KEY 값을 생성합니다.

    $ docker compose run --rm shell bundle exec rake mastodon:webpush:generate_vapid_key 
    

    비슷한 출력을 얻을 수 있습니다.

    VAPID_PRIVATE_KEY=u2qsCs5JdmdmMLnUuU0sgmFGvZedteJz-lFB_xF4_ac=
    VAPID_PUBLIC_KEY=BJXjE2hIXvFpo6dnHqyf1i-2PcP-cBoL95UCmhhxwlAgtFw_vnrYp4GBneR7_cmI9LZUYjHFh-TBAPSb9WTqH9A=
    

    PostgreSQL 및 Elasticsearch 암호를 생성하려면 openssl 유틸리티를 사용하십시오.

    $ openssl rand -hex 15
    

    마스토돈 환경 파일

    편집을 위해 application.env 파일을 엽니다.

    $ sudo nano application.env
    

    다음 줄을 붙여넣습니다.

    # environment
    RAILS_ENV=production
    NODE_ENV=production
    
    # domain
    LOCAL_DOMAIN=mastodon.example.com
    
    # redirect to the first profile
    SINGLE_USER_MODE=false
    
    # do not serve static files
    RAILS_SERVE_STATIC_FILES=false
    
    # concurrency
    WEB_CONCURRENCY=2
    MAX_THREADS=5
    
    # pgbouncer
    #PREPARED_STATEMENTS=false
    
    # locale
    DEFAULT_LOCALE=en
    
    # email, not used
    SMTP_SERVER=email-smtp.us-west-2.amazonaws.com
    SMTP_PORT=587
    SMTP_LOGIN=AES_USER
    SMTP_PASSWORD=AES_PWD
    
    
    # secrets
    SECRET_KEY_BASE=c09fa403575e0b431e54a2e228f20cd5a5fdfdbba0da80598959753b829a4e3c0266eedbac7e3cdf9f3345db36c56302c0e1bc5bfc8c5d516be59a2c41de7e37
    OTP_SECRET=febb7dbb0d3308094083733fc923a430e52ccec767d48d7d2e0c577bfcb6863dbdfc920b1004b1f8c2967b9866bd7a0b4a15460f9fc7687aa4a42acf54e5a3d4
    
    # Changing VAPID keys will break push notifications
    VAPID_PRIVATE_KEY=13RgrfOY2tkwuUycylDPOkoHennkJ0ZAPV_fUwDy7-g=
    VAPID_PUBLIC_KEY=BDAQuGwPbh1kbCV904adYXHvz9lLRaJHkiQkihRDPyBn3QmkAYbR21WHYoP8TkyG6dylG6IXpEVfLwdoW7fJVns=
    
    # IP and session retention
    # -----------------------
    # Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
    # to be less than daily if you lower IP_RETENTION_PERIOD below two days (172800).
    # -----------------------
    IP_RETENTION_PERIOD=2592000
    SESSION_RETENTION_PERIOD=2592000
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    Amazon SES 메일링 서비스를 활성화했습니다. 필요하지 않은 경우 섹션을 삭제할 수 있습니다. 기본적으로 Mastodon은 1년 동안 IP 주소를 유지하지만 30일(2592000초)로 변경했습니다. 요구 사항에 따라 변경할 수 있습니다. 2일 이상 보관해야 합니다. 그렇지 않으면 튜토리얼 범위를 벗어나는 약간의 수정 작업이 필요합니다.

    편집을 위해 database.env 파일을 엽니다.

    $ sudo nano database.env
    

    다음 줄을 붙여넣습니다.

    # postgresql configuration
    POSTGRES_USER=mastodon
    POSTGRES_DB=mastodon
    POSTGRES_PASSWORD=15ff12dcb93aa60680d2aadb4032ee
    PGPASSWORD=15ff12dcb93aa60680d2aadb4032ee
    PGPORT=5432
    PGHOST=postgresql
    PGUSER=mastodon
    
    # pgbouncer configuration
    #POOL_MODE=transaction
    #ADMIN_USERS=postgres,mastodon
    #DATABASE_URL="postgres://mastodon::5432/mastodon"
    
    # elasticsearch
    ES_JAVA_OPTS=-Xms512m -Xmx512m
    ELASTIC_PASSWORD=13382e99f6b2d4dc7f3d66e4b9872d
    
    # mastodon database configuration
    #DB_HOST=pgbouncer
    DB_HOST=postgresql
    DB_USER=mastodon
    DB_NAME=mastodon
    DB_PASS=15ff12dcb93aa60680d2aadb4032ee
    DB_PORT=5432
    
    REDIS_HOST=redis
    REDIS_PORT=6379
    
    CACHE_REDIS_HOST=redis-volatile
    CACHE_REDIS_PORT=6379
    
    ES_ENABLED=true
    ES_HOST=elasticsearch
    ES_PORT=9200
    ES_USER=elastic
    ES_PASS=13382e99f6b2d4dc7f3d66e4b9872d
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    마스토돈 준비하기

    Nginx에서 제공할 정적 파일을 준비합니다. 이 단계는 Docker가 처음으로 모든 이미지를 가져오기 때문에 다소 시간이 걸립니다.

    $ docker compose run --rm shell bash -c "cp -r /opt/mastodon/public/* /static/"
    

    데이터 계층을 불러옵니다.

    $ docker compose up -d postgresql redis redis-volatile
    

    컨테이너의 상태를 확인합니다.

    $ watch docker compose ps
    

    실행 중(정상)을 기다린 후 Ctrl + C를 누르고 다음 명령을 사용하여 데이터베이스를 초기화합니다.

    $ docker compose run --rm shell bundle exec rake db:setup
    

    5단계 - Nginx 설치

    Ubuntu 22.04는 이전 버전의 Nginx와 함께 제공됩니다. 최신 버전을 설치하려면 공식 Nginx 저장소를 다운로드해야 합니다.

    Nginxs 서명 키를 가져옵니다.

    $ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    	| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
    

    Nginxs 안정 버전용 리포지토리를 추가합니다.

    $ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] \
    http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
        | sudo tee /etc/apt/sources.list.d/nginx.list
    

    시스템 리포지토리를 업데이트합니다.

    $ sudo apt update
    

    Nginx를 설치합니다.

    $ sudo apt install nginx
    

    설치를 확인하십시오.

    $ nginx -v
    nginx version: nginx/1.22.1
    

    Nginx 서버를 시작합니다.

    $ sudo systemctl start nginx
    

    서버의 상태를 확인하십시오.

    $ sudo systemctl status nginx
    ? nginx.service - nginx - high performance web server
         Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
         Active: active (running) since Sat 2022-12-31 02:23:12 UTC; 6s ago
           Docs: https://nginx.org/en/docs/
        Process: 22129 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
       Main PID: 22130 (nginx)
          Tasks: 3 (limit: 4575)
         Memory: 2.5M
            CPU: 17ms
         CGroup: /system.slice/nginx.service
                 ??22130 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
                 ??22131 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
                 ??22132 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
    

    6단계 - SSL 설치

    SSL 인증서를 생성하려면 Certbot을 설치해야 합니다. Ubuntus 저장소를 사용하여 Certbot을 설치하거나 Snapd 도구를 사용하여 최신 버전을 가져올 수 있습니다. 우리는 Snapd 버전을 사용할 것입니다.

    Ubuntu 22.04는 기본적으로 Snapd가 설치된 상태로 제공됩니다. 다음 명령을 실행하여 Snapd 버전이 최신인지 확인하십시오. Snapd 버전이 최신인지 확인하세요.

    $ sudo snap install core
    $ sudo snap refresh core
    

    Certbot을 설치합니다.

    $ sudo snap install --classic certbot
    

    다음 명령을 사용하여 /usr/bin 디렉터리에 대한 심볼릭 링크를 생성하여 Certbot 명령이 실행되도록 합니다.

    $ sudo ln -s /snap/bin/certbot /usr/bin/certbot
    

    다음 명령을 실행하여 SSL 인증서를 생성합니다.

    $ sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m  -d mastodon.example.com
    

    위의 명령은 서버의 /etc/letsencrypt/live/mastodon.example.com 디렉토리에 인증서를 다운로드합니다.

    Diffie-Hellman 그룹 인증서를 생성합니다.

    $ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096
    

    SSL 갱신이 제대로 작동하는지 확인하려면 프로세스를 시험 실행하십시오.

    $ sudo certbot renew --dry-run
    

    오류가 표시되지 않으면 모든 설정이 완료된 것입니다. 인증서가 자동으로 갱신됩니다.

    7단계 - Nginx 구성

    편집을 위해 /etc/nginx/nginx.conf 파일을 엽니다.

    $ sudo nano /etc/nginx/nginx.conf
    

    include /etc/nginx/conf.d/*.conf; 줄 앞에 다음 줄을 추가합니다.

    server_names_hash_bucket_size  64;
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    편집을 위해 /etc/nginx/conf.d/mastodon.conf 파일을 만들고 엽니다.

    $ sudo nano /etc/nginx/conf.d/mastodon.conf
    

    다음 코드를 붙여넣습니다.

    map $http_upgrade $connection_upgrade {
      default upgrade;
      ''      close;
    }
    
    upstream backend {
        server 127.0.0.1:3000 fail_timeout=0;
    }
    
    upstream streaming {
        server 127.0.0.1:4000 fail_timeout=0;
    }
    
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g;
    
    server {
      listen 80 default_server;
      server_name mastodon.example.com;
      location / { return 301 https://$host$request_uri; }
    }
    
    server {
       listen 443 ssl http2;
       server_name mastodon.example.com;
       
       access_log  /var/log/nginx/mastodon.access.log;
       error_log   /var/log/nginx/mastodon.error.log;
    
       http2_push_preload on; # Enable HTTP/2 Server Push
    
       ssl_certificate /etc/letsencrypt/live/mastodon.example.com/fullchain.pem;
       ssl_certificate_key /etc/letsencrypt/live/mastodon.example.com/privkey.pem;
       ssl_trusted_certificate /etc/letsencrypt/live/mastodon.example.com/chain.pem;
       ssl_session_timeout 1d;
    
       # Enable TLS versions (TLSv1.3 is required upcoming HTTP/3 QUIC).
       ssl_protocols TLSv1.2 TLSv1.3;
    
       # Enable TLSv1.3's 0-RTT. Use $ssl_early_data when reverse proxying to
       # prevent replay attacks.
       #
       # @see: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data
       ssl_early_data on;
    
       ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384';
       ssl_prefer_server_ciphers on;
       ssl_session_cache shared:SSL:10m;
       ssl_session_tickets off;
       
       keepalive_timeout    70;
       sendfile             on;
       client_max_body_size 80m;
    
       # OCSP Stapling ---
       # fetch OCSP records from URL in ssl_certificate and cache them
       ssl_stapling on;
       ssl_stapling_verify on;
       ssl_dhparam /etc/ssl/certs/dhparam.pem;
    
       add_header X-Early-Data $tls1_3_early_data;
       
       root /opt/mastodon/web/public;
       
       gzip on;
       gzip_disable "msie6";
       gzip_vary on;
       gzip_proxied any;
       gzip_comp_level 6;
       gzip_buffers 16 8k;
       gzip_http_version 1.1;
       gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon;
       
       add_header Strict-Transport-Security "max-age=31536000" always;
    
      location / {
        try_files $uri @proxy;
      }
    
      location ~ ^/(system/accounts/avatars|system/media_attachments/files) {
        add_header Cache-Control "public, max-age=31536000, immutable";
        add_header Strict-Transport-Security "max-age=31536000" always;
        root /opt/mastodon/;
        try_files $uri @proxy;
      }
    
      location ~ ^/(emoji|packs) {
        add_header Cache-Control "public, max-age=31536000, immutable";
        add_header Strict-Transport-Security "max-age=31536000" always;
        try_files $uri @proxy;
      }
    
      location /sw.js {
        add_header Cache-Control "public, max-age=0";
        add_header Strict-Transport-Security "max-age=31536000" always;
        try_files $uri @proxy;
      }
    
      location @proxy {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Proxy "";
        proxy_pass_header Server;
    
        proxy_pass http://backend;
        proxy_buffering on;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    
        proxy_cache CACHE;
        proxy_cache_valid 200 7d;
        proxy_cache_valid 410 24h;
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        add_header X-Cached $upstream_cache_status;
        add_header Strict-Transport-Security "max-age=31536000" always;
    
        tcp_nodelay on;
      }
    
      location /api/v1/streaming {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Proxy "";
    
        proxy_pass http://streaming;
        proxy_buffering off;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    
        tcp_nodelay on;
      }
    
      error_page 500 501 502 503 504 /500.html;
    }
    
    # This block is useful for debugging TLS v1.3. Please feel free to remove this
    # and use the `$ssl_early_data` variable exposed by NGINX directly should you
    # wish to do so.
    map $ssl_early_data $tls1_3_early_data {
      "~." $ssl_early_data;
      default "";
    }
    

    완료되면 Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    Nginx 구성 파일 구문을 확인합니다.

    $ sudo nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
    

    Nginx 서버를 다시 시작합니다.

    $ sudo systemctl restart nginx
    

    8단계 - 마스토돈 시작

    Tootctl CLI 도구

    Tootctl CLI 도구는 Mastodon에서 관리 작업을 수행하는 데 사용됩니다. 호스트 셸에서 액세스할 수 있도록 해야 합니다.

    /usr/local/bin/tootctl 파일을 만들고 편집을 위해 엽니다.

    $ sudo nano /usr/local/bin/tootctl
    

    다음 코드를 붙여넣습니다.

    #!/bin/bash
    docker compose -f /opt/mastodon/docker-compose.yml run --rm shell tootctl ""
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    파일 실행 권한을 부여하십시오.

    $ sudo chmod +x /usr/local/bin/tootctl
    

    마스토돈 서비스 파일

    Docker 작성 명령을 사용하여 Mastodon 컨테이너를 시작할 수 있지만 시스템 단위 파일을 통해 더 쉽게 수행할 수 있습니다.

    편집을 위해 Mastodon 서비스 파일을 생성하고 엽니다.

    $ sudo nano /etc/systemd/system/mastodon.service
    

    다음 코드를 붙여넣습니다.

    [Unit]
    Description=Mastodon service
    After=docker.service
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    
    WorkingDirectory=/opt/mastodon
    ExecStart=/usr/bin/docker compose -f /opt/mastodon/docker-compose.yml up -d
    ExecStop=/usr/bin/docker compose -f /opt/mastodon/docker-compose.yml down
    
    [Install]
    WantedBy=multi-user.target
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    시스템 데몬을 다시 로드하여 서비스 파일을 시작합니다.

    $ sudo systemctl daemon-reload
    

    Mastodon 서비스를 활성화하고 시작합니다.

    $ sudo systemctl enable --now mastodon.service
    

    Docker 컨테이너의 상태를 확인합니다.

    $ watch docker compose -f /opt/mastodon/docker-compose.yml ps
    

    컨테이너 상태가 실행 중(정상)으로 변경되면 Ctrl + C를 눌러 화면을 종료합니다.

    Mastodon에 대한 관리 사용자를 생성하고 제공된 암호를 기록해 둡니다.

    $ tootctl accounts create navjot --email  --confirmed --role Owner
    OK
    New password: 1338afbe1b4e06e823b6625da80cb537
    

    사용자 등록을 종료하려면 다음 명령을 사용하십시오.

    $ tootctl settings registrations close
    

    등록을 다시 열려면 다음 명령을 실행하십시오.

    $ tootctl settings registrations open
    

    검색 초기화

    Elasticsearch 인덱스를 생성하고 채우기 전에 툿트를 만들어야 합니다. 툿을 만든 후 다음 명령을 내립니다.

    $ tootctl search deploy
    

    다음과 같은 오류가 발생할 수 있습니다.

    /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/progress.rb:76:in `total=': You can't set the item's total value to less than the current progress. (ProgressBar::InvalidProgressError)
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/base.rb:178:in `block in update_progress'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/output.rb:43:in `with_refresh'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/base.rb:177:in `update_progress'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/base.rb:101:in `total='
            from /opt/mastodon/lib/mastodon/search_cli.rb:67:in `deploy'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/command.rb:27:in `run'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/invocation.rb:116:in `invoke'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor.rb:243:in `block in subcommand'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/command.rb:27:in `run'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch'
            from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/base.rb:485:in `start'
            from /opt/mastodon/bin/tootctl:8:in `<main>'
    

    이 경우 웹 사이트 컨테이너 셸을 입력합니다.

    $ docker exec -it mastodon-website-1 /bin/bash
    

    다음 명령을 실행합니다.

    $ sed -E '/progress.total = /d' -i lib/mastodon/search_cli.rb
    

    컨테이너 셸을 종료합니다.

    $ exit
    

    Elasticsearch 배포 명령을 다시 실행합니다. 때로는 명령이 나중에 작동할 수도 있습니다. 이것은 Mastodon에서 진행 중인 문제이므로 현재로서는 명확한 수정 사항이 없습니다.

    $ tootctl search deploy
    

    추가 도우미 서비스

    다운로드한 미디어 파일을 제거하기 위한 다른 서비스를 만들어 보겠습니다.

    편집을 위해 Mastodon 미디어 제거 서비스를 생성하고 엽니다.

    $ sudo nano /etc/systemd/system/mastodon-media-remove.service
    

    다음 코드를 붙여넣습니다.

    [Unit]
    Description=Mastodon - media remove service
    Wants=mastodon-media-remove.timer
    
    [Service]
    Type=oneshot
    StandardError=null
    StandardOutput=null
    
    WorkingDirectory=/opt/mastodon
    ExecStart=/usr/bin/docker compose -f /opt/mastodon/docker-compose.yml run --rm shell tootctl media remove
    
    [Install]
    WantedBy=multi-user.target
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    미디어 제거를 예약하려면 타이머 서비스를 설정할 수 있습니다.

    $ sudo nano /etc/systemd/system/mastodon-media-remove.timer
    

    다음 코드를 붙여넣습니다.

    [Unit]
    Description=Schedule a media remove every week
    
    [Timer]
    Persistent=true
    OnCalendar=Sat *-*-* 00:00:00
    Unit=mastodon-media-remove.service
    
    [Install]
    WantedBy=timers.target
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    OpenGraph 태그를 사용하여 생성된 리치 미리보기 카드를 제거하도록 다른 서비스를 설정할 수 있습니다.

    $ sudo nano /etc/systemd/system/mastodon-preview_cards-remove.service
    

    다음 코드를 붙여넣습니다.

    [Unit]
    Description=Mastodon - preview cards remove service
    Wants=mastodon-preview_cards-remove.timer
    
    [Service]
    Type=oneshot
    StandardError=null
    StandardOutput=null
    
    WorkingDirectory=/opt/mastodon
    ExecStart=/usr/bin/docker compose -f /opt/mastodon/docker-compose.yml run --rm shell tootctl preview_cards remove
    
    [Install]
    WantedBy=multi-user.target
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    해당 타이머 서비스를 설정합니다.

    $ sudo nano /etc/systemd/system/mastodon-preview_cards-remove.timer
    

    다음 코드를 붙여넣습니다.

    [Unit]
    Description=Schedule a preview cards remove every week
    
    [Timer]
    Persistent=true
    OnCalendar=Sat *-*-* 00:00:00
    Unit=mastodon-preview_cards-remove.service
    
    [Install]
    WantedBy=timers.target
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    시스템 데몬을 다시 로드합니다.

    $ sudo systemctl daemon-reload
    

    타이머를 활성화하고 시작합니다.

    $ sudo systemctl enable --now mastodon-preview_cards-remove.timer
    $ sudo systemctl enable --now mastodon-media-remove.timer
    

    모든 타이머를 나열하여 Mastodon 서비스 일정을 확인하십시오.

    $ systemctl list-timers
    .....
    Sat 2023-01-07 00:00:00 UTC 6 days left    n/a                         n/a                mastodon-media-remove.timer         mastodon-media-remove.service
    Sat 2023-01-07 00:00:00 UTC 6 days left    n/a                         n/a                mastodon-preview_cards-remove.timer mastodon-preview_cards-remove.service
    

    액세스 마스토돈

    URL https://mastodon.example.com을 방문하여 인스턴스에 액세스하면 유사한 페이지가 표시됩니다.

    위의 스크린샷에서 2명의 사용자가 있고 그 중 1명(나)이 관리자로 설정되어 있는 것을 볼 수 있습니다. 일반적으로 그렇지 않습니다. 관리자 계정을 생성해도 최초 실행시 메인페이지에 노출되지 않습니다. 그렇게 하려면 인스턴스에 로그인하면 다음 페이지로 이동합니다.

    오른쪽 사이드바에서 기본 설정 옵션을 클릭하여 설정에 액세스합니다. 여기에서 왼쪽 메뉴의 관리 옵션을 클릭하여 Mastodons 관리 패널에 액세스합니다.

    왼쪽 사이드바에서 사이트 설정 옵션을 클릭합니다.

    여기에서 서버 홈페이지에 반영될 연락처 사용자 이름과 비즈니스 이메일을 입력하십시오. 또한 서버 설명, 로고 및 서버 규칙을 포함한 다양한 기타 정보를 입력하여 Mastodon 인스턴스를 사용자 정의하십시오.

    9단계 - 마스토돈 유지 관리

    Mastodon 인스턴스의 성능과 로그를 보려면 https://mastodon.example.com/sidekiq/로 이동하세요.

    여기에서 Mastodon 인스턴스와 관련된 다양한 프로세스 및 예약된 작업 목록을 볼 수 있습니다. Dead 또는 Retries 섹션에서 실패한 작업을 확인할 수도 있습니다. 또한 인스턴스의 메모리 사용량도 알려줍니다.

    https://mastodon.example.com/pghero/에서 인스턴스 데이터베이스의 상태를 확인할 수 있습니다.

    데이터베이스 유지 관리를 수행하고, SQL 쿼리를 실행하고, 사용하지 않는 인덱스를 제거할 수 있습니다. 쿼리 통계를 활성화하려면 위 페이지에서 활성화 버튼을 클릭하면 다음 정보가 표시됩니다.

    루트 사용자로 전환합니다.

    $ sudo -i su
    

    /opt/mastodon/database/postgresql 디렉터리로 전환합니다.

    $ cd /opt/mastodon/database/postgresql
    

    postgresql.conf 파일을 엽니다.

    $ nano postgresql.conf
    

    #shared_preload_libraries=# (change wants restart) 줄을 찾아 다음으로 바꿉니다.

    shared_preload_libraries = 'pg_stat_statements'
    

    파일 끝에 다음 줄을 추가합니다.

    pg_stat_statements.track = all
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    Mastodon 컨테이너를 다시 시작합니다.

    $ systemctl restart mastodon.service
    

    루트 쉘을 종료하십시오.

    $ exit
    

    데이터베이스 상태 페이지를 확인하면 지금 느린 쿼리가 있는지 확인할 수 있습니다.

    참고: 기본 설정 메뉴에서 PgHero 및 Sidekiq URL을 실행할 수도 있습니다.

    어떤 이유로 사이트가 로드되지 않으면 Docker에서 생성된 로그를 확인할 수 있습니다.

    $ docker logs <container-name>
    

    10단계 - 마스토돈 백업

    Mastodon을 백업하기 위해 Restic이라는 타사 도구를 사용합니다. Restic을 사용하여 백업하는 첫 번째 단계는 모든 파일과 디렉토리를 저장소 목록에 추가하는 것입니다.

    편집을 위해 리포지토리 목록 파일을 만들고 엽니다.

    $ sudo nano /opt/mastodon/backup-files
    

    다음 줄을 붙여넣습니다.

    /etc/nginx
    /etc/letsencrypt
    /etc/systemd/system
    /root
    /opt/mastodon/database/pgbackups
    /opt/mastodon/*.env
    /opt/mastodon/docker-compose.yml
    /opt/mastodon/branding
    /opt/mastodon/database/redis
    /opt/mastodon/web/system
    /opt/mastodon/backup-files
    /opt/mastodon/mastodon-backup
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    레스틱을 설치합니다.

    $ sudo apt install restic
    

    백업 리포지토리를 만들고 초기 백업을 만듭니다. 데이터를 S3 서비스에 백업하고 있습니다.

    $ restic -r s3:https://$SERVER:$PORT/mybucket init
    $ restic -r s3:https://$SERVER:$PORT/mybucket backup $(cat /opt/mastodon/backup-files) --exclude  /opt/mastodon/database/postgresql
    

    Mastodon 백업 서비스 타이머를 생성하고 편집을 위해 엽니다.

    $ sudo nano /etc/systemd/system/mastodon-backup.timer
    

    다음 코드를 붙여넣습니다.

    [Unit]
    Description=Schedule a mastodon backup every hour
    
    [Timer]
    Persistent=true
    OnCalendar=*:00:00
    Unit=mastodon-backup.service
    
    [Install]
    WantedBy=timers.target
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    Mastodon 백업 서비스 파일을 생성하고 편집을 위해 엽니다.

    $ sudo nano /etc/systemd/system/mastodon-backup.service
    

    다음 코드를 붙여넣습니다.

    [Unit]
    Description=Mastodon - backup service
    # Without this, they can run at the same time and race to docker compose,
    # double-creating networks and failing due to ambiguous network definition
    # requiring `docker network prune` and restarting
    After=mastodon.service
    
    [Service]
    Type=oneshot
    StandardError=file:/var/log/mastodon-backup.err
    StandardOutput=file:/var/log/mastodon-backup.log
    
    WorkingDirectory=/opt/mastodon
    ExecStart=/bin/bash /opt/mastodon/mastodon-backup
    
    [Install]
    WantedBy=multi-user.target
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    다음으로 편집을 위해 /opt/mastodon/mastodon-backup 파일을 만들고 엽니다. 여기에는 실제 백업 명령이 포함됩니다.

    $ sudo nano /opt/mastodon/mastodon-backup
    

    다음 코드를 붙여넣습니다.

    #!/bin/bash
    
    set -e
    
    AWS_ACCESS_KEY_ID=
    AWS_SECRET_ACCESS_KEY=
    SERVER=
    PORT=
    RESTIC_PASSWORD_FILE=/root/restic-pasword
    
    docker compose -f /opt/mastodon/docker-compose.yml run --rm postgresql sh -c "pg_dump -Fp  mastodon | gzip > /backups/dump.sql.gz"
    restic -r s3:https://$SERVER:$PORT/mybucket --cache-dir=/root backup $(cat /opt/mastodon/backup-files) --exclude  /opt/mastodon/database/postgresql
    restic -r s3:https://$SERVER:$PORT/mybucket --cache-dir=/root forget --prune --keep-hourly 24 --keep-daily 7 --keep-monthly 3
    

    Ctrl + X를 누르고 메시지가 표시되면 Y를 입력하여 파일을 저장합니다.

    백업 스크립트에 실행 권한을 부여하십시오.

    $ sudo chmod +x /opt/mastodon/mastodon-backup
    

    서비스 데몬을 다시 로드하고 백업 서비스 및 타이머를 시작합니다.

    $ sudo systemctl daemon-reload
    $ sudo systemctl enable --now mastodon-backup.service
    $ sudo systemctl enable --now mastodon-backup.timer
    

    다음 명령을 사용하여 시간별 백업이 발생하고 액세스 가능한지 확인합니다.

    $ restic -r s3:https://$SERVER:$PORT/mybucket snapshots
    $ restic -r s3:https://$SERVER:$PORT/mybucket mount /mnt
    

    11단계 - 마스토돈 업그레이드

    Mastodon을 업그레이드하려면 여러 단계가 필요합니다. 먼저 디렉터리로 전환합니다.

    $ cd /opt/mastodon
    

    Mastodon의 최신 컨테이너 이미지를 가져옵니다.

    $ docker compose pull
    

    원하는 경우 docker-compose.yml을 변경합니다.

    모든 데이터베이스 마이그레이션을 수행합니다.

    $ docker compose run --rm shell bundle exec rake db:migrate
    

    정적 파일의 사본을 업데이트하십시오.

    $ docker compose run --rm shell bash -c "cp -r /opt/mastodon/public/* /static/"
    

    Mastodon 컨테이너를 다시 시작합니다.

    $ sudo systemctl restart mastodon.service
    

    위의 지침은 일반적인 업데이트 지침입니다. 항상 Mastodon의 GitHub 릴리스 페이지를 확인하여 모든 것이 원활하게 진행되도록 버전 간의 특정 업데이트 작업 및 명령을 찾습니다.

    결론

    이것으로 Ubuntu 22.04 서버에서 Docker를 사용하여 Mastodon 소셜 네트워크를 설치하는 방법에 대한 자습서를 마칩니다. 질문이 있으시면 아래 의견에 게시하십시오.