1、集成目的
? ? ? ?目前項目中需要實現(xiàn)水利機理模型的容器化部署,使用docker實現(xiàn)模型容器化部署操作,互相隔離,就是一個不錯的方案。
2、需要docker-java 實現(xiàn)
- 實現(xiàn)docker遠程連接、遠程安全連接
- 構(gòu)建鏡像、加載鏡像、刪除鏡像、拉取鏡像、創(chuàng)建容器、啟動容器、停止容器、刪除容器。
- 執(zhí)行模型運行命令
- 實現(xiàn)路徑掛載。
- 實現(xiàn)宿主機模型包復制
3、使用docker-java
3.1創(chuàng)建遠程連接
3.1.1修改docker配置信息(普通連接)
打開docker.service文件
sudo vi /lib/systemd/system/docker.service
找到ExecStart 開頭的配置,注釋原配置 進行備份
插入以下內(nèi)容
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
保存退出
sudo systemctl daemon-reload
sudo systemctl restart docker
注意:
1、我使用的是Ubutun系統(tǒng),需要使用sudo進行操作。防火墻如果啟動,需要開發(fā)2375端口,不然無法訪問到docker。
2、瀏覽器輸入:http://IP:2375/version?如果響應(yīng)正常,則配置生效
3.1.2添加maven依賴
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.2.13</version>
</dependency>
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java-transport-httpclient5</artifactId>
<version>3.2.13</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
3.1.3創(chuàng)建普通連接
package com.sf.engines.config;
import com.alibaba.fastjson.JSONObject;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Info;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import java.time.Duration;
@Configuration
public class DockerConfig {
/**
* 連接docker服務(wù)器
* @return
*/
@Bean("dockerClient")
public DockerClient connectDocker(){
DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerTlsVerify(false)
// 這里填最上面填的ip端口號,ip換成服務(wù)器ip
.withDockerHost("tcp://192.168.10.8:2375")
// docker API版本號,可以用docker version查看
.withApiVersion("1.41")
// 默認
.withRegistryUrl("https://index.docker.io/v1/").build();
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(config.getDockerHost())
.sslConfig(config.getSSLConfig())
.maxConnections(100)
.connectionTimeout(Duration.ofSeconds(30))
.responseTimeout(Duration.ofSeconds(45))
.build();
DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
Info info = dockerClient.infoCmd().exec();
String infoStr = JSONObject.toJSONString(info);
System.out.println("docker的環(huán)境信息如下:=================");
System.out.println(infoStr);
return dockerClient;
}
}
控制臺打印了docker信息,說明已經(jīng)連上docker。
注意:
- DOCKER_HOST Docker的地址,比如: tcp://localhost:2376 或者unix:///var/run/docker.sock
- DOCKER_TLS_VERIFY 是否開啟 TLS 驗證 (http 和 https 之間切換)
- DOCKER_CERT_PATH TLS 驗證的證書路徑
- DOCKER_CONFIG 其他docker配置文件的路徑 (比如 .dockercfg)
- api.version API version版本
- registry.url 下載源地址(docker鏡像存放的地址)
- registry.username 登陸用戶名 (推送鏡像到docker云倉庫時需要)
- registry.password 登陸用戶密碼(推送鏡像到docker云倉庫時需要)
- registry.email 登陸賬戶的郵箱(推送鏡像到docker云倉庫時需要)
3.1.4創(chuàng)建安全連接?
- 首先修改docker.service
sudo vi /lib/systemd/system/docker.service
找到ExecStart 開頭的配置,注釋原配置 進行備份
插入以下內(nèi)容
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock -D --tlsverify --tlscert=/etc/docker/certs.d/server-cert-docker.pem --tlskey=/etc/docker/certs.d/server-key-docker.pem --tlscacert=/etc/docker/certs.d/ca-docker.pem
保存退出
sudo systemctl daemon-reload
sudo service docker restart
? ?2. 證書腳本編寫
#!/bin/bash
#
# -------------------------------------------------------------
# 自動創(chuàng)建 Docker TLS 證書
# -------------------------------------------------------------
# 以下是配置信息
# --[BEGIN]------------------------------
CODE="docker"
IP="192.168.1.1"
PASSWORD="123456"
COUNTRY="CN"
STATE="SHANDONG"
CITY="JINAN"
ORGANIZATION="thyc"
ORGANIZATIONAL_UNIT="Dev"
COMMON_NAME="$IP"
EMAIL="zjbang.ok@163.com"
# --[END]--
# Generate CA key
openssl genrsa -aes256 -passout "pass:$PASSWORD" -out "ca-key-$CODE.pem" 4096
# Generate CA
openssl req -new -x509 -days 365 -key "ca-key-$CODE.pem" -sha256 -out "ca-$CODE.pem" -passin "pass:$PASSWORD" -subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORGANIZATION/OU=$ORGANIZATIONAL_UNIT/CN=$COMMON_NAME/emailAddress=$EMAIL"
# Generate Server key
openssl genrsa -out "server-key-$CODE.pem" 4096
# Generate Server Certs.
openssl req -subj "/CN=$COMMON_NAME" -sha256 -new -key "server-key-$CODE.pem" -out server.csr
echo "subjectAltName = IP:$IP,IP:127.0.0.1" >> extfile.cnf
echo "extendedKeyUsage = serverAuth" >> extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -passin "pass:$PASSWORD" -CA "ca-$CODE.pem" -CAkey "ca-key-$CODE.pem" -CAcreateserial -out "server-cert-$CODE.pem" -extfile extfile.cnf
# Generate Client Certs.
rm -f extfile.cnf
openssl genrsa -out "key-$CODE.pem" 4096
openssl req -subj '/CN=client' -new -key "key-$CODE.pem" -out client.csr
echo extendedKeyUsage = clientAuth >> extfile.cnf
openssl x509 -req -days 365 -sha256 -in client.csr -passin "pass:$PASSWORD" -CA "ca-$CODE.pem" -CAkey "ca-key-$CODE.pem" -CAcreateserial -out "cert-$CODE.pem" -extfile extfile.cnf
rm -vf client.csr server.csr
chmod -v 0400 "ca-key-$CODE.pem" "key-$CODE.pem" "server-key-$CODE.pem"
chmod -v 0444 "ca-$CODE.pem" "server-cert-$CODE.pem" "cert-$CODE.pem"
# 打包客戶端證書
mkdir -p "tls-client-certs-$CODE"
cp -f "ca-$CODE.pem" "cert-$CODE.pem" "key-$CODE.pem" "tls-client-certs-$CODE/"
cd "tls-client-certs-$CODE"
tar zcf "tls-client-certs-$CODE.tar.gz" *
mv "tls-client-certs-$CODE.tar.gz" ../
cd ..
rm -rf "tls-client-certs-$CODE"
# 拷貝服務(wù)端證書
mkdir -p /etc/docker/certs.d
cp "ca-$CODE.pem" "server-cert-$CODE.pem" "server-key-$CODE.pem" /etc/docker/certs.d/
腳本是網(wǎng)上找的,可以自動生成證書并完成拷貝的腳本。腳本命名自由:sudo vi auto_gen_docker.sh?
3.執(zhí)行腳本
chmod a+x auto_gen_docker.sh
sh auto_gen_docker.sh
3.2鏡像管理
3.2.1從dockerfile構(gòu)建鏡像
/**
* 從Dockerfile構(gòu)建鏡像
* @return
* @throws URISyntaxException
*/
public String buildImage(String imageName, String imageTag, String dockerFile) throws URISyntaxException {
ImmutableSet<String> tag = ImmutableSet.of(imageName + ":" + imageTag);
String imageId = dockerClient.buildImageCmd(new File(dockerFile))
.withTags(tag)
.start()
.awaitImageId();
return imageId;
}
提示:我在使用buileImageCmd時,無法正確構(gòu)建鏡像。不得已采取比較笨的辦法,將dockerfile文件上傳到服務(wù)器目標文件夾中,通過ssh遠程調(diào)用的方式,進行遠程鏡像構(gòu)建操作。具體實現(xiàn)代碼如下:
//遠程構(gòu)建鏡像
private String buildImage(EnginesModelImageEntity image) {
String tag = image.getImageName() + ":" + image.getImageTag();
//模型解壓命令
StringBuffer buildBuffer = new StringBuffer("");
buildBuffer.append(ModelConstant.CD_STR)
.append(image.getDockerFileUrl())
.append(ModelConstant.AND_STR)
.append(ModelConstant.DOCKER_BUILD)
.append(tag)
.append(ModelConstant.DOCKER_BUILD_END);
//調(diào)用遠程命令進行模型解壓
Connection unzipCoon = SshUtil.login(ip,userName,password);
String result = SshUtil.execute(unzipCoon, buildBuffer.toString());
return result;
}
注意:遠程連接的代碼在以前的文章里有,可以去查閱。
1、首先通過CD名稱進入到存放dockerfile的文件夾,然后執(zhí)行鏡像build命令: docker build -t imageName:imageTag .?
2、構(gòu)建命令最后的. 一定不能省略。
3.2.2獲取鏡像ID
? ? ? ? 構(gòu)建鏡像有時會沒有那么快,可以通過接口獲取鏡像ID:
public R getImageID(EnginesModelImageEntity image) {
if(ObjectUtils.isEmpty(dockerClient)){
initDockerClient();
}
String tag = image.getImageName() + ":" + image.getImageTag();
List<Image> images = dockerClient.listImagesCmd().withImageNameFilter(image.getImageName()).exec();
if(images.isEmpty()){
return R.fail("鏡像構(gòu)建中,請稍后再試!");
}
String imageId = "";
for(Image i : images){
if(Arrays.asList(i.getRepoTags()).contains(tag)){
String id = i.getId();
imageId = id.substring(id.indexOf(":")+1,id.indexOf(":")+13);
}
}
if(ObjectUtils.isNotEmpty(imageId)){
image.setImageId(imageId);
image.setImageStatus("2");
baseMapper.updateById(image);
return R.success("獲取鏡像ID成功!");
}
return R.success("未查詢到鏡像ID,請稍后再試!");
}
注意:如果使用同一個dockerfile文件構(gòu)建鏡像,則鏡像ID是一致的,會存在多個鏡像TAG,可以根據(jù)repoTags中是否包含自己的鏡像標簽來判斷,鏡像是否構(gòu)建完成。
3.2.3刪除鏡像
docker-java刪除鏡像是通過鏡像ID進行刪除操作的:
public void removeImage(DockerClient client,String imageId){
dockerClient.removeImageCmd(imageId).exec();
}
但是會把同一個dockerfile構(gòu)造的鏡像全部刪除,我是通過鏡像tag進行鏡像刪除操作的:
public boolean removeImage(Long id) {
EnginesModelImageEntity image = baseMapper.selectById(id);
if(ObjectUtils.isEmpty(dockerClient)){
initDockerClient();
}
image.setImageStatus("3");
if(baseMapper.updateById(image)>=0){
String tag = image.getImageName() + ":" + image.getImageTag();
//模型解壓命令
StringBuffer removeBuffer = new StringBuffer("");
removeBuffer.append(ModelConstant.DOCKER_REMOVE)
.append(tag);
//調(diào)用遠程命令進行模型解壓
Connection unzipCoon = SshUtil.login(ip,userName,password);
SshUtil.execute(unzipCoon, removeBuffer.toString());
return true;
}
return false;
}
注意:通過刪除鏡像命令:docker rmi imageName:imageTag進行鏡像刪除操作。
3.3容器管理
3.3.1創(chuàng)建容器文章來源:http://www.zghlxwxcb.cn/news/detail-762436.html
private String createContainers(EnginesModelContainerEntity entity){
EnginesModelImageEntity image = imageMapper.selectById(entity.getImageId());
String result = "";
for(int i=0;i<entity.getContainerNumber();i++){
EnginesModelContainerChildEntity childEntity = new EnginesModelContainerChildEntity();
childEntity.setModelId(entity.getModelId());
String containerName = entity.getContainerName().replace(" ","") + ModelConstant.UNDERLINE +i;
childEntity.setContainerName(containerName);
childEntity.setContainerStatus("2");
childEntity.setParentId(entity.getId());
childEntity.setStatus(0);
childEntity.setIsDeleted(0);
childEntity.setCreateTime(new Date());
childEntity.setCreateUser(entity.getCreateUser());
String buffer = ModelConstant.DOCKER_RUN + containerName + ModelConstant.SPACE + image.getImageName() + ModelConstant.COLON + image.getImageTag();
Connection pullCoon = SshUtil.login(ip,userName,password);
result = SshUtil.execute(pullCoon, buffer);
if(ObjectUtils.isNotEmpty(result)){
childMapper.insert(childEntity);
}
}
return result;
}
使用的遠程ssh方式創(chuàng)建容器,容器啟動命令:文章來源地址http://www.zghlxwxcb.cn/news/detail-762436.html
docker run --gpus all -itd --name= containerName imageName:imageTag
4、遠程服務(wù)器docker容器中寫文件
public static void main(String[] args) {
String containerId = "61cf0a2e4c4a0ae00ad8aa1193f3abfbdd9a48e6236f427d2c37fae23b84ae6f"; // Replace with your container ID
String filePath = "/opt/model/twoHydrodynamic/ProcessData/Inflow_3.json";
String fileContent = "Create JSON FILE";
DockerClientService.initDockerClient("ip","docker端口");
// 創(chuàng)建一個tar歸檔,將內(nèi)容寫入tar文件
ByteArrayInputStream tarStream = null;
try {
tarStream = TarUtils.createTarArchive(filePath, fileContent);
} catch (IOException e) {
throw new RuntimeException(e);
}
ExecCreateCmdResponse response = dockerClient.execCreateCmd(containerId)
.withCmd("tar", "-C", "/", "-x")
.withAttachStdout(true)
.withAttachStderr(true)
.exec();
ExecStartCmd startCmd = dockerClient.execStartCmd(response.getId())
.withDetach(false)
.withTty(true);
ExecStartResultCallback callback = new ExecStartResultCallback();
startCmd.exec(callback);
// 發(fā)送tar歸檔數(shù)據(jù)
dockerClient.copyArchiveToContainerCmd(containerId)
.withRemotePath("/")
.withTarInputStream(tarStream)
.exec();
// 關(guān)閉tarStream
try {
tarStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
5、遠程服務(wù)器docker容器中讀文件
public static void main(String[] args) {
String containerId = "61cf0a2e4c4a0ae00ad8aa1193f3abfbdd9a48e6236f427d2c37fae23b84ae6f"; // Replace with your container ID
String filePath = "/opt/model/twoHydrodynamic/OutputData/X2DProcess.dat";
DockerClientService.initDockerClient("IP","docker端口");
String taskId = UUID.randomUUID().toString();
String basePath = "/Users/admin/Documents/shuke/modelFile/depth/"+taskId;
File folder = new File(basePath);
//如果文件夾不存在,則創(chuàng)建文件夾
if (!folder.exists()) {
folder.mkdirs();
}
String outPath = basePath + "/X2DProcess.dat";
try {
InputStream inputStream = dockerClient.copyArchiveFromContainerCmd(containerId, filePath).exec();
try (OutputStream outputStream = new FileOutputStream(outPath)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
contentGet(folder,outPath,taskId);
} catch (NotFoundException e) {
System.err.println("Container not found: " + e.getMessage());
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
到了這里,關(guān)于java集成Docker-java實現(xiàn)遠程鏡像、容器創(chuàng)建,服務(wù)發(fā)布的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!