用 Traefik 搭配 Docker 快速架設服務

更新: 2019.01.10 新增教學影片


drone traefik docker deploy


相信大家在架設服務肯定會選一套像是 HAProxy, Nginx, ApacheCaddy,這四套架設的難度差不多,如果要搭配 Let’s Encrypt 前面兩套需要自己串接 (Nginx, Apache),而 Caddy 是用 Golang 開發裡面已經內建了 Let’s Encrypt,,管理者不用擔心憑證過期,相當方便。但是本篇我要介紹另外一套工具叫 Traefik,這一套也是用 Go 語言開發,而我推薦這套的原因是,此套可以跟 Docker 很深度的結合,只要服務跑在 Docker 上面,Traefik 都可以自動偵測到,並且套用設定。透過底下的範例讓 Traefik 串接後端兩個服務,分別是 domain1.comdomain2.com。來看看如何快速設定 Traefik。


traefik + docker + golang



影片教學


不想看內文的,可以直接參考 Youtube 影片,如果喜歡的話歡迎訂閱



撰寫服務


我們先透過底下 Go 語言實作後端,並且放到 Docker Hub 內,方便之後透過 docker-compose 設定。


package main

import (
“flag”
“fmt”
“log”
“net/http”
“os”
“time”
)

// HelloWorld for hello world
func HelloWorld() string {
return “Hello World, golang workshop!”
}

func handler(w http.ResponseWriter, r *http.Request) {
log.Printf(“Got http request. time: %v”, time.Now())
fmt.Fprintf(w, “I love %s!”, r.URL.Path[1:])
}

func pinger(port string) error {
resp, err := http.Get(“http://localhost:" + port)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf(“server returned not-200 status code”)
}

return nil

}

func main() {
var port string
var ping bool
flag.StringVar(&port, “port”, “8080”, “server port”)
flag.StringVar(&port, “p”, “8080”, “server port”)
flag.BoolVar(&ping, “ping”, false, “check server live”)
flag.Parse()

if p, ok := os.LookupEnv("PORT"); ok {
    port = p
}

if ping {
    if err := pinger(port); err != nil {
        log.Printf("ping server error: %v\n", err)
    }

    return
}

http.HandleFunc("/", handler)
log.Println("http server run on " + port + " port")
log.Fatal(http.ListenAndServe(":"+port, nil))

}


撰寫 Dockerfile


FROM alpine:3.8

LABEL maintainer=“Bo-Yi Wu <appleboy.tw@gmail.com>”
org.label-schema.name=“Drone Workshop”
org.label-schema.vendor=“Bo-Yi Wu”
org.label-schema.schema-version=“1.0”

RUN apk add –no-cache ca-certificates &&
rm -rf /var/cache/apk/*

ADD release/linux/i386/helloworld /bin/

ENTRYPOINT [“/bin/helloworld”]


設定 Drone 自動上傳到 DockerHub,使用 drone-docker 外掛。


  - name: publish
image: plugins/docker:17.12
settings:
repo: appleboy/test
auto_tag: true
dockerfile: Dockerfile.alpine
default_suffix: alpine
username:
from_secret: docker_username
password:
from_secret: docker_password
when:
event:
- push
- tag

其中 docker_usernamedocker_password 可以到 drone 後台設定。


啟動 Traefik 服務


如果只是單純綁定在非 80 或 443 port,您可以用一般帳號設定 Traefik,設定如下:


debug = false

logLevel = “INFO”
defaultEntryPoints = [“http”]

[entryPoints]
[entryPoints.http]
address = “:8080”

[retry]

################################################################

Docker Provider

################################################################

Enable Docker Provider.

[docker]

Docker server endpoint. Can be a tcp or a unix socket endpoint.

#

Required

#
endpoint = “unix:///var/run/docker.sock”

Enable watch docker changes.

#

Optional

#
watch = true


上面設定可以看到將 Traefik 啟動在 8080 port,並且啟動 Docker Provider,讓 Traefik 可以自動偵測目前 Docker 啟動了哪些服務。底下是啟動 Traefik 的 docker-compose 檔案


version: ‘2’

services:
traefik:
image: traefik
restart: always
ports:
- 8080:8080
# - 80:80
# - 443:443
networks:
- web
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik.toml:/traefik.toml
# - ./acme.json:/acme.json
container_name: traefik

networks:
web:
external: true


啟動 Traefik 環境前需要建立虛擬網路名叫 web


$ docker network create web
$ docker-compose up -d

啟動 App 服務


接著只要透過 docker-compose 來啟動您的服務


version: ‘3’

services:
app_1:
image: appleboy/test:alpine
restart: always
networks:
- web
logging:
options:
max-size: “100k”
max-file: “3”
labels:
- “traefik.docker.network=web”
- “traefik.enable=true”
- “traefik.basic.frontend.rule=Host:domain1.com”
- “traefik.basic.port=8080”
- “traefik.basic.protocol=http”

app_2:
image: appleboy/test:alpine
restart: always
networks:
- web
logging:
options:
max-size: “100k”
max-file: “3”
labels:
- “traefik.docker.network=web”
- “traefik.enable=true”
- “traefik.basic.frontend.rule=Host:domain2.com”
- “traefik.basic.port=8080”
- “traefik.basic.protocol=http”

networks:
web:
external: true


大家可以清楚看到透過設定 docker label 可以讓 Traefik 自動偵測到系統服務


    labels:
- “traefik.docker.network=web”
- “traefik.enable=true”
- “traefik.basic.frontend.rule=Host:domain2.com”
- “traefik.basic.port=8080”
- “traefik.basic.protocol=http”

其中 traefik.basic.frontend.rule 可以填入網站 DNS Name,另外 traefik.basic.port=8080 則是服務預設啟動的 port (在 Go 語言內實作)。


驗證網站是否成功


$ curl -v http://domain1.com:8080/test
$ curl -v http://domain2.com:8080/test

bash screen


搭配 Let’s Encrypt


這邊又要感謝 Go 語言內建 Let’s Encrypt 套件,讓 Go 開發者可以快速整合憑證,這邊只需要修正 Traefik 服務設定檔


logLevel = “INFO”
defaultEntryPoints = [“https”,“http”]

[entryPoints]
[entryPoints.http]
address = “:80”
[entryPoints.http.redirect]
entryPoint = “https”
[entryPoints.https]
address = “:443”
[entryPoints.https.tls]

[retry]

[docker]
endpoint = “unix:///var/run/docker.sock”
watch = true
exposedByDefault = false

[acme]
email = “appleboy.tw@gmail.com”
storage = “acme.json”
entryPoint = “https”
onHostRule = true
[acme.httpChallenge]
entryPoint = “http”


跟之前 Traefik 比較多了 entryPointsacme,另外在 docker-compose 內要把 80 及 443 port 啟動,並且將 acme.json 掛載進去


version: ‘2’

services:
traefik:
image: traefik
restart: always
ports:
- 80:80
- 443:443
networks:
- web
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik.toml:/traefik.toml
- ./acme.json:/acme.json
container_name: traefik

networks:
web:
external: true


其中先建立 acme.json 並且設定權限為 600


$ touch acme.json
$ chmod 600 acme.json

再重新啟動 Traefik 服務


$ docker-compose down
$ docker-compose up -d

最後只要改 traefik.basic.frontend.rule 換成真實的 Domain,你會發現 Traefik 會將憑證內容寫入 acme.json。這也為什麼我們需要將 acme.json 建立在 Host 空間上。


搭配 Drone 自動化更新服務


未來所有服務都可以透過 docker-compose 來啟動,所以只要透過 Drone 將 一些 yaml 設定檔案傳到服務器即可


  - name: scp
image: appleboy/drone-scp
pull: true
settings:
host: demo1.com
username: deploy
key:
from_secret: deploy_key
target: /home/deploy/gobento
rm: true
source:
- release/*
- Dockerfile
- docker-compose.yml

上面將檔案丟到遠端機器後,再透過 ssh 編譯並且部署


  - name: ssh
image: appleboy/drone-ssh
pull: true
settings:
host: console.gobento.co
user: deploy
key:
from_secret: deploy_key
target: /home/deploy/demo
rm: true
script:
- cd demo && docker-compose build
- docker-compose up -d –force-recreate –no-deps demo
- docker images –quiet –filter=dangling=true | xargs –no-run-if-empty docker rmi -f

心得


本篇教大家一步一步建立 Traefik 搭配 Docker,相對於 Nginx 我覺得簡單非常多,尤其時可以在 docker-compose 內設定 docker Label,而 Traefik 會自動偵測設定,並且重新啟動服務。希望這篇對於想要快速架設網站的開發者有幫助。如果您有在用 AWS 服務,想省錢可以使用 Traefik 幫您省下一台 ALB 或 ELB 的費用。最後補充一篇效能文章:『NGINX、HAProxy和Traefik负载均衡能力对比』有興趣可以參考一下。



comments powered by Disqus