Docker 實戰系列(1):帶你 一步一步 dockerize 的應用
想像一個情況,當你在本機開發完準備要部屬到自己的 server 上時,你發現 server 的系統不是你熟悉的 Ubuntu 而是 CentOS,除此之外你還需要自己安裝 php7、設定 MySQL 的帳號密碼跟架設 Apache,光想到要設定這些環境頭都痛了,這時候你就需要 Docker 了
Docker 可以幫你把 Ubuntu + php7 + MySQL + Apache 的環境跟你的程式碼打包起來,整包丟到 CentOS的 server 上去跑,不用很勉強的在不熟悉的 CentOS 上配置環境,弄個不好說不定還會影響到其他正在跑的程式
Docker 是什麼
Docker 是個輕量級的虛擬化技術,底層使用 cgroup、chroot、namespace 實作,可以把你的應用程式連同環境一起打包,部屬的時候就不用再擔心環境的問題
下圖的例子就是使用 Docker 將三個已經打包起來的程式跑在不同的 container(容器)中,每個 container 都是一個獨立的環境,可以跑不同的系統跟安裝不同的資料庫、編譯器等等,意思就是說你可以 A 專案用 php5.3,另外一個用 php7,完全不會衝突
Dockerize
所謂的 dockerize 就是把你的應用程式 Docker 化,把你的程式跟環境包成一個 image,部屬的時候就直接使用這個 image 不需要額外安裝其他東西
事前準備
你可以使用自己最近在做的 side project,也許是一個用 Django 寫的 api server,或是用 Node.js 寫的爬蟲等等,手邊沒有 side project 的話我有寫一個簡單的 simple-express-server,他會啟動一個 server 監聽 8080 port,到 127.0.0.1:8080 就可以看到 Hello World!
安裝 Docker
目前 Docker 有支援 Linux、Mac 跟 Windows,到 Get Docker 找到適合自己的版本,安裝完之後跑 docker version,有跑出版本就代表安裝成功了~
從 Docker Hub 上 pull image
在 Docker Hub 上有很多官方的 image 可以直接拿來用,所謂的 image 就是一個已經打包好的環境,譬如說 Debian 或是 Ubuntu + Node.js 等等,需要這個環境時就可以直接把這個 image pull 下來用。
使用 docker pull ubuntu 拿到最新的 ubuntu image,pull 的過程中會看到進度條
完成後跑 docker image ls 就能看到有一個 ubuntu 的 image
REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |
---|---|---|---|---|
ubuntu | latest | 20c44cd7596f | 2 weeks ago | 123MB |
接著 docker run -it ubuntu bash把 ubuntu 的 image 跑起來變成 container,-it 是為了讓我們進到這個 container 的 shell 下指令,進到 container 後跑跑看 cat /etc/*release,我這邊的版本是 16.04.3 LTS
到目前為止,我們已經可以在任何系統上用 Docker 跑 ubuntu 的環境了
很多人看到這裡可能會有點搞混 image 跟 container,說明一下,image 指的是已經打包好的環境,但是還沒有跑起來,就是一堆檔案而已,而 container 則是把 image 跑起來之後產生的 instance,他們之間的關係就像「程式碼」跟「跑起來的程式」,程式碼不跑就只是一堆檔案,但跑起來之後他就會變成一個真的在跑 process
開始 dockerize 你的應用程式
我們的終極目標是要把你的程式碼跟想要的環境打包起來,變成一個 image,之後不管到哪一台機器上,只要有裝 Docker 而且有這個 image 就可以把你的程式跑起來
雖然程式有千百種,dockerize 的過程也不盡相同,但大致上可以分成這 6 個步驟,接下來會以上面提供的 simple-express-server 作為範例,大家可以視情況調整成適合自己應用的樣子
Step 0 — 新增 Dockerfile
為了把環境跟程式碼包成一個 image,我們需要一個 Dockerfile 把打包的步驟寫在裡面,先在專案目錄下新增一個空檔案叫做 Dockerfile
├── Dockerfile <– 這裡
├── README.md
├── index.js
├── node_modules
└── package.json
Step 1 — 找到適合的 base image
Docker 的 image 是一層一層疊加上去的,所以我們需要先在 Docker Hub 上選擇一個 base image,然後再慢慢把它加工成我們要的樣子
因為我這個專案是用 Node.js 寫的,所以我需要一個已經裝好 Node.js 的環境,有兩個選擇:
直接用 node 作為我的 base image,裡面就已經有 Node.js 環境了
用 ubuntu 當 base image,再參考 Installing 把 node 裝在 ubuntu 上
因為第一種作法比較簡單,這邊就直接使用 node,如果你是 python, golang 等等其他語言也都可以找到相對應的 image,決定 base image 後就在 Dockerfile 的第一行寫 FROM node:9.2.0,9.2.0 指的是我們使用的 node image 版本,如果想要最新版的的話也可以指定 node:latest
Step 2 — copy 原始碼
有了 Node.js 環境之後要把 code 也包進去,這邊使用 COPY 指令,把 index.js 跟 package.json 複製到 /app 資料夾裡面
Step 3 — 安裝 dependencies
現在環境都配置好了,code 也已經在裡面了,我們要做的事情就是切換到 /app 這個目錄安裝 dependencies
使用 WORKDIR 切換到 /app 目錄,再用 RUN 跑 npm install && npm cache clean –force,清 npm cache 是為了讓 build 出來的 image 不要包含這些 cache,這樣 image 會小一點
如果是其他語言就把 npm install … 換成其他指令就可以了,像 python 可能就是 pip install …,有 cache 的話記得要清一下
Step 4 — 設定 initial command
現在環境、程式碼、dependencies 都準備好了,只剩把程式跑起來,這裡會用到 CMD 設定這個 image 被跑起來時的預設指令,對我這個程式來說要跑起來就是 node index.js,這樣就完成了 Dockerfile
Step 5 — build 出 image
完成後就可以開始 build image,在專案目錄下跑 docker build -t simple-express-server . 就會根據 Dockerfile build 出你的 image
build 完跑 docker image ls 可以看到多出一個 simple-express-server,這就是剛剛 build 出來的 image,裡面包含 node 的環境、程式碼還有 dependencies 跟預設啟動指令
REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |
---|---|---|---|---|
simple-express-server | latest | dd05512eea91 | 2 minutes ago | 677MB |
ubuntu | latest | 20c44cd7596f | 2 weeks ago | 123MB |
把 image 跑起來
有了 image 後可以 docker run -p 3000:8080 simple-express-server 把 image 跑起來,在 container 內跑的就是剛剛設定的預設指令 node index.js,-p 3000:8080 則是把 container 內的 8080 port 跟外部的 3000 port 接通,如此一來只要用瀏覽器到 127.0.0.1:3000 就可以看到 Hello World!,這樣就完成 dockerize 了,現在你的電腦即便什麼都沒有只裝 docker 也可以把這個程式跑起來
跑在背景
如果照上面跑 docker run -p 3000:8080 <image> 的話會把終端機卡住,所以部屬的時候都會跑在背景,要跑在背景只要加一個 -d 就可以了,變成 docker run -d <image>,下指令後會得到一個 container ID,要看 log 的話可以跑 docker logs <container ID>
server 上沒有這個 image 怎麼辦
因為現在是在本機 build,遠端的 server 還沒有這個 image,如果要部屬必須先把 image 傳到遠端的機器,但這樣很麻煩,比較好的方式是把 Dockerfile 跟所有原始碼放在 git repo 裡面,直接在 server 上 build image,因為都是照 Dockerfile 裡面的內容跑,所以不管是在自己電腦還是 server 上 build 結果都是一樣的