Rails master.key

問題描述

在開發與部署 Rails 專案的時候,我們會需要存放一些敏感的資訊,例如 API key、secret key 等等,這些資訊可以被加密存放到 config/credentials.yml.enc 檔案中。
而這些加密的資訊需要一個 master.key 來進行解密,而這個 master.key 通常會存放在 config/master.key 檔案中。以下是關於 master.key 的相關操作。

解決方法

  1. 執行 rails credentials:edit 會同時檢查 config/master.key 是否存在,如果不存在,則會提示你建立一個新的 master.key。並且會開啟 credentials.yml.enc 檔案讓你進行編輯。
    1
    2
    3
    4
    $ EDITOR="code --wait" rails credentials:edit  # 使用 VSCode 編輯 credentials.yml.enc
    $ EDITOR="vi" rails credentials:edit # 使用 vi 編輯 credentials.yml.enc
    $ EDITOR="nano" rails credentials:edit # 使用 nano 編輯 credentials.yml.enc
    # ...以此類推,可以指定其他編輯器
  2. credentials.yml.enc 的內容如下,預設會看到 secret_key_base 這個 key,還有其他加密後的資訊。
    1
    secret_key_base: ggg4ded928834c777106xxxe2453739d22cf4b2046d161da8575e0dbbulahbulah
  3. 如果需要產生新的 master.key,可以執行 rails credentials:setup,這樣會同時產生 config/master.key 檔案,以及 credentials.yml.enc 檔案。
    1
    $ rails credentials:setup
  4. master.key 記得要加入到 gitignore 中,避免被 commit 到遠端倉庫中。並且不要隨意分享給其他人。
  5. 如果需要更換 master.key,可以執行 rails credentials:regenerate,這樣會重新產生 credentials.yml.enc 檔案,並且不會改變 master.key
    1
    $ rails credentials:regenerate
  6. 如果需要查看 credentials.yml.enc 檔案的內容,可以執行 rails credentials:show
    1
    $ rails credentials:show

參考資料

Install Python 3.10.x on Ubuntu 20.04

問題描述

在做一些 Python 專案的時候,需要用到 Python 3.10.x 以上的版本,但是 Ubuntu 20.04 預設的 Python 版本是 3.8.x,所以需要安裝 Python 3.10.x。

解決方法

Prerequisite

1
2
sudo apt update
sudo apt install software-properties-common -y

Add custom APT repository

1
2
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update

Press ENTER to confirm adding repository.

Install Python 3.10

1
2
3
sudo apt install python3.10 python3.10-venv python3.10-dev
python3.10 --version # Check Python version
sudo apt install python3.10-pip # Install pip for Python 3.10

參考資料

Nginx 502 Bad Gateway

問題描述

架構說明:

AWS LB -> EC2 * 4 (Nginx -> Puma 2 worker / 10 threads)

描述:

某些時段與某些 EC2 主機會出現 502 Bad Gateway,還沒有特定的規律。通常不會持續太久,問題發生時 CPU 會飆高,但是 Nginx 與 Puma 都沒有錯誤訊息。

推測:

可能是因為某些時段流量過大,導致 Nginx 與 Puma 之間的連線出現問題。 Nginx 的 Upstream 被 Puma 拒絕。

解決方法

  1. 嘗試從 TIME_WAIT 的角度來解決,調整 Nginx 與 Puma 的設定。由於 TIME_WAIT 的消失需要約 1 分鐘(2MSL),容易造成連線不足。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 可以先查看 Port 可用範圍,不夠大的話可以調整
    # port range - bucket size 約當 2 * worker * threads 即可
    $ cat /proc/sys/net/ipv4/ip_local_port_range
    $ cat /proc/sys/net/ipv4/tcp_max_tw_buckets

    # 開啟 TCP_TW_REUSE
    $ sysctl -w net.ipv4.tcp_tw_reuse=1
    # (option) NAT環境中不要開啟 TCP_TW_RECYCLE
    $ sysctl -w net.ipv4.tcp_tw_recycle=1
  2. 嘗試調整 Nginx Upstream 為長連接,避免短連接造成的問題。
    1
    2
    3
    4
    5
    upstream puma {
    server 127.0.0.1:5000;
    keepalive 32; # 這邊可以調整長連接數量
    keepalive_time 30s; # 這邊可以調整長連接時間
    }

參考資料

6 Log Parsing Commands

問題描述

六大日誌解析指令

解決方法

GREP

GREP 搜索任何給定的輸入文件,選擇匹配一個或多個模式的行。

  1. 查找匹配的文件名
    1
    $grep -l "PATTERN" *.log
  2. 不區分大小寫的單詞匹配
    1
    $grep -i "PATTERN" test.log
  3. 顯示行號
    1
    $grep -n "PATTERN" test.log
  4. 反向匹配
    1
    $grep -v "PATTERN" test.log
  5. 從文件中取模式
    1
    2
    $grep -f pattern_file test.log
    # pattern_file 內容以換行符分隔
  6. 在目錄中遞歸搜索
    1
    $grep -R "PATTERN" /var/log

CUT

CUT 從每個文件的每一行中剪出選定的部分,並將它們寫入標準輸出。

  1. 剪下前 3 個字節
    1
    $cut -b 1,2,3 test.log
  2. 選擇以空格分隔的第 2 列
    1
    $cut -d " " -f 2 test.log
  3. 指定字符位置
    1
    $cut -c 1-8 test.log

SED

SED 讀取指定的文件,根據命令列表修改輸入。

  1. 替換字符串
    1
    $sed 's/old/new/g' test.log
  2. 替換第二次出現的字符串
    1
    $sed 's/old/new/2' test.log
  3. 替換第四行的字符串
    1
    $sed '4s/old/new/' test.log
  4. 替換指定範圍行的字符串
    1
    $sed '2-4 s/old/new/' test.log
  5. 刪除一行
    1
    $sed '4d' test.log

AWK

AWK 掃描每個輸入文件中的行,匹配任意一組模式。

  1. 打印匹配的行
    1
    $awk '/PATTERN/ {print}' test.log
  2. 將一行分成多個字段
    1
    $awk '{print $1,$3}' test.log
  3. 打印第 2 到第 7 行
    1
    $awk 'NR==2, NR==7 {print NR, $0}' test.log
  4. 打印超過 10 個字符的行
    1
    $awk 'length($0)>10' test.log
  5. 在列中查找字符串
    1
    $awk '{ if($4=="PATTERN" print $0; }' test.log

SORT

SORT 按行排序文本和二進制文件。

  1. 輸出到文件
    1
    $sort -o output.txt input.txt
  2. 逆序排序
    1
    $sort -r test.log
  3. 數值排序
    1
    $sort -n test.log
  4. 基於第 3 列排序
    1
    $sort -k -3n test.log
  5. 檢查文件是否有序
    1
    $sort -c test.log
  6. 排序並刪除重複項
    1
    $sort -u test.log

UNIQ

UNIQ 讀取指定的輸入文件,比較相鄰的行,並將每個唯一的輸入行複製到輸出文件中。

  1. 告訴某行重複的次數
    1
    $uniq -c test.log
  2. 打印唯一的行
    1
    $uniq -u test.log
  3. 跳過前兩個字段
    1
    $uniq -f 2 test.log
  4. 打印重複的行
    1
    $uniq -d test.log
  5. 忽略大小寫比較
    1
    $uniq -i test.log

TAIL

搭配 GREP 使用,用於查看文件的末尾。

1
2
3
4
$tail -f test.log
$tail -f test.log | grep "PATTERN"
$tail -f test.log | grep -C 3 "PATTERN"
$tail -f test.log | grep -C 3 -i -E "PATTERN1|PATTERN2"

參考資料

ZTAIL

ztail 是 tail 的增強版,可以顯示壓縮文件的末尾。

1
$ztail test.log.gz

ztail 也可以搭配 GREP 使用,用於查看壓縮文件的末尾。
1
$ztail test.log.gz | grep "PATTERN"

ZGREP

zgrep 是 grep 的增強版,可以搜索壓縮文件。

1
$zgrep "PATTERN" test.log.gz

Nginx Settings Reference

問題描述

Nginx 需要注意一些設定值來調教,優化對高併發的處理能力。

解決方法

  1. backlog 參數決定 socket queue 的大小,預設為 511,參照內核參數調整,最高為 65535。
    1
    2
    3
    4
    server {
    listen 80 backlog=4096;
    ...
    }
  2. 關於全列隊與半列隊的設定,可以參考以下設定,預設為全列隊。全列隊長度的計算方式為 min(backlog, somaxconn),半列隊則是 min(backlog, somaxconn, tcp_max_syn_backlog)
    1
    2
    3
    4
    5
    6
    7
    8
    server {
    listen 80;
    # 全列隊
    accept_mutex on;
    # 半列隊
    accept_mutex off;
    ...
    }
  3. net.core.somaxconn 是內核參數,決定全列隊的大小,預設為 128,可以調整為 65535。 net.core.tcp_max_syn_backlog 是內核參數,決定半列隊的大小,預設為 1024,可以調整為 65535。
    1
    2
    3
    4
    5
    6
    $ sysctl -w net.core.somaxconn=65535
    $ sysctl -w net.core.tcp_max_syn_backlog=65535
    # 永久生效
    $ echo "net.core.somaxconn=65535" >> /etc/sysctl.conf
    $ echo "net.core.tcp_max_syn_backlog=65535" >> /etc/sysctl.conf
    $ sysctl -p

參考資料

  1. SYNC Cookie 時,net.ipv4.tcp_syncookies = 1,可以防止 SYN Flood 攻擊。但此時 tcp_max_syn_backlog 會被忽略,因為 SYN Cookie 會將 SYN 放在 Cookie 中,所以不會佔用半列隊的空間。
  2. 全列隊與半列隊的差異,全列隊是一個全局的列隊,半列隊是每個 listen socket 有一個列隊。用途上,全列隊適合高併發,半列隊適合低併發。例如,全列隊適合網站,半列隊適合 API 服務。

Remove Domain from Nginx & Certbot

問題描述

之前都沒有思考過要不要移除 Certbot 的 SSL 憑證,因為測試的 domain 比較多了,也有更換 domain 的需求,所以就想說移除掉不用的 domain。

解決方法

ㄧ、移除 Nginx 的設定檔

1
2
3
4
5
6
7
# Remove the domain config files
$ sudo rm /etc/nginx/sites-available/sample.com
$ sudo rm /etc/nginx/sites-enabled/sample.com

# Check valid configurations then reload
$ sudo nginx -t
$ sudo service nginx restart

二、移除 Certbot 的憑證

1
2
3
4
5
# List of certificates
$ sudo certbot certificates

# Remove certificates for a domain
$ sudo certbot delete --cert-name sample.com

參考資料

Namecheap - Create CSR Using OpenSSL

問題描述

公司使用 Namecheap 購買 SSL 憑證,將 CSR 產生的過程紀錄一下。

解決方法

  1. 在 Issue SSL 訂單頁面,點選 Activate
  2. 選擇 Generate a new CSR,填入資料。
  3. 產生私鑰與 CSR 檔案。
    1
    2
    3
    4
    5
    6
    # 私鑰 private.key 與 CSR csr.pem 檔名都可以自行命名:
    $ sudo openssl genrsa -out private.key 2048
    $ sudo openssl req -new -key private.key -out csr.pem
    # 你也可以合併兩個指令:
    $ sudo openssl req -new -newkey rsa:2048 -nodes -keyout private.key -out csr.pem
    # 填寫資料的時候,Common Name 要填寫你的網域名稱,例如: example.com or *.example.com ... etc.
  4. 將 CSR 檔案內容貼到 Namecheap 的 CSR 欄位。
    1
    $ sudo cat csr.pem
  5. 等待驗證通過,即可下載 SSL 憑證。

參考資料

Generating A CSR on AWS

Logrotate - Linux log management

問題描述

在公司的 EC2 主機上,我們是運行在 Linux 系統的基礎上,然而服務的日誌檔會不斷的累積,如果不處理的話,會佔用很多空間,所以這邊紀錄一下如何使用 logrotate 來管理日誌檔。當然關於日誌檔的管理,還可以配合 CloudWatch Logs 來做,以後再補充。

解決方法

  1. 安裝 logrotate
    1
    $ sudo yum install logrotate
  2. 建立設定檔
    1
    $ sudo vim /etc/logrotate.d/<file_name>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /var/log/<file_name> {
    daily
    size 500M
    rotate 7
    compress
    missingok
    notifempty
    create 0644 <user> <group>
    }
    • daily: 每天執行一次
    • size: 檔案大小超過多少時,就執行 (即可不設週期)
    • rotate: 保留幾份日誌檔
    • compress: 壓縮舊的日誌檔
    • missingok: 如果日誌檔不存在,不要報錯
    • notifempty: 如果日誌檔為空,不要報錯
    • create: 建立新的日誌檔,並指定擁有者與群組
  3. 測試
    1
    $ sudo logrotate -d /etc/logrotate.d/<file_name>
  4. 設定 crontab
    1
    $ sudo vim /etc/cron.daily/logrotate
    1
    2
    3
    4
    5
    6
    7
    8
    #!/bin/sh

    /usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf
    EXITVALUE=$?
    if [ $EXITVALUE != 0 ]; then
    /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
    fi
    exit 0
    1
    $ sudo chmod 755 /etc/cron.daily/logrotate
  5. 測試
    1
    $ sudo logrotate -f /etc/logrotate.conf
  6. 查看 logrotate 狀態
    1
    $ sudo cat /var/lib/logrotate/logrotate.status
  7. 查看 logrotate.conf 設定檔
    1
    $ sudo cat /etc/logrotate.conf
  8. 查看 logrotate.d 服務設定檔
    1
    $ sudo cat /etc/logrotate.d/<file_name>
  9. 查看 crontab 的運行週期
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    sudo cat /etc/anacrontab

    # /etc/anacrontab: configuration file for anacron

    # See anacron(8) and anacrontab(5) for details.

    SHELL=/bin/sh
    PATH=/sbin:/bin:/usr/sbin:/usr/bin
    MAILTO=root
    # the maximal random delay added to the base delay of the jobs
    RANDOM_DELAY=45
    # the jobs will be started during the following hours only
    START_HOURS_RANGE=3-22

    #period in days delay in minutes job-identifier command
    1 5 cron.daily nice run-parts /etc/cron.daily
    7 25 cron.weekly nice run-parts /etc/cron.weekly
    @monthly 45 cron.monthly nice run-parts /etc/cron.monthly
    以上就會是你的 crontab 日週月的運行時間,如果 logrotate.d 中的各設定檔有指定 size 參數,則可以不必設定週期,會依照日誌大小做切割管理。

關於 Daily 執行的小問題

由於使用 daily 的設定來執行 logrotate 的話,他其實是在一個執行時間範圍內去執行,無法指定執行的時間,所以我們可以考慮自己設定一個 cron job 來執行,以下是設定檔的範例:

  1. 將 /etc/logrotate.d/<設定檔>搬到一個新的目錄,例如 /etc/logrotate.d/daily
    1
    2
    $ sudo mkdir -p /etc/logrotate.d/daily
    $ sudo mv /etc/logrotate.d/<設定檔> /etc/logrotate.d/daily/.
  2. 建立一個新的 cron job
    1
    2
    3
    $ sudo crontab -e
    # 每天 23:59 執行 logrotate
    59 23 * * * /usr/sbin/logrotate -f /etc/logrotate.d/daily/<設定檔> > /dev/null 2>&1
    這樣應該就可以指定時間去執行 logrotate 了。那麼原先的 daily 是在什麼時候執行呢?以 cron.daily 為例,是在每天的 03:05 執行,但因為 RANDOM_DELAY 的關係,所以實際上會在 03:05 ~ 03:50 之間的某個時間執行。又因為 START_HOURS_RANGE 的關係,所以實際上會在 03:05 ~ 22:00 之間的某個時間執行。有這樣的不確定性存在。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ sudo cat /etc/anacrontab
    # the maximal random delay added to the base delay of the jobs
    RANDOM_DELAY=45
    # the jobs will be started during the following hours only
    START_HOURS_RANGE=3-22
    #period in days delay in minutes job-identifier command
    1 5 cron.daily nice run-parts /etc/cron.daily
    7 25 cron.weekly nice run-parts /etc/cron.weekly
    @monthly 45 cron.monthly nice run-parts /etc/cron.monthly

參考資料

Linux 日誌切割神器 logrotate 原理介紹和配置詳解
linux logrotate 精確時間點執行切割檔案
logrotate自定义切割时间的一些坑

AWS Memos

說明

工作中的主要部署環境是 AWS,所以這邊紀錄一些常用的指令。

TIPS

  1. CloudFront 中的 504 錯誤
    這是一個有趣的問題,在我們的專案裡,504 gateway timeout 的原因是 server 端的 websocket faye 的連線逾時設定為 60 秒,而因為 CloudFront 有自己的 timeout 設定,預設是 30 秒,所以當連線超過 30 秒沒有回應時,就會回傳 504 錯誤。在把 faye 的連線逾時設定改為 50 秒並將 CloudFront改為 60 秒後,就不會再出現 504 錯誤了。

  2. EC2 的 EBS 空間擴展
    檢查 File System Type: df -hT
    檢查 Volume ID: lsblk (xvda or nvme0n1看這裡)
    擴展 Volume: sudo growpart /dev/xvda 1 or sudo growpart /dev/nvme0n1 1
    a. 擴展 xfs File System: sudo xfs_growfs /dev/xvda1 or sudo xfs_growfs -d /
    b. 擴展 ext4 File System: sudo resize2fs /dev/xvda1 or sudo resize2fs /dev/nvme0n1p1

  3. DynamoDB Local 的使用
    我打算用 Docker Image 來跑本地端的 DynamoDB 開發,安裝時遇到一個使用者權限問題, 如果直接使用 image,data 的路徑是 root 建立的,所以 docker user 沒有權限寫入,所以我決定自己建立一個 image,以下是我的 Dockerfile:

    1
    2
    3
    4
    FROM amazon/dynamodb-local
    WORKDIR /home/dynamodblocal
    RUN mkdir ./data && chown -R 1000 ./data
    CMD ["-jar", "DynamoDBLocal.jar", "-sharedDb", "-dbPath", "/home/dynamodblocal/data"]

    然後要整合 Docker Compose 的話,可以參考以下的設定:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    version: '3.8'
    services:
    dynamodb-local:
    build:
    context: .
    dockerfile: Dockerfile
    ports:
    - 8000:8000/tcp
    volumes:
    - dynamodb_data:/home/dynamodblocal/data

    這樣就可以在本地端使用 Docker Image 來跑 DynamoDB Local 了。用以下指令來測試一下連線:

    1
    aws dynamodb list-tables --endpoint-url http://localhost:8000

    如果有成功的話,就會看到以下的回應:

    1
    2
    3
    {
    "TableNames": []
    }
  4. CloudWatch Agent EC2 Instances 監控設定

    1. 安裝 CloudWatch Agent in Ubuntu EC2 Instances
      1
      2
      wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
      sudo dpkg -i -E ./amazon-cloudwatch-agent.deb
    2. IAM 權限設定要加入 CloudWatchAgentServerPolicy,這樣才能正確的上傳資料到 CloudWatch
    3. 執行 CloudWatch Agent Wizard
      1
      sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
      這邊要注意的是,如果是要監控 EC2 Instances 的話,要選擇 EC2,如果是要監控 Docker 的話,要選擇 Others,然後選擇 CollectD,其他的就照著 wizard 的指示設定就可以了。
    4. Logs的設置可以在稍後的設定檔中修改,我的部分大概長這樣:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
       {
      "agent": {
      "run_as_user": "root"
      },
      "logs": {
      "logs_collected": {
      "files": {
      "collect_list": [
      {
      "file_path": "/home/user/app/log/sample.log*",
      "log_group_name": "app",
      "log_stream_name": "{instance_id}",
      "retention_in_days": 180,
      "filters": [
      {
      "type": "include",
      "expression": "P(UT|OST)|GET"
      }
      ]
      }
      ]
      }
      }
      }
      }
    5. 設定完成後,會產生一個 amazon-cloudwatch-agent.json 的設定檔案,可以用以下指令來啟動 CloudWatch Agent:
      1
      2
      3
      4
      5
      6
      7
      8
      # Ubuntu 要有安裝 collectd
      sudo apt-get update && sudo apt-get install collectd -y
      # 啟動 CloudWatch Agent
      sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s
      # 查看 CloudWatch Agent 狀態
      sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a status
      # 停止 CloudWatch Agent
      sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a stop

參考資料

  1. CloudFront 504 Gateway Timeout
  2. CloudWatch Agent Wizard
  3. CloudWatch Agent Manual
  4. 調整磁碟區大小後擴展 Linux 檔案系統
  5. Day 27 將 EC2 的 Log 轉發到 CloudWatch Log

Docker Memos

說明

作為一個後端工程師,Docker總是要用到的,所以這邊紀錄一些常用的指令。包含 Docker Compose 使用上實際會需要知道的部分。

TIPS

  1. If else statement in Dockerfile

    1
    2
    3
    4
    5
    RUN if [ "$XYZ" = "ABC" ] ; then \
    echo do something ; \
    else \
    echo do something else ; \
    fi
  2. Docker Compose 的 network 設定
    在編輯 docker-compose.yml 時,每個專案都會有預設的 network,但是如果要讓不同的專案可以互相溝通,就需要設定 network,讓不同的 container 之間可以透過 service name 來互相溝通。而預設的 network 會是 <專案名稱>_default,以下是一個自訂 network 的範例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 專案A
    version: "3.9"
    services:
    web:
    build: .
    ports:
    - "5000:5000"
    networks:
    - my-network
    redis:
    image: "redis:alpine"
    networks:
    - my-network
    networks:
    my-network:

    也就是說,如果要讓 web container 可以透過 service name 來連接 redis container,就需要將兩個 container 加入同一個 network。同樣的,另一個專案也可以使用同一個 network,這樣就可以讓兩個專案的 container 互相溝通。
    另外,external network 會嘗試使用已經存在的 network 不會在 up 的時候建立新的 netwrok,只要在 networks 加上 external: true 即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 專案B
    version: "3.9"
    services:
    app:
    build: .
    ports:
    - "8000:8000"
    networks:
    - my-network
    db:
    image: "postgres"
    networks:
    - my-network
    networks:
    my-network:
    external: true

    P.S. 手動建立 network 的指令為 docker network create

  3. Docker Permission Denied on Ubuntu
    當我在 Ubuntu 上執行 docker 時,會出現以下錯誤訊息:

    1
    Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.39/containers/json: dial unix /var/run/docker.sock: connect: permission denied
    1. 這是因為 docker 預設只有 root 可以執行,所以要加上 sudo
    2. 或是可以將自己加入 docker group
      1
      2
      3
      $ sudo groupadd docker
      $ sudo usermod -aG docker $USER
      $ newgrp docker
    3. 有時候需要做更多的權限設定(其中之一)
      1
      2
      3
      4
      5
      # Group ownership
      $ sudo chown root:docker /var/run/docker.sock
      # Group ownership of user's home directory
      $ sudo chown "$USER":"$USER" /home/"$USER"/.docker -R
      $ sudo chmod g+rwx "$HOME/.docker" -R

參考資料