Table of contents
1 Docker 簡介
Docker 係一種利用 Linux 內核既功能黎達到將程式同系統資源分隔既虛擬化技術。
1.1 同其他技術既分別
喺 Docker 出現之前,大家一般都係用具備完整 Windows 或者 Linux OS 既 virtual machines,但咁做係有好多壞處,例如慢、需要大量儲存空間、難自動化、建設成本高、維護成本高。
呢啲 virtual machines 如果要做到程式與程式之間完全隔離,就要每一個程式都用一個完整既 OS 裝住佢,非常麻煩。相反,Docker 就非常方便,需要時又可以做到有限度咁共用資源。
產品 | 描述 | 虛擬化步驟、使用情況 |
---|
VMware Workstation、VirtualBox、Parallels Desktop | 因為需要透過一個完整既主 OS 黎儲取硬件,所以屬於 Type 2 hypervisor。 | 先喺硬件上面安裝完整既主 OS,再喺上面安裝帶虛擬化功能既軟件,再由呢個軟件虛擬化一個 OS。喺隔離狀況下執行 Windows GUI 程式。 |
VMware ESXi、Hyper-V | 因為可以唔需要透過主 OS 而係直接存取硬件,所以屬於 Type 1 hypervisor。 | 先喺硬件上面安裝帶虛擬化功能既系統,再安裝虛擬化既 OS,再喺裡面執行 Windows GUI 程式,虛擬化 OS 之間可以係完全隔離,但因為虛擬化 OS 係一個完整既 OS 環境,所以裡面既程式係共用資源既。 |
Sandboxie Plus | OS 層級既輕量級虛擬化軟件。 | 喺 Windows 環境裡面安裝帶沙盒功能既程式,再喺隔離狀態下執行 Windows GUI 程式。 |
Windows Sandbox | Windows 10 既 1903 版開始內置既基於 Hyper-V 技術既輕量級虛擬化軟件。 | 喺 Windows 環境裡面啟動,再喺隔離狀態下執行 Windows GUI 程式。 |
Docker | 利用 Linux 內核既功能達到 OS 層級既虛擬化。 | 喺隔離狀態下執行非 GUI 既多層儲存結構既 Docker images。 |
喺 Docker 裡面,執行緊既 runtime 叫 Docker containers。因為用左 OverlayFS(overlay2)儲存技術做到多層儲存結構,所以每個 Docker container 其實都只係文件系統上既一層 layer,只有 container(隔離區)裡面新增既檔案先會佔用儲存空間。
1.2 使用場景
對於開發者,使用 Docker 用好處係:
- 只需要安裝一個 Docker 程式,就可以用到唔同既 infrastructure(例如 JDK、Maven、MySQL、Redis、RabbitMQ),唔需要自己下載個別既程式再安裝。
- 因為資源隔離既關係,要重新設定呢啲 infrastructure 非常簡單,只需要將個 container throw away 再重新
run
一個就得。
- 想保留資料既話,又可以用 volume。
- 用同一個方式就可以改到任何程式執行既 port,唔需要爬文睇下個別既程式要點樣改配置。
對於企業,Docker 就用喺 K8s(Kubernetes)上,用黎做雲端部署。
2 Image
喺 Docker,如果我地想 reference 一個 Docker image,我地可以用:
方式 | 解釋 | 例子 |
---|
Image ID | 一組 alphanumeric 既 ID(注意:同 digest 係兩樣野黎)。同 Git commit ID 一樣,只要提供既 image ID 既一部分能夠唯一咁辨別到個 image 就已經足夠(冇兩個或以上既 image IDs 係以相同 value 開頭)。 | 3e 、3edbb69f9a49 |
Image repo | 一個 fully qualified 既 image name,如果係喺 Docker Hub registry 既官方 images,就可以省卻 host name 同埋 username;如果係 Docker Hub registry 既非官方 images,就會有 username;但如果係其他 registry(例如機構自己既 artifactory)就會有 host name。 | openjdk 、localhost:5000/mick/my-image |
Image repo + image tag | Image tag 一般用黎表示版本號碼。如果唔提供 tag,就係指緊 latest tag。 | redis:7 、rabbitmq:management |
如果係官方 images 既話,以下都係指緊同一個 image:
redis
redis:latest
library/redis
library/redis:latest
registry.hub.docker.com/library/redis
registry.hub.docker.com/library/redis:latest
2.1 查詢 image 列表
查詢所有 images(包括我地自己 build image 果陣產生既冇 repo、冇 tag 既 intermediate images/layers):
docker image ls -a
2.2 下載 image
下載 latest
tag:
docker image pull "<repo>"
下載特定 tag:
docker image pull "<repo>:<tag>"
例子:
docker image pull redis:latest
docker image pull rabbitmq:management
2.3 Tag image
Tag image 既作用就好似我地畀一個別名佢咁。
根據 image ID 去新增 tag:
docker image tag "<image ID>" "<same repo>:<new tag>"
docker image tag "<image ID>" "<new repo>:<any tag>"
根據 repo + tag 黎為指定 image 新增 tag:
docker image tag "<repo>:<tag>" "<same repo>:<new tag>"
docker image tag "<repo>:<tag>" "<new repo>:<any tag>"
Tag 一個 latest
tag 既 repo:
docker image tag "<repo>" "<any repo>:<any tag>"
2.4 上載 image
上載 image 到 registry:
docker image push "<repo>"
docker image push "<repo>:<tag>"
2.5 儲存 image 檔案
我地可以儲存一個 image 做一個 .tar
檔。
docker image save -o "<file name.tar>" "<image ID>"
docker image save -o "<file name.tar>" "<repo>"
docker image save -o "<file name.tar>" "<repo>:<tag>"
例子:
docker image save -o "C:/Users/Michael/Desktop/busybox.tar" busybox:latest
之後會開啟一個 interactive shell 畀我地執行 Linux commands。
2.6 移除 tag/刪除 image
喺 Docker 裡面,無論係移除 tag 或者刪除 image,我地都係用同一個 command。
個邏輯係:
- 如果根據 image ID 黎刪除,咁我地可能係想喺本地刪除呢個 image,但 Docker 唔知你係咪真係想咁做,所以如果呢個 image 畀多過 1 個 repo + tag reference 緊,咁我地就需要用
-f
黎強制刪除。
- 當我地移除一個 image 既最後一個 repo + tag 既時候,Docker 就會喺本地刪除呢個 image。
- 如果執行操作之後呢個 image 仲有至少 1 個 repo + tag,咁 Docker 就唔會喺本地刪除呢個 image,只會移除 tag。
移除 latest
tag:
docker image rm "<repo>"
移除特定 repo + tag:
docker image rm "<repo>:<tag>"
用 image ID 黎直接刪除 image(如果一個 image ID 有好幾個 repo + tag,就需要用 -f
黎強制刪除):
docker image rm "<image ID>"
docker image rm -f "<image ID>"
例子:
docker image rm redis
docker image rm rabbitmq:management
docker image rm 3e
docker image rm -f 3e
3 Container
3.1 查詢 container 列表
查詢所有 containers(包括 exit 左既):
docker container ls -as
查詢特定 status 既 containers(-f
指 filter):
docker container ls -f status="<status>"
例子:
1docker container ls -f status=created
2docker container ls -f status=running
3docker container ls -f status=paused
4docker container ls -f status=restarting
5docker container ls -f status=removing
6docker container ls -f status=exited
7docker container ls -f status=dead
3.2 執行 image/建立 container
一般我地都會用個 image 既 Dockerfile
裡面預先定義左既 CMD
或者 ENTRYPOINT
黎啟動程式,但如果呢個 image 有好幾個程式既 binaries 喺裡面,或者呢啲程式可以加 options 黎 customize 佢既 behaviors 而我地又有需要既話,我地可以 override 佢既 command。
docker container run "<image ID>" "[override command]"
docker container run "<repo>:<tag>" "[override command]"
執行 latest
tag 既 image:
docker container run "<repo>" "[override command]"
Option | 解釋 |
---|
--name | Container name,一個固定既 container 名。 |
-d | Detached,個 container process 唔會 attach 落現時既 console window,只會返回 container ID,如果唔介意要 keep 住呢個 console window,或者想即刻睇住啲 log,咁可以唔用呢個 option。 |
--rm | Remove,即係當個 container exit 左,Docker 會將佢自動 remove。 |
-p <host port>:<container port> | Publish/bind ports。如果有兩個程式指定左同一個 port,咁我地可以將佢地 map 落主 OS 既兩個唔同 ports。 |
-e <key>:<value> | Container 裡面既 environment variable。 |
-v <host path>:<container path> | Volume,可以令 container 入面既某個 path map 到主 OS 既某個 path,當有檔案寫落去,就會去左主 OS 度,做到保存資料既效果,重開 container 都可以沿用返呢啲資料。 |
註:
- 如果本地冇呢個 image,Docker 會自動幫我地下載。
- 如果個 Docker Desktop 唔係用 WSL,咁就可能需要先去 Docker Desktop 既設定度將
C:/
加落 Resources ➜ File Sharing。
3.3 執行 image/建立 container 既熱門例子
3.3.1 Busybox
busybox
係測試用既 image,裡面包含一啲 Linux 既工具,可以畀我地學習或者測試 Linux 既 commands。
docker container run -it --rm busybox:latest
3.3.2 MySQL
Server:
docker container run -d --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_USER=mysql -e MYSQL_PASSWORD=mysql -e MYSQL_DATABASE=dev -v "C:/docker-data/mysql:/var/lib/mysql" --name mysql mysql:latest
CLI:
docker container exec -it mysql mysql -u"root" -p"root" dev
docker container exec -it mysql mysql -u"mysql" -p"mysql" dev
- JDBC connection string:
jdbc:mysql://localhost:3306/dev
- 用戶:
mysql
- 密碼:
mysql
3.3.3 MariaDB
Server:
docker container run -d --rm -p 3306:3306 -e MARIADB_ROOT_PASSWORD=root -e MARIADB_USER=mariadb -e MARIADB_PASSWORD=mariadb -e MARIADB_DATABASE=dev -v "C:/docker-data/mariadb:/var/lib/mysql" --name mariadb mariadb:latest
CLI:
docker container exec -it mariadb mariadb -u"root" -p"root" dev
docker container exec -it mariadb mariadb -u"mariadb" -p"mariadb" dev
- JDBC connection string:
jdbc:mariadb://localhost:3306/dev
- 用戶:
mariadb
- 密碼:
mariadb
3.3.4 PostgreSQL
Server:
docker container run -d --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres -v "C:/docker-data/postgres:/var/lib/postgresql/data" --name postgres postgres:latest
- JDBC connection string:
jdbc:postgresql://localhost:5432/postgres
- 用戶:
postgres
- 密碼:
postgres
3.3.5 Oracle Express Edition
Server:
docker container run -d --rm -p 1521:1521 -e ORACLE_PWD=oracle -v "C:/docker-data/oracle:/opt/oracle/oradata" --name oracle container-registry.oracle.com/database/express:21.3.0-xe
注意:Oracle Express Edition 21 會使用大約 12 GB 儲存空間。
CLI:
docker container exec -it oracle sqlplus system/oracle
- JDBC connection string:
jdbc:oracle:thin:@localhost:1521:xe
- 用戶:
system
- 密碼:
oracle
- SID:
xe
3.3.6 Microsoft SQL Server
Server:
docker container run -d --rm -p 1433:1433 -e "ACCEPT_EULA=Y" -e MSSQL_SA_PASSWORD=StrongPassword123 -v "C:/docker-data/sqlserver:/var/opt/mssql/data" --name sqlserver mcr.microsoft.com/mssql/server:2022-latest
CLI:
docker container exec -it sqlserver /opt/mssql-tools18/bin/sqlcmd -No -S localhost -U sa -P StrongPassword123
然後:
1CREATE DATABASE dev;
2GO
3ALTER DATABASE dev SET READ_COMMITTED_SNAPSHOT ON;
4GO
5SELECT name, is_read_committed_snapshot_on FROM sys.databases WHERE name = 'dev';
6GO
- JDBC connection string:
jdbc:sqlserver://localhost:1433;databaseName=dev;encrypt=false
- 用戶:
sa
- 密碼:
StrongPassword123
3.3.7 MongoDB
Server:
docker container run -d --rm -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=mongodb -e MONGO_INITDB_ROOT_PASSWORD=mongodb -v "C:/docker-data/mongodb:/data/db" --name mongodb mongo:latest
CLI:
docker container exec -it mongodb mongosh -u mongodb -p mongodb
然後:
use mydb
db.createCollection("mycollection")
- MongoDB connection string:
mongodb://mongodb:mongodb@localhost:27017
- 用戶:
mongodb
- 密碼:
mongodb
3.3.8 RabbitMQ
Server(連同 management website):
docker container run -d --rm -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=rmq -e RABBITMQ_DEFAULT_PASS=rmq -e RABBITMQ_NODENAME=rabbit@docker-rabbitmq -v "C:/docker-data/rabbitmq:/var/lib/rabbitmq/mnesia" -h "docker-rabbitmq" --name rmq rabbitmq:management
Management website:
對某個 virtual host 開啟 Firehose Tracer 功能:
docker container exec rmq rabbitmqctl trace_on -p <vhost>
3.3.9 Kafka
Server:
docker container run -d --rm -p 9092:9092 -v "C:/docker-data/kafka:/tmp/kraft-combined-logs" --name kafka apache/kafka:latest
然後創建 topic:
docker container exec kafka /opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic demo-topic --partitions 2
列出所有 topics:
docker container exec kafka /opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe
然後發送訊息:
docker container exec -it kafka /opt/kafka/bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic demo-topic --property parse.key=true --property key.separator="|"
之後輸入訊息內容,例如 1|foo
、2|bar
,撳 Enter
掣可以立即發送訊息。想退出既話可以撳 Ctrl+C
。
3.3.10 Redis
Server:
docker container run -d --rm -p 6379:6379 -v "C:/docker-data/redis:/data" --name redis redis:latest redis-server --save 10 1 --requirepass redispw
CLI:
docker container exec -it redis redis-cli -a redispw
3.3.11 OpenJDK(Azul Zulu、Microsoft)
查詢 JDK 版本:
docker container run --rm azul/zulu-openjdk:17 java -version
docker container run --rm mcr.microsoft.com/openjdk/jdk:17-ubuntu java -version
列出 JDK 檔案:
docker container run --rm azul/zulu-openjdk:17 ls -l /usr/lib/jvm/zulu17-ca-amd64
docker container run --rm mcr.microsoft.com/openjdk/jdk:17-ubuntu ls -l /usr/lib/jvm/msopenjdk-17-amd64
喺一個 project folder 度執行 java
command:
:: Windows Command Prompt
CD C:\docker-data\spring-boot-demo
docker container run --rm -p 8080:8080 -v "%CD%/target:/usr/project" --name=spring-boot-demo azul/zulu-openjdk:17 java -jar "/usr/project/spring-boot-demo-1.0.0.jar"
3.3.12 Maven
查詢 Maven 以及 JDK 版本:
docker container run --rm maven:3-openjdk-17 mvn -v
喺一個 project folder 度執行一個 Maven plugin goal(用返 user 既 .m2
folder):
:: Windows Command Prompt
CD C:\docker-data\spring-boot-demo
docker container run --rm -p 8080:8080 -v "%CD%:/usr/project" -v "%USERPROFILE%/.m2:/root/.m2" --name=spring-boot-demo maven:3-openjdk-17 mvn spring-boot:run -f /usr/project
3.3.13 Snyk
要 scan 一個 Java Maven project,喺 project root directory(有 pom.xml
既 folder)度執行:
set SNYK_TOKEN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
docker container run --rm --env SNYK_TOKEN -v "%CD%:/app" snyk/snyk:maven-3-jdk-17 snyk test
docker container run --rm --env SNYK_TOKEN -v "%CD%:/app" snyk/snyk:maven-3-jdk-17 snyk monitor
檢視 CVE 測試結果:
3.3.14 Selenium Grid
Selenium Grid server(standalone Chrome):
docker container run -d --rm -p 4444:4444 -p 7900:7900 -e SE_NODE_MAX_SESSIONS=3 -e SE_NODE_OVERRIDE_MAX_SESSIONS=true --shm-size 2g --name selenium-chrome selenium/standalone-chrome:latest
介面:
3.3.15 Sonatype Nexus
Sonatype Nexus repository server(可能要幾分鐘啟動):
docker container run -d --rm -p 8081:8081 -v "C:/docker-data/nexus:/nexus-data" --name nexus sonatype/nexus3:latest
- 用戶:
admin
- 密碼:(執行以下 command 獲取初始密碼,然後完成 setup。)
3.4 對 container 執行 commands
方法一,直接對 container 執行一個 command:
docker container exec "<container ID or name>" "<command>"
例子:
docker container exec redis redis-cli -a redispw keys *
方法二,喺 container 打開一個 shell,然後喺 shell 度打 commands:
docker container exec -it "<container ID or name>" "<command>"
例子:
docker container exec -it redis redis-cli
docker container exec -it spring-boot-demo /bin/sh
docker container exec -it spring-boot-demo /bin/bash
3.5 重新啟動 container
docker container restart "<container ID or name>"
3.6 暫停/恢復運行 container
暫停 run 緊既 container:
docker container pause "<container ID or name>"
恢復運行 pause 左既 container:
docker container unpause "<container ID or name>"
3.7 停止/立即停止 container
停止(SIGTERM
,等 10
秒後再 SIGKILL
):
docker container stop "<container ID or name>"
立即停止(直接 SIGKILL
):
docker container kill "<container ID or name>"
3.8 刪除 container
docker container rm "<container ID or name>"
一次過刪除曬特定 status 既 containers:
:: Windows Command Prompt
FOR /F "tokens=*" %i IN ('docker container ls --filter "status=<status>" -q') DO docker container rm %i
刪除所有 exit 左既 containers 既例子:
:: Windows Command Prompt
FOR /F "tokens=*" %i IN ('docker container ls --filter "status=exited" -q') DO docker container rm %i
4 參考資料