# outline 部署 ## 什麼是 Outline --- <https://www.getoutline.com/> 想找一個能夠替代 Confluence 管理文檔 ,既開源可本地部署,layout 又不會過於年代感的 wiki,找到了這個與 notion 87 分像的開源項目,於是著手開始進行部署設定。 ![](https://i.imgur.com/uQEL1in.png) \ ## 環境建立 本篇將使用 docker 完成部署,參考 outline 的官方文件有直接建議 hosting outline 的做法 [Docker](https://app.getoutline.com/share/770a97da-13e5-401e-9f8a-37949c19f97e/doc/docker-7pfeLP5a8t) ### 資料設定 1. Install [Docker Compose](https://docs.docker.com/compose/install/) 2. 建立 `docker-compose.yml` 檔案,並參考以下設定: ```yaml version: "3" services: outline: image: outlinewiki/outline env_file: ./docker.env ports: - "3000:3000" depends_on: - postgres - redis - storage redis: image: redis env_file: ./docker.env ports: - "6379:6379" volumes: - ./redis.conf:/redis.conf command: ["redis-server", "/redis.conf"] healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 30s retries: 3 postgres: image: postgres:14-alpine env_file: ./docker.env ports: - "5432:5432" volumes: - database-data:/var/lib/postgresql/data healthcheck: test: ["CMD", "pg_isready -U user"] interval: 30s timeout: 20s retries: 3 environment: POSTGRES_USER: user POSTGRES_PASSWORD: pass POSTGRES_DB: outline storage: image: minio/minio env_file: ./docker.env ports: - "9000:9000" - "9001:9001" entrypoint: sh volumes: - https-portal-data:/var/lib/https-portal healthcheck: test: ["CMD", "service", "nginx", "status"] interval: 30s timeout: 20s retries: 3 command: -c 'minio server /data --console-address ":9001"' deploy: restart_policy: condition: on-failure volumes: - storage-data:/data healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 https-portal: image: steveltn/https-portal env_file: ./docker.env ports: - '80:80' - '443:443' links: - outline - storage restart: always volumes: - https-portal-data:/var/lib/https-portal healthcheck: test: ["CMD", "service", "nginx", "status"] interval: 30s timeout: 20s retries: 3 volumes: https-portal-data: storage-data: database-data: ``` 與官方建議不同,需要注意的地方: * 建議將 storage( minio )的兩個 port 輸出出來,`:9000` 指向 api,`:9001` 指向 console,承如 minIO 官方建議那樣,稍後也比較好設定其他配置。 3. 建立環境參數 `docker.env` ```bash # –––––––––––––––– REQUIRED –––––––––––––––– # Generate a hex-encoded 32-byte random key. You should use `openssl rand -hex 32` # in your terminal to generate a random value. SECRET_KEY=generate_a_new_key # Generate a unique random key. The format is not important but you could still use # `openssl rand -hex 32` in your terminal to produce this. UTILS_SECRET=generate_a_new_key # For production point these at your databases, in development the default # should work out of the box. # DATABASE_URL=postgres://user:pass@postgres:5432/outline # DATABASE_URL_TEST=postgres://user:pass@postgres:5432/outline-test # DATABASE_CONNECTION_POOL_MIN= # DATABASE_CONNECTION_POOL_MAX= # Uncomment this to disable SSL for connecting to Postgres # PGSSLMODE=disable # For redis you can either specify an ioredis compatible url like this REDIS_URL=redis://redis:6379 # or alternatively, if you would like to provide addtional connection options, # use a base64 encoded JSON connection option object. Refer to the ioredis documentation # for a list of available options. # Example: Use Redis Sentinel for high availability # {"sentinels":[{"host":"sentinel-0","port":26379},{"host":"sentinel-1","port":26379}],"name":"mymaster"} # REDIS_URL=ioredis://eyJzZW50aW5lbHMiOlt7Imhvc3QiOiJzZW50aW5lbC0wIiwicG9ydCI6MjYzNzl9LHsiaG9zdCI6InNlbnRpbmVsLTEiLCJwb3J0IjoyNjM3OX1dLCJuYW1lIjoibXltYXN0ZXIifQ== # URL should point to the fully qualified, publicly accessible URL. If using a # proxy the port in URL and PORT may be different. URL=<your-outline-url> PORT=3002 # See [documentation](docs/SERVICES.md) on running a separate collaborationNGUAGE # server, for normal operation this does not need to be set. COLLABORATION_URL= # To support uploading of images for avatars and document attachments an # s3-compatible storage must be provided. AWS S3 is recommended for redundency # however if you want to keep all file storage local an alternative such as # minio (https://github.com/minio/minio) can be used. ## 此段落設定給 storage - minio MINIO_SERVER_URL=<your-minio-api-url> MINIO_BROWSER_REDIRECT_URL=<your-minio-admin-url> MINIO_ROOT_USER=<your-minio-username> MINIO_ROOT_PASSWORD=<your-minio-password> ##MinIO 自己可以當 Object Storage,如果要 MinIO 去連 S3 需要加上 MINIO_REGION_NAME #MINIO_REGION_NAME= # A more detailed guide on setting up S3 is available here: # => https://wiki.generaloutline.com/share/125de1cc-9ff6-424b-8415-0d58c809a40f AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER} AWS_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD} #AWS_REGION=${MINIO_REGION_NAME} AWS_S3_UPLOAD_BUCKET_URL=${MINIO_SERVER_URL} AWS_S3_UPLOAD_BUCKET_NAME=<your-minio-bucket-name> AWS_S3_UPLOAD_MAX_SIZE=26214400 AWS_S3_FORCE_PATH_STYLE=true AWS_S3_ACL=private # –––––––––––––– AUTHENTICATION –––––––––––––– # Third party signin credentials, at least ONE OF EITHER Google, Slack, # or Microsoft is required for a working installation or you'll have no sign-in # options. # To configure Slack auth, you'll need to create an Application at # => https://api.slack.com/apps # # When configuring the Client ID, add a redirect URL under "OAuth & Permissions": # https://<URL>/auth/slack.callback SLACK_CLIENT_ID=get_a_key_from_slack SLACK_CLIENT_SECRET=get_the_secret_of_above_key # To configure Google auth, you'll need to create an OAuth Client ID at # => https://console.cloud.google.com/apis/credentials # # When configuring the Client ID, add an Authorized redirect URI: # https://<URL>/auth/google.callback GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= # To configure Microsoft/Azure auth, you'll need to create an OAuth Client. See # the guide for details on setting up your Azure App: # => https://wiki.generaloutline.com/share/dfa77e56-d4d2-4b51-8ff8-84ea6608faa4 AZURE_CLIENT_ID= AZURE_CLIENT_SECRET= AZURE_RESOURCE_APP_ID= # To configure generic OIDC auth, you'll need some kind of identity provider. # See documentation for whichever IdP you use to acquire the following info: # Redirect URI is https://<URL>/auth/oidc.callback # OIDC_CLIENT_ID=75fe43a9d5b8cdd0f154f7ac8e95d668 # OIDC_CLIENT_SECRET=1029d3ca78ac322d495fbaa59afd394f OIDC_AUTH_URI= OIDC_TOKEN_URI= OIDC_USERINFO_URI= # Specify which claims to derive user information from # Supports any valid JSON path with the JWT payload OIDC_USERNAME_CLAIM=preferred_username # Display name for OIDC authentication OIDC_DISPLAY_NAME=OpenID # Space separated auth scopes. OIDC_SCOPES=openid profile email # –––––––––––––––– OPTIONAL –––––––––––––––– # Base64 encoded private key and certificate for HTTPS termination. This is only # required if you do not use an external reverse proxy. See documentation: # https://wiki.generaloutline.com/share/1c922644-40d8-41fe-98f9-df2b67239d45 SSL_KEY= SSL_CERT= # If using a Cloudfront/Cloudflare distribution or similar it can be set below. # This will cause paths to javascript, stylesheets, and images to be updated to # the hostname defined in CDN_URL. In your CDN configuration the origin server # should be set to the same as URL. CDN_URL= # Auto-redirect to https in production. The default is true but you may set to # false if you can be sure that SSL is terminated at an external loadbalancer. FORCE_HTTPS=false # Have the installation check for updates by sending anonymized statistics to # the maintainers ENABLE_UPDATES=true # How many processes should be spawned. As a reasonable rule divide your servers # available memory by 512 for a rough estimate WEB_CONCURRENCY=1 # Override the maxium size of document imports, could be required if you have # especially large Word documents with embedded imagery MAXIMUM_IMPORT_SIZE=5120000 # You can remove this line if your reverse proxy already logs incoming http # requests and this ends up being duplicative DEBUG=http # For a complete Slack integration with search and posting to channels the # following configs are also needed, some more details # => https://wiki.generaloutline.com/share/be25efd1-b3ef-4450-b8e5-c4a4fc11e02a # SLACK_VERIFICATION_TOKEN=your_token SLACK_APP_ID=A0XXXXXXX SLACK_MESSAGE_ACTIONS=true # Optionally enable google analytics to track pageviews in the knowledge base GOOGLE_ANALYTICS_ID= # Optionally enable Sentry (sentry.io) to track errors and performance SENTRY_DSN= # To support sending outgoing transactional emails such as "document updated" or # "you've been invited" you'll need to provide authentication for an SMTP server SMTP_HOST= SMTP_PORT= SMTP_USERNAME= SMTP_PASSWORD= SMTP_FROM_EMAIL= SMTP_REPLY_EMAIL= SMTP_TLS_CIPHERS= SMTP_SECURE=true # Custom logo that displays on the authentication screen, scaled to height: 60px # TEAM_LOGO=https://example.com/images/logo.png # The default interface language. See translate.getoutline.com for a list of # available language codes and their rough percentage translated. DEFAULT_LANGUAGE=zh_TW ``` ### 取得 Oauth outline 較麻煩得是需要經過第三方認證,在註解 **AUTHENTICATION** Block 的部分,設定幾個項目就有幾個進入方式,但進入後,上方的 `.env` 註解的部分也有介紹,以下提供幾個方案 #### Google Oauth * 在[ https://console.cloud.google.com/apis ]( https://console.cloud.google.com/apis ) 底下建立相關資訊,並取得 `GOOGLE_CLIENT_ID `與 `GOOGLE_CLIENT_SECRET` * 需要注意的是使用 Google Oauth 被授權的 URL 要帶有 **https** ![](https://i.imgur.com/FA10dOA.png) #### Slack OAuth * 在 **Settings** → **Base Information** 取得 `SLACK_CLIENT_ID` 與 `SLACK_CLIENT_SECRET` * 與 Google oauth 相同,在設定重新導向一樣要帶有 **https** 再到 **Features** → **Base Information** 設定 `https://<URL>/auth/slack.callback` ![](https://i.imgur.com/aRSzKfC.png) #### Custom OIDC 另外找了兩個可以自架第三方授權的項目,可以到此文章最下方的參考文件中的『其他人部署方法』,參考其他人部署 SSO 的方法。 * [Keycloak](https://www.keycloak.org/) * [docker-sso-server](https://github.com/soulteary/docker-sso-server) \ ### Database 接下來繼續按照官方的方法繼續把資料庫,資料表建立起來: ```bash docker-compose run --rm outline yarn sequelize db:create --env=production-ssl-disabled ``` Migrate the new database to add needed tables, indexes, etc: ```bash docker-compose run --rm outline yarn sequelize db:migrate --env=production-ssl-disabled ``` ### Running 建立好 `docker-compose.yml` 與 `docker.env` 後,在此目錄底下執行 ```javascript docker-compose up -d ``` 就可以看到畫面了!但在看到畫面後,又遇到一些問題需要修正,再繼續往下看⋯⋯ ## 解決問題 ### 解決無法編輯文章的問題 部署完畢成功進到 outline 畫面後,會發現右下角出現斷線的 icon,並從 dev tool 中看到錯誤訊息 ![](https://i.imgur.com/wx6NXJ0.png) 原因是因為 WebSocket 服務的 Nginx 配置出現問題,WebSocket 有連接的要求,因為要告訴服務端從 http 協議更換到 ws 協議,request / response header 都必須設置: `Upgrade: webSocket`、`Connection: Upgrade`。 ### 為什麼要在 Nginx 配置以上兩項 header? 由於 `Upgrade`、`Connection` 都是 hop-by-hop header,在 domain 使用 nginx 做反向代理時,由於以上兩項 header 不會被帶到代理伺服器上,所以無法成功建立 ws 連線。 > hop-by-hop header:only for a single transport-level connection and must not be retransmitted by proxies or cached <Connection、Keep-Alive、Proxy-Authenticate、Trailer、TE、Transfer-Encoding、Upgrade>) 為了解決這個問題,Nginx 提供了 `proxy_set_header` 的配置,將資訊帶至代理伺服器上。 Nginx 配置以下: ``` location / { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; add_header Service-Worker-Allowed /; } ``` ## 上傳圖片 反向代理設定 minIO ,一樣也需要進行 Nginx 設定,參考[==官網文件==](https://docs.min.io/docs/setup-nginx-proxy-with-minio.html)配置以下: ``` location / { 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 Host $http_host; proxy_connect_timeout 300; proxy_http_version 1.1; proxy_set_header Connection ""; chunked_transfer_encoding off; proxy_pass http://sit-minio-api; } ``` ### minIO 管理介面 storage(minIO)中的 `:9001` 指向 console,即是 minIO 的管理介面。 1. 使用配置中 `MINIO_ROOT_USER` 、 `MINIO_ROOT_PASSWORD` 內容進行登入之後,能夠看到以下畫面 2. 創建一個新的 bucket * 名稱為配置中的 `AWS_S3_UPLOAD_BUCKET_NAME` * 再進入 **Manage** → **Access Rules** 寫入以下設定 ![](https://i.imgur.com/n01wO49.png) * 完成後回到 **Summary** 把 **Access Policy** 從 private 改為 custom,並寫入以下設定 ``` { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "*" ] }, "Action": [ "s3:GetBucketLocation", "s3:ListBucketMultipartUploads" ], "Resource": [ "arn:aws:s3:::outline" ] }, { "Effect": "Allow", "Principal": { "AWS": [ "*" ] }, "Action": [ "s3:DeleteObject", "s3:GetObject", "s3:ListMultipartUploadParts", "s3:PutObject", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::outline/avatars*", "arn:aws:s3:::outline/public*", "arn:aws:s3:::outline/uploads*" ] }, { "Effect": "Allow", "Principal": { "AWS": [ "*" ] }, "Action": [ "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::outline" ], "Condition": { "StringEquals": { "s3:prefix": [ "avatars", "public", "uploads" ] } } } ] } ``` 以上設定完成後就可以成功上傳圖片與附加檔案! :::tip 在 outline 中移除圖片並不會徹底刪除,如需徹底刪除圖片需要進到 minIO 的管理頁面,找到上傳的名稱後進行刪除! ::: ## 參考文件 * outline 官方部署: <https://app.getoutline.com/share/770a97da-13e5-401e-9f8a-37949c19f97e/doc/docker-7pfeLP5a8t> * outline github&Discussions:<https://github.com/outline/outline> * minIO 官方文件:<https://docs.min.io/docs/> * outline 討論串 <https://www.reddit.com/r/selfhosted/comments/hq6m6b/how_to_self_host_outline_wiki/> * 其他人的部署方法: * <https://blog.gurucomputing.com.au/doing-more-with-docker/deploying-outline-wiki/> * <https://www.luckzym.com/posts/a239536c/> * <https://biteax.com/2022/05/07/Outline/%E3%80%90Outline%E3%80%91%E7%BA%AFDocker%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97/> * <https://soulteary.com/2021/09/05/opensource-documentation-wiki-software-outline-part-1.html>