本文講解:使用Dockerfile構建鏡像 & 使用docker-compose 一鍵部署IM項目。
im項目地址:xzll-im ,歡迎志同道合的開發者 一起 維護,學習,歡迎star 😄
1、Dockerfile編寫與鏡像構建&容器運行
Dockerfile 是構建鏡像的基礎 ,首先創建兩個空文件夾,用于存放im-connect和im-business的jar: 之后使用 maven命令打包 注意此處選擇 profile為 test 對應我的虛擬機環境
mvn celan package -P test
:
之后上傳打好的jar到虛擬機: ``` sudo scp /Users/hzz/myselfproject/im-開源04/xzll-im-parent/im-connect/im-connect-service/target/im-connect-service.jar root@172.30.128.65:/usr/local/softhzz/xzll-im/jar-file/docker-file-way/connect
sudo scp /Users/hzz/myselfproject/im-開源04/xzll-im-parent/im-business/im-business-service/target/im-business-service.jar root@172.30.128.65:/usr/local/softhzz/xzll-im/jar-file/docker-file-way/business
```
之后分別編寫Dockerfile文件,內容如下:
```bash
business 的 Dockerfile 內容
FROM openjdk:11-jre-slim # jdk鏡像 VOLUME /tmp # 掛載 COPY im-business-service.jar business.jar ENTRYPOINT ["java", "-jar", "/business.jar"] # 啟動命令 EXPOSE 8083 # 暴露端口 與服務端口保持一致
connect的 Dockerfile 內容
FROM openjdk:11-jre-slim # jdk鏡像 VOLUME /tmp # 掛載 COPY im-connect-service.jar connect.jar ENTRYPOINT ["java", "-jar", "/connect.jar"] # 啟動命令 EXPOSE 10001 # 暴露端口 與服務端口保持一致 ```
現在這倆服務的 Dockerfile就就緒了之后我們制作鏡像:
```bash . 代表當前目錄 前邊鏡像名 后邊版本號
docker build -t im-business:0.0.2 . docker build -t im-connect:0.0.2 .  使用docker images看一下 有沒有:  使用鏡像啟動容器:
bash docker run -d -p 8083:8083 --restart always --name im-business im-business:0.0.2 docker run -d -p 10001:10001 --restart always --name im-connect im-connect:0.0.2  查看啟動日志
docker logs -f --tail 50 im-business docker logs -f --tail 50 im-connect ```
發消息試試能不能走通流程: 可以看到是沒問題的。至此Dockerfile方式構建鏡像 啟動容器就說完了 但是這種方式比較繁瑣,每一個服務都得搞個Dockerfile 服務多了頭炸了。所以有了服務編排工具,常見的: - docker-compose (姑且可以將其歸納為容器編排 但是論功能上 和k8s還是差挺遠的,k8s后續會用) - k8s - OpenShift - Docker Swarm
下邊就以docker-compose開刀 方便輕松管理多服務的情況!
2、docker-compose安裝與容器編排
為什么使用docker-compose
在上邊我們介紹了使用 Dockerfile 構建 docker 鏡像 然后 在鏡像基礎上啟動應用程序,乍看起來已經能夠滿足我們的日常需求了,無論需要什么環境,在 Dockerfile 里逐步構建,然后 build、run,就 ok 了,也滿足了我們docker 隔離性、快速部署的要求,為什么還需要docker-compose呢?
首先我們要知道docker 是輕量化的應用程序,docker 官方推薦每個 docker 容器中只運行一個進程,那么就是說,我們需要分別為我們的應用以及中間件創建單獨的 docker 容器,然后分別啟動它,想象一下,構建好 docker 之后,每次啟動我們的網站,如果有n個服務 那么就得docker run n次,是不是很繁瑣?而且此時這幾個 docker 是分散獨立的,很不方便管理,既然這幾個 docker 都是為了同一個網站服務,是不是應該把它們放到一起?這就引出了 docker-compose 項目。
什么是docker-compose
docker-compose是 docker 官方的開源項目,使用 python 編寫,實現上調用了 Docker 服務的 API 進行容器管理,其官方定義為為:定義和運行多個 Docker 容器的應用(Defining and running multi-container Docker applications))
,其實就是上面所講的功能。
安裝Docker-Compose
Docker Compose是一個用來定義和運行復雜應用的Docker工具,一個使用Docker容器的應用,通常由多個容器組成。使用Docker Compose不再需要使用shell腳本來啟動容器。
Compose 通過一個配置文件來管理多個Docker容器,在配置文件中,所有的容器通過services來定義,然后使用docker-compose腳本來啟動,停止和重啟應用,和應用中的服務以及所有依賴服務的容器,非常適合組合使用多個容器進行開發的場景
注意這個docker-compose和docker是有對應關系的,一定要在github找好關系,對應錯了的話挺麻煩。 我用的docker版本是26.1.4 所以我要下載的docker-compose就是:v2.27.2
如果網絡好的話 直接 下載: bash sudo curl -L "https://github.com/docker/compose/releases/download/v2.27.2/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
但是我的虛擬機上網絡很差 所以直接在宿主機下載好 然后上傳到虛擬機。
上傳到虛擬機的/usr/local/bin 目錄: bash sudo scp /Users/hzz/Downloads/docker-compose-linux-x86_64 root@172.30.128.65:/usr/local/bin
改個名叫docker-compose bash mv docker-compose-linux-x86_64 docker-compose
之后授權: bash sudo chmod +x /usr/local/bin/docker-compose
之后驗證下: bash docker-compose -v
docker-compose
安裝成功 ,接下來開始docker-compose之旅。
docker-compose.yml編寫與批量上傳
首先: 需要在項目根目錄編寫個docker-compose.yml文件 當然你vim方式在虛擬機上創建也行,但是為了直觀我這里先在idea中創建,完事上傳到虛擬機。
```yml version: '3' services: im-gateway: build: ./im-gateway hostname: im-gateway containername: im-gateway restart: always ports: - "8081:8081" networks: - defaultnetwork volumes: - "/tmp/data/logs:/logs" im-auth: build: ./im-auth hostname: im-auth containername: im-auth restart: always ports: - "8082:8082" networks: - defaultnetwork volumes: - "/tmp/data/logs:/logs" im-business: # 可以根據Dockerfile構建鏡像(但是,Docker Compose 會在檢測到上下文變化時重新構建鏡像。也就是說如果你不修改Dockerfile docker-compose應該不是每次都構建鏡像 實測確實如此) build: ./im-business # 也可以指定鏡像名 # image: im-business:0.0.2
# 設置容器的主機名 即修改 : /etc/hosts 中的內容
hostname: im-business
# 容器名稱
container_name: im-business
# 重啟策略, always 表示無論哪種狀態退出,都會重啟容器
restart: always
ports:# 設置主機與容器的端口映射- "8083:8083"
networks:# 使用默認網絡即:docker0 橋接- default_network
volumes:# 將主機的 /tmp/data/logs 目錄掛載到容器的 /logs 目錄。這樣可以實現數據的持久化,當容器重啟時,數據不會丟失- "/tmp/data/logs:/logs"
im-connect: build: ./im-connect hostname: im-connect containername: im-connect restart: always ports: - "10001:10001" networks: - defaultnetwork volumes: - "/tmp/data/logs:/logs" im-console: build: ./im-console hostname: im-console containername: im-console restart: always ports: - "8084:8084" networks: - defaultnetwork volumes: - "/tmp/data/logs:/logs" networks: default_network: # 橋接 driver: bridge ```
之后我們在每個項目的resource下創建對應的Dockerfile以便構建對應項目的鏡像,如下是網關的:
接下來我們mvn clean package -P test
打包
現在jar包,Dockerfile docker-compose.yml都有了 剩下的工作就是將其上傳到虛擬機然后構建并啟動了。一共有5個springboot服務 5個Dockerfile 5個jar包 一個docker-compose.yml文件 一共11個文件 如果一個個執行scp上傳的話太費勁,這里寫個腳本,執行腳本就一次性上傳完成,這就舒服了,但是我在腳本中使用的是sshpass工具上傳,所以先在mac安裝下 sshpass工具:
bash brew install hudochenkov/sshpass/sshpass
bash brew link sshpass
安裝好sshpass后 就是編寫批量上傳腳本了,內容如下: ```bash
!/bin/bash
批量上傳本地文件到 虛擬機 以便鏡像構建以及 容器運行
定義本地和遠程目錄前綴
localbasedir="/Users/hzz/myselfproject/im-開源04/xzll-im-parent" remotebasedir="/usr/local/softhzz/xzll-im/jar-file/docker-compose-way"
定義要上傳的文件和目標目錄
files=( "$localbasedir/im-gateway/target/im-gateway.jar:$remotebasedir/im-gateway/" "$localbasedir/im-auth/target/im-auth.jar:$remotebasedir/im-auth/" "$localbasedir/im-business/im-business-service/target/im-business-service.jar:$remotebasedir/im-business/" "$localbasedir/im-connect/im-connect-service/target/im-connect-service.jar:$remotebasedir/im-connect/" "$localbasedir/im-console/im-console-service/target/im-console-service.jar:$remotebasedir/im-console/" "$localbasedir/im-gateway/src/main/resources/Dockerfile:$remotebasedir/im-gateway" "$localbasedir/im-auth/src/main/resources/Dockerfile:$remotebasedir/im-auth/" "$localbasedir/im-business/im-business-service/src/main/resources/Dockerfile:$remotebasedir/im-business/" "$localbasedir/im-connect/im-connect-service/src/main/resources/Dockerfile:$remotebasedir/im-connect/" "$localbasedir/im-console/im-console-service/src/main/resources/Dockerfile:$remotebasedir/im-console/" "$localbasedir/docker-compose.yml:$remotebasedir/" )
遠程服務器的用戶名和主機名
remote_user="root"
本機上的 環境變量 放到了: sudo vi ~/.zshrc 中
remotehost="$XUNIJIADDRESS" remotepassword="$REMOTEPASSWORD"
日志文件
logfile="uploadlog.txt"
遍歷數組并執行上傳
for filepair in "${files[@]}"; do file="${filepair%%:}" remote_dir="${file_pair##:}"
echo "確保遠程目錄存在: $remotedir" | tee -a "$logfile" sshpass -p "$remotepassword" ssh ${remoteuser}@${remotehost} "mkdir -p $remotedir"
echo "上傳文件 $file 到 $remotedir" | tee -a "$logfile" if sshpass -p "$remotepassword" scp -r "$file" ${remoteuser}@${remotehost}:"$remotedir"; then echo "上傳成功: $file" | tee -a "$logfile" else echo "上傳失敗: $file" | tee -a "$logfile" fi echo "" | tee -a "$log_file" done ``` 注意:此腳本會判斷 如果目標文件夾不存在則創建
上傳日志如下: 可以看到虛擬機中已有了:
構建鏡像并一鍵啟動(包含服務和中間件)
之后我們直接 使用下邊命令構建&啟動 docker-compose 中定義的服務: bash docker-compose up -d --build
執行效果如下: 看下我們掛載到宿主機的日志文件 :
從日志上看 都已經啟動成功。至此,本xzll-im項目就可以使用docker-compose啟動啦 當然后續將依賴的中間件也加到docker-compose中去 一鍵啟動項目+中間件。更方便了。
后續補充:
將依賴的中間件也寫到docker-compose.yml中 這樣的話 真正做到一鍵啟動。
改后的docker-compose.yml文件: ```yml version: '3.9' services: im-gateway: build: context: ./im-gateway dockerfile: Dockerfile image: im-gateway:latest
hostname: im-gateway
container_name: im-gateway
restart: always
ports:- "8081:8081"
networks:- default_network
volumes:- "/tmp/data/logs:/logs"
depends_on:- nacos- zookeeper- redis- rmq_broker- rmq_namesrv
im-auth: build: context: ./im-auth dockerfile: Dockerfile image: im-auth:latest
hostname: im-auth
container_name: im-auth
restart: always
ports:- "8082:8082"
networks:- default_network
volumes:- "/tmp/data/logs:/logs"
depends_on:- nacos- zookeeper- redis- rmq_broker- rmq_namesrv
im-business: # 可以根據Dockerfile構建鏡像(但是,Docker Compose 會在檢測到上下文變化時重新構建鏡像。也就是說如果你不修改Dockerfile docker-compose應該不是每次都構建鏡像 實測確實如此) build: context: ./im-business # 指定Dockerfile文件位置 dockerfile: Dockerfile # 指定名稱 image: im-business:latest # 指定生成鏡像的 名稱
# 也可以直接指定鏡像名 但是要確保鏡像存在 (如果在docker倉庫, 則不需要再本地存在鏡像 會自動pull)
# image: im-business:0.0.2# 設置容器的主機名 即修改 : /etc/hosts 中的內容
hostname: im-business
# 容器名稱
container_name: im-business
# 重啟策略, always 表示無論哪種狀態退出,都會重啟容器
restart: always
ports:# 設置主機與容器的端口映射- "8083:8083"
networks:# 使用默認網絡即:docker0 橋接- default_network
volumes:# 將主機的 /tmp/data/logs 目錄掛載到容器的 /logs 目錄。這樣可以實現數據的持久化,當容器重啟時,數據不會丟失- "/tmp/data/logs:/logs"
depends_on:- nacos- zookeeper- redis- rmq_broker- rmq_namesrv
im-connect: build: context: ./im-connect dockerfile: Dockerfile image: im-connect:latest
hostname: im-connect
container_name: im-connect
restart: always
ports:- "10001:10001"
networks:- default_network
volumes:- "/tmp/data/logs:/logs"
depends_on:- nacos- zookeeper- redis- rmq_broker- rmq_namesrv
im-console: build: context: ./im-console dockerfile: Dockerfile image: im-console:latest
hostname: im-console
container_name: im-console
restart: always
ports:- "8084:8084"
networks:- default_network
volumes:- "/tmp/data/logs:/logs"
depends_on:- nacos- zookeeper- redis- rmq_broker- rmq_namesrv
# ######################################### 以下是此im項目 依賴的中間件 #########################################
# rocketMq nameServer rmqnamesrv: image: apache/rocketmq:4.8.0 containername: rmqnamesrv restart: always ports: - "9876:9876" volumes: - /usr/local/softhzz/docker/rocketmqnamesrv/store:/root/store - /usr/local/softhzz/docker/rocketmqnamesrv/logs:/root/logs command: sh mqnamesrv # rocketMq broker rmqbroker: image: apache/rocketmq:4.8.0 containername: rmqbroker restart: always ports: - "10911:10911" - "10909:10909" volumes: - /usr/local/softhzz/docker/rocketmqbroker/store:/root/store - /usr/local/softhzz/docker/rocketmqbroker/logs:/root/logs - /usr/local/softhzz/docker/rocketmqbroker/conf/broker.conf:/opt/rocketmq-4.8.0/conf/broker.conf environment: - LOCALIP=${LOCALIP} command: sh mqbroker -c /opt/rocketmq-4.8.0/conf/broker.conf
rocketmq-console: image: styletang/rocketmq-console-ng containername: rocketmq-console restart: always ports: - "8080:8080" environment: JAVAOPTS: "-Drocketmq.namesrv.addr=${LOCALIP}:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" dependson: - rmqnamesrv - rmqbroker # nacos nacos: image: nacos/nacos-server:2.0.3 containername: nacos restart: always ports: - "8848:8848" volumes: - /usr/local/softhzz/docker/nacos/data:/home/nacos/data - /usr/local/softhzz/docker/nacos/logs:/home/nacos/logs environment: MODE: standalone #redis redis: image: redis containername: redis restart: always ports: - "6379:6379" volumes: - /usr/local/softhzz/docker/redis/data:/data - /usr/local/softhzz/docker/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf command: redis-server /usr/local/etc/redis/redis.conf # zk zookeeper: image: zookeeper containername: zookeeper restart: always ports: - "2181:2181" volumes: - /usr/local/softhzz/docker/zk/data:/data - /usr/local/softhzz/docker/zk/datalog:/datalog - /usr/local/softhzz/docker/zk/conf/zoo.cfg:/conf/zoo.cfg
networks: default_network: # 橋接 driver: bridge ```
配置文件: 首先需要設置環境變量,方便我們在docker-compose.yml或者配置文件中引用:
```bash
編輯bash文件
sudo vim ~/.bashrc
設置環境變量
export LOCAL_IP=$(ip addr show enp0s3 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
使其生效
source ~/.bashrc
打印看看 將輸出enp0s3網卡對應的ip
echo $LOCAL_IP ```
```bash
zoo.cfg配置文件內容:
tickTime=2000 dataDir=/data dataLogDir=/datalog clientPort=2181 initLimit=5 syncLimit=2 server.1=zookeeper:2888:3888
redis.conf文件內容:
appendonly yes requirepass 123456
broker.conf文件 內容:
brokerClusterName = DefaultCluster brokerName = broker-a brokerId = 0 deleteWhen = 04 fileReservedTime = 48 brokerRole = ASYNCMASTER flushDiskType = ASYNCFLUSH namesrvAddr=${LOCALIP}:9876 autoCreateTopicEnable=true brokerIP1=${LOCALIP} ```
由于我將中間件和服務全部搞到docker-compose中 所以以后不需要docker run方式啟動了 這里把之前啟動的容器和下載的鏡像 全部清理掉。 執行 ```bash
將停止并刪除所有容器
docker rm -f $(sudo docker ps -a -q)
刪除所有鏡像
docker rmi $(docker images -q) ``` 之后再執行命令 構建 或者 pull鏡像并啟動容器:
- (自己寫的服務是構建 即根據docker-compose.yml中的buil指令 來使用Dockerfile文件build)
- (中間件是直接指定了鏡像名稱 所以直接去docker倉庫 pull)
bash docker-compose up -d --build
可以看到 項目服務和相關中間件 一個命令全部啟動成功了,挺爽的:
有個點要注意,就是日志輸出框架的配置,需要指定目錄到容器的 /logs, 這樣才能和docker-compose中的日志掛載配置相吻合 ,我使用的是log4j2 ,我是這么定義的(以im-console為例): ```xml
im-console /logs
<DefaultRolloverStrategy max="40" /><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" /></RollingRandomAccessFile>
<logger level="OFF" name="jdbc.sqltiming" additivity="false"><AppenderRef ref="Console" /><AppenderRef ref="File" /></logger><logger level="OFF" name="jdbc.companyAudit" additivity="false"><AppenderRef ref="Console" /><AppenderRef ref="File" /></logger><logger level="OFF" name="jdbc.resultset" additivity="false"><AppenderRef ref="Console" /><AppenderRef ref="File" /></logger><logger level="OFF" name="jdbc.connection" additivity="false"><AppenderRef ref="Console" /><AppenderRef ref="File" /></logger><logger level="OFF" name="jdbc.audit" additivity="false"><AppenderRef ref="Console" /><AppenderRef ref="File" /></logger><logger level="OFF" name="serviceStatsLog" additivity="false"><AppenderRef ref="Console" /><AppenderRef ref="File" /></logger>
```