9102 年了,學點 Docker 知識

HelKyle發表於2019-01-21

最近工作需要,開發時需要用到 Docker。這篇文章從零開始演示幾個 Demo,如果你之前沒接觸過 Docker,可以一步步跟著操作,加深對 docker 的理解。

多圖,請儘量在?上觀看

本文由 DJI 前端開發工程師 HelKyle 原創並分享於掘金,如需轉載請註明出處

Docker 能解決什麼問題

無論你所處的公司大或小,多多少少都遇到開發環境和生產環境不一致的問題。有些開發者用 Windows,有些開發者用 Mac,而生產環境可能用的是 Linux,同時跑著多個應用,每個應用依賴的 node 版本版本不一致,不同服務可能還佔用相同的埠。於是我們常常聽到這樣的疑問:“本地明明是好的啊,為什麼到線上就不行了?”

(我自己的親身經歷:很早以前開發前端專案,我需要在本地搭建 LEMP 環境,照著教程搗鼓好幾天,一行前端程式碼都沒有寫。後來還因為一些“莫名其妙”的問題,反反覆覆重灌了好幾次。)

而使用了 Docker 之後呢,我們能通過配置檔案一條命令快速構建環境,並且可以做到和其他服務隔離,互不影響,通過例子來講解。

演示環境

這篇文章執行的環境是 macOS 10.14.1, Docker version 18.09.0

概念:映象 vs 容器

映象是以一些列歷史操作疊加而成的,對映象的每一次操作都會產生新的只讀層,比方說你往容器寫入內容,提交,再把它移除,則會產生兩個歷史只讀層,有點像 git commits,而容器可以理解問為映象歷史層 + 可寫層的可執行系統,在可寫層做任何操作都沒問題,不提交的話,容器被刪除後相應的改動也會丟失,有點像 git 暫存區

(個人理解,不一定對)

docker

安裝 Docker

Mac 安裝 docker

brew cask install docker複製程式碼

其他環境安裝 Docker 檢視 這裡

安裝完成之後,執行 hello-world 試一下。

$ docker run hello-world複製程式碼
Unable to find image 'hello-world:latest' locallylatest: Pulling from library/hello-world1b930d010525: Pull completeDigest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535Status: Downloaded newer image for hello-world:latestHello from Docker!This message shows that your installation appears to be working correctly.複製程式碼

這條命令會連線本地 Docker 服務,Docker 服務檢測到本地沒有 hello-world 映象,於是去 Docker 映象市場下載這個映象,然後建立新的容器,執行特定的命令,輸出 Hello from Docker!

這篇文章不講解 docker 有哪些基礎命令,直接從案例入手。

啟動 nginx 服務

docker run -d -p 80:80 --restart=always  nginx:latest複製程式碼

引數說明:run 啟動某個映象-d 讓容器在後臺執行-p 指定埠對映,宿主機的80埠對映到容器的80埠--restart 重啟模式,設定 always,每次啟動 docker 都會啟動 nginx 容器。

nginx

由於我本地沒有 nginx:latest 的映象,同樣會先去映象市場下載。啟動完成開啟 http://localhost:80 就能立馬看到 nginx 的歡迎頁面。

hello nginx

如果想修改歡迎頁面,可以進入到容器內修改頁面。

docker exec -it 4591552a4185 bash複製程式碼

引數說明:
exec 對容器執行某些操作
-it 讓容器可以接受標準輸入並分配一個偽tty
4591552a4185 是剛剛啟動的 nginx 容器唯一標記
bash 指定互動的程式為 bash

exec

nginx 預設檔案路徑是 /usr/share/nginx/html/index.html ,直接用 echo 寫入內容即可。

echo '<
h1>
Hello Docker<
h1/>
'
>
/usr/share/nginx/html/index.html複製程式碼

ctrl + D 退出容器,重新訪問 localhost:80 即可看到 Hello Docker。

hello docker

每次修改內容都需要手動進入容器,太過繁瑣,並且?提到了,對容器的直接修改不會持久儲存,如果容器被刪,資料也會跟著丟失。

(由於之前的 demo 已經佔用了 80 埠,我們們先 kill 掉它。)

docker kill 4591552a4185複製程式碼

Docker 提供資料掛載的功能,即可以指定容器內的某些路徑對映到宿主機器上,修改命令,新增 -v 引數,啟動新的容器。

docker run -d -p 80:80  -v ~/docker-demo/nginx-htmls:/usr/share/nginx/html/ --restart=always  nginx:latest複製程式碼

啟動成功之後,docker 會幫你生成目錄 ~/docker-demo/nginx-htmls,現在裡面什麼都沒有,新增個 index.html。

html

再次開啟 http://localhost:80, 同樣能看到 hello docker。

hello docker

接著我們來用 Node + Redis + Docker 做一個 PV 展示的 DEMO。

執行命令:

docker run -d -p 6379:6379 -v ~/docker-demo/redis:/data redis:latest複製程式碼

啟動一個 redis 容器,並將資料持久化到 ~/docker-demo/redis 目錄。(考慮效能,redis 並不會實時寫入資料到磁碟)

用 koa 啟動一個 node server,並連線 redis , 每次訪問 / 都給計數器加一。

const Redis = require('ioredis');
const Koa = require('koa');
const Router = require('koa-router');
const router = new Router();
const app = new Koa();
const redis = new Redis(`redis://127.0.0.1:6379/0`);
router.get('/', async (ctx, next) =>
{
await next();
await redis.incr('pv');
const current = await redis.get('pv');
ctx.body = `current pv: ${current
}
`;

});
app .use(router.routes()) .use(router.allowedMethods());
app.listen(3000);
複製程式碼

訪問 http://localhost:3000,就能看到輸出結果。

pv

推薦使用 medis 視覺化檢視 redis 資料。

medis

OK,開發環境完成功能開發,交付運維上線。我們假設生成環境會啟動四個 node 服務,一個 redis 服務,和一個 nginx 做負載。

這時候需要把我們的 node 服務也構建成映象,新增 dockerfile 檔案。

# 基於最新的 node 映象FROM node:latest# 複製當前目錄下所有檔案到目標映象 /app/ 目錄下COPY . /app/# 修改工作目錄WORKDIR /app/# yarn 一下,安裝依賴RUN ["yarn"]# 啟動 node serverENTRYPOINT ["node", "index.js"]複製程式碼

更多 dockerfile 指令看這裡

可以在本地構建一下,執行命令 docker build . --tag=pv,然後通過 docker images 就能看到剛剛構建的新映象。

繼續往下走,編排一組容器,docker 官方提供了 docker-compose 工具。在專案目錄下新增 docker-compose.yml 檔案。

# 使用 docker-compose 2.2 版本version: "2.2"# 定義 servicesservices:  redis:    image: redis:latest    volumes:      - "~/docker-demo/pv/data/:/data/"  web:    # 放大4倍,也就會有四個 node server    scale: 4    build: .    # 新增環境變數    environment:      - REDIS_HOST=redis://redis:6379/0    # 依賴關係    depends_on:      - redis  nginx:    image: nginx:latest    depends_on:      - web      - redis    ports:      - 80:80    volumes:      - "./default.conf:/etc/nginx/conf.d/default.conf"複製程式碼

更多 docker-compsoe 指令看這裡

service web 新增環境變數 REDIS_HOST=redis://redis:6379/0 是給 ioredis 連結用的,對應的要修改 js 檔案。

const redis = new Redis(process.env.REDIS_HOST);
複製程式碼

redis://redis:6379/0 第一個 redis 是協議,第二個 redis 是 service host。service 之間可以通過 host 互相通訊。

複製 nginx 容器下的 default.conf 檔案出來修改

upstream web { 
server pv_web_1:3000;
server pv_web_2:3000;
server pv_web_3:3000;
server pv_web_4:3000;

}server {
#... location / {
proxy_pass http://web;

} #...
}複製程式碼

新增上游服務 web,這裡的 pv 是我的專案檔名,web 是 docker-compose 檔案中定義的 service name, 1 – 4 則是 scale 出來 docker 自動給定的序號。啟動起來之後,nginx 訪問 http://pv_web_1:3000 的請求就會到達第一個 web 容器。

萬事具備,let’s compose up!

compose-up

好了,現在訪問 http://localhost:80

result

到目前為止,我們已經把應用部署完成,每次訪問 pv 數量自動加一,並且經過 nginx 負載均衡,會隨機打到不同的容器上面。???

總結

這篇文章演示了和前端相關的一些 docker 操作,從中我們可以看到其對於軟體的開發,測試,部署都帶來了極大的便利。文中的內容僅僅是冰山一角,更全面的學習可以看Docker — 從入門到實踐這本書。

示例也只能作為學習使用,請不要直接用在生產環境。同時部分內容是個人理解,沒有權威性,深入學習 Docker ,你應該去看詳細的文件。

本文由 DJI 前端開發工程師 HelKyle 原創並分享於掘金,如需轉載請註明出處

相關文章

來源:https://juejin.im/post/5c2c69cee51d450d9707236e#comment

相關文章