docker
- 鏡像操作
- 搜索鏡像
- 拉取鏡像
- 查看鏡像
- 刪除鏡像
- 容器操作!
- 查看容器
- 運行容器
- run命令詳細介紹
- 啟動容器
- 停止容器
- 重啟容器
- 查看容器狀態
- 查看容器日志
- 刪除容器
- 進入容器
- 保存鏡像
- 提交
- 保存
- 加載
- 分享社區
- 登錄
- 命名
- 推送
- docker存儲
- 目錄掛載
- 卷映射
- 查看所有容器卷
- 創建容器卷
- 查看容器卷詳情
- docker網絡
- 容器間通信-通過主機訪問
- 容器間通信-通過ip訪問
- 容器間通信-通過域名訪問
- 創建自定義網絡
- 查看網絡列表
- 示例
- docker實現Redis主從復制集群
- docker compose
- 示例
- docker compose 增量更新
- docker compos 下線
- docker file
- docker file 常見指令
- 示例
鏡像操作
本次操作以nginx為例,需要注意nginx容器默認配置文件目錄所在位置/etc/nginx 下 歡迎頁所在位置/usr/share/nginx/html 下 可以在docker hub官網中查詢
搜索鏡像
docker search
示例docker search nginx
當然這里搜索鏡像大概率是無法搜索到的,因為docker search命令訪問的是docker hub 而docker hub在國內是無法訪問的。在這里我們需要區別一個概念,就是我們之前安裝docker的時候配置的鏡像倉庫,我們已經在/etc/docker/daemon.json中配置了如下,為什么這里的docker search任然無法使用?
"registry-mirrors": ["https://mirror.ccs.tencentyun.com","https://docker.m.daocloud.io"]
參考deepseek給出的答案
綜上:除了使用梯子否則我們是無法訪問到docker hub的。
拉取鏡像
docker pull
示例:docker pull nginx:1.26.0
或docker pull nginx
拉取鏡像實際是由鏡像名稱+版本號組成,如果沒有版本號,則默認拉取最新的。當我們不知道要拉取哪個版本的時候,需要去[docker hub](https://registry.hub.docker.com/上去查詢,或者使用上面的docker search 命令來查詢,可惜的是docker hub在國內無法訪問,需要梯子。
查看鏡像
docker images
# 或者
docker images ls
刪除鏡像
docker rmi
例如下圖中兩個mysql,如果我們要刪除則分別使用命令docker rmi mysql:latest
docker rmi mysql:5.7
或者使用每個鏡像的唯一ID也就是下圖中的IMAGE ID來刪除 docker rmi 5568fddd4f66
docker rmi aa803eda0f25
當然這里刪除鏡像的前提是沒有使用該鏡像生成任何容器,如果一旦生成了容器,則需要先把容器刪除才能刪除鏡像。
如果容器沒有刪除就刪除鏡像,則會報如下錯誤
容器操作!
查看容器
docker ps #查看運行中的容器
docker ps -a #查看所有容器,包括未運行的
運行容器
docker run
根據鏡像生成容器并運行,我們可以使用help查看該命令的使用方法,如下圖,分為 docker run [options] IMAGE [command] [arg…]
- options 參數項,啟動一個容器需要用到的參數項都在下圖中Options中所示
- IMAGE 鏡像
- command 命令
- arg 參數
一般來說鏡像后面的啟動命令和參數我們不用寫,因為每個鏡像都有自己的啟動命令和參數,是鏡像自帶的,除非我們要改變鏡像的默認行為。最簡單的啟動方式就是 docker run 鏡像名
例如 docker run ngixn
這里的docker run nginx 默認啟動的是最新版nginx鏡像,如果我們沒有下載最新版,這個命令會自動下載最新版nginx然后運行。由于我們之前已經下載過1.26.0版本的nginx,因此我們需要使用 docker run nginx:1.26.0
或者 docker run 鏡像ID
運行效果如下,可以看到此時控制臺處于運行狀態,我們一旦結束這個命令窗口則運行的容器就會停止,因此一般情況我們會讓他后臺運行
當我們把這個運行的容器退出后,使用命令docker ps -a
查看所有已經運行的容器命令,如下圖:
我們來解釋一下這個打印的內容
- container id 容器id
- image 鏡像
- command 鏡像啟動的默認命令
- created 創建時間
- status 狀態 目前是exited 已退出
- ports 端口映射狀況
- names 名稱,不指定名稱的時候會自動生成一個隨機的名字
run命令詳細介紹
上面的run命令我們只是簡單的使用,從鏡像啟動了一個容器,那么同時也存在很多問題,例如控制臺關閉就容器停止,例如無法訪問等。現在我們想讓容器后臺啟動并且可以訪問,可以使用如下命令dcoker run -d --name nginx -p 80:80 nginx:1.26.0
啟動之后如下圖,我們分別看一下這些內容,其他的在上面都已經解釋了,主要看一下這個port,這個port的就是說主機的80端口映射到容器的80端口,表示任何IP訪問80端口就映射到容器的80端口。
那么這時候就存在一個問題,假如我們又啟動了一個nginx的容器,那么請問還能用同樣的命令來啟動嗎?啟動之后80端口是映射到哪里了呢?如下圖:
從這里我們可以看出來,每個容器都有自己獨立的運行環境,因此每個容器都有自己獨立的80端口,然而主機自己只有一個80端口,已經映射給了容器1的80端口,因此無法再映射到容器2的80端口,因此只能讓主機的其他端口去映射到容器2的80端口。
啟動容器
docker start 容器名稱或容器ID
示例:使用容器名稱的方式啟動docker start suspicious_black
或者使用容器id的方式啟動 docker start b71b196ccef0
或者只寫容器ID的前三位,能與其他容器ID區分開即可
停止容器
docker stop 容器名稱或容器ID
示例:使用容器名稱的方式啟動docker stop suspicious_black
或者使用容器id的方式啟動 docker stop b71b196ccef0
或者只寫容器ID的前三位,能與其他容器ID區分開即可
重啟容器
docker restart 容器名稱或容器ID
示例:使用容器名稱的方式啟動docker restart suspicious_black
或者使用容器id的方式啟動 docker restart b71b196ccef0
或者只寫容器ID的前三位,能與其他容器ID區分開即可
查看容器狀態
docker stats 容器名稱或容器ID
示例:使用docker stats b71
(b71是上面我們nginx的容器id的前三位)命令查看后如下圖,這里顯示了該容器的cpu 內存 io 等占用情況,并且是實時的
查看容器日志
docker logs 容器名稱或容器ID
示例:使用docker logs b71
b71是上面我們nginx的容器id的前三位)命令查看后如下圖,這里顯示該容器從啟動至今的所有日志信息。
刪除容器
docker rm 容器名稱或容器ID
示例:使用docker rm b71
(只能刪除未在運行中的容器) 強制刪除使用 docker rm -f b71
-f 的意思就是 force 武力有力的意思
當我們想要批量刪除的時候可以使用 docker rm $(docker ps -aq)
docker ps -aq
的意思就是返回當前所有容器的ID,$(docker ps -aq)
就是將該命令的結果作為參數給docker rm
進入容器
docker exec
保存鏡像
提交
docker commit
示例:docker commit -a "作者" -m "修改歡迎頁" nginx newnginx:v1.0.1
這個命令參考下圖意思就是將nginx容器(這里的nginx是我們之前啟動容器時給起的名字,換成容器ID也可以)提交為一個新的鏡像叫newnginx 版本號是v1.0.1 同時指定了作者和提交信息
我們使用-- help命令查看一下這個命令是如何使用的,我們可以看到這個命令的描述信息: 創建一個新鏡像從一個改變的容器。命令參數有-a
作者信息 -c
容器改變的信息列表 -m
提交信息 -p
暫停容器的運行。
下面我們把我們剛才安裝的nginx容器的index頁面修改一下,然后將該容器重新保存為一個新鏡像并發布:
-
進入容器
docker exec -it nginx /bin/bash
-
進入容器的nginx的index頁面
cd /usr/share/nginx/html/
(我們怎么知道nginx容器的歡迎頁面在哪呢?還是要去docker hub的官網上找) 這是默認的nginx歡迎頁面
-
修改index頁面
echo "<h1>hello docker I'm Iron man</h1>" > index.html
(由于新容器中沒有vi命令,因此我們直接修改這個文件內容) 給歡迎頁面打印一句話 你好docker 我是鋼鐵俠
-
使用
docker commit
來提交這個容器為一個新的鏡像docker commit -a "iron man" -m "202503提交" nginx newnginx:v1
-
使用
docker images
查看新生成的鏡像,從下圖我們可以看到已經有了一個新的鏡像叫newnginx 版本tag是v1
保存
docker save
在上面的步驟中我們已經將這個nginx容器提交成了一個新的鏡像,現在我們需要將這個鏡像打包并分享給其他人來用,那么就首先把這個鏡像打包,使用docker save --help
查看命令用法,只有一個-o
的參數,這個參數是指定輸出為一個tar文件或一個路徑下的tar文件
我們使用docker save -o newnginx.tar newnginx:v1
命令來將這個鏡像打成一個tar包,執行完這個命令會在當前目錄下生成一個tar包,我們就可以將這個tar包傳給其他人來使用,也就是下面的加載
加載
docker load
在上面的示例中我們已經將容器提交為一個新的鏡像,并且將該鏡像保存為一個tar包,現在我們把tar包發送給一個新的docker環境中,在新的docker環境中去加載這個tar包并生成鏡像,然后運行該鏡像生成容器并訪問,如果顯示的是我們修改過的歡迎頁,則說明成功。
我們在新的機器或docker環境中,或者將現有機器上的鏡像和容器都刪掉,使用docker load --help
查看該命令的用法
有兩個參數 -i
指定從哪個路徑加載哪個tar文件,我們使用docker load -i newnginx.tar
來加載剛才的tar包,然后使用docker images
查看從剛才的tar包里加載到的鏡像,然后用docker run -d --name hellonginx -p 80:80 newnginx:v1
來指定后臺運行端口映射命名為hellonginx運行鏡像,用docker ps
來查看容器
分享社區
在上面的例子中我們通過分享文件的方式來將鏡像分享給其他人使用,那么我們如果想將鏡像推送至社區讓大家都可以docker pull
的方式來下載使用該如何做呢?
登錄
- 這是docker hub 的官網,我們可以在這里登錄和注冊,
- 然后在命令行窗口中登錄,使用
docker login
命令登錄,然后輸入用戶名和密碼,顯示login succeeded 表示登錄成功,國內登錄應該是鏈接超時。
命名
docker tag 原鏡像 目標鏡像
使用docker tag newnginx:v1 用戶名/新名字:版本號
再使用docker images
這時就有兩個鏡像,但是它們的鏡像ID是一樣的,接下來就可以用docker push
將這個新鏡像推送到docker hub的個人倉庫中
推送
docker push
docker存儲
目錄掛載,數據卷等
目錄掛載
docker run -v /usr/local/nginx/conf:/etc/nginx/conf nginx:1.26.0
示例:
- 首先刪除所有容器
docker rm $(docker ps -aq)
- 然后刪除nginx鏡像
docker rmi 鏡像ID
- 然后重新從pull一個新的nginx鏡像
docker pull nginx:1.26.0
- 然后使用命令運行鏡像生成容器
docker run -v /usr/local/nginx/conf:/etc/nginx/conf -p 80:80 -d --name nginx:1.26.0
重點解釋這里的-v參數 -v /usr/local/nginx/conf:/etc/nginx/conf 的意思就是將本機的/usr/local/nginx/conf目錄映射為容器的/etc/nginx/conf目錄由于nginx容器啟動時我們將nginx的配置文件目錄映射為本機的目錄,而此時我們并沒有這個目錄,因此run命令運行時會自動創建該目錄,而新建出來的目錄是空的,因此這個容器的啟動必然是失敗的,我們可以用docker ps -a
和docker logs 容器ID
來查看 - 啟動第一個80端口映射
- 啟動第二個81端口映射
- 啟動第三個掛載目錄的82端口映射
docker ps -a
查看三個容器狀況
那么如何讓這個82端口映射的nginx正常啟動呢?第一種方法是可以將nginx的配置文件上傳到本機的/usr/local/nginx/conf ,同時需要注意上傳的nginx配置文件內容是否正確,否則會出現404 index頁面,因為容器的默認index頁面目錄在/usr/share/nginx/html下,而非容器的nginx配置的歡迎頁在conf文件夾同級的html文件夾內。第二種方法就是下一節的卷映射,何為卷映射?就是將容器內原本存在的文件映射到主機目錄中- 多個目錄映射
docker run -d -p 82:80 \-v /usr/local/nginx/conf:/etc/nginx \-v /your/local/html:/usr/share/nginx/html \ # 新增掛載--name nginx82 nginx:1.26.0
卷映射
- 卷映射與目錄掛載的區別是
- 卷映射命令
docker run -v 文件夾:/etc/nginx ....
以nginx為例完整的卷配置文件映射命令為docker run -v dc_ng_conf:/etc/nginx -d -p 83:80 --name nginx83 nginx:1.26.0
- 目錄掛載命令
docker run -v /usr/local/nginx/conf:/etc/nginx ...
- 區別在于卷映射本機目錄僅寫一個目錄名稱(文件夾名稱)即可,而目錄掛載是需要一個完整的目錄,也就是說核心就是目錄掛載比卷掛載多了一個
/
。
- 卷映射命令
- 如何找到卷映射目錄?
- 當我們使用卷映射時只寫了一個文件夾的名字,該文件夾會自動幫我們創建,那么這個文件夾在哪呢?這個位置docker統一放在了
/var/lib/docker/volumes/<volume-name>
下
- 當我們使用卷映射時只寫了一個文件夾的名字,該文件夾會自動幫我們創建,那么這個文件夾在哪呢?這個位置docker統一放在了
下面我們使用掛載目錄和掛載容器卷的兩種方式來啟動一個映射83端口的nginxdocker run -v /usr/local/nginx/html:/usr/share/nginx/html -v dc_ng_conf:/etc/nginx -d -p 83:80 --name nginx83 nginx:1.26.0
我們將nginx的歡迎頁用目錄掛載的方式映射到本機的/usr/local/nginx/html文件夾下,由于該文件夾是空的,因此我們使用touch index.html
命令來創建一個歡迎頁,用echo "你好歡迎來到83號nginx" >> index.html
命令來給這個文件寫一句話,然后訪問IP:83
來訪問這個nginx,歡迎頁顯示如下:
然后我們去找nginx的配置文件,使用命令cd /var/lib/docker/volumes
看到這個文件夾內有個文件夾叫dc_ng_conf 然后pwd
命令查看當前目錄及目錄內的文件,如下圖我們可以看到容易將配置文件nginx.conf映射到該文件夾內
目錄掛載初始的時候是空的,容器卷掛載初始的時候是以容器內的文件為準,后面不論是在容器內修改還是容器外修改都是一樣的
查看所有容器卷
docker volume ls
創建容器卷
docker volume create myvs # 自定義容器卷名稱
查看容器卷詳情
docker volume inspect myvs
下圖是容器卷操作的相關命令,與network基本一致
此時無論我們是否刪除這個容器,它的數據依然存在,下次我們還可以用同樣的目錄啟動容器,那么就相當于是恢復了之前的容器。
docker網絡
容器間通信-通過主機訪問
- 我們現在清除所有容器,然后重新使用命令新建兩個容器
docker run -d -p 80:80 --name nginx80 nginx1.26.0
運行nginx80容器docker run -d -p 81:80 --name nginx81 nginx1.26.0
運行nginx81容器
- 然后進入80容器訪問81容器
docker exec -it nginx80 bash
進入80容器curl 主機ip:81
訪問81容器,打印歡迎頁面
此時的網絡請求是80容器訪問了主機的81端口,然后主機81端口映射到了81容器的80端口,然后81容器響應了歡迎頁面,這么一來就相當于繞了一圈比較麻煩,那么怎么能直接讓容器之間互相通信?
容器間通信-通過ip訪問
- 每一個docker應用在啟動的時候都會加入一個docker的默認網絡,我們使用
ip a
命令查看一下
- 我們可以看到,這里有一個docker0的網卡,這個網絡是在安裝docker的時候就有了,并且它的ip是172.17.0.1,這個ip并不固定,docker每啟動的任何一個應用都會加入到這個網絡中,并且docker會為每個容器分配它自己的ip,我們可以使用命令
docker inspect nginx80
或者docker container inspect nginx80
來查看這個容器的詳細信息,如下圖中所示,172.17.0.2是該容器的ip,而172.17.0.1則是它的網關,也就是docker0網卡的ip。 - 由此我們可以推斷出,容器之間既然已經都加入了這個docker0的網絡,并且互相有了獨立的ip,那么我們應該可以通過他們各自獨立的ip能訪問對方,此時我們再次測試
docker exec -it nginx80 bash
進入80容器curl 172.17.0.2:80
訪問nginx81容器的ip
容器間通信-通過域名訪問
docker0 默認不支持主機域名
創建自定義網絡,容器名就是穩定域名
docker network --help
查看docker network用法
connect
連接一個容器到網絡中create
創建一個網絡disconnect
從網絡中斷開一個容器的鏈接inspect
顯示詳細信息在一個或多個網絡ls
網絡列表prune
刪除所有未使用的網絡rm
刪除一個或多個網絡
創建自定義網絡
docker network create 自定義名
查看網絡列表
docker network ls
示例
- 刪除所有現有容器,因為他們加入的是默認docker0的網絡,而docker0不支持主機域名
docker rm $(docker ps -aq)
- 創建自定義網絡
docker network create mynet
- 查看創建的網絡
docker network ls
如下圖,我們可以看到有多個網絡,第一個橋接網絡就是之前默認使用的,而我們自己創建的mynet也是一個橋接網絡
- 重新運行兩個nginx容器并將其加入我們新建的網絡中(通過
--network
參數)docker run -d -p 80:80 --name nginx80 --network mynet nginx:1.26.0
docker run -d -p 81:80 --name nginx81 --network mynet nginx:1.26.0
- 使用
docker inspect nginx80
查看該容器的細節,我們發現這個容器的網關和ip都變了
- 進入容器中用容器名稱作為域名來訪問
docker exec -it nginx80 bash
進入80容器curl http://nginx81
訪問81容器。為啥容器名字就可以作為域名來訪問呢?可能是因為在加入網絡的時候做了什么處理吧
docker實現Redis主從復制集群
# 主節點啟動命令
docker run -d -p 6379:6379 \
> -v /usr/local/redis/data:/bitnami/data \ # 目錄掛載
> -e REDIS_REPLICATION_MODE=master \ # 設置節點為主節點
> -e REDIS_PASSWORD=123456 \ # 設置密碼
> --network mynet --name redis01 \ # 加入網絡
> bitnami/redis # 非官方鏡像,bitnami鏡像主從復制集群不需要修改配置文件,而是用設置環境變量的方式
# 從節點啟動命令
docker run -d -p 6389:6379 \
> -v /usr/local/redis_slave/data:/bitnami/data \
> -e REDIS_REPLICATION_MODE=slave \ # 設置節點為從節點
> -e REDIS_MASTER_HOST=redis01 \ # 配置主節點域名、網絡ip、主機地址
> -e REDIS_MASTER_PORT=6379 \ # 配置主節點端口
> -e REDIS_MASTER_PASSWORD=123456 \ # 配置主節點密碼
> -e REDIS_PASSWORD=123456 \ # 設置從節點密碼
> --network mynet --name redis02 \ # 加入網絡
> bitnami/redis # 同上
解釋:
\
表示調到下一行繼續輸入命令-e
表示環境變量 environment的意思-v
目錄掛載,將鏡像的data目錄掛載到本機的/usr/local/redis/data
目錄,那么我們怎么知道這個鏡像的data目錄在哪個位置?還是要上docker hub官網找到bitnami/redis這個鏡像的官方文檔里看- 主從復制集群主要是配置
REDIS_REPLICATION_MODE
參數,然后給從機設置主機地址即可
docker compose
docker compos 的作用就是用來批量管理容器,將我們要部署的容器全部編寫到一個yaml文件中,然后使用命令批量執行
示例
下面我們用docker啟動一個博客系統,WordPress,這是一個開源的博客系統,會將博客內容存儲到數據庫中,因此我們要安裝兩個容器,一個是WordPress,另一個是數據庫
-
傳統方式啟動
#創建網絡 docker network create blog#啟動mysql docker run -d -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -e MYSQL_DATABASE=wordpress \ -v mysql-data:/var/lib/mysql \ -v /app/myconf:/etc/mysql/conf.d \ --restart always --name mysql \ --network blog \ mysql:8.0#啟動wordpress docker run -d -p 8080:80 \ -e WORDPRESS_DB_HOST=mysql \ -e WORDPRESS_DB_USER=root \ -e WORDPRESS_DB_PASSWORD=123456 \ -e WORDPRESS_DB_NAME=wordpress \ -v wordpress:/var/www/html \ --restart always --name wordpress-app \ --network blog \ wordpress:latest
- 首先創建網絡blog,讓WordPress和數據庫容器加入同一個網絡,用于互相通信
- 然后啟動mysql,
-d
后臺啟動-p
端口映射3306-e MYSQL_DATABASE=wordpress
容器啟動創建默認的數據庫-e MYSQL_ROOT_PASSWORD=123456
指定root用戶的密碼-v mysql-data:/var/lib/mysql
掛載mysql數據存儲容器卷v /app/myconf:/etc/mysql/conf.d
掛載mysql配置文件目錄 這里注意區分與上面的容器卷的區別--restart always
容器隨系統自動啟動--name mysql
指定容器名稱--network blog
加入指定網絡,可以讓其他容器用容器名稱訪問
- 然后啟動WordPress,
-d
后臺啟動-p
端口映射到8080-e WORDPRESS_DB_HOST=mysql
指定環境變量數據庫為mysql-e WORDPRESS_DB_USER=root
數據庫用戶root-e WORDPRESS_DB_PASSWORD
數據庫密碼123456-v wordpress:/var/www/html
容器卷掛載,- 然后依次是隨機自啟,指定容器名稱,加入blog網絡,使用的鏡像
-
使用docker compose方式啟動
-
編寫compose.yml文件
name
compose文件的編寫根據docker官方文檔描述共有6個頂級元素分別是name services networks volumes configs secrets
對應到下面我們編寫的compose.yml文件來說就是首先要用name來指定項目名,其次使用services來指定要啟動的容器services
使用services指定啟動的容器,實際上與上面命令啟動類似,只不過是將命令中的各個部分持久化到這個文件中用來重復使用。參考 官方文檔- 在services下面的mysql和wordpress可以任意命名,能區分出來即可,在mysql下面有個
container_name
指定容器的名稱,我們這個任意命名的也可以當做容器名稱 image
指定鏡像prots
指定端口映射environment
指定環境變量volumes
指定容器卷或目錄映射,如果是容器卷映射,則需要在與services同級的volumes頂級元素下聲明容器卷networks
指定加入的網絡,如果指定加入的網絡,則需要在與services同級的networks的頂級元素下聲明網絡restart
指定隨機器重啟
- 在services下面的mysql和wordpress可以任意命名,能區分出來即可,在mysql下面有個
name: myblog services:mysql:container_name: mysqlimage: mysql:8.0ports:- "3306:3306"environment:- MYSQL_ROOT_PASSWORD=123456- MYSQL_DATABASE=wordpressvolumes:- mysql-data:/var/lib/mysql- /app/myconf:/etc/mysql/conf.drestart: alwaysnetworks:- blogwordpress:image: wordpressports:- "8080:80"environment:WORDPRESS_DB_HOST: mysqlWORDPRESS_DB_USER: rootWORDPRESS_DB_PASSWORD: 123456WORDPRESS_DB_NAME: wordpressvolumes:- wordpress:/var/www/htmlrestart: alwaysnetworks:- blogdepends_on:- mysqlvolumes:mysql-data:wordpress:networks:blog:
-
運行compose文件
docker compose -f compose.yml up -d
其中-f
表示指定compose文件位置,-d
表示后臺啟動,至此就可以根據compose來啟動容器了。
-
docker compose 增量更新
當我們將上面的compose.yml文件修改了之后,如何讓容器重新運行新修改后的內容呢?,還是同樣的啟動命令 docker compose -f compose.yml up -d
此時docker會重新加載該文件并且判斷哪些是修改的內容需要重啟的,哪些沒有修改的不動。
docker compos 下線
docker compose -f compose.yml down
該命令會移除compose.yml文件中的所有容器,但是并不一會移除容器的映射卷
docker file
當我們用java開發了一個應用并打成jar包后,需要將其發布,那么我們需要將其制作為一個鏡像,這個鏡像不僅要包含操作系統的相關指令和內容還要包括JDK 應用jar包以及啟動命令等,那么該如何制作呢?
docker file 常見指令
參考 官方文檔
示例
FROM openjdk:17LABEL author=leifengyangCOPY app.jar /app.jarEXPOSE 8080ENTRYPOINT ["java","-jar","/app.jar"]