在安裝之前,要特別聲明筆者是用Linux安裝Docker的,
因為Windows版的Docker要求要Windows 10 Pro才有的Hyper-V,
否則就要透過VirtualBox來執行(但我試了無數次都會失敗)
作業的境大致如下:
OS : CentOS 7 64-bit RAM: 4G CPU: 2-Core HDD: 32GB
由於Docker只支援kernel版本3.10以上的Linux,
先用uname -a檢查一下是x64且3.10以上,如下圖:
安裝的部份就是一些指令而已,依官方的文件有三個步驟:
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
yum install docker-ce
但其實我自己的經驗還要再加一個步驟才會正常運作,
就是將docker加到systemd裡:
systemctl daemon-reload
systemctl enable docker
systemctl restart docker
這個時候輸入指令docker version
會出現以下畫面:
請留意,一定要上下都有才是對的喔!
否則所有的基本操作都不會動的。
不管到什麼地方,身為一位專業的工程師,Hello World是一定要會寫的,
同理,Docker的Hello World要怎麼樣執行呢?
雖然不到一鍵完成
,但一行指令
是OK的:
docker run hello-world
hello-world:latest
這個image,Image對Docker來說就是一個類似藍圖的東西,
Docker Engine會透過Image裡的描述建立一個Container,
而Container就是一個完整的可以跑的應用程式了。
以上面的例子來說hello-world:latest
就是image,
而冒號後面接的是它的Tag,latest代表最新的版本,
當然是可以用什麼6.6.1之類的來取代,看建立Image的人如何定義的。
小結:Docker透過Docker Engine將Image轉換成Container後執行。
好,那docker run hello-world之後,我們能看到什麼關於它的資訊嗎?
答案就在docker ps這個指令裡,實際輸入之後:
咦?怎麼一片空白?
別急,docker ps還有一個參數-a
,可以列出完整的清單:
如上圖,我們可以看到剛剛的docker run建出來的hello-world Container:
既然可以看到container的資訊,是不是也能看到image的資訊呢?
答案是肯定的,透過docker images,可以看到目前的docker engine下已經存在什麼image:
如上圖,除了repository之外大概都很淺顯易懂,針對repository有幾個重點:
目前我們已經知道如何利用docker run讓hello-world跑起來,
那是不是如果再也用不到這個Container可以把它移除呢?
利用docker rm就可以達到這個效果,如下圖:
透過docker rm hardcore_wu
我們移除了這個container,
再透過docker ps -a
檢查一下,真的被刪除了!
(p.s. docker rm也可以依container id作用)
同理,image也是可以刪除的!
如上圖,有兩個重點:
首先要在Docker建立一個postgresql的container:
docker run --name db -e POSTGRES_PASSWORD=letmein -d -p 5432:5432 postgres
這段指令有幾個重點:
接著我們下指令看一下是不是服務真的有起來了:
docker ps -a
如上圖,Up 2 seconds
告訴我們服務起來了。
接著我們要透過postgresql的cli建立一個table,
但在那之前我們要先連線到這個Container的bash,
才能執行命令列的指令:
docker exec -it db /bin/bash
如上圖,有3個重點:
接著我們可以登入postgresql,並建立一個table叫account:
如上圖,其實是postgresql的CLI操作,不在此多作說明。
建立好table之後我們來寫一段Java程式透過JDBC連線到postgre這個預設的Database,
並對account這個table下一個INSERT的指令:
public class JDBCConnectionTest {
public static void main(String[] args) {
JDBCConnectionTest.testPostgreSQLConnection();
}
private static boolean testPostgreSQLConnection() {
try {
final String url = "jdbc:postgresql://192.168.11.86:5432/postgres";
final String user = "postgres";
final String password = "letmein";
Connection conn = DriverManager.getConnection(url, user, password);
if(conn == null)
return false;
final String sql = "INSERT INTO account(username, password, email) VALUES(?,?,?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "Tom");
pstmt.setString(2, "sunshine");
pstmt.setString(3, "test@gmail.com");
int updated = pstmt.executeUpdate();
if(updated > 0) {
System.err.println("Inserted 1 record");
}
else {
System.err.println("Nothing inserted");
}
}
catch(SQLException ex) {
System.err.println("Cannot establish connection!");
System.err.println(ex);
return false;
}
System.err.println("Connection to PostgreSQL Server established!");
return true;
}
}
再來我們回到postgresql的cli下SELECT看看是不是真的有插了一筆記錄:
如上圖,到此看起來JDBC的程式是有連通並成功插入一筆記錄的!
如果要暫時關閉這個postgresql的Container,
只要下docker stop db
就可以了,如下圖:
有幾個重點:
docker start db
再重新將它打開,所有本來在裡面的資料都還會在的,直到這個Container被刪除為止。docker restart db
達到這個效果。Docker預設是透過一種叫作Union File System的機制在存放資料的,
透過這個機制,多個Container間若有重覆性的資料則不會重新下載,
而各自Container保有它獨特的一個區塊供自己作Read/Write。
然而隨著docker rm
的指令執行,那個Read/Write區塊也會消失不見,
這對於需要保存已經寫進Container資訊的人來說是一個困擾,
還好還好,有docker volume
這個指令可以將檔案放在Host,
以下我們將透過一個簡單的例子介紹如何利用上述的指令保存檔案:
首先,我們透過docker run
的選項-v
建立一個debian的container:
docker run -it -v $(pwd):/home/inside debian:latest /bin/bash
如上圖,有幾個重點:
pwd
是Linux bash的指令,是Print Working Directory
的意思-it
這個選項,接著我們建立一個叫hello.txt的文字檔,透過vi editor在裡面輸入Hello World,
最後再將這個Container移除:
如上圖,各位可以發現下docker ps -a
的時候debian已經狀態是Exited,
這是因為我們透過exit
指令離開debian bash的時候,Container就離開主程序,
此時這個Container就會被停止,這一點要特別留意思一下,
如果執行的Container因為某種原因開啟沒多久狀態就變成Exited,
也許就是有在某個地方發生什麼錯誤造成Container離開主程序了。
接著我們再檢查看看檔案是不是真的有被保留下來了:
如上圖,檔案確實被保留下來了,內容也是當初在Container內加入的Hello World
而且原本的debian Container也透過docker ps -a
確認早已不存在!
雖然上例透過-v
這個選項達到了效果,但其實官方並不建議這個解法,
因為畢竟是寫死了host的路徑,所以推薦的方式是建立docker volume,
如下幾張圖以同樣的例子示範:
首先利用docker volume create outside
建立一個名稱為outside的volume,
接著透過docker volume ls
列出目前docker engine內的volume清單檢查是否真已建立,
最後再用docker inspect outside
觀看outside的相關資訊。
再來!
這裡跟前面的例子有一點小差異,就是把$(pwd)
改成outside了,
所以這個debian會將檔案存到outside這個volume對應到的真實路徑,
而真實路徑在前面的步驟有看到,就是/var/lib/docker/volumes/outside/_data
。
實際檢查一下,裡面真的有這個檔案呢!
小結: 第一種方法叫bind mount,第二種叫named volume,
官方建立使用第二種是因為這樣檔案會是統一由docker管理,
如果自己bind mount host的路徑的話,deploy到別台機器還要手動維持同樣的架構。
前面講了這麼多,好像都在用別人建好的Image?
是時候該自己手動建一個來玩玩了!
假設今天我有一個hello.war,是要部署在tomcat上,
而且唯一的一個功能就是在畫面上輸出Hello World的這麼個簡單的小系統,
我該如何把它建置成一個image並放到docker hub上呢?
首先我們要建立的是Dockerfile,一個描述建置步驟的檔案,
目前切出的檔案架構如下圖:
其中/app/demo.war就是我們的小系統,
而Dockerfile的內容如下:
FROM tomcat:9.0-jre8-alphine
COPY ./app/demo.war /usr/local/tomcat/webapps/demo.war
接著就要建置映象了,指令:
docker build -t dreamfulfil/tomcat-demo .
如上圖,有幾個重點:
.
,給的是Dockerfile所在的位置,.
就可以告知這個訊息。STEP
如同我們在Dockerfile裡所定義的,它先去pull tomcat相關的所有image,建置完之後我們可以利用docker images
在本機的Docker machine裡看到這個image:
而且還可以執行呢docker run
呢!
如上圖,利用docker run啟動Container後,利用docker ps確定正常執行了,
接著我們就可以利用瀏覽器檢查這個Container是不是真的能提供小系統的服務:
事實證明是可行的!
最後就是docker push將image丟到docker hub:
如上圖,有兩個重點:
dreamfulfil
)。取後我們試著把本機的image透過docker rmi
移除後,
再重新利用docker run執行看看:
如上圖,docker run發現本機端沒有image了,
就會去docker hub上面把image拉下來(不管有沒有登入),
同時我們也利用curl確定了拉下來的東西是可以執行的。
待補…
有沒有想過Container間彼此是可以溝通的呢?
透過docker network
就可以達到這樣的功能了,
預設它是透過bridge的方式互通有無,詳細請見這裡。
情境: 原本的Hello World小系統,要改成從postgresql DB撈出資訊,
不再只是單純的輸出,而且兩個都要用Docker Container的方式啟動,該如何做呢?
首先我們利用以下語法建立一個network:
docker network create demo
這會建立一個network名稱叫demo,
只要執行docker run的時候有加上設定要使用這個network的Container,
都會被加到同一個network裡面,而預設該Container的domain就是它的–name屬性值:
docker run --net demo --name mydb -p 5432:5432 -e POSTGRES_PASSWORD=letmein -d postgres
docker run --net demo --name webapp -p 8080:8080 -d dreamfulfil/tomcat-demo
如上圖,可以正確的讀到所有的accounts。
特別留意程式的部份jdbc的url可以寫成如下:
final String url = "jdbc:postgresql://mydb:5432/postgres";
mydb的部份就是啟動postgresql Container時所給定的–name值。
微服務是近幾年非常夯的話題,Docker也只是其中一個小環節而已,
希望本篇的介紹能稍微給各位帶來一些基本的概念。
Docker
CentOS
Docker簡介
一、前言
在看書之前,我完全不知道Docker是什麼,
只知道它的Logo是一隻可愛的鯨魚,但Docker究竟是什麼呢?
Docker其實是一個利用Container執行Application的工具,
Container裡包含了所有相依的套件、類別庫…等等,
在傳統上要分開分別以繁瑣的流程打包或建置環境的一切,
都由Docker的Container全部包起來跑在Docker Engine上,
這樣比較不會有可是在我的電腦上跑得起來啊的問題。
簡單說,Docker Container有幾個特性: