Jex’s Note

Golang - Redis

redigo

github.com/garyburd/redigo/redis

Connect to redis server

One connection

conn, err := redis.Dial("tcp", ":6379")
if err != nil {
    panic(err)
}
defer conn.Close()

Connection pool

pool := &redis.Pool{
    MaxIdle:     300,               // Maximum number of idle connections in the pool.
    MaxActive:   5,                 // Maximum number of connections allocated by the pool at a given time.
    IdleTimeout: 20 * time.Second,  // Close connections after remaining idle for this duration. Applications should set the timeout to a value less than the server's timeout.
    Dial: func() (redis.Conn, error) {
        c, err := redis.Dial("tcp", "127.0.0.1:6379")
        if err != nil {
            return nil, err
        }
        return c, err
    },
    TestOnBorrow: func(c redis.Conn, t time.Time) error {
        _, err := c.Do("PING")
        return err
    },
}

// 檢查連線是否成功
_, err := pool.Dial()
if err != nil {
    return nil, err
}

Command

GET

val, err := redis.String(conn.Do("GET", "key"))

SET

_, err := conn.Do("SET", workerID, "value")

DEL

_, err := conn.Do("DEL", "key")

LLEN

count, err := redis.Int(conn.Do("LLEN", "list_key"))

LPOP

val, err := redis.Bytes(conn.Do("LPOP", "list_key"))

RPUSH

_, err := conn.Do("RPUSH", "list_key", "val")

HGETALL

res, err = redis.StringMap(conn.Do("HGETALL", "key"))

MHSET

var args []interface{}
args = append(args, "my_hash_key")
for field, val := range hash {
args = append(args, field, val)
}
_, err = conn.Do("HMSET", args...)

or

_, err = conn.Do("HMSET", redis.Args{}.Add("my_hash_key").AddFlat(hash)...)

HGET

val, err = redis.String(conn.Do("HGET", "key", "field"))

HSET

_, err = conn.Do("HSET", key, field, value)

KEYS (show keys with prefix)

vals, err := redis.Values(conn.Do("KEYS", "device:*"))

// 將第一個 key 取出來
_, err := redis.Scan(vals, &value)

其他

如果重覆用同一條 connection 做 del key 的動作可能會引發隨機出現的錯誤

use of closed network connection, short write

原因 : Concurrent writes are not supported

解決方法 : 使用 connection pool,要用的時候就拿一個新的 connection,不用的時候再放回去

Error dial tcp 127.0.0.1:6379: too many open files

使用 pool 要注意取出來的 connection 要釋放, 如果沒釋放有可能會出現此 error,因為系統資源的限制, 一個 process 不能超過 1024 個 socket

redisConn := redisPool.Get()
defer redisConn.Close()

Docker 基本教學

介紹

有點像 VM 但又不全然是 VM, 最主要它跟一般 VM 的差別在於 VM 所需的 OS 及 bins/libs 是安裝在 VM 本身裡面的

但 docker 的 container 概念會共用 OS 及 bins/libs, 但他們仍是獨立的, 由 Docker Engine 管理

最大好處可以快速啟動環境並且設定好, 很適合用在自動化測試或開發上

基本觀念

名詞解釋

  • image : 用來建立 container 的東西, 每一個 image 都只能讀且不能被改變
  • container : 是由一個 image 建立的一個的正在執行中的環境, 可以把它想成一台主機
  • 官方 registry : 就是 Docker Hub

Image

  • 一個完整 image 的名稱是 : username/image_name:tag
  • 一個 image 可以跑多個 container
  • 可以 commit 這個 container 的變動製作新的 image
  • image 想當於是 AWS EC2 的 AMI

Container

  • 把它想像 AWS EC2 的 Instances 清單 (相當於 docker ps -a)
  • 每一個主機都是一個 container,有些是 stopped 有些是 running (相當於 docker ps, 只列出 running container)
  • 只要被 Stop 的主機 (相當於 docker stop) 都是關機的狀態但還在列表上 (相當於還存在在 list 上 docker ps -a)
  • 被 Stop 的主機你可以再對它執行 Start (相當於 docker start)
  • 你可以對主機打成 image (相當於 docker commit)
  • 而被 Terminate 的主機就會被刪除,你無法在 Instances 上看到 (相當於 docker rm 後你無法在 docker ps -a 上再看到它)
  • 比較不一樣的是,執行中的 container 可以是直接進入(就像執行 ssh 到主機一樣)也可以是背景執行
  • 停止 container 不只是關機這麼簡單,連修改過的資料都會不見 (但可使用 mount 外部的檔案或資料夾來解決)

安裝

mac

可從官網下載

ubuntu

sudo apt-get install update && sudo apt-get install docker.io

執行 sudo adduser <username> docker, 將你加入到 docker group

windows

下載頁面

Exec : C:\Program Files\Boot2Docker for Windows\start.sh

putty / xshell connect to docker@127.0.0.1:2022

  • 預設的 VM VirtualBox 就已經設定好 port forwarding 了, 預設是 127.0.0.1:2022
  • Default account (docker / tcuser)

開始試玩

將 mysql 跑起來

Pull image mysql from docker hub

docker pull mysql

Run container mysql

docker run -t -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql

Test

mysql -h 127.0.0.1 -u root -p

將 redis 跑起來

Pull Docker Hub 上的 redis image

docker pull redis

將 redis image 啟動成 container, 並且將 6379 port 接出來

docker run -t -d -p 6379:6379 redis:latest

測試用 redis-cli 是否可以連進去

將 ubuntu image 跑起來 (建立一個 container)

Pull Docker Hub 上的 ubuntu image

docker pull ubuntu

以 ubuntu image 建立一個 container

docker run -t -d ubuntu

看看執行中的 container 有哪些,你可以看到剛剛建立的 container

docker ps

CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                               NAMES
bba4d449e053        ubuntu                       "/bin/bash"              52 seconds ago      Up 51 seconds                                           sharp_mestorf

Stop 後 docker ps 就看不到它了

docker stop bba4d449e053

但你顯示全部的 container 就看的到了

docker ps -a

你可以完全地刪除它

docker rm bba4d449e053

常用指令

build : 建立寫好的 Dockerfile

docker build -t jxxx/golang-env .

images : image list

docker images

rmi : 刪除 image

docker rmi eg_sshd

// 刪除全部 image
docker rmi $(docker images -q)

inspect : 查看此 container 的 info (不常用)

docker inspect f244dc51e1d9

ps : running container list (有哪些主機正在跑)

docker ps
  • -a : 所有 container ,即使是被 stop 的都會列出來
  • -q : 只顯示 CONTAINER ID

stop : 停掉在跑的 container (相當於關機)

docker stop {CONTAINER ID}

// 停掉全部在跑的 container
docker stop $(docker ps -a -q)

start : 將一個已經被 stop 的 container 再跑起來 (相當於開機)

docker start {CONTAINER ID}

rm : 刪除 container (相當於刪除這台主機)

docker rm {CONTAINER ID}

// 刪除全部 container
docker rm $(docker ps -a -q)

stop vs rm

  • stop : 將這個 container 關機並保存它的狀態, 只能在 docker ps -a 看到它, 你可以對它 commit 製作新的 image
  • stop : It sends SIGTERM first, then, after a grace period, SIGKILL.
  • rm : 將這個 container 真的刪除, 無法在 docker ps -a 看到它
  • rm : 你沒辦法 remove 一個 running container,必須先 stop 再 remove

start vs run

  • start : 將一個被 stop 的 container 重新啟動,就像開機一樣
  • run : 主機(container) 還沒建立,所以要將一個 image 建立一個 container

run

以 ubuntu image 建立 container 並進入到 container 裡面 (有點像 SSH 連進去,但 exit 後就是 stopped 的狀態了)

docker run -t -i ubuntu:14.04 /bin/bash
  • 如果沒有指定版本,預設會用最新的 ubuntu:latest
  • flag 可簡化為 -it
  • bash 也可以指定絕對路徑 /bin/bash

背景執行 container

docker run -t -d ubuntu:14.04

在 container 裡執行指令

docker run jxxx/golang-env touch ff.md

本機與 container 所有的 port

docker run -d -P jxxx/golang-env

-P flag to publish all of the exposed ports.

50080 port -> 80 port

docker run -d -p 50080:80 jxxx/golang-env

-p flag to publish a range of ports

將 domain 對應到 image name

docker run -d --link my_mysql:db.example.com jxxx/golang-env

將目錄名稱 folder_src mount 到 container 家目錄下(?)

docker run -v `pwd`/folder_src:/home/jex/into_container jxxx/golang-env ls -al into_container

Exec

進入正在執行中的container

docker exec -it <containerIdOrName> bash

Cp

將檔案複製到 container 裡

docker cp foo.txt {CONTAINER ID}:/foo.txt

將檔案從 container 裡複製出來

docker cp {CONTAINER ID}:/foo.txt ./

Commit

docker commit -m "Add ff.md" e3015 jxxx/golang-env

Push

docker push jxxx/golang-env

Attach

  • docker attach [CONTAINER ID] : 進入 container

Logs

將 container 的標準輸出接出來

docker logs {CONTAINER ID}

在 Docker config 設定將 log 接到標準輸出

RUN ln -sf /dev/stdout /var/log/test.log

其他指令

  • expose
  • add/copy
  • volume
  • attach/detach
  • pause/unpause
  • link between containers

Dockerfile 怎麼寫

FROM : 使用每個 image 為基礎

FROM ubuntu
FROM ubuntu:14.04

MAINTAINER : 設定產生的 imgage 的 Author

MAINTAINER Jex "xxxxxxx@gmail.com"

USER : 使用哪個 user 去執行 RUN, CMD etc.

USER daemon

RUN : 執行指令 (使用預設的 shell /bin/sh -c)

RUN apt-get update && apt-get -y upgrade

CMD : 執行 command

CMD echo "This is a test." | wc -       // 預設是使用 shell 執行  /bin/sh -c

CMD ["/usr/bin/wc","--help"]            // 執行其他指令不使用 shell 的話要使用 JSON format

ENV : 設定環境變數

ENV HOME /root

WORKDIR 設定工作目錄 (for any RUN, CMD, ENTRYPOINT, COPY and ADD in the Dockerfile)

WORKDIR /root
或
WORKDIR ${HOME}

ADD : 新增檔案

ADD xx.md /root
ADD xx.md $HOME

ADD test relativeDir/          # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/         # adds "test" to /absoluteDir/

COPY : 複製檔案

COPY \$HOME /tmp

COPY test relativeDir/   # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/  # adds "test" to /absoluteDir/

EXPOSE : 開放哪些 Port

EXPOSE 80 443

HEALTHCHECK (https://docs.docker.com/engine/reference/builder/#/healthcheck)

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

SHELL : 更改預設的 shell (linux default : /bin/sh -c)

SHELL ["executable", "parameters"]

# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello

docker-compose

啟動

docker-compose up -d

指定 config

docker-compose -f custom.yml up -d

config

docker-compose.yml

version: "2"
services:

  test-redis:
    container_name: test-redis
    image: redis
    expose:
      - "6379"
    ports:
      - "6379:6379"
    volumes:
      - redis-data-test:/data-test

  test-mysql:
    container_name: test-mysql
    image: mysql/mysql-server:5.7
    expose:
      - "3306"
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
    volumes:
      - dbdata-test:/var/lib/mysql

volumes:
  redis-data-test:
  dbdata-test:

Q&A

dial unix /var/run/docker.sock: permission denied

2014/12/23 14:14:33 Get http:///var/run/docker.sock/v1.12/images/json: dial unix /var/run/docker.sock: permission denied

sudo adduser <username> docker

Raspberry Pi With Ffmpeg

rpi camera 只支援輸出 raw h.264

雖然 chrome 跟 safari (firefox 不援, 而 safari 因為是 call 自己的 quicktime 去播放影片所以支援的又更廣) 都支援解碼 h.264 codec 但不支援解 raw h.264

但可以解 container 一定要是 mp4, 所以輸出 h.264 還需要經過一層轉檔成 mp4 才能在瀏覽器上看到

install ffmpeg

sudo apt-add-repository ppa:jon-severinsson/ffmpeg
sudo apt-get update
sudo apt-get install ffmpeg

convert raw h264 to mp4

ffmpeg -r 30 -i test2.h264 -vcodec copy test2.mp4

ffserver

/etc/ffserver.conf 加上

<Stream live.mp4>
   Format mp4
   Feed feed1.ffm

   VideoCodec libx264
   VideoFrameRate 30
   VideoSize 320x240

   NoAudio
</Stream>

啟動 ffserver : ffserver -f /etc/ffserver.conf

將 ffmpeg 轉檔的 stream 傳到 feed1.ffm : ffmpeg -i /tmp/t.h264 -copyinkf -codec copy -f mpegts udp://localhost:8090/feed1.ffm

打開瀏覽器 : http://107.185.47.26:9080/live.mp4

MP4 H.264 (AAC or MP3)

H.264 video codec 及 ACC audio codec 的 MP4 container 格式可以被桌機及手機的 IE, Safari, Chrome 原生支援, 但 Chromium 及 Opera 不支援此格式, IE 及 Chrome 也支援 MP4 container 裡的 MP3 audio codec, 但 Safari 不支援, 不過 Firefox 在 Android 及 Firefox OS 裡只有在 thrid-party decoder 支援的情況下才支援

Browser support

  • chrome : webm ogv mp4
  • firefox : webm ogv
  • opera : webm ogv
  • safari : mp4
  • ie10, ie9 : mp4

加密方式 (AES)

AES

可正解及反解, 常用的有 ECB, CBC

ECB 速度快, 但每一次編碼後都長一樣

CBC 較耗效能, 但每一次編碼後會不一樣, 安全性高

ECB, CBC Benchmark 與如何選擇

這是由前同事 Chris 測試出來的效能比較與結論

1) 測試內容:

台北市長當選人柯文哲以85萬票獲得台北市民青睞當選台北長,然而,開票勝選以來,無論在市府人事與政策政見上,柯文哲接慢慢浮現不少有待討論的議題與爭議。人事上,包括台北市副市長林欽榮與勞動局長遴選制度都出現爭議;政策政見上,一上任就面對忠孝西路公車專用道是否拆除的兩難,現在又突然拋出裁撤派出所的想法,這些風波紛紛引發網友熱議。

2) 內容長度: 162個字

3) 測試方法: 連續對測試內容執行 AES 加密解密 10 萬次

4) 測試平台: dlink device S150

5) 測試結果

AES performace  |  CBC mode     ECB mode
--------------------------------------------------
cost time       |  9895 msec    9715 msec
cost time       |  9894 msec    9703 msec
cost time       |  9849 msec    9726 msec

6) 結論: 使用 cbc mode 對平台增加的負擔近乎其微, 為了資料安全性, 使用 cbc mode 是更好的方式

AWS 各項服務

(最後更新: 2016-04-27)

介紹

AWS (Amazon Web Services) 是針對主機或 App 提供整套 solution 的雲端服務

Install/Upgrade AWS CLI

安裝

sudo pip install --upgrade awscli

如果安裝出問題, 強制重新安裝可以解決

sudo pip install awscli --force-reinstall --upgrade
  • Ubuntu (建議不要使用 apt-get 安裝, 版本很舊)
  • 有些安裝完會有設定檔 /var/awslogs/etc/awslogs.conf

Stop the agent

sudo service awslogs stop   // Works on Ubuntu
sudo service awslogsd stop  // Amazon Linux 2

stop|restart|start

Region & AZ

  • region 是 Data Center 的集合
  • AZ 與 AZ 至少隔70公里以上
  • AZ 與 AZ 是用光纖連接
  • AZ 裡有很多 DC
  • 每個 DC 是 25Tbps
  • 同一 region 我的帳號的 eu-west-1a 跟你的帳號的 eu-west-1a 不一定是同一個 AZ, 避免太多人選擇同一 AZ

IAM

  • 如果有很多開發者要共用一個 AWS 帳號, 這算是必用的服務
  • 可以針對不同開發者給與不同權限
  • 一般來說程式透過 Access key & Secret Key 調用 AWS API 是要寫在程式裡的, 但會遇到 Credentials embedded in code問題, 如果 push 到 public 的 github 就有資安風險, 也可以改成在 ec2 裡(環境)設定 secret key 較安全 (aws command)
  • 角色可分 3 種 :
    • user : 可以讓多個開發者共用同一個 AWS, 每個人可以有不同的登入帳密及權限
    • group : 可以設定權限, 再把要執行這些權限的 user 圈到這個 group 裡
    • role : 這是給 ec2 執行的權限, 開 ec2 時 IAM role 選這個, 而此 ec2 就有權限執行開放的服務

[操作] 增加一個登入帳號給團隊的成員

Users -> Create Users -> 輸入 user name -> copy Access Key ID 及 Secret Access Key (目前還不會用到) -> 回到 Users 列表就會看到剛剛新加的 user

但加完帳號其實還沒有密碼 : Has Password: No, 所以在那 User 下面選擇 Manage Password 可以手動指定一組密碼給它

接著他只要到 IAM users sign-in link 輸入他的 user name 及 password, 就可以登入了

如何用同一個瀏覽器切換不同的 AWS account

假設有 QA & Prod 的 AWS account

  1. 在 QA 建立一個 IAM Role 選擇 Another AWS Account 填入 Prod account ID, 然候權限給予 AdministratorAccess
  2. 在 Prod Account 右上角選擇 Switch Role, 填入 QA Account ID 及上一步建立的 Role name

Route 53

DNS 服務, 使用上很直覺簡單. 一個 domain 可以對應多個 ip (可以填 TW 及 US IP)

[操作]

Create Hosted Zone

輸入 Domain Name 後, 會新增一筆 Zone (EX: qq.test.com), 然候進入 Zone 裡面

AWS 預設裡面會有兩筆 Record

自己再新增每一筆 Sub domain record :

s1.qq.test.com   A   55.123.122.33

如果上例設定的 Zone 也是主站的 sub domain, 必須在去主要的 domain 設定

首先先去 qq.test.com 這個 Zone copy NS 那筆 Record 的 Value, 再到 test.com 新增一筆 Record

qq.test.com    NS    (將剛 copy 的 ns value 貼到這的 value field)
  • A : 將 domain 指向 address
  • NS : 將 domain 指向 Name Server

一個 domain 對全球,讓 route 53 自動幫我們把 user 的 region 導到對應的 region 的 elb

1) 先設定 domain 對應到 ELB 的 endpoint

2) domain 右邊設定 Routing Policy 選擇 lantency, Set ID 隨便打,它們就對應好關係了

3) user 連上來會根據 latency 指派到最近 region 的 elb

Internal domain

如果建立 mysql 要使用 interntal domain e.g. mysql.xxx.local 指到那台 endpoint 要記得將它的 VPC 的 hostnames 打開,否則不會通

不知道有沒有打開可以到 VPC 那個網段可以下面是否顯示 DNS hostnames: yes

VPC

  • 切割 sub-net
  • 與 AZ 是沒關係的, 而是用 IP 去指定 AZ
  • (? 不太懂, 不太確定) 它的 group 是 stateless, 而 EC2 的 security group 是 stateful 的, EC2 的防火牆 ACL 進或出其中一個有通就行了, VPC 是進出都要通才行
  • Route 可指定 instance (作 NAT), IGW, VGW (外部儲存 data center)
  • 預設的 VPC 不要刪掉, 否則之後要建立 CloudFormation 會有問題而無法建立

[備註]

  • EIP = Elastic IP
  • NET ACL = NET Access Control List
  • IGW = Internet Gateway
  • ENI = Elastic Network Interfaces

[操作步驟]

1) Your VPCs : 建立一個 VPC, CIDR block 為 10.0.0.0/16
2) Subnets : 實務上可以依這樣分類去切, 這樣對之後設定 security group 比較方便
10.0.1.0/24     : base
10.0.11.0/24    : api-zone-a
10.0.12.0/24    : api-zone-b
10.0.21.0/24    : db-zone-a
10.0.22.0/24    : db-zone-b

能不能對外可以再以 security group 去個別設定某個網段(i.e. 10.0.11.0/24)要不要對外

3) Internet Gateways : 建立一個可以讓外部連進來的 Gateway, 再 attach 到 vpc
4) Route Tables : 建立兩個 Route Table

建立好的 VPC 的 Routes (在畫面下面) 新增 0.0.0.0/0 指向步驟 3 建立的 internet gateway.

Subnet Associations 指向步驟2建立的 subnet

注意! 要連到外面 Routes 一定要將 0.0.0.0/0 指向 internet gateway, 並且 Security Groups 的 SSH Source 也要記得設定成 0.0.0.0/0

5) Security Groups

Inbound :

TYPE            Port    Source
==================================
ALL Traffic     ALL     10.0.0.0/16
SSH             22      0.0.0.0/0
HTTP            80      0.0.0.0/0
HTTPS           443     0.0.0.0/0
ALL ICMP        ALL     0.0.0.0/0           # ping
6) EC2

Security Group 選擇步驟5建立的 Private Group

Public :

Network : 選擇剛建立的 VPC
Subnet : 選擇 Public subnet
Auto-assign Public IP : Enabled

Private :

Network : 選擇建立的 VPC
Subnet : 選擇 Private subnet
Auto-assign Public IP : Use subnet setting (Disable)   => 就算 Enabled 也沒有用, 因為外部無法 Access 到裡面

總結

EC2 主要是看 Security Group, 但 EC2 是掛在 VPC 的 Subnet 裡面, 每個 Subnet 又有他自己的 Route, 所以如果要對外的話

Route 要先設定成對外(加上 internet gateway), 而 EC2 的 Security Group 設定允許外面連進來的的防火牆 rule 才有意義

[VPC connect another vpc in the same region]

VPC 下的 Peering Connections

[VPC cross region, 需透過 VPN 達成]
  • 不同 region 的 VPN 就要其中一邊有一個 EC2 連到另一個 region 的 VGW (只需一台 EC2 即可)
  • Connect Multiple VPCs with EC2 Instances (IPSec or SSL)
  • 在 VPC 主頁(不是 sidebar)下面就有個 Create VPN Connection, 必須要先建立 Private Gateway 才可以建立 VPN
  • 或者用兩個 EIP 的 EC2 用 VPN 軟體將兩個 VPC 連起來

EC2

  • 就像是一台 VPS 主機讓你使用, 不支援 IPv6, 裡面絕對不會有 AWS 程式跑在裡面 (阿里雲好像會)
  • 要注意的是在主機裡面設 iptables 是沒有用的, 必須在 console -> EC2 -> Security group 設定才算生效
  • Design for failure : 務必將兩台EC2放在不同AZ, 避免兩台 EC2 在同一個 AZ 又剛好在同一台物理機上
  • 比較要注意的是 public ip 要不要打開, 建議都要開啟, 否則 apt-get update 都會連不出去

申請固定IP

Elastic IPs -> Allocate New Address

[備註]

  • Create Image : 將指定的 EC2 打成映像檔, 會被存在 AMIs 裡, 需要啟動一樣的 EC2 直接 Launch 就行了
  • Snapshots : EC2 輩份機制存放的地方, 是 server 當前的狀況, 如果要 Launch 要先 Create Image, 再到 AMIs 那 Launch
  • Meta data : dns ip, public ip 等等設定
  • User data : 要執行的 script, 不要用 windows 因為換行字元關係, 所以 EC2 執行會失敗. 最多 16k 字節, 但可以用 wget 來解決此限制

IPv4 and IPv6

IPv6 was supported on “EC2 Classic” but not on EC2 VPC. And EC2 Classic is no longer available for new AWS accounts. Also, EC2 Classic is deprecated and should not be used, all new accounts use VPC.

Update (Sept 2016): IPv6 is now supported for Elastic Load Balancer end points. Internally in the VPC, IPv4 is still used, but it’s now possible to provide an IPv6 endpoint for public web sites (important for Apple devices!)

流量價錢

  • 在 aws services 與 ec2 之間傳輸是不需要算錢的
  • 資料傳輸到 ec2 裡面是不用錢的, 傳出才要收錢 (比較低等級的 ec 為 15 G/month)
  • 可使用 aws 算費用的工具來算 (http://calculator.s3.amazonaws.com/index.html)

t2 CPU Credit

CPU Credit 保持的越高, query 處理的效率會越好, 反之如果 credit 用完了, 它的處理效率就差

如果 cpu 保持低於基礎線以下的話, credit 是會加回來的, 直到加到上限

可以參考官方的連結

CPU Credit usage 如果是 1 的話代表要升級機器了

以 t2.small 為例, credit 基礎線為 CPU utilization 20%, 以上的話會扣 credit, 以下的話會加回來

新開的 ec2 e.g. t2.small 開啟動它的 CPU CreditBalance 是從 0 開始, 而不是 288 (updated on 2018-06-28)

另外建議在 production 不要用 t 系列, 除非是成本考量

因為 CPU Credit Balance 很容易被漏看, 且也需要了解它的規則

對 CPU Credit Balance 做監控比對 CPU Utilization 設重要多了, 因為當 Credit Balance 用完之後, CPU 才會開始衝高, 而且 EC2 CPU 裡面充到 100% 但外面的 CPU Utilization 卻只會顯示只有 20~30%,

而且 EC2 CPU 裡面充到 100% 但外面的 CPU Utilization 卻只會顯示只有 20~30%, 因為主要的 CPU 使用被外面掐住了

ELB / ALB

ELB

Load balance 服務, 可以將多台主機放在 Load balance 下, 做隨機或權重分配, 減輕單一 server 負擔

可以設定接 HTTPS 內部再導向 HTTP, 內部都是走 VPC 的網段

設定完大概15分鐘 ELB 的 DNS 才會通

將 ELB 的 domain 貼在 route53 record 新增的 CNAME item 裡

ELB 有內建 graceful shutdown,當把它移除掉,它會先停接新的 request,等到目前的 request 結束才移除

ALB

需要先設 Target Groups 主要是 port -> EC2 instance

再到 ELB 選擇 application type (一般的 ELB 是 classic) 然候再選擇 url path 對應到哪個 target group

注意:

  • ALB 有自已的 security group ,在 Load Balancers 的 ALB 右鍵 -> Description -> 可以看 e.g. 開放 80 / 443 port 0.0.0.0/0
  • ALB 設定 EC2 的 health check 的連結要在那個 Target Groups 上面右鍵-> Path e.g. /ok.html, EC2 的 security group 要記得設定 80 Port 10.0.0.0/16 讓 alb 可以打進來

SSL

如果在 godaddy 買 domain,用 CNAME 連到這個 ELB 的 dns CNAME,然候再把 godaddy 的憑證上傳到 ELB,然候就 OK 了,

  • private key : 轉過的 .key
  • public key certificate : .crt
  • chain 貼上 openssl x509 -inform PEM -in GC_bundle_SHA2.crt 的結果 (bundle 是 伺服器 SSL 憑證 + 中繼憑證)

Auto Scaling

  • 可以針對網站設定 Policy, 當主機到達某個條件增/減機器 (EC2), 需搭配使用 ELB

[操作]

  1. 一開始要先建立 Launch Configurations (要吃哪個 AMI ID 等等) 才能設定 Auto Scaling Groups (自動調整 size 的 Policy)
  2. 一旦建立好的 Launch Configuration 就不能改 AMI ID 了, 所以主機更新後除了重新打一個新的 Image, 再點原本的 Launch Configuration 複製建立新的並且選擇新的 AMI ID, 再到 Auto Scaling 的 config 選擇新的 Launch Configuration.
  3. 如果要一次對兩個 AZ 輪流新增 instance 的話在選擇 Subnet 時就要選擇兩個 AZ 所屬的 Subnet
  4. IP Address Type 要選擇 Assign a public IP address to every instance. 否則預設不會配 public ip, 但 ELB 走 LAN 還是可以讓此 API 對外開放, 只是如果要更新系統或抓新 code 就會受限無法 access internet
  5. 新啟動的 instance 大約在 1 分鐘內可以上線服務, (讓 CPU% 上升到 100% 的測試指令 : while true; do echo; done)

註: 如果要在機器啟動時執行一些 shell 指令, 可以在 Launch Configuration 的 User data 的 type 選擇 file, 內容 :

#!/bin/sh
echo "qq white into dd" > /tmp/dd

那麼機器啟動時就會執行這份檔案的指令了

[Autoscaling with custom metrics]

1) Put metric data

可以直接在程式引用別人寫好的套件, 直接透過程式做或使用 command line

2) 到 cloudWatch 確認 metric 是否上傳成功 (Metrics 搜尋 JobCount 看是否存在)

成功後寫一個會定時回報 metric 的 script 或程式, 讓 Auto-Scaling 把 AMI 啟動成 EC2 可以自動執行

3) Create Alarm (Action 是當超過設定值要做什麼事, 例如寄信等等的, 可以不用), 目的是為了讓 Auto-Scaling 的判斷機制可以選擇這個 metric

建兩個 Alarm 一個是 JobCount>=100 另一個是 JobCount <=10

4) 將 EC2 打成 Image

5) 建立 Auto Scaling configuration 及 group, 並持續 put-metric-data 測試是否 scale 成功

CloudWatch

監控 EC2 的狀態 (CPU usage, Network I/O ,etc..)

[備註]

預設默認一些 cpu 使用, io 等資訊

但不包括 ram 使用, 因為 AWS 是與 Hight provider 拿取使用狀況, 如果要拿到 ram 使用率勢必要放 agent 在 ec2, 而 aws 沒有做這件事

如果 launch 一個 instance 沒有打開 monitor, 預設的 CloudWatch 會紀錄

  • CPUUtilization
  • DiskReadBytes
  • DiskReadOps
  • DiskWriteBytes
  • DiskWriteOps
  • NetworkIn
  • NetworkOut

打開後會多紀錄

  • CPUCreditUsage
  • CPUCreditBalance
  • SatatusCheckFailed
  • StatusCheckFailed_Instance
  • StatusCheckFailed_System

Install AWS CloudWatch Log Agent

curl https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py -O
sudo python ./awslogs-agent-setup.py --region us-east-1

ref: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/QuickStartEC2Instance.html

重新設定要監聽的log, 再下一次

sudo python ./awslogs-agent-setup.py --region

log agent 的位置

/var/log/awslogs.log

[Put custom metric (command line)]

1) 安裝 AWS CLI tool

2) EC2 要有權限, 打開 put metric data 給 cloudWatch 的權限有兩種方法

Note: 不需要在 AWS Console 建立一個新的 metric,當你報上來的時候如果是新的 metric name,CloudWatch 會自動新增

第一種是建立 User 並給予對應權限, 再到 EC2 上設定 Access 及 Secret
  1. 到 AWS Console 的 IAM : 新增或選擇舊有的 User, 新增 Policy : AWS Service Roles -> Amazon EC2 (Select) -> cloudWatch 全部權限
  2. 再到主機裡設定 AWS config
1
2
3
4
5
aws configure
AWS Access Key ID [None]: A******************Q
AWS Secret Access Key [None]: 9**************************************O
Default region name [None]: us-west-2   (Oregon 是 us-west-2)
Default output format [None]:text
第二種是建立 Role 然候在起 EC2 的時候指定 Role

(?) 先不要用 role 來做 put-metric-data, 不知道為什麼, 就是會一直噴錯誤

一開始以為到 AWS Console 的 IAM : Create Role -> Role Type 選擇 Amazon EC2 -> CloudWatch Full Access -> Create Role

然候再選擇這個 role 建立 EC2 權限是可以的, 但卻會噴出 A client error (InvalidClientTokenId) occurred when calling the PutMetricData operation: The security token included in the request is invalid. 這樣的錯誤

註 : 目前這個 Role 只能給你目前帳號使用, 假如你跟別人共同開發, IAM 建立多個 User 並且每個人使用不同的 User 登入 AWS Console, 那麼其他人建 EC2 是看不到這個 Role 的, 解決辦法是在其他人的 User 或 Group 增加 Policy 打開 AWS Data Pipeline Full Access, 這樣其他人在建立 EC2 就可以看到 IAM Role 了

再到主機裡設定 AWS config

$ aws configure
AWS Access Key ID [None]: (留空 直接 enter)
AWS Secret Access Key [None]: (留空 直接 enter)
Default region name [None]: us-west-2
Default output format [None]:text

3) 測試 put metric data

aws cloudwatch put-metric-data --namespace "Job worker metrics" --metric-name JobCount --unit "Count" --dimensions "AutoScalingGroupName=myAutoScalingGroup" --value 23

如果沒有其他的訊息代表上傳成功

Custom Metrics 計價方式

以下是官方的範例

If your application runs on 10 Amazon EC2 instances 24x7 for a 30-day month, and you published 5 custom metrics every 5 minutes via the PutMetricData API, your charges would be as follows:

Total number of metrics = 5 metrics per instance * 10 instances = 50 metrics
Monthly CloudWatch Metrics Charges @$0.30 per custom metric = 50 * $0.30 = $15

Total number of minutes in the month = 60 * 24 * 30 = 42,300 minutes
Total Number of API requests = 10 instances * (42,300 minutes/5 minutes) = 84,600 requests
First 1,000,000 API requests = $0

Monthly CloudWatch charges = $15 + $0 = $15 per month
  • 如果你的量不是特別大 (要就是不需要每幾秒就 put custom metric) 的話, 那就不太需要太擔心價格了, 如果是的話就要細算了
  • 由上面可以看出來, 在 put 總月份不到一百萬次不用錢 (就算超過每百萬次也只收 $1)

常收到 State is OK 的通知 OK: "API CPU >= 70%", 但狀態都沒有變成 alarm

這是因為 cloudwatch 沒有收到足夠的資料所以 Alarm updated from OK to INSUFFICIENT_DATA

這時只要修改這個設定將 Treat missing data as: ignore (maintain the alarm state) 就行了,

它就不會在服務正常的時候還報 OK, 只有在 Alarm -> OK 才會收到

EBS

  • SSD 硬碟, EC2 透過 Network I/O 掛載進來
  • 我曾聽一個 AWS 講師說 : AWS 對硬碟的觀念是不管再好的硬碟都有壞的一天, 與其買很好的硬碟不如把備援系統做到最好, 所以他們不怕硬碟常常壞, 反而專注在備援上, 蠻特別的
  • 硬碟直的進到 AWS 但碎的出來, 不讓有心人士還原資料
  • 注意!!! EBS vs Instance Storage

IS 是跟 EC2 同一台機器上; EBS 是在雲上的硬碟.

如果 EC2 stop 再 start, 而 EC2 會換一台機器上, 所以 IS 就無法使用了, 但 EBS 則不受限制

IS 不收錢, 提供比 EBS 更高的 io/ps 因為沒有網路

一開始選擇 OS 時會標示他預設是使用 IS 還是 EBS, 但幾乎都是 EBS, 除非是在 AMI markets 就會看到有些是 IS 的

Glacier

存的資料不常使用, 能接受 3~5 小時的取回時間, 成本只有 s3 的 1/10, EX: 常用來封存 log 等等之類的久放但沒事不會拿出來的資料

RDS

  • 勾選 multi-AZ 會在另一個 AZ 做 slave, 當 master 斷了會將 slave 轉成 master, 而 master 大約會在 5 分鐘恢復
  • 快照 DB schema 或輩份
  • 可以 replica 到另一個 region (做全球 deploy 會需要)
  • memory 到紅色線沒關係 (from 官方)
  • RDS upgrade 一定會有 downtime
  • Security Group 一定要選 MySQL/Aurora, 選 All traffic 不會開到 3306
  • reboot 約需花 6 秒 (按下重啟後前30秒能連, 30秒後突然不能連, 然後6秒後恢復)
  • upgrade / degrade 需花約 70 秒左右 (按下升級後前5分鐘都還能連, 5分鐘後突然不能連, 然後70秒後恢復)

[操作]

1) 建立好 VPC
  1. RDS 預設不是 public, 如果要對外, 在建立 instance 時 Publicly Accessible 要選擇 yes
  2. 也可以使用自定義的 VPC, 先分好網段, 一個對內一個對外

如果勾選 Publicly Accessible 則 VPC 要將 DNS resolution 及 hostnames 打開

2) 先建立 Subnet GRoups
  1. 點擊 Create DB Subnet Group,
  2. 然候選擇 VPC ID 及 VPC 的 public 的 subnet,
  3. Subnet Group 規定 Subnet 要設定可以通兩個 AZ, 所以 VPC 的 Subnet 要有兩組不同的網段對應到不同的 AZ
3) 建立 Security Group, 讓 RDS 可以對外
  • Port : 3306
  • Source : 0.0.0.0/0
4) 建立 RDS MySQL, 並且選擇 Public 的 Subnet, 及 Secruity Group
  • 如果要對外 Publicly Accessible 要選擇 yes,
  • RDS 也要放在可以對外的 Public Subnet, 最外層的防火牆還需要讓 3306 port 可以接收外界

[主 DB 在美西, 其他 region 要 update DB 問題 (全球 DB 佈署問題)]

因為需要考慮到 Auto Scaling 的問題, 新的主機會不知道 IP, 所以無法先設定在 Security Group

有四種方法解決 :

1) singapore 用 mysql 內建的 SSL 連到美西的 DB master, 然候再 call 美西那邊的 security group API 新增讓此 IP 通過的 rule

比較麻煩, 不是個很好的方法

2) 用 VPN IPSec 將兩個 region 的 VPC Subnet 連在一起, 其通道本身就加密了, 所以可以直接通過 LAN 的方式更新

維護主機的成本提高

3) 在美西架一個 API server, 所有主機要更新 DB master 都透過此 API server, 並且用 https, 本身也是很安全的

幾乎不用花時間設定, 但需要花時間寫 API 接口

4) MySQL MMM 架構

需要擔心 DB 互相 sync 時引發的錯誤

結論 : 建議使用第 3 種方法, 可能 Update 速度稍慢, 但出錯的成本最少

RDS - MySQL

max_connection 不同 size 支援的 max_connections 量不同 :

t2.micro    66
t2.small    150
m3.medium   296
t2.medium   312
M3.large    609
t2.large    648
M4.large    648
M3.xlarge   1237
R3.large    1258
M4.xlarge   1320
M2.xlarge   1412
M3.2xlarge  2492
R3.xlarge   2540

從 snapshow 復原 DB

  1. DB Instance Identifier 是 AWS RDS 辨認的 ID, 只要不要重覆就好
  2. 記得取選 Security group
  3. 修改 Master password (也就是 DB admin 的 password)

如果要恢復整個 DB => 將程式 RDS endpoint 直接指向新的, 舊的再砍掉

如果要恢復每個 TABLE => 要先用 mysqldump 指令把那個 TABLE 匯出, 再到要復原的 DB TRUNCATE 那個 TABLE 後再匯入

Slow query

在 instance 下的 Parameter group, 點進去輸入 slow, 看 slow_query_log 有沒有打開

進入 mysql console 去 query mysql.slow_log

找出排名前 5 筆花最久時間的 query

SELECT * FROM slow_log ORDER BY query_time DESC LIMIT 5;

找出某天前 5 筆花最久時間的 query

SELECT * FROM slow_log WHERE start_time BETWEEN '2018-06-28 00:00:00' AND '2018-06-29 00:00:00' ORDER BY query_time DESC LIMIT 5;

Dynamo DB

  • No-sQSL, 性能保證, 使用 SSD, 請求可在 10 毫秒內完成, 也不會因資料量大而變慢
  • 無 DB 概念, 最多 256 個 TABLE
  • 資料是用 partition 機制, 盡量把資料分到各 node, ex: Key=a => Key=a1, a2, a3…
  • 避免 request 都戳到同一個 partition
  • 區域性服務(跨AZ), 雖然是提供一個DNS, 但服務是散佈在很多DC
  • 分散式儲存, 本身就會跨AZ並且複製3份

細節特性

  • 第一個欄位是 Key
  • Key 也可以用兩個欄位組合(composed key),但最多為 2 個
  • 在取資料的時候一定要由 Key 去取,不可以用其中一個欄位 = XXX 來取
  • 有像 Array 的型別,叫作 SS 表示為 { … }
  • 蠻特別的是你可以在 Update 過後傳回舊資料或更新後的資料,官方有定義幾種 type

Elastic Cache

  • Cache 服務
  • 支援 memcache 及 redis
  • 支援 Multi-AZ, 但不能 across region, 外部無法連進來, 即使 security group 允許 0.0.0.0/0, 也就是不支援從 internet 進入, 但可以透過一些 hack 方式例如 ssh 的 port forwarding 做到.

[操作]

先新增 Cache Subnet Groups, 把 VPC 的 Subnet 選進去

Security Group 要開 6379 port

CloudFront

  • CDN 服務
  • s3 如果要 https 的話需要使用 cloudfront
  • 可以指定到 bucket 下的 folder
  • 有支援 pre-signed url
  • 應用 : 在 A bucket 裡有 aa folder 需要 pre-signed url 才能下載, bb folder 不需要(公開下載)

cloudfront + s3 (with custom domain over SSL)

1) 建立

  • Origin Domain Name: (選擇 s3 bucket)
  • Origin Path: (如果要指定哪一個 folder 下才要填 e.g. /cover)

2) 送出後, 設定 route53 把 sub domain 指到這個 cloudfront 的 Domain Name

3) Edit Distribution -> General

  • Alternate Domain Names (CNAMEs): (輸入剛指過來的 sub domain)
  • 選擇 Custom SSL Certificate (example.com):
    • 如果都沒有的話, 要點 Request or Import a Certificate with ACM 新增, 可以透過 Request a certificateImport a certificate, 這裡我選擇前者; domain 要輸入兩個 domain example.com*.example.com, 然候再請這個 domain 的管理者到信箱 approval
    • 如果這個選項是 disabled 的話, 有可能是這個 distribution 的 status 是 In Progress, 因為只要修改過就會是 In Progress, 要等一陣子變成 Deployed 才會是 enabled

4) Edit Distribution -> Origin

  • Restrict Bucket Access: (Yes: 開放透過這個 domain 從外部可以 access 這個 bucket, No: 無法透過這個 domain 從外部 access)

5) 測試

  • 上傳圖片到 s3 bucket, 圖片的 Permissions -> Public access -> Read object 打勾
  • 看上傳的圖片是否可以 access, e.g. https://t1.example.com/test.jpg

pre-signed url + custom domain

  1. 將基本的 cloudfront custom domain 設定好
  2. 建立 cloudfront 專用的 key pair
    • 一定要用主帳號新增 (IAM users can’t create CloudFront key pair. You must log in using root credentials to create key pair.)
    • 右上角帳號選單 -> My Security Credentials -> CloudFront key pairs -> Create New Key Pair -> Download Private Key File (Pulblic Key File 可以先載但用不到) -> Copy Access Key ID (實際上只會用到 Private Key, 它只能載一次)
  3. 設定 Distribution 的 Origin
    • Restrict Bucket Access : Yes.
    • Your Identities : Use an Existing Identity (沒有的話就選 Create a New Identity)
    • Grant Read Permissions on Bucket : Yes, Update Bucket Policy (註1)
  4. 新增 S3 bucket + 放一個 test.jpg 測試是否可以通, (Permissions -> Public access -> Read object 打勾) e.g. https://example.com/test.jpg
  5. 禁止外部直接取得 s3 資源 CloudFront 的 Behavior 設定
    • Viewer Protocol Policy : HTTPS Only or Redirect HTTP to HTTPS
    • Restrict Viewer Access (Use Signed URLs or Signed Cookies) : Yes (允許可以用 signed URL or signed cookie access 檔案)
    • Trusted Signers : Self
  6. 已經禁止外部直接 e.g. https://example.com/test.jpg 就看不到東西了
  7. 將 private_key 上傳到主機並以各語言實作

註1 : 如果本身 bucket 有其他 policy 當你選擇 Yes, Update Bucket Policy 送出後,它會變回 No, I Will Update Permissions,實際上它已經更新 s3 bucket 的 policy 了

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E3D7VZ7WMEZSYS"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::test-bucket/*"
        }
    ]
}

ref :

清除 cache

目的: 如果 cloudfront 上的檔案還沒 expire, 你的靜態檔如果更新了, 但拉到也可能還會是舊的, 可以選擇手動將 cache 清掉

CloudFront Distributions -> (選 ID,點進去) -> Invalidations -> Create Invalidation -> 輸入要清除的 uri(也可以用 * 清掉全部) -> Invalidate

S3

  • For web (low latency)
  • 區域性服務 (跨AZ)
  • 適合存放靜態資料
  • 其實是有 region 的, S3 是存放在 AZ 的
  • 當上傳完一個檔案, 但回應成功背景會複製多份到其他 DC 或 AZ, 不會跨 region 複製, 但會在短時間內複製到其他
  • 最嚴重是發生在 us-standard, 不存在 aws 上, 是 s3 特有的, 與其他 region 是分開的 (最終一致性)
  • 資料永久, 唯一地址 (unique key)
  • 事件驅動, 上傳可以傳簡訊(SNS), SQS, Lambda function (圖片壓縮, 上傳檔案時 s3 call function, 自動壓圖)
  • version control, 每次 update 會記錄變動
  • 可透過 cloudFront 做 CDN, 只需要設定一下就好了
  • By default, all Amazon S3 resources are private, including buckets, objects, and related subresources.
  • 上傳不算流量
  • 預設一個 aws account 最多可以建立 100 bucket, 如果超過要聯絡 aws support
  • folder / 檔案 的數量都沒有上限

權限

要先了解 S3 有三種權限控制它

  1. Bucket policies : Attached to bucket
  2. ACLs : Grant access to specific AWS account or anonymous,可以各別定義 Grantee (一種身份) 可以對這個 bucket 做什麼事,預設是 root account 都可以
  3. IAM : Attached to user, json based ,讓你能不能進入 S3 的 WebUI,但實際上跟你有沒有權限用 API 的方式操作這個 Bucket 沒有關係

IAM 需要去 IAM 服務才能給予權限,而 Bucket policies 及 ACLs 可以在 S3 webUI 就可以選擇了

ACLs 只有一些 Group 讓你選 :

  • Authenticated Users – This should be an AWS account’s either email address or canonical user ID.
  • Everyone – For anonymous access
  • Log Delivery – This group is used if you enabled logging on your bucket.

Bucket policies :

  • List – Permission to view a list of the objects in the bucket.
  • Upload/Delete – Permission to upload and delete the object if the grantee is logged in
  • View Permissions – Permission to see the permissions for objects
  • Edit Permissions – Permission to edit the permissions for objects

AWS-SDK / AWS-CLI 上傳及下載 (必要設定)

如果發生沒有設定,在上傳時會發生 Access Denied

upload failed: ./test.txt to s3://buname_name/test.txt A client error (AccessDenied) occurred when calling the PutObject operation: Access Denied

剛建立的 Bucket 你一定要額外給予它被上傳的權限,否則無法透過 API 的方式上傳 (與 IAM Policy AmazonS3FullAccess 無關)

本以為新增 permission (Add more permissions) 就可以做到,但發現是不行的,所以要使用以下的方法去新增它的權限

Properties -> Permission -> Edit bucket policy -> 點選下面的 AWS Policy Generator 它可以幫你產生 Policy,但我們需要把表填一填

  1. Select Type of Policy : S3 Bucket Policy
  2. Effect : Allow (維持預設)
  3. Principal : arn:aws:iam::6**********4:user/app_server (其實就是 IAM 的某位 User 的 User ARN)
  4. AWS Service : Amazon S3 (維持預設)
  5. Actions : 選三個就好 DeleteObject GetObject PutObject
  6. Amazon Resource Name (ARN) : arn:aws:s3:::my-bucket/*

Generate Policy 後複製下來,再貼到 Edit bucket policy 那裡

{
    "Version": "2012-10-17",
    "Id": "Policy1461726945589",
    "Statement": [
        {
            "Sid": "Stmt1461726943472",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::624758352504:user/app_server"
            },
            "Action": [
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::my-bucket/*"
        },

        # 這邊是另外加上的, 讓外部可以直接 Access 檔案
        {
            "Sid": "Stmt1461734747651",
            "Action": [
                "s3:GetObject"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::my-bucket/*",
            "Principal": "*"
        }
    ]
}

然候應該就可以上傳,也可以下載了。

下載也是要使用上述的設定才可以設定公開,我試過在 Bucket 最頂層 Add more permission 設定 Grantee : Everyone, Action 只允許 List,但結果是沒有用的

ref : 關於 S3 權限

對這個 bucket 設定任何人都可以下載檔案

click {bucket_name} -> Properties -> Edit bucket policy

{
    "Version": "2008-10-17",
    "Id": "Policy1416798504862",
    "Statement": [
        {
            "Sid": "Stmt1416798496088",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::{this_bucket_name}/*"
        }
    ]
}

AWS-CLI command 上傳到 s3

安裝 aws cli

sudo apt-get install awscli

上傳

$ aws configure
AWS Access Key ID :
AWS Secret Access Key :
region name : us-west-2                     # 可以在 S3 該 Bucket 的 URL 找到 i.e. ?region=ap-northeast-1#&bucket=
output format : none
$ echo "test" > test.txt
$ aws s3 cp test.txt s3://bucket_name/      # 將 bucket_name 替換為你的 bucket name
$ curl http://bucket_endpoint/test.txt
test

其他相關 cli 指令

aws s3 cp s3://another-bucket/file.txt s3://my-bucket/          # 從其他 bucket copy
aws s3 cp test.txt s3://my-bucket/                              # copy local file to my-bucket
aws s3 ls s3://my-bucket/                                       # my-bucket 下的檔案列表
aws s3 ls                                                       # bucket list

Delete folder

There is no such thing as folders in S3; There are simply files with slashes in the filenames.

Many S3 tools will visualize these slashes as folders, but they’re not real.

You can delete all files with the same prefix, but first you need to look them up with list_objects(), then you can batch delete them.

ref : https://forums.aws.amazon.com/message.jspa?messageID=249514

刪除所有檔案, 目錄也會自動消失

SQS

AWS SQS 是一套 Queue 的系統,儲存要處理的工作,通常都是一個 message 用 json encode 存進去,再來就是自已實作 worker 那一段再串 SQS。

  • 支援 across regions, 即使在特定 region 下建立的 Queue, 但是 URL 是公開的, 外部也可以做 enqueue
  • 在 Send (enqueu) 時可以指定 DelaySeconds (0~900秒),被新增進 Queue 後 delay 多久才可以被取出
  • 你無法在 Send 時又指定 visibility timeout, ref: delay seconds vs visibility timeout
  • 取出 message 後你可以指定 VisibilityTimeout 要多久 (它下次被看到的時間,最長可以到 12 小時)
  • 也可以在取完 message 後再針對這個 message 修改 VisibilityTimeout
  • 內建的 Retry 機制可以透過在 Web UI 或是用程式的方式中修改 Queue 的 attribute 來達到,我們可以利用程式去修改 RedrivePolicy 屬性;我們只需要去指定參數 maxReceiveCount (一個 message 最多被接收幾次)及 deadLetterTargetArn (當達到上限的次數後要把它移到哪一個 Queue),其他就交給 SQS 了
  • 如何知道過去的 queue 有沒有屯積, 還是馬上就被取出? 看該 queue 的 monitoring 的 ApproximateNumberOfMessagesVisible, 最好的狀況是都是0, 代表這個 queue 沒有處理不完的 message

SES

  • 寄 Email 服務
  • 根據 AWS 自己的說法, 因為 AWS 對寄送的郵件有做控管, 所以不太會被當作垃圾信
  • 當使用 SES 寄太多不存在的 email 且到達一定的數量有可能會被暫停寄信, 可以設定 Enabling Email Feedback Forwarding 當有發生不存在的 email 時就通知某個 email

修改 mailed-by

The mailed-by header is the usually used to persist the content of the envelope from or MAIL FROM through forwarding.

Amazon do not allow the MAIL FROM to be customised. The following quote is from an Amazon employee in a comment on an blog post about SPF & DKIM

The headers you mentioned [mailed-by] seem to be something appended by an ISP after the message left Amazon SES, rather than standard message headers.
We unfortunately do not have control over receiver add-ons.
Nevertheless, assuming that the “mailed-by” value is based on the MAIL-FROM, the answer would be that right now all emails sent through Amazon SES have amazonses.com (or a subdomain of that) as the MAIL-FROM domain.
We don't currently support its customization

Posted by Adrian@AWS on November 4, 2014 8:31:29 AM PST

ref : https://serverfault.com/questions/641262/remove-or-replace-mailed-by-field-with-dkim-spf-enabled

gmail 顯示的 from & singed-by

signed-by 顯示的 domain 是通過 domain Auth 的 SES ARN

寄信的時候你可以只指定 from, ses 會判斷 from 的 domain 並且使用對應的 SES ARN

也可以指定 SES ARN 寄信,但其實沒那麼必要,因為 fromdomain 的 SES ARN 的 domain 必須要是一樣的,否則信會寄不出去

當一封信收到後,fromsigned-by 一定會是一樣的

Cloud Formation

將 region 下所有的主機資訊輸出成 JSON, 如果下次要重 build 一個環境可以直接執行, 但記得要改參數

[操作]

  1. 進去會看到兩個按鈕, 上面是 Create New Stack, 如果已有建立好的 Template 就選擇它, 下面是 Launch CloudFormer, 如果還沒有建立 Template 就選擇它, 將你目前的 AWS 的服務轉成 Json
  2. 所以先選 Launch CloudFormer, 注意! 必須要有 AWS 本身 region 的 default VPC, 否則會一直出現 error 啟不起來,
  3. 成功後會自動在 EC2 建立一個 instance, 接著連到這個 instance 的 public IP, 就可以開始設定了 (建立的那個帳號要有 IAM Full Access 權限才可以將 instance 跑起來)
  4. 設定完後會產生一份 JSON 格式的 template, 把它存起來
  5. 需要改的地方, 以 singapore 與 frankfurt 舉例, 簡稱 sin 跟 fr, 將 sin 的 AZ 名稱取代成 fr 的 AZ 名稱

無法解決

Security Group 的 outbound 0.0.0.0/0 一直出現 AWS::EC2::SecurityGroupEgress Encountered unsupported property CidrIP, 找不出解決方法, 最後就放棄使用 CloudFormation 了

Opsworks

  • Deploy 大量主機
  • Configuration Management 管理設定檔, 一次修改多個 config 及升級

Elastic Beanstalk

可以起一個 web server 並且放在 auto scaling 下, 或者是選擇一個 docker file

並且選擇要如何更新 server, 有兩種方法,

  1. 一種是百分比的, 如果是 30%, 那麼 AWS 就會以一次 shutdown 30% 的主機, 進行更新
  2. 另種是一次幾台, 如果是一次一台的話, 那麼就會一台一台更新

Cognito

不需要煩惱及建置後端架構, 透過此服務即可達到 mobile 互相交換訊息

Mobile Analytics

Mobile 的 GA

CloudTrail

AWS console log

Lambda

  • 像 IFTTT 是一個 event trigger 的服務
  • 例如 s3 一有上傳觸發一個你的 function
  • call lambda 時可以自已決定要不要 blocking

建立 Function,使用 IAM Role 讓它可以 call DynamoDB

1) 在 IAM Role 建立一個 role,policy 這個 AmazonDynamoDBFullAccess 就好

2) 到 Lambda 建立 function : Configure function

  • Code entry type 選擇上傳 zip,但上傳檔案先不選,因為會用指令上傳
  • Role 選擇剛剛新增的 role

3) 先選擇 code inline 否則如果一開始沒有檔案會無法建立

讓lambda 的 log 可以紀錄在 cloudwatch Log Groups /aws/lambda/{lambda_name}

先去看該 lambda function 的 Existing role 叫什麼, 然候再到 IAM Role 那將 Policy CloudWatchLogsFullAccess 加到這個 role, 就可以在 cloudwatch 看到執行這個 function 時的 log 了

Index

  • 每個 Table 都一定有一個主鍵,第二鍵則是 optional 的
  • 如果你要 query 的欄位不是主鍵那就要加 index

SMS

寄 簡訊

SNS

  • 可以推送 notification 到手機(支援 APN, GCM 等等..)
  • 它不算是完整的 messaging service, 只有 aws 特定服務才可以 sub, 但觸發的 endpoint 就蠻多樣的 e.g. email, lambda, endpoint, etc.

Registger SNS

  • 在 AWS Web Console 建立 SNS ARN
  • App 的 token : 如果是 android 會先去跟 gcm 確認身上有沒有 gcm token, 沒有的話就會先去要一把
  • 將 token 帶給 server, server call aws register SNS API, 給它 SNS ARN + token
  • 完成

宣要注意的是

  • AWS SNS 是認 token 的, 只要 token 都是一樣就只能註冊一次, 所以重覆拿同一個 token 註冊是不會成功的

Push Notification

  • 先去 SNS 的 Applications 註冊 Push Notification 的服務
  • 手機裡的 App 會有個 UUID,帶這個上來到 Server
  • 拿這個 UUID 向 SNS 註冊 Token (createPlatformEndpoint 帶上面註冊 SNS 的 ARN, 及 app UUID, enabled: true (enabled 預設是 false, 所以要改成 true)),會拿到 EndpointArn
  • IOS 在 simulator 或是手機 debug mode, 系統會把 token 扔到 sandbox 的 apns 上, 只有在上架版(app store) 下載下來的才會丟到 production,所以要注意的是使用的環境要與 aws sns apns 的環境一致才發送的出去
  • 註冊完後 SNS 後台就有一筆 record ,也可以直接用 web ui 發送 notification 做測試
  • 每筆 record 後面都有 enabled 值,如果是 false 就代表不能推送,只要 SNS 推送一次但送不成功後就會把它改成 false
  • 發送時如果 client 端沒開網路的話,SNS 不會當它是錯誤 (error),等到 client 把網路打開就會收到之前送的 notification
  • 後端要推送只要對 EndpointArn 發送 message 就可以了
  • Mobile 可以選擇要不要過濾 Notification
  • 格式它可以選擇 raw 或 json,要確定送的格式對不對

Create Notification ARN by Platform

  • GCM 申請時需要填上 API key 是手機去 GCM 那裡申請時拿到的, 要用 server key(A*************************************o)來申請
  • APNS 申請時需要有 .p12 檔案及密碼.p12 是手機跟 apple 申請時會拿到,申請 aws arn 只需要上傳 .p12 及輸入密碼,再選它的按鈕 Load credentials from file (上傳的 .p12 檔名用英文, 有遇過中文檔名解失敗的情況)
  • IOS certificate 一年會到期, 所以要每一年申請且到 sns push application 更新 certificate

發送 GCM notification 格式 (不管 production 還是 dev 都用這個)

{ "GCM": "{ \"notification\": { \"body\": \"test body\",\"title\": \"test title\",\"icon\": \"test icon\" },\"data\": { \"custom_field\": \"custom_value\" } }" }

Andriod 的聲音是 app 自已決定的

發送 APNS notification 格式 (production)

 { "APNS":"{\"aps\":{\"alert\":\"Hello World!!\",\"sound\":\"default\"},\"custom_field\": \"custom_value\"}" }

自行決定是否要帶聲音的參數

APNS_SANDBOX 格式 (dev)

 { "APNS_SANDBOX":"{\"aps\":{\"alert\":\"Hello World!!\"},\"custom_field\": \"custom_value\"}" }

Apple notification 的 title 不像 GCM 可以自訂, 它是不能動的, title 固定都顯示 app name.

Error: PlatformApplicationDisabled: Platform application is disabled

IOS credentials 在 AWS SNS 上過期了, 需要請 IOS developer 重新產生 credentials, 產生完選擇要 renew 的 ARN, 再選 Update credentials 重新上傳就可以了

註: IOS credentials 一年就會過期, 需要每年更新

android 不會有這個問題

Kinesis

主要是用來做 log 收集及分析, 跟 Apache Kafka 做的事情很類似

Kinesis vs Kafka

將 cloudwatch log 傳送到 kinesis firehose 處理, 並將結果存到 s3

步驟可參考官方文件, 但裡面都是用指令去建立, 而我是用 Web Console 來設定

1~4 可以用 Web Console 達成, 5 之後就要下指令才能達成

[1] 先建立一個 s3 專門給 cloudwatch 或 kinesis 使用

[2] 建立新的 IAM Role 給 Kinesis, Create role -> AWS Service -> Kinesis (這邊很重要, 一定要選, 否則第 3 步驟要選 IAM Role 會看不到這個), 建立完 Role 選擇 Attach policies 旁邊的 Add inline policy, 為的是讓它有權限寫入到步驟 1 的 S3

{
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:GetBucketLocation",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::qa-test-cloudwatch-logs",        // 要改成步驟1 建立的 bucket
                "arn:aws:s3:::qa-test-cloudwatch-logs/*"       // 要改成步驟1 建立的 bucket
            ]
        }
    ]
}

[3] 建立 Kinesis firehose

  • 選 s3 destination 要選步驟1 建立的 bucket
  • Prefix: firehose_
  • Buffer conditions: 5MB or 60s -> 先暫時這樣設定比較能快點看到測試資料
  • Compression: Disabled
  • Encryption: Disabled
  • IAM Role -> Create new or choose, 要選擇步驟2 建立的 Role 及 Policy

[4] 測試 Kinesis firehose

點選 Start sending demo data, 邊觀察下面的 Monitoring 及 S3 是否有資料

cloudwatch logs 會出現這個 group e.g. /aws/kinesisfirehose/, 不過都不會有 log, 可能是 kinesis 有 error 才會在這

Kinesis 的測試資料

{"ticker_symbol":"HJK","sector":"TECHNOLOGY","change":0,"price":4.78}
{"ticker_symbol":"XTC","sector":"HEALTHCARE","change":-4.34,"price":108.7}

(optional) 當 kinesis horse 建立完成後, 建立 Data Analytics 分析結果

Data Analytics 只有幾個 rigion 支援

[1] Create

[2] Kinesis firehose 點選 Start sending demo data, 讓資料一直進來, 因為要一直有資料進來才好完成接下來的步驟

[3] SQL Query

1) SQL (這裡做的是把 price > 80 的資料儲存下來)

CREATE OR REPLACE STREAM "DESTINATION_SQL_STREAM" (ticker_symbol VARCHAR(4), sector VARCHAR(12), change DOUBLE, price DOUBLE);

CREATE OR REPLACE PUMP "STREAM_PUMP" AS INSERT INTO "DESTINATION_SQL_STREAM"
   SELECT STREAM "ticker_symbol", "sector", "change", "price"
   FROM "SOURCE_SQL_STREAM_001" WHERE "price" > 80

欄位要用 " 包起來, 否則它內部會幫你自動轉大寫, 送出後會跟你說找不到這個欄位的錯誤

2) Save and run SQL (如果要改可以隨時改, 再點擊這個讓它生效)

[4] Connect to a destination

SQL 的結果總要有地方來儲存吧? 它這裡無法直接寫入 S3, 必須要靠 firehose 寫入

  1. 建立 Kinesis Firehose delivery stream 給這個 Data Analytics 儲存結果, 建立過程跟之前一樣, 只是產生在 S3 的 Prefix 改成用 Data-Analytics_ 以便分類
  2. Destination -> Kinesis Firehose delivery stream -> 選擇剛建立的 firehose
  3. Connect in-application stream -> DESTINATION_SQL_STREAM (SQL Query 建立的 stream name)

[5] 看測試結果

看 s3 bucket 下有沒有 folder data-analytics_2018, 並且看裡面的檔案, 看看 price 是否 > 80

(optional) 當 kinesis horse 建立完成後, 將 CloudWatch Log 傳到 Kinesis

接下來就要下指令了, 無法用 Web Console 達成

/tmp/TrustPolicyForCWL.json

{
  "Statement": {
    "Effect": "Allow",
    "Principal": { "Service": "logs.ap-northeast-1.amazonaws.com" },     // region 要改
    "Action": "sts:AssumeRole"
  }
}

建立 IAM Role

aws iam create-role --role-name CWLtoKinesisFirehoseRole --assume-role-policy-document file:///tmp/TrustPolicyForCWL.json

file://{file path}

/tmp/PermissionsForCWL.json

{
    "Statement":[
      {
        "Effect":"Allow",
        "Action":["firehose:*"],
        "Resource":["arn:aws:firehose:region:123456789012:*"]
      },
      {
        "Effect":"Allow",
        "Action":["iam:PassRole"],
        "Resource":["arn:aws:iam::123456789012:role/CWLtoKinesisFirehoseRole"]
      }
    ]
}

裝 policy 跟 IAM Role 關聯

aws iam put-role-policy --role-name CWLtoKinesisFirehoseRole --policy-name Permissions-Policy-For-CWL --policy-document file:///tmp/PermissionsForCWL.json

將要傳送 CloudWatch Log 的 group 連接到 Kinesis

aws logs put-subscription-filter --log-group-name "dev-worker-logs" --filter-name "FirehoseDestination" --filter-pattern ""  --destination-arn "arn:aws:firehose:ap-northeast-1:3**********2:deliverystream/qa-kinesis-test" --role-arn "arn:aws:iam::3**********2:role/CWLtoKinesisFirehoseRole"

[6] 確認是否 log 都有被送到 Kinesis Firehose

看 monitoring 就能知道

如果不要將 cloudwatch logs 傳到 kinesis (取消訂閱), 在 cloudwatch logs 的該 group 上選擇 Remove Subscription Filter

ref:

Other

安裝 AWS CLI tool

$ curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
$ unzip awscli-bundle.zip
$ sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws

[建立可以看 billing 的帳號]

除了建立一個 IAM User 並給予權限 (AWSAccountActivityAccess), 但光是這樣還是看不到花費狀況, 還要去主帳號的 Dashboard -> 帳號 -> IAM 用戶對賬單信息的訪問權限 -> 激活IAM 訪問權限, 這樣就可以了

現在登入 IAM User 帳號, 應該就能看到帳單資料了

[其他服務]

  • chaos monkey 可以將 EC2 terminal 或 security group 動手腳模擬網路不通測試 single point failure

DB 相關

ACID

  • 原子性 (atomicity, 或稱不可分割性) : 一個交易(transaction)中的所有操作,要就全部完成,不然就全部不完成,不會結束在中間某個環節。事務執行過程中其中一個環境發生錯誤,會被回滾(Rollback)到一開始的狀態,就像事務從來沒有執行過一樣。
  • 一致性 (consistency) : 在事務開始之前和事務結束以後,資料庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及後續資料庫可以自發性地完成預定的工作。
  • 隔離性 (isolation, 又稱獨立性) : 隔離性:當兩個或者多個事務並發訪問(此處訪問指查詢和修改的操作)資料庫的同一數據時所表現出的相互關係。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和串列>化(Serializable)。
  • 持久性 (durability) : 持久性:在事務完成以後,該事務對資料庫所作的更改便持久地保存在資料庫之中,並且是完全的。

MySQL InnoDB vs MyIsam

  • InnoDB 支援 transaction
  • InnoDB 支援 row lock
  • InnoDB 不需要等 row 上的 lock 釋放就可以讀取 row 上的 snapshot

MySQL int length

型態                Byte(s)     預設長度    範圍                                        Unsigned
---------------------------------------------------------------------------------------------------------------
TINYINT[(長度)]     1           4           -128~127                                    0~255 (unisgned 的預設長度會是 3, 因為沒有負號, 最多就 3 位數)
SMALLINT[(長度)]    2           6           -32768~32767                                0~65535
MEDIUMINT[(長度)]   3           9           -8388608~8388607                            0~16777215
INT[(長度)]         4           11          -2147683648~2147683647                      0~4294967295
BIGINT[(長度)]      8           20          -9223372036854775808~9223372036854775807    0~18446744073709551615

MySQL Char vs Varchar 比較

官方詳細說明 : https://dev.mysql.com/doc/refman/5.7/en/char.html

如果字串長度都固定的話使用 char, mysql 會分配固定的 bytes 儲存它

如果字串長度不固定使用 varchar, mysql 會分配浮動的 bytes + 長度 byte 給它, 而長度 byte 有可能是 1 byte 或 2 bytes, 取決於儲存的 bytes 有沒有超過 255 bytes

e.g. 如果儲存 ‘abcd’ 會用到幾 bytes?

  • char : 4 bytes
  • varchar : 4 + 1 bytes

e.g. 如果儲存 ‘abcd..(略)…’, 假設共有256個字元, 會用到幾 bytes?

  • char : 256 bytes
  • varchar : 256 + 2 bytes

MySQL 型態儲存長度 (Data Type and Storage Required)

  • ‘abcd’ in CHAR(72) CHARACTER SET ascii occupies 72 bytes on disk.
  • ‘abcd’ in CHAR(72) CHARACTER SET utf8 occupies 3*72 bytes on disk.
  • ‘abcd’ in CHAR(72) CHARACTER SET utf8mb4 occupies 4*72 bytes on disk.
  • ‘abcd’ in VARCHAR(72) occupies 1+4 bytes on disk.
  • ‘abcd’ in TINYTEXT occupies 1+4 bytes on disk.
  • ‘abcd’ in TEXT occupies 2+4 bytes on disk.
  • ‘abcd’ in LONGTEXT occupies 4+4 bytes on disk.

詳細

  • CHAR(M) M × w bytes, 0 <= M <= 255, where w is the number of bytes required for the maximum-length character in the character set. See Section 14.2.6.7, “Physical Row Structure” for information about CHAR data type storage requirements for InnoDB tables.
  • BINARY(M) M bytes, 0 <= M <= 255
  • VARCHAR(M), VARBINARY(M) L + 1 bytes if column values require 0 − 255 bytes, L + 2 bytes if values may require more than 255 bytes
  • TINYBLOB, TINYTEXT L + 1 bytes, where L < 28
  • BLOB, TEXT L + 2 bytes, where L < 216
  • MEDIUMBLOB, MEDIUMTEXT L + 3 bytes, where L < 224
  • LONGBLOB, LONGTEXT L + 4 bytes, where L < 232
  • ENUM(‘value1’,‘value2’,…) 1 or 2 bytes, depending on the number of enumeration values (65,535 values maximum)
  • SET(‘value1’,‘value2’,…) 1, 2, 3, 4, or 8 bytes, depending on the number of set members (64 members maximum)

Process Manager Allows You to Keep Applications Alive

Introduction

If you want a program or application to run in background, you always need process manager to help you monitor them like supervisor, pm2, etc.

Supervisor

安裝參考這裡

ubuntu 安裝指令

sudo apt-get install supervisor

唯一要注意的是 command=/usr/local/bin/long.sh command 要寫完整的指令路徑

/etc/supervisor/conf.d/aws-sqs.conf :

[program:aws-sqs]
command=/home/ubuntu/mygo/bin/aws-sqs
autostart=true
autorestart=true
stderr_logfile=/var/log/golang.err.log
stdout_logfile=/var/log/golang.out.log
stderr_logfile_maxbytes=10MB
user=ubuntu
environment=USER='jex',BRAND='Company_name',HOME=/home/my_user
directory=/var/www

將 supervisor 跑起來

sudo service supervisor start

對 program conf 的指令

sudo supervisorctl
> restart worker
> reread
> status
> start resque
> tail resque    // error log
  • or sudo supervisorctl reread goworker
  • 有時候不管怎麼 restart 及 reread 都沒有用時用 reload

Error CRIT Server 'unix_http_server' running without any HTTP authentication checking

設定 unix_http_server 帳密

[unix_http_server]
file=/var/run/supervisor.sock   ; (the path to the socket file)
chmod=0700                       ; sockef file mode (default 0700)
username = user
password = 123

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL  for a unix socket
username = user
password = 123

Pm2

Install

npm install pm2 -g

If you haven’t installed node.js and npm, please take a look

Usage

Monitor a process / Stop / Delete from list

pm2 start test.sh
pm2 stop test.sh
pm2 delete test.sh

List

pm2 list

Pm2 provides some useful modules.

pm2 install <module_name>
  • pm2-logrotate : auto rotate logs of PM2 and applications managed
  • pm2-webshell : expose a fully capable terminal in browsers
  • pm2-server-monit : monitor your server health

Set logrotate

pm2 set pm2-logrotate:max_size 5M   The max size limits up to 5MB.
pm2 set pm2-logrotate:retain 10     Preserve 10 log files. (include current one)

pm2 show pm2-logrotate              Show the configuration of pm2-logrotate

Grace stop

First a SIGINT a signal is sent to your processes, signal you can catch to know that your process is going to be stopped. If your application does not exit by itself before 1.6s customizable it will receive a SIGKILL signal to force the process exit.

Lenghten timeout of graceful stop

$ pm2 start app.js --kill-timeout 3000          // 3000ms

Test at 2016-12-23: It doesn’t work and I don’t know why. It’s just like that it will wait 3~5 seconds then it force to kill the process.

使用 Guard 偵測檔案變動

介紹

guard 是一個 node.js 的套件, 它可以偵測資料夾下面的檔案發生變動然候再去做相對應的事, 修改的檔案變動就 reload 瀏覽器或執行一段 shell

Install rvm

首先要先安裝 rvm, 我們再利用 rum 安裝 guard

[1] Install

sudo apt-get update
sudo apt-get install curl
curl -L https://get.rvm.io | bash -s stable --auto-dotfiles

如果執行失敗照著指示做 gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3, 然候再執行一次安裝指令

[2] Exec source /etc/profile.d/rvm.sh

rvm requirements

[3] Install ruby

rvm install ruby
rvm use ruby --default

[4] Install RubyGems

rvm rubygems current

安裝 guard 及 guard-livereload

guard 是偵測檔案變動你再跟它說要做什麼事

guard-livereload 是利用 guard 偵測檔案發生變動就 reload 瀏覽器

[1] 安裝 guard-livereload

sudo apt-get install ruby-dev make build-essential g++
gem install guard
gem install guard-livereload

gem install 不要用 sudo, 否則執行時會報錯, 詳細原因參考這裡

[2] 執行 guard init livereload 會在目錄下產生一個 Guardfile, 裡面定義一些偵測的規則

Guardfile (修改後) :

guard 'livereload' do
  watch(%r{app/controllers/.+\.rb})
  watch(%r{app/models/.+\.rb})
  watch(%r{app/views/.+\.(erb|haml|slim)$})
  watch(%r{app/helpers/.+\.rb})
  watch(%r{public/.+\.(css|js|html)})
  watch(%r{config/locales/.+\.yml})
  # Rails Assets Pipeline
  watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html|png|jpg))).*}) { |m| "/assets/#{m[3]}" }
end

[3] 下載 livereload chrome extension

[4] 點擊 extension 讓 livereload 與 guard-livereload 連接

  • 如果是用本機開發 :
    • 執行 guard, 再到 chrome 打開 livereload extension 就好了
  • 如果是用 ssh 連到主機開發 :
    • 1) 執行 guard, 預設 port 是 35729 (注意! server 要記得開放 35729 port)
    • 2) 利用 ssh 的 port forwarding 將主機的 35729 導到本機的 35729, 參考 SSH 設定 - Port Forwarding 部份
    • 3) 再到 chrome 打開 livereload extension

[5] 完成, 只要 guard 偵測到 server 端的檔案有異動就會自動通知 livereload chrome extension 做 reload

偵測檔案變動並執行特定指令

[1] Install : gem install guard-shell

[2] Exec : guard init shell

Guardfile :

guard :shell do
  watch(/(.*).cpp/) {|m| `g++ #{m[0]} -o runMe && ./runMe` }
end

.cpp 變動自動編譯

偵測 golang 檔案變動

Install : gem install guard-go

偵測 php 檔案變動

所有 *.php 設定 :

guard 'livereload' do
  watch(%r{.+\.(php)$})
end

ci 設定 :

guard 'livereload', :grace_period => 0 do
  watch(%r{application/.+\.(php)$})
  watch(%r{public/.+\.(css|js)})
end

laravel 設定 :

guard 'livereload', :grace_period => 0 do
  watch(%r{app/.+\.(php)$})
  watch(%r{public/assets/.+\.(css|js)})
end

grace_period 是指 delay 幾秒才 reload

測測 ruby 檔案變動檢查 coding style

Install : gem install guard-rubocop

Exec : guard init rubocop

使用 Gulp Compile Sass

介紹

使用 node.js 的 gulp 套件可以偵測當 .scss 發生變動時可以自動 compile 並放到對應目錄

Install gulp

npm install gulp
npm install gulp-compass

gulpfile.js :

var gulp = require('gulp');
var compass = require('gulp-compass');
gulp.task('default', function() {
    gulp.run('compass');
    gulp.watch([
        '../../sass/**',
    ], function(event) {
        gulp.run('compass');
    });
});

gulp.task('compass', function() {
    gulp.src('../../sass/**')
    .pipe(compass({
        comments: false,
        css: '../../../public/static/css', // css folder
        sass: '../../sass', // scss folder
    }));
});

在 package.json 下執行 npm install

Redis

介紹

  • Open source
  • Key-Value 儲存型態
  • Value 可以是 strings, hashes, lists, sets, ordered sets
  • 運作在 memory, 可用 snapshot 儲存在硬碟
  • 不支援 2 層以上的 hash

Install

server

brew install redis                      // Mac
sudo apt-get install redis-server       // Ubuntu

Mac 需執行 redis-server 指令才會啟動

client : 只需安裝 redis-tools

sudo apt-get install redis-tools

Command

keys

全部的 key

keys *

Key 的數量

info keyspace
> db0:keys=74415,expires=63940,avg_ttl=306997004

Find prefix string key : ‘queue:’

keys queue:*

刪除 prefix key

redis-cli KEYS "prefix:*" | xargs redis-cli DEL

Count matching keys

redis-cli keys "prefix:*" | wc -l

SET, GET

SET users:jex "job: php backend, born: 1989"
GET users:jex                                       // "job: php backend, born: 1989"

A String value can be at max 512 Megabytes in length.

hash : 裡面可以有很多 key 跟 value, ex: {name: jex}

HSET 'me' 'name' 'jex'

HGET 'me' 'name'    //得到 jex

HDEL 'me' 'name'

HGETALL 'me'     //得到所有 key: value

HMSET user:1000 username antirez password P1pp0 age 34

HGETALL user:1000

HSET user:1000 password 12345

HGETALL user:1000

list

LPUSH mylist a
LPUSH mylist b

RPUSH mylist c

LRANGE mylist 0 -1
1) "b"
2) "a"
3) "c"

LPOP mylist     // 輸出 b, 剩下 a, c

LBPOP mylist mylist3 mylist3 0    // 非 block 依序彈出 value, 但如果所有的 list 都是空的, 就會 block 直到超時或有 list 被 push 值進來

LLEN mylist     // count 裡面的數量
  • LRANGE鍵
  • 返回列表key中指定區間內的元素,區間以偏移量start和stop指定。
  • 下標(index)參數start和stop都以0為底,也就是說,以0表示列表的第一個元素,以1表示列表的第二個元素,以此類推。
  • 你也可以使用負數下標,以-1表示列表的最後一個元素,-2表示列表的倒數第二個元素,以此類推。

expired time

EXPIRE key 10      // This key has been set expired time and it'll expire after 10 seconds

Show the rest of expired time

TTL key
  • 0 ~ ? => Existed time of seconds.
  • -1 => Never expire.
  • -2 => Doesn’t exist.

increase

SET mykey "10"
> "OK"
INCR mykey
> (integer) 11

Transactions : MULTI + (一串指令) + EXEC

MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1

查看 Connection list

client list

SETNEX (SET if Not eXists) (如果沒存在, 行為跟 SET 一樣)

SETNX mykey "Hello"

其他指令

Connect to redis server

redis-cli -h xxx-cache.cnhynt.0001.usw2.cache.amazonaws.com -p 6379

可不加 -p, 預設會使用 6379 port

latency: 跑一個 loop PING command 100 times per second 來測試, 回應的數字代表是 ms

redis-cli --latency
min: 2, max: 4, avg: 2.78 (98 samples)

ref: https://redis.io/topics/rediscli

benchmark: 它會測試不同項目

redis-benchmark

(...略...)

====== SET ======
  10000 requests completed in 0.57 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

0.01% <= 2 milliseconds
87.84% <= 3 milliseconds
97.55% <= 4 milliseconds
98.97% <= 5 milliseconds
99.40% <= 6 milliseconds
99.51% <= 14 milliseconds
99.66% <= 15 milliseconds
99.93% <= 16 milliseconds
100.00% <= 16 milliseconds
17543.86 requests per second

(...略...)

Export and Import

Backup to another redis server

Export

$ redis-cli
127.0.0.1:6379> SAVE
OK

Default backup path: /var/lib/redis/dump.rdb

Copy backup(/var/lib/redis/dump.rdb) to another server

$ scp /var/lib/redis/dump.rdb myuser@B:/tmp/dump.rdb

Import

$ sudo service redis-server stop
$ sudo cp /tmp/dump.rdb /var/lib/redis/dump.rdb
$ sudo chown redis: /var/lib/redis/dump.rdb
$ sudo service redis-server start

Import mass data

Create import.txt file :

SET job1 "First job"

SET job2 "Second job"

SET job2 "Second job"

要空一格, 不然執行後會有錯誤

import command :

cat import.txt | redis-cli --pipe

(cat import.txt; sleep 10) | nc localhost 6379 > /dev/null

對 lan 開放

/etc/redis/redis.conf :

bind 0.0.0.0

ref : https://sendgrid.com/blog/get-going-go-redis/

其他

將比對到的 key 做 HGET

redis-cli -h redis.example.ap-northeast-1.local KEYS "user:????????????????????????????????" | xargs -i@ redis-cli -h redis.example.ap-northeast-1.local hget @ name