69
54

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

クラウドワークスAdvent Calendar 2019

Day 23

リアルタイム共同編集ツール『HackMD』をオンプレ運用して、好きな機能を追加して幸せになった話

Last updated at Posted at 2019-12-22

はじめに

最近、ふと自分の寝顔が気になるので録画して観てみたのですが、
顎が完全に無くなっており完全にピピ美ちゃん状態になっていた @k-waragai です。
痩せようと決意した12月でした。

さて、みなさんは仕事中に作業ログなどを残していますか?

自分は言語化することで見落としが無いことを確認したり他の人へ共有する目的としてメモをしています。

実装する中で分かった事実や背景、バグなどをメモとしてまとめながら進めることで
PR 作成時の Description を作る際にも役に立ち時短にも繋がっています。
ペッって貼り付けるだけで済んだりするので楽

メモツールとして有名なものを上げると

あたりを使っている人が多いのではないでしょうか?

その中でも今回は 『 HackMD 』 に目を付けて
オンプレ運用してみたら好きな機能を追加出来て幸せになった話を書いていこうかなと思います。
(全面的に「HackMD」最高だからみんな使おうぜっていう記事です)

なぜHackMDがいいの?比較してみた

メモツールとして有名なものを比較してみると DocBase と HackMD がいい感じということが分かりました。
※ あくまで個人的な感想を元にまとめています

サービス名 共同編集 画像upload マークダウン メモ単位のロール制限機能 料金 個人的な好み
Evernote × - ベーシック: 無料
プラス: 年額3,100円
×
HackMD 個人利用: 無料
エンタープライズ: 要相談
(人数にもよりますが1人500円程)
Love
Qiita Team × Micro: 月額1,520円
Extra: 月額15,300円
DocBase スターター: 月額900円
プレミアム: 月額19,500円

個人的に ドキュメントとして利用するには、Qiita Team もしくは DocBase がいいとは思うのですが作業メモ程度で使う分にはオーバースペックでした。
重要なドキュメントと混じってしまうのであまり良くありませんでした。

DocBaseであれば Private というロール設定があるので自分のメモ板用に1つドキュメントを作ってという使い方をしていました。
ただ、めんどくさがりの自分は保存ボタンを押すのですら 面倒くさい ので長くは続きませんでした。
(最終的に Atom でメモ取ってました)

そこでいろいろ試した中で良かったのが『 HackMD 』でした。

HackMDはリアルタイム共同編集が可能で保存ボタンとかは特になく 常に保存 されます。
また PDF や Slide などへもエクスポートできます。
自分だけが見れるPrivateや他者の編集を不可能にするLockedなど5種類の設定が行なえます。

HackMD と CodiMD についての概要説明

image.png

HackMD は リアルタイムマークダウン共同編集ツール として 無料 で提供されております。

また HackMD にはオープンソースバージョンがあり CodiMD というものがあります。

👉 hackmdio/codimd: CodiMD - Realtime collaborative markdown notes on all platforms.

CodiMD では HackMD の大部分の機能を オープンソースとして提供 しており
これらをクローンするだけで簡単に手元でHackMDを動かす事が可能です。

ライセンスも AGPL となっています。

素晴らしいな、おい...。。。

しかも、ここ最近でドキュメントの整備などもしっかりと行われており充実してきています。
👉 CodiMD Documentation - HackMD

さらにさらに!
Heroku へのワンクリック構築ボタンがあったり、
👉 https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-heroku-deployment

k8s helm が用意されていたり、
👉 https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-kubernetes-deployment

Docker Image が用意されていたり、
👉 https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-docker-deployment

多方面へのカバーも充実しているので CodiMD 様様って感じです٩(๑`ω´๑)۶

まずは clone してみよう

node は Node.js 8.x LTS up to 11.x. となっているようです。
(なので 8以上12以下にしてねって事らしい)

余談ですが、自分が1年ほど前にやった時は node 6系 以上は保証しないよ!と言ってたはずなのに
気づいたらバージョン対応しているので結構メンテされてますね(ㆁωㆁ*) 控えめに言って最高か
ローカル開発用にDockerとかも自分で構築した良い思い出...

MacOS

$ git clone https://github.com/hackmdio/codimd.git
$ cd codimd

環境変数や設定ファイルを眺めて必要な情報をまとめてみた

環境変数についてはこちらを見ると良いです。

本番運用で最低限設定すべき項目は以下の通り

config.json

environment example value description
loglevel info 標準出力に提供されるログの種類を定義します。
domain gyokuro-team.com ドメイン名
useSSL true SSLを利用するか
protocolUseSSL true SSLプロコトルを利用するか
urlAddPort 80 アプリケーションのポート番号
allowAnonymousEdits false 匿名ユーザーの編集を可能にするか
defaultPermission private デフォルトのPermissionの設定
- freely: 誰でも閲覧編集可能
- editable: サインインしていれば閲覧編集可能
- limited: ゲスト以外は閲覧編集可能
- locked: オーナのみ編集可能
- protected: ゲスト以外のオーナのみ
- private: オーナのみしか閲覧編集はできない
allowEmailRegister false Eメールでの登録を可能にするか
email false Eメールでのサインインを許可するか
allowGravatar true アバターを利用するか
sslKeyPath ./cert/privkey.pem SSLを使用する際にprivkeyの場所を指定
sslCertPath ./cert/cert.pem SSLを使用する際にcertの場所を指定
dhParamPath ./cert/fullchain.pem SSLを使用する際にFullChainの場所を指定
sslCAPath ./certchain.pem/ SSLを使用する際にCAの場所を指定
imageuploadtype s3 アップロードの際の画像の置き場
- imgur
- s3
- minio
- azure
- filesystem
github {clientID: "x", clientSecret: "x"} githubでの登録を可能にする場合
s3bucket hackmd-bucket s3でのアップロードを指定した場合
db いろいろ dbの設定情報

環境変数

environment example value description
NODE_ENV production 環境状況
HMD_IMAGE_UPLOAD_TYPE s3 画像のアップロード方法
CMD_DOMAIN production ドメイン名
CMD_URL_ADDPORT production ポート番号
HMD_DB_URL 例: mysql://hackmd:hackmdpass@127.0.0.1:3306/hackmd mysqlやpostgresqlのurlを置く

ローカルで動かしてみよう(Docker)

ドキュメントにもありますが codimd 用の image が配布されています。
image: nabo.codimd.dev/hackmdio/hackmd:1.4.0
👉 Docker Deployment

ただし今回はそれを使いません。
理由としては、自分が過去にやっていた時は node 6.x系 になっており
当時 11.x系 が最新であったことから、どうしても11.x系にしたく 必要最小限構成で自作 したのでそれで紹介します。

Step1. docker-compose.yml の作成

MacOS

$ touch docker-compose.yml

docker-compose.yml

version: '3'
services:

  # MySQL
  database:
    image: mariadb:10.4.4
    container_name: hackmd_mysql
    environment:
      MYSQL_USER: "${MYSQL_USER:-hackmd}"
      MYSQL_PASSWORD: "${MYSQL_PASSWORD:-hackmdpass}"
      MYSQL_DATABASE: "${MYSQL_DATABASE:-hackmd}"
      MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:-rootpassword}"
    volumes:
      - database:/var/lib/mysql
      - ./provisioning/resources/utf8mb4.cnf:/etc/mysql/conf.d/utf8mb4.cnf
    restart: always
    ports:
      - "${MYSQL_USE_PORT:-3306}:3306"
    networks:
      - app-net

  # Node.js
  app:
    build:
      context: .
      dockerfile: ./provisioning/Dockerfile
    container_name: hackmd_app
    tty: true
    volumes:
      - .:/app
      - /app/node_modules
    working_dir: /app
    env_file:
      - ./provisioning/docker-env
    restart: always
    depends_on:
      - database
    ports:
      - "${APP_USE_PORT:-3000}:3000"
    networks:
      - app-net

networks:
  app-net:
    driver: bridge

volumes:
  database:

Step2. Dcokerfile などを作成

MacOS

$ mkdir -p provisioning/resources
$ touch provisioning/Dockerfile
$ touch provisioning/resources/utf8mb4.cnf
$ touch provisioning/docker-env
$ touch provisioning/resources/docker-entrypoint.sh

  • Dockerfile
provisioning/Dockerfile

FROM node:11.14.0-stretch

ENV DEBCONF_NOWARNINGS=yes

# Set some default config variables
ENV DOCKERIZE_VERSION=v0.6.1

RUN wget https://github.com/jwilder/dockerize/releases/download/${DOCKERIZE_VERSION}/dockerize-linux-amd64-${DOCKERIZE_VERSION}.tar.gz && \
    tar -C /usr/local/bin -xzvf dockerize-linux-amd64-${DOCKERIZE_VERSION}.tar.gz && \
    rm dockerize-linux-amd64-${DOCKERIZE_VERSION}.tar.gz

RUN mkdir /app
WORKDIR /app
COPY . /app

# update
RUN apt-get update

# install
RUN apt-get install -y build-essential vim

RUN yarn install && \
    yarn global add webpack && \
    npm run build

EXPOSE 3000

COPY ./provisioning/resources/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

CMD ["node", "app.js"]

  • utf8mb4.cnf
provisioning/resources/utf8mb4.cnf

# MariaDB-specific config file.
# Read by /etc/mysql/my.cnf

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4

[mysqld]
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'
character-set-server=utf8mb4

# Import all .cnf files from configuration directory
!includedir /etc/mysql/mariadb.conf.d/

  • docker-env
.envrc

# dockerで動かすのはdevelopment前提です。

NODE_ENV=development
CMD_CONFIG_FILE=config.json
DEBUG=true
CMD_LOGLEVEL=info
CMD_PROTOCOL_USESSL=false
CMD_URL_ADDPORT=false
CMD_USECDN=true
CMD_ALLOW_ANONYMOUS=false
CMD_ALLOW_ANONYMOUS_EDITS=false
CMD_ALLOW_FREEURL=false
CMD_DEFAULT_PERMISSION=limited
CMD_EMAIL=false
CMD_ALLOW_PDF_EXPORT=true
CMD_ALLOW_EMAIL_REGISTER=false
CMD_ALLOW_GRAVATAR=true
CMD_IMAGE_UPLOAD_TYPE=filesystem

# Change Require
CMD_DB_URL=mysql://hackmd:hackmdpass@database:3306/hackmd
# Githubログイン使いたいなら
# CMD_GITHUB_CLIENTID={YOUR_GITHUB_CLIENT_ID}
# CMD_GITHUB_CLIENTSECRET={YOUR_GITHUB_SECRET_KEY}

# option: Docker Environment
# MYSQL_USER=
# MYSQL_PASSWORD=
# MYSQL_DATABASE=
# MYSQL_ROOT_PASSWORD=
# APP_USE_PORT=

  • docker-entrypoint.sh
provisioning/resources/docker-entrypoint.sh

#!/usr/bin/env bash

dockerize -wait tcp://database:3306 -timeout 30s

node_modules/.bin/sequelize db:migrate

exec "$@"

Step3. configファイル等を整える

MacOS

$ cp config.json.example config.json
$ cp .sequelizerc.example .sequelizerc

  • config.json

一旦、SAML とか 画像S3に上げるととかは一旦考えず最小限で行う。

ログインは Githubログイン で行いたいが後で変更するためここでは行わない。

config.json

{
  "development": {
    "useSSL": false,
    "tmpPath": "./tmp/",
    "defaultNotePath": "./public/default.md",
    "docsPath": "./public/docs",
    "viewPath": "./public/views",
    "uploadsPath": "./public/uploads"
  }
}

Step4. docker up してみよう

MacOS

$ docker-compose up -d

これで localhost:3000 にアクセスすれば見れるので
ローカルでの開発が捗る٩(๑`ω´๑)۶

image.png

※ もし アクセス後 ビルドエラーのような画面が出た場合は以下をお試しください。

$ docker-compose run --rm app npm run build
$ docker-compose run --rm yarn install

Step5. タイトルを変えてみよう

public/views/index/body.ejs の 43行目付近にある以下を修正


- <h1 class="cover-heading"><i class="fa fa-file-text"></i> CodiMD</h1>
+ <h1 class="cover-heading"><i class="fa fa-file-text"></i> k-waragaiMD</h1>

image.png

これでやる気UP (´∀`∩)↑age↑

Step6. Githubログインを出来るようにしてみよう

👉 https://github.com/settings/developers にアクセスを行い New OAuth App をクリック

必要な情報を入力して Register application をクリック

OAuth_application_settings.png

ClientIDSecretKey が吐き出されるのでそれをメモします。

OAuth_application_settings.png

一度 docker-compose down を行います。

docker-env を編集してください。

docker-env

...

  # Change Require
  CMD_DB_URL=mysql://hackmd:hackmdpass@database:3306/hackmd
  # Githubログイン使いたいなら
- # CMD_GITHUB_CLIENTID={YOUR_GITHUB_CLIENT_ID}
- # CMD_GITHUB_CLIENTSECRET={YOUR_GITHUB_SECRET_KEY}
+ CMD_GITHUB_CLIENTID=SampeClientIdDesu
+ CMD_GITHUB_CLIENTSECRET=SampeSecretKeyDesu

  # option: Docker Environment
  # MYSQL_USER=
  # MYSQL_PASSWORD=
  # MYSQL_DATABASE=
  # MYSQL_ROOT_PASSWORD=
  # APP_USE_PORT=

編集が完了したら docker-compose up -d をすると
先程は無かった サインイン のボタンが現れるようになります。

試しにログインをすると、Githubからユーザー名などを取れておりログインできてる事がわかります。
image.png

EC2で動かしてみよう(ホスト)

Ubuntu でざっくりと docker を使用せずに構築したいと思います。
基本的なセキュリティの設定などは各々あると思いますので省かせていただきます。

すでにEC2にsshしている状態でのお話になります。

Step1. 事前準備

  • apt update upgrade
EC2インスタンス[ubuntuユーザー]

$ sudo apt update
$ sudo apt upgrade

Step2. ユーザーの作成とsudoグループ追加

EC2インスタンス[ubuntuユーザー]
# ユーザーの作成
$ sudo adduser hackmd
    Adding user `hackmd' ...
    Adding new group `hackmd' (1001) ...
    Adding new user `hackmd' (1001) with group `hackmd' ...
    Creating home directory `/home/hackmd' ...
    Copying files from `/etc/skel' ...
    Enter new UNIX password: passwordを入力...
    Retype new UNIX password: passwordを入力...
    passwd: password updated successfully
    Changing the user information for hackmd
    Enter the new value, or press ENTER for the default
        Full Name []:
        Room Number []:
        Work Phone []:
        Home Phone []:
        Other []:

# sudo グループへ hackmd ユーザーを追加
$ sudo gpasswd -a hackmd sudo

# スイッチッ!!!!!!
$ sudo su - hackmd

Step3. 必要なパッケージのインストール

  • コアになりそうなもののインストール
EC2インスタンス[hackmdユーザー]

[hackmd]$ sudo apt-get install -y git build-essential wget vim unzip libssl-dev python

  • nvm のインストール
EC2インスタンス[hackmdユーザー]

[hackmd]$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.0/install.sh | bash
[hackmd]$ export NVM_DIR="$HOME/.nvm"
[hackmd]$ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[hackmd]$ [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
[hackmd]$ source ~/.bashrc
[hackmd]$ nvm --version
0.35.0
  • node のインストール
EC2インスタンス[hackmdユーザー]

[hackmd]$ nvm install v11.14.0
[hackmd]$ nvm use v11.14.0
[hackmd]$ node -v
11.14.0
  • yarn のインストール
EC2インスタンス[hackmdユーザー]

[hackmd]$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
[hackmd]$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
[hackmd]$ sudo apt-get update && sudo apt-get -y install yarn
[hackmd]$ export PATH="$PATH:/opt/yarn-1.15.2/bin"
[hackmd]$ source ~/.bashrc
[hackmd]$ yarn -v
1.15.2
  • mariaDBインストール(MySQLでも良い)
EC2インスタンス[hackmdユーザー]

[hackmd]$ curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash
[hackmd]$ sudo apt -y update
[hackmd]$ apt install -y mariadb-server-10.3
[hackmd]$ mysql_secure_installation


        Enter current password for root (enter for none):
        > パスワード

        Change the root password? [Y/n]
        > Y
        > New password: パスワード
        > Re-enter new password: パスワード

        Remove anonymous users? [Y/n]
        > n

        Disallow root login remotely? [Y/n]
        > Y

        Remove test database and access to it? [Y/n]
        > Y

        Reload privilege tables now? [Y/n]
        > Y

[hackmd]$ mysql -uroot -p -h 127.0.0.1 -e "show databases;"
> ルートパスワード

        +--------------------+
        | Database           |
        +--------------------+
        | information_schema |
        | mysql              |
        | performance_schema |
        +--------------------+

# ユーザー作成
[hackmd]$ mysql -uroot -p -h 127.0.0.1 -e "CREATE USER ユーザー名 IDENTIFIED BY 'パスワード';"

# 権限付与
[hackmd]$ mysql -uroot -p -h 127.0.0.1 -e "GRANT ALL ON *.* TO 'ユーザー名'@'localhost' IDENTIFIED BY 'パスワード';"

# 適用
[hackmd]$ mysql -uroot -p -h 127.0.0.1 -e "FLUSH PRIVILEGES;"

[hackmd]$ mysql -uユーザー名 -p -h 127.0.0.1 -e "create database データベース名;"
  • nginx
EC2インスタンス[hackmdユーザー]

[hackmd]$ curl http://nginx.org/keys/nginx_signing.key | sudo apt-key add -
[hackmd]$ VCNAME=`cat /etc/lsb-release | grep DISTRIB_CODENAME | cut -d= -f2` && sudo -E sh -c "echo \"deb http://nginx.org/packages/ubuntu/ $VCNAME nginx\" >> /etc/apt/sources.list"
[hackmd]$ VCNAME=`cat /etc/lsb-release | grep DISTRIB_CODENAME | cut -d= -f2` && sudo -E sh -c "echo \"deb-src http://nginx.org/packages/ubuntu/ $VCNAME nginx\" >> /etc/apt/sources.list"
[hackmd]$ sudo apt-get update
[hackmd]$ sudo apt-get install -y nginx

Step4. リポジトリのクローンと設定

github の .netrc などの設定は一旦省かせていただきます。
また config.json や sequelizerc はローカルのときとほとんど同じとなっていますのでSKIPしていただいても構いません。
ただ、SSLの関係上環境変数などが若干増えています。

EC2インスタンス[hackmdユーザー]

[hackmd]$ git clone https://github.com/hackmdio/codimd.git
[hackmd]$ cd codimd
[hackmd]$ cp .sequelizerc.example .sequelizerc
[hackmd]$ cp config.json.example config.json

  • utf8mb4.cnf の設定
EC2インスタンス[hackmdユーザー]

[hackmd]$ sudo vim /etc/mysql/mariadb.conf.d/utf8mb4.cnf

/etc/mysql/mariadb.conf.d/utf8mb4.cnf

    # MariaDB-specific config file.
    # Read by /etc/mysql/my.cnf

    [client]
    default-character-set=utf8mb4

    [mysql]
    default-character-set=utf8mb4

    [mysqld]
    collation-server=utf8mb4_general_ci
    init-connect='SET NAMES utf8mb4'
    character-set-server=utf8mb4

    # Import all .cnf files from configuration directory
    !includedir /etc/mysql/mariadb.conf.d/

  • config.json の設定

  • 画像は S3 へ アップロード

  • ログインは メール登録を禁止しGithubログインのみ

  • ゲストユーザーは書き込み閲覧を出来ない

  • ドメインの指定

という形でconfigを作っていきます。

SSLはこの段階では行わないが後述で行います。

Github の

EC2インスタンス[hackmdユーザー]

[hackmd]$ vim config.json

config.json

{
  "production": {
    "loglevel": "info",
    "useSSL": false,
    "protocolUseSSL": false,
    "domain": "xxxxxxxxxxx.com", #--ドメインがあるのであれば
    "urlAddPort": "80",
    "allowAnonymousEdits": false,
    "defaultPermission": "private",
    "allowEmailRegister": false,
    "email": false,
    "allowGravatar": true,
    "db": {
      "username": "xxxxxxxxxxx",
      "password": "xxxxxxxxxxx",
      "database": "xxxxxxxxxxx",
      "host": "127.0.0.1",
      "port": "3306",
      "dialect": "mysql"
    },
    "github": {
      "clientID": "xxxxxxxxxxxxxxxxxxxxxx",
      "clientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    },
    "imageuploadtype": "s3",
    "s3": {
      "accessKeyId": "xxxxxxxxxxxxxxxxxxxxxx",
      "secretAccessKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "region": "ap-northeast-1"
    },
    "s3bucket": "codimd-production"
  }
}

  • nginx の設定
EC2インスタンス[hackmdユーザー]

[hackmd]$ sudo vim /etc/nginx/conf.d/node-app.conf

/etc/nginx/conf.d/node-app.conf

server {
  listen 80;
  # listen  443 ssl;
  ssl on;

  # 編集すること
  server_name  {YOUR_DOMAIN}.com;

  # 今はまだ使わない
  # ssl_certificate      /etc/letsencrypt/live/{YOUR_DOMAIN}.com/fullchain.pem;
  # ssl_certificate_key  /etc/letsencrypt/live/{YOUR_DOMAIN}.com/privkey.pem;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

  proxy_redirect                          off;
  proxy_set_header Host                   $host;
  proxy_set_header X-Real-IP              $remote_addr;
  proxy_set_header X-Forwarded-Host       $host;
  proxy_set_header X-Forwarded-Server     $host;
  proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;

  location / {
    proxy_pass https://localhost:3000;
      proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }

  # あとで SSL取得に使う
  location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root         /home/hackmd/codimd/public;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /home/hackmd/codimd/public;
  }
}

EC2インスタンス[hackmdユーザー]

[hackmd]$ sudo systemctl restart nginx

  • 無料枠だとRAMに限界あるので SWAP領域を作成
EC2インスタンス[hackmdユーザー]

[hackmd]$  sudo dd if=/dev/zero of=/swap bs=1M count=1024
[hackmd]$  sudo mkswap /swap
[hackmd]$  sudo swapon /swap
[hackmd]$  sudo chmod 600 /swap

Step5. 立ち上げ

EC2インスタンス[hackmdユーザー]

[hackmd:~/codimd]$ yarn install
[hackmd:~/codimd]$ yarn global add webpack
[hackmd:~/codimd]$ npm run build
[hackmd:~/codimd]$ node_modules/.bin/sequelize db:migrate
[hackmd:~/codimd]$ node app.js

Step6. 永続的にappを立ち上げさせる

EC2インスタンス[hackmdユーザー]

[hackmd:~/codimd]$ npm install -g forever
[hackmd:~/codimd]$ forever start app.js

Step7. SSL化(HTTPS)

今回は Let's Encrypt を利用します

  • certbot challenge
EC2インスタンス[hackmdユーザー]

[hackmd:~/codimd]$ mkdir -p .well-known/acme-challenge/
[hackmd:~/codimd]$ chmod 644 .well-known/acme-challenge/
[hackmd:~/codimd]$ cd ~/
[hackmd:~/]$ git clone https://github.com/certbot/certbot.git && cd certbot
[hackmd:~/]$ sudo ./certbot-auto certonly --webroot -w /home/hackmd/codimd/public -d {YOUR_DOMAIN}.com -m {YOUR_MAIL_ADDR} --debug

  • application側の対応
EC2インスタンス[hackmdユーザー]

[hackmd:~/]$ cd codimd

  • 環境変数の変更
EC2インスタンス[hackmdユーザー]

[hackmd:~/codimd]$ direnv edit .

.ervrc
- export CMD_PROTOCOL_USESSL=false
+ export CMD_PROTOCOL_USESSL=true

  • config.json の変更
EC2インスタンス[hackmdユーザー]

[hackmd:~/codimd]$ vim config.json

config.json
{
  "production": {
    "loglevel": "info",
-   "useSSL": false,
-   "protocolUseSSL": false,
+   "useSSL": true,
+   "protocolUseSSL": true,
+   "sslKeyPath": "/etc/letsencrypt/live/xxxxxxxxxxx.com/privkey.pem",
+   "sslCertPath": "/etc/letsencrypt/live/xxxxxxxxxxx.com/cert.pem",
+   "dhParamPath": "/etc/letsencrypt/live/xxxxxxxxxxx.com/fullchain.pem",
+   "sslCAPath": ["/etc/letsencrypt/live/xxxxxxxxxxx.com/chain.pem"],
    "domain": "xxxxxxxxxxx.com", #--ドメインがあるのであれば
    "urlAddPort": "80",
    "allowAnonymousEdits": false,
    "defaultPermission": "private",
    "allowEmailRegister": false,
    "email": false,
    "allowGravatar": true,
    "db": {
      "username": "xxxxxxxxxxx",
      "password": "xxxxxxxxxxx",
      "database": "xxxxxxxxxxx",
      "host": "127.0.0.1",
      "port": "3306",
      "dialect": "mysql"
    },
    "github": {
      "clientID": "xxxxxxxxxxxxxxxxxxxxxx",
      "clientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    },
    "imageuploadtype": "s3",
    "s3": {
      "accessKeyId": "xxxxxxxxxxxxxxxxxxxxxx",
      "secretAccessKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "region": "ap-northeast-1"
    },
    "s3bucket": "codimd-production"
  }
}

  • nginx の変更
EC2インスタンス[hackmdユーザー]

[hackmd:~/codimd]$ sudo vim /etc/nginx/conf.d/node-app.conf

/etc/nginx/conf.d/node-app.conf

upstream node-app {
  server localhost:3000;
}

server {
  listen  443 ssl;
  ssl on;

  server_name  xxxxxxxxxxxx.com;

  ssl_certificate      /etc/letsencrypt/live/xxxxxxxxxxx.com/fullchain.pem;
  ssl_certificate_key  /etc/letsencrypt/live/xxxxxxxxxxx.com/privkey.pem;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

  proxy_redirect                          off;
  proxy_set_header Host                   $host;
  proxy_set_header X-Real-IP              $remote_addr;
  proxy_set_header X-Forwarded-Host       $host;
  proxy_set_header X-Forwarded-Server     $host;
  proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;

  location / {
    proxy_pass http://node-app/;
  }

  location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root         /home/hackmd/codimd/public;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /home/hackmd/codimd/public;
  }
}

EC2インスタンス[hackmdユーザー]

[hackmd:~/codimd]$ sudo systemctl restart nginx

Step8. Restart

EC2インスタンス[hackmdユーザー]

[hackmd:~/codimd]$ forever app.js restart

アクセス!

image.png

Github の New OAuth App はSSLが出来たタイミング等で先程と同じ手順で行ってください。

ちゃんと鍵も出てるし ログインも出来る〜。 \ 最 高 /

トラブルシューティング

  1. SSL化にて challenge に失敗した場合、原因として .well-known/acme-challenge/ にアクセス出来てない可能性があります。
    • chmod 644 .well-known/acme-challenge/ などをしてみてください。
    • また、 challenge 時 80番(HTTP)でアクセスが来るはずなので 更新の際は listen 80 にするのを忘れずに
  2. 今回はドメインの設定を公開してないですが タイプA の設定をしています。(google で買ったドメインを利用しています。)

チームで使っている板を自動でQiita Teamに投稿できるようにしてみた

私達のチームでは、1つの板を専有し毎日更新し続けています。
その板に書いているものは以下の通りとなっております。

  • 共有事項
    • 有給取る日やフレックス出社日など
  • 今日のゴール
    • 今日終わらせるべきタスクを明確にする
  • 作業ログ
    • 作業を行う際、個人個人がそれぞれh2でタイトルを切ってログ等をまとめたりする

今までは翌日の朝にこれらを全コピしてQiita Teamに転記していたのですが、
まあちょっとの繰り返しさえも自動化したくなるのがエンジニア魂ですよね(ㆁωㆁ*) めんどくさいだけ

やり方は簡単で Qiita API を使ったスクリプトを Cron で平日毎日 22:00 頃に回すだけです。

sampleコードを載せておきます。

post_daily_report_by_cosmos_team.rb

require 'net/https'
require 'mysql2'
require "json"
require "date"

mysql_username = ENV['MYSQL_USERNAME'].freeze
mysql_password = ENV['MYSQL_PASSWORD'].freeze
mysql_database = ENV['MYSQL_DATABASE'].freeze
team_note_id   = ENV['TEAM_NOTE_ID'].freeze
qiita_api_key  = ENV['QIITA_API_KEY'].freeze
domain         = ENV['TEAM_DOMAIN'].freeze

client = Mysql2::Client.new(
  socket: '/var/run/mysqld/mysqld.sock',
  username: mysql_username,
  password: mysql_password,
  encoding: 'utf8',
  database: mysql_database
)

statement = client.prepare('SELECT content FROM Notes WHERE id = ?')
# 事前にタイトルで select しておき ID を控えています。
# example: 
#   mysql -uhackmd -p hackmd -e "select id from Notes where title = 'cosmos開発日報'"
results = statement.execute(team_note_id)

weekday = ["日", "月", "火", "水", "木", "金", "土"]
today = Date.today
title = "cosmos開発日報" + today.strftime("%Y/%m/%d (#{weekday[today.wday]})")
content = ""

results.each do |row|
  content = row["content"]
end

# 組み立て
qiita = "https://#{domain}.qiita.com/"
path = '/api/v2/items'
url = qiita + path

uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)

http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

req = Net::HTTP::Post.new(uri.request_uri)
req["Authorization"] = "Bearer #{qiita_api_key}"
req["Content-Type"] = "application/json"

post_data = {
  title: title,
  body: content,
  coediting: true,
  group_url_name: "cosmos",
  private: false,
  tags: [{name: "cosmos"},{name: "日報"}],
  tweet: false,
}.to_json

# 投稿
req.body = post_data
# 結果
res = http.request(req)
puts res.code, res.msg

crontab

# Edit this file to introduce tasks to be run by cron.
00 13 * * 1-5 bash -cl 'cd /home/hackmd/cron_scripts/ && ruby -Ku post_daily_report_by_cosmos_team.rb'

image.png

毎日こんな感じで投稿されるようになりました! \ 自 動 化 バ ン ザ イ /

こんな感じで好きな機能を追加したりをしてみたり実験場としても活躍しています!

最後に

今回は docker で構築する方法と ホスト上に直接構築する方法の2種類を紹介いたしました!
応用すれば ECS にすることも出来ますね(ㆁωㆁ*)

エンジニアとして、「あのサービスにこの機能があったら最高なんだけど...」って思うことって多々ありますよね。

もともと利用してたHackMDをそのまま改造出来るって最高だなって思いました。 THE 幸せ

HackMDはエンタープライズ版があるとお話していましたが、エンタープライズ版では高頻度で504になり 自動復旧を待つ ということが高頻度で起きました。
連続で起きるときもあれば起きないときもあり、不安定な状態が続いた為オンプレ化を進めたのが実背景となっています。

実際やってみるとそこまで難しいことはなく、 機能の拡張 なども容易に行えてしばらくは遊べそうです(ㆁωㆁ*)

料金も EC2 インスタンスの無料枠(t2.micro)を使っていますが、アクティブユーザーは最大15人とかでも落ちるといったことは今までありませんでした。

画像もS3にあげているので容量圧迫とかも起きず快適に利用できています!

文字だけなのでmysql自体の負荷も少ないことから、 社内で50人くらいで使う分には無料枠で全然問題ない かと思われます。

ぜひ皆様も CodiMD を利用してみてくださいね(ㆁωㆁ*) bye

👉 hackmdio/codimd: CodiMD - Realtime collaborative markdown notes on all platforms.

またもっと良いツールがあるよ!って方はぜひ教えて下さい!

Twitterもやってるのでぜひフォローください。
👉 つなまよ(@mayoxtuna)さん / Twitter
Apex Legends(PC版)とか一緒に趣味開発してくれるお友達募集中です٩(๑`ω´๑)۶

次回予告

明日は クリスマス・イブ ですね٩(๑`ω´๑)۶ 🎄 :.。.:May your Christmas wishes come true:.。.: 🎄

12月24日は クラウドワークスエンジニアである @cesare による 「複業を支える技術」です!
お楽しみに〜bye

2019/12/24 追記

この記事を公開したあと Twitter 経由で HackMD の CEO の方から直々に感謝のDMを頂きました。
そこで エンタープライズ版の 504 になる問題について教えていただきました。

現在では高パフォーマンスで動く状態を維持しており、それらの問題は起きないとのことでした (ㆁωㆁ*)

日々進化し続ける HackMD 最高ですね٩(๑`ω´๑)۶

69
54
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
69
54

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?