Docker Compose 整合發(fā)布應用相關服務

首先诫咱,祝各位新年快樂笙隙,萬事如意,雞年大吉坎缭。

這次要來說說一個和前端并不太相關的東西——docker compose竟痰,一個整合發(fā)布應用的利器。

如果掏呼,你對 docker 有一些耳聞坏快,那么,你可能知道它是什么憎夷。

不過假消,你不了解也沒有關系,在作者眼中岭接,docker 就類似于一個沙箱,而你的應用起在這個沙箱里臼予,不受服務器系統(tǒng)環(huán)境的影響鸣戴,同時也不污染服務器,配置完成之后往服務器部署或移除應用都相當方便粘拾。

而 compose 就如同它的字面意思組合窄锅,它就好像是一個大箱子,可以把幾個不相關的沙箱給組合起來缰雇,變成一個整體入偷,就如同小時候動畫片中變形金剛的合體變身。

Awesome?
Awesome?

理論知識就沒有什么比官方文檔更好的了械哟,這里就不講了疏之,主要來看看如何應用。本文主要包含以下幾個部分:

如果暇咆,你只對前端技術感興趣锋爪,那么,這篇文章可能不適合你爸业。

常言道:一個不懂運維的設計其骄,不是一個好前端。

<a name="Install"></a>

安裝

Windows 和 Mac 裝了 Docker 之后已經自帶 docker-compose扯旷,其他環(huán)境根據(jù) Docker 官網(wǎng)介紹拯爽,簡單幾步也能完成安裝。

這里要提一下钧忽,在亞馬遜 aws 上安裝 docker-compose毯炮,由于沒有 root 權限會遇到官網(wǎng)上所提到的 Permission denied 錯誤逼肯,加了 sudo 也是無法直接下載到 /usr/local/bin 目錄下的。

硬來不行否副,還可以曲線救國嘛~

先將文件下載到 aws 服務器上汉矿,再將文件移動到 /usr/local/bin 目錄就可以了。

curl -L https://github.com/docker/compose/releases/download/1.9.0/docker-compose-`uname -s`-`uname -m` > docker-compose
sudo chown root docker-compose
sudo mv docker-compose /usr/local/bin
sudo chmod +x /usr/local/bin/docker-compose

驗證是否安裝成功备禀,試試 docker-compose version洲拇。如果有輸出版本信息,就說明 docker-compose 已經安裝好了曲尸。

docker-compose 雖然安裝好了赋续,但并不一定能用,因為 docker 和 docker-compose 是分開安裝另患,即使它倆各自運行正常纽乱,在一起就不一定合拍了。

那怎么知道它倆合不合拍昆箕?答案很簡單鸦列,hello world~

<a name="HelloWorld"></a>

Hello world

在任意的目錄下,創(chuàng)建一個 docker-compose.yml 文件鹏倘,并添加下面的內容薯嗤。

version: '2'
services:
  helloworld:
    image: 'hello-world'

然后,在當前目錄下使用 docker-compose up 啟動 docker-compose纤泵。

啟動時骆姐,如遇到

client and server don't have same version (client : 1.22, server: 1.18)

類似這樣的錯誤,可以通過設置 docker-compose 的 api 版本來解決捏题。

COMPOSE_API_VERSION=auto

不要嘗試通過一次次安裝不同的 docker-compose 版本來解決玻褪,你會 ?? 的。如果公荧,還遇到

docker.errors.InvalidVersion: inspect_network is not available for version < 1.21

這是 Ubuntu 14.04 LTS 默認的 docker 版本太低引起的带射,需要升級 docker。然而循狰,在 aws 的服務器上升級 docker 版本時庸诱,需要先創(chuàng)建 /etc/apt/sources.list.d/docker.list 文件,并添加

deb https://packages.docker.com/1.12/apt/repo ubuntu-trusty main

再運行

sudo apt-get update && sudo apt-get upgrade docker-engine

就能升級成功晤揣∏潘看到??這樣的結果,就表示 docker 和 docker-compose 都安裝成功昧识,而且它倆很搭钠四。

Hello world result
Hello world result

<a name="Command"></a>

常用命令

docker-compose 的命令很簡單,它已經將一些 docker 常用關于 image, container & volume 的命令都整合在了一起,使發(fā)布變得極其簡單缀去。比如侣灶,之前剛剛提到的 docker-compose up,就類似于 docker build & run缕碎,用來創(chuàng)建并啟動 container褥影。

其他常用的命令有:

  • build:構建或重新構建 services
  • config:驗證 docker-compose 配置文件
  • create:創(chuàng)建 services
  • down:與 up 相對,停止并刪除 container, image, volumn 等
  • kill:殺死某個 container
  • logs:查看 container 日志
  • ps:查看 container 信息
  • restart:重啟 services
  • rm:刪除已經停止的 container
  • start:啟動 services
  • stop:停止 service
  • version:顯示 docker-compose 版本

是不是發(fā)現(xiàn)有幾個命令和 docker 的命令一樣咏雌?的確凡怎,但就如同之前的安裝過程一樣,docker-compose 是依賴于 docker 的赊抖,docker 命令更底層统倒。比如 docker-compose ps 這個命令,它只會顯示由 docker-compose 啟動的容器信息氛雪,但不包含 docker 啟動的容器信息房匆,相反 docker ps 可以查看由 docker-compose 啟動的容器信息。

還剩幾個命令沒有列出來报亩,有興趣的童鞋可以通過 docker-compose help 命令或上官網(wǎng)查看更多信息浴鸿。

光說不練假把式。docker-compose 究竟好不好用弦追,只有用了才知道岳链。

<a name="RealWorld"></a>

Real world

之前,個人博客的靜態(tài)資源一直都是通過 node 提供服務骗卜。這的確可以,但這不是 node 的強項左胞。

專業(yè)的事交給專業(yè)的人去做寇仓。 - by S(ome)B(ody)

這個專業(yè)的人就是 nginx。

除此之外烤宙,2017 年起水果和古哥都強推 https遍烦,升級 https 也是箭在弦上(雖然一直有這個打算,也拖到了現(xiàn)在??彡(-_-;)彡)躺枕。

于是服猪,程序不再是原先單一的 node 服務,而是拐云,變成了一系列密切相關的服務罢猪。如果,通過基礎的 docker 命令來一個個啟動叉瘩、停止服務的話膳帕,那么,就需要額外添加一個復雜的腳本來控制薇缅。

docker-compose 就是用來處理類似的問題危彩。它可以做到通過一條命令來控制一個應用相關的一系列服務的啟動攒磨、停止等,并且不依賴于機器環(huán)境汤徽,作到隨時可以將應用遷移至其他的機器上發(fā)布娩缰。

知道了準備做什么,先看看最終設計的應用結構和之前的對比谒府。

直接看這張圖可能有點蒙圈拼坎,沒事,一點點來看狱掂。

<a name="Transform"></a>

docker 到 docker-compose 的轉換

本文一開始就有提到演痒,docker 可以看做是一個小箱子,而 docker-compose 是一個大箱子用來裝這些小箱子趋惨。

那么鸟顺,如何將小箱子放入這個大箱子里哪?

非常簡單器虾!只需告訴 docker-compose 如何啟動你的應用就可以了讯嫂,那就先看看原先的啟動命令。

docker run -d -p 80:8080 --name blog

啟動命令中兆沙,主要配置了一個端口的映射 -p欧芽,以及命名了容器名,用于方便地啟動葛圃、停止應用千扔。清楚了這些,那么改成 docker-compose 的文件也就輕而易舉了库正。

version: '2'
services:
  node:
    build: .
    container_name: node
    ports:
     - "80:8080"

docker 到 docker-compose 的轉換就這樣完成了曲楚,這些更新都不需要修改任何的業(yè)務邏輯或者打包配置。

試著使用 docker-compose up -d 啟動服務驗證看看褥符。

啟動正常之后龙誊,還是一步步來,先引入 nginx喷楣。

<a name="Nginx"></a>

引入 Nginx

Nginx 是一個高性能的 Web 服務器趟大,它具有配置簡單、運行穩(wěn)定和負載均衡等特點铣焊,常被作為靜態(tài)資源服務器逊朽。(詳細的 Nginx 信息,請自行查詢資料曲伊,這方面本人也不是行家)

Nginx 在 docker hub 上有現(xiàn)成的官方鏡像惋耙,直接拿來用就可以了。

version: '2'
services:
  # ...

  nginx:
    image: nginx:stable
    container_name: nginx
    ports:
      - "80:80"
    restart: always

此時,啟動服務會失敗并報錯绽榛,因為 nginx 和原有的 node 容器都綁定到了 80 端口湿酸。docker-comopse 各個容器之間是相互獨立的,容器內部的接口相互之間不影響灭美,但對外暴露的接口不能相同推溃,不然就會引起沖突。

從之前的結構圖可以看到届腐,請求全部由 nginx 接受并轉發(fā)到 node 服務铁坎,也就是說,node 不直接對外提供服務犁苏。那么硬萍,docker-compose 中也就可以移除 ports 部分(這里便于測試 node 服務依舊暴露 8080 端口)。

其次围详,靜態(tài)文件是由 node 打包后生成的朴乖,也就是說需要將 node 服務中的數(shù)據(jù)共享給 nginx 服務,這就需要用到 volume(數(shù)據(jù)卷)助赞。數(shù)據(jù)卷可以將數(shù)據(jù)在宿主機和容器之間买羞、容器和容器之間共享,即使容器被刪除了雹食,數(shù)據(jù)卷依舊存在畜普。

這里就需要將服務器上的 nginx 配置文件和 node 構建之后的靜態(tài)文件共享給 nginx。

version: '2'

services:
  node:
    build: .
    container_name: node
    # node service port export for test
    ports:
     - "8080:8080"
    volumes:
     - ./log/node:/var/log/node

  nginx:
    image: nginx:stable
    container_name: nginx
    depends_on:
      - node
    volumes:
      - ./config/nginx:/etc/nginx/conf.d:ro
      - ./log/nginx:/var/log/nginx
    volumes_from:
      - node:ro
    ports:
      - "80:80"
    restart: always

volume 是 docker 中相當重要及常用的一部分群叶,理解它對使用 docker 解決問題有巨大的幫助吃挑。推薦一篇關于 docker volume 的文章,有助于理解 volume街立。

負載均衡

docker-compose 配置完了舶衬,再來看看 nginx 配置。本章一開始有提到 nginx 可以做負載均衡几晤,那該如何配置哪约炎?

在 nginx 中配置負載均衡相當簡單植阴,只需在 upstream 里配置一下目標服務器蟹瘾。

然而,這里就會遇到一個問題掠手。由于憾朴,容器之間是相互獨立的,于是喷鸽,localhost 便無法在容器之間相互訪問众雷。不過,由同一 docker-compose 所起的容器之間可以通過容器名相互訪問,這里就是

upstream node_server  {
    server node:8080 max_fails=2 fail_timeout=30s;
}

如果要額外再起一個服務砾省,只需在 docker-compose 文件中再啟動一個容器(可以依賴同一套代碼)鸡岗,并將之前所配的 upstream 中額外多添加一條 server 信息,比如:

upstream node_server  {
    server node:8080 max_fails=2 fail_timeout=30s;
    server node-backup:8080 max_fails=2 fail_timeout=30s;
}

這樣即使一個服務掛了编兄,只要另一個服務還運行正常轩性,nginx 會將請求轉發(fā)給運行正常的服務。一個最簡單的復雜均衡就做好了狠鸳,所有這些都不需要修改任何功能性的代碼揣苏。

知道了 nginx 可以提供負載均衡,但也不要忘了老朋友 pm2件舵。

pm2 通過命令行參數(shù) -i卸察,或配置文件通過起多個實例來做負載均衡(本人的小博客也是用的這個方式)。

引入 nginx 之后铅祸,將全站升級成 https 就輕而易舉了坑质,只需在配置文件中標明證書及秘鑰文件的位置就可以了。接下去个少,就看看如何生成證書和秘鑰洪乍。

<a name="Letsencrypt"></a>

使用 Letsencrypt 生成 SSL 證書

獲取 ssl 證書的方式有許多種,有的買域名就送證書夜焦,這里介紹一下用 letsencrypt(現(xiàn)已更名為 certbot)獲取免費 ssl 證書壳澳。

常言道:前人栽樹,后人乘涼茫经。

同樣的巷波,letsencrypt 在 docker hub 上也有現(xiàn)成的鏡像。鏡像有了卸伞,剩下的就只需根據(jù)不同的場景來生成證書抹镊。

certbot 支持 5 種生成證書的模式,分別是:apache, nginx, webroot, standalonemanual荤傲,分別用于不同的場景垮耳。這里 nginx 和 certbot 使用的是不同的鏡像,所以選用的模式是 webroot遂黍。

選定了鏡像和模式终佛,那么參照 certbot 的文檔就能夠簡單地生成證書了。

docker run -it --rm --name certbot \
  -v /letsencrypt/etc/letsencrypt:/etc/letsencrypt \
  -v /letsencrypt/lib/letsencrypt:/var/lib/letsencrypt \
  -v /letsencrypt/challenge:/usr/share/nginx/html \
  -v /var/log/letsencrypt:/var/log/letsencrypt \
  deliverous/certbot \
  certonly --webroot -w /usr/share/nginx/html

需要注意的是雾家,在 webroot 模式下申請證書铃彰,需要向 certbot 證明服務器能被訪問。certbot 驗證程序會訪問 web root 目錄(這里是 /usr/share/nginx/html)來驗證芯咧。這里又要用到之前提到的 volume 將目錄共享給 nginx牙捉,讓 nginx 能夠訪問到目錄內部的文件竹揍。

server {
    listen 80;
    listen [::]:80;

    server_name discipled.me;

    # ...
    
    # letsencrypt challenge file location
    location /.well-known {
        root /usr/share/nginx/html;

        access_log  /var/log/nginx/challenge-access.log  main;
        allow all;
    }
    
    ...
}

修改 nginx 配置之后,別忘重啟 nginx 服務邪铲。

docker-compose restart nginx

重啟 nginx 之后芬位,然后再運行上面生成證書的命令就能生成證書了。

ssl 證書生成成功
ssl 證書生成成功

看到 Congratulations带到!晶衷,證書就生成成功了。

再一次修改 nginx 配置阴孟,添加 ssl 證書信息晌纫,并監(jiān)聽 443 端口。

# redirect host http://domain to https://domain
server {
    listen 80;
    listen [::]:80;

    server_name discipled.me;

    # letsencrypt challenge file location
    location /.well-known {
        root /usr/share/nginx/html;

        access_log  /var/log/nginx/challenge-access.log  main;
        allow all;
    }

    location / {
        return 301 https://discipled.me$request_uri;
    }
}

# https://domain server
server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name discipled.me;
    charset utf-8;

    gzip on;
    gzip_types    text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
    root /usr/app/build/client/;

    ssl_certificate /etc/letsencrypt/live/discipled.me/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/discipled.me/privkey.pem;

    location / {
        try_files $uri @node;
    }

    location @node {
        proxy_pass http://node_server;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

重啟 nginx 服務后永丝,訪問網(wǎng)站就可以看到

小鎖加上锹漱,大功告成。

七牛的圖床用 https 還要實名認證慕嚷,為了保護(pa)個(cha)人(shui)隱(biao)私哥牍,就暫時用 Github 來救一下急。(誰知道有啥好用的圖床麻煩推薦一下喝检,像七牛一樣支持 qrsync 用腳本批量上傳的就最好了~先謝過...)

證書更新

letsencrypt 生成的證書有效期是 3 個月嗅辣,所以,至少 3 個月內需要更新一次證書挠说。

certbot 提供了 renew 命令可以方便地更新證書澡谭,使用 --dry-run 參數(shù)可以驗證證書更新命令是否正確。

docker run -it --rm --name certbot \
  -v /letsencrypt/etc/letsencrypt:/etc/letsencrypt \
  -v /letsencrypt/lib/letsencrypt:/var/lib/letsencrypt \
  -v /letsencrypt/challenge:/usr/share/nginx/html \
  -v /var/log/letsencrypt:/var/log/letsencrypt \
  deliverous/certbot \
  renew --dry-run

同樣损俭,看到 Congratulations 說明證書更新成功了蛙奖。

由于,本人每月都會發(fā)布文章并重啟服務杆兵,就可以把證書更新一起交由 docker-compose 管理雁仲。(這里偷了個懶,增加了證書同應用之間的耦合關系琐脏,還是建議大家證書是通過系統(tǒng)定時任務來更新攒砖,省得哪天忘更新證書,證書就過期了)日裙。

<a name="Conclusion"></a>

最后

看一下最終的 docker-compose 配置文件和發(fā)布腳本吹艇。

# docker-compose.yml
version: '2'

services:
  node:
    build: .
    image: "blog:${TAG_NAME}"
    container_name: node
    # node service port export for test
    ports:
     - "8080:8080"
    volumes:
     - ./log/node:/var/log/node

  nginx:
    image: nginx:stable
    container_name: nginx
    depends_on:
      - node
      - letsencrypt
    volumes:
      - ./config/nginx:/etc/nginx/conf.d:ro
      - ./letsencrypt/etc/letsencrypt:/etc/letsencrypt
      - ./letsencrypt/lib/letsencrypt:/var/lib/letsencrypt
      - ./letsencrypt/challenge:/usr/share/nginx/html
      - ./log/nginx:/var/log/nginx
    volumes_from:
      - node:ro
    ports:
      - "80:80"
      - "443:443"
    restart: always

  letsencrypt:
    image: deliverous/certbot
    container_name: certbot
    volumes:
      - ./letsencrypt/etc/letsencrypt:/etc/letsencrypt
      - ./letsencrypt/lib/letsencrypt:/var/lib/letsencrypt
      - ./letsencrypt/challenge:/usr/share/nginx/html
      - ./log/letsencrypt:/var/log/letsencrypt
    command: renew

發(fā)布腳本主要用來更新代碼,以及獲取應用版本號阅签。

# deploy.sh
# git operation
git reset HEAD --hard
git fetch
git pull

# TAG_NAME used to set docker image tag
export TAG_NAME=`git tag -l | sort -r | head -n 1`

# docker operation
docker-compose down --volumes

docker-compose up --build -d

其他配置可以上 github 查看掐暮。

一扯似乎又扯遠了蝎抽,歡迎提意見和建議政钟,順便再問一下有啥好的圖床推薦路克。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市养交,隨后出現(xiàn)的幾起案子精算,更是在濱河造成了極大的恐慌,老刑警劉巖碎连,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灰羽,死亡現(xiàn)場離奇詭異,居然都是意外死亡鱼辙,警方通過查閱死者的電腦和手機廉嚼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倒戏,“玉大人怠噪,你說我怎么就攤上這事《捧危” “怎么了傍念?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長葛闷。 經常有香客問我憋槐,道長,這世上最難降的妖魔是什么淑趾? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任阳仔,我火速辦了婚禮,結果婚禮上扣泊,老公的妹妹穿的比我還像新娘驳概。我一直安慰自己,他們只是感情好旷赖,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布顺又。 她就那樣靜靜地躺著,像睡著了一般等孵。 火紅的嫁衣襯著肌膚如雪稚照。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天俯萌,我揣著相機與錄音果录,去河邊找鬼。 笑死咐熙,一個胖子當著我的面吹牛了赵,可吹牛的內容都是我干的斥废。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蛾魄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拉背,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體默终,經...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡椅棺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了齐蔽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片两疚。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖含滴,靈堂內的尸體忽然破棺而出鬼雀,到底是詐尸還是另有隱情,我是刑警寧澤蛙吏,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布源哩,位于F島的核電站,受9級特大地震影響鸦做,放射性物質發(fā)生泄漏励烦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一泼诱、第九天 我趴在偏房一處隱蔽的房頂上張望坛掠。 院中可真熱鬧,春花似錦治筒、人聲如沸屉栓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽友多。三九已至,卻和暖如春堤框,著一層夾襖步出監(jiān)牢的瞬間域滥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工蜈抓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留启绰,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓沟使,卻偏偏與公主長得像委可,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子腊嗡,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內容