➜ Old React website
Chung Cheuk Hang MichaelJava Web Developer
Spring Webflux(一)Java 測試(五):WireMock

Docker 基本操作

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 PlusOS 層級既輕量級虛擬化軟件。喺 Windows 環境裡面安裝帶沙盒功能既程式,再喺隔離狀態下執行 Windows GUI 程式。
Windows SandboxWindows 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 開頭)。3e3edbb69f9a49
Image repo一個 fully qualified 既 image name,如果係喺 Docker Hub registry 既官方 images,就可以省卻 host name 同埋 username;如果係 Docker Hub registry 既非官方 images,就會有 username;但如果係其他 registry(例如機構自己既 artifactory)就會有 host name。openjdklocalhost:5000/mick/my-image
Image repo + image tagImage tag 一般用黎表示版本號碼。如果唔提供 tag,就係指緊 latest tag。redis:7rabbitmq: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解釋
--nameContainer name,一個固定既 container 名。
-dDetached,個 container process 唔會 attach 落現時既 console window,只會返回 container ID,如果唔介意要 keep 住呢個 console window,或者想即刻睇住啲 log,咁可以唔用呢個 option。
--rmRemove,即係當個 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|foo2|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"
假如個 Spring Boot project 有 expose 到 Actuator Health API:http://localhost:8080/actuator/health

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
假如個 Spring Boot project 有 expose 到 Actuator Health API:http://localhost:8080/actuator/health

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。)
    • docker container exec nexus cat /nexus-data/admin.password

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 參考資料