Docker容器 - compose容器编排(Java微服务项目实例,解决 docker-compose up Mysql 的报错问题)

24 篇文章 7 订阅
5 篇文章 2 订阅

目录

是什么

能干什么

安装

一、官网

二、下载

三、安装步骤

四、卸载

Compose核心概念

一、一个文件

二、两个要素

1.服务(service)

2.工程(project)

Compose的操作步骤

Compose常用命令

Compose编排Java微服务

一、sql

二、POM

三、YML

四、主启动类

五、config配置类

1.RedisConfig

2.SwaggerConfig

六、entities类

1.User

2.UserDTO

七、mapper类

1.UserMapper

2.UserMapper.xml

八、service类

九、controller类

十、打成jar包并上传

十一、编写Dockerfile

十二、构建镜像

不使用Compose

一、mysql容器实例

二、redis容器实例

三、微服务

swagger测试

一、验证写操作

二、验证读操作

存在的问题

使用Compose

一、编写docker-compose.yml文件

二、修改微服务项目

1.修改yml

三、再次进行swagger测试

四、关停

五、报错

Creating jardocker_mysql_1 ... error

ERROR: for mysql  Cannot start service mysql: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/app/mysql/conf/my.cnf" to rootfs at "/etc/my.cnf": mount /app/mysql/conf/my.cnf:/etc/my.cnf (via /proc/self/fd/6), flags: 0x5000: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected typeERROR: Encountered errors while bringing up the project.


是什么

Docker-Compose是Docker官方的开源项目。负责实现对Docker容器集群的快速编排。可以管理多个 Docker 容器组成一个应用。你需要定义一个 yml 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器。

这玩意儿可是个项目哇!


能干什么

还记得咱们Java里面怎么新建对象吗,直接new一个出来。

//没有对象怎么办 new一个出来

在spring里面,咱们是通过xml来对多个对象进行统一的管理:在容器applicationContext.xml中对bean对象进行统一集中的管理。

<bean id="" class="">

而在Docker中,容器之间的启动可能会存在一定的先后顺序,加载条件等因素。所以咱们使用docker compose来对容器实例进行统一的管理,这里要使用到yml文件:docker-compose.yml这样可以做到对大量的容器实例进行统一的一键start,一键stop等大量操作。

docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等。Compose允许用户通过一个单独的docker-compose.yml模板文件(yml格式)来定义一组相关联的应用容器为一个项目(project)。可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。


安装

一、官网

官网地址:Compose file version 3 reference | Docker Documentation

eb07e2c53cba4cac8aa4bbc68f535742.png

可以看到左侧有2和3两个版本,官方给的建议是用v3。官网的内容其实非常多,跟着官网学其实也不错。


二、下载

下载地址:Install Docker Compose | Docker Documentation

这里只是把地址列出来,也不需要去官网下载再安装,直接跟着下一步来即可

24ea151d75584bed9162e00dd2c00d4d.png

官网的更新速度可能较快,直接跟着以下的步骤来


三、安装步骤

直接依次执行

curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

2bca21ac8cc64c11a2449f9e078340fa.png

chmod +x /usr/local/bin/docker-compose

95954e8f95994fb5b1a7d67b742b0207.png

docker-compose --version

3eb28f42a7c642ba8ea07690ce899dd6.png

检查到版本号后代表安装成功


四、卸载

这里就不做截图演示了,卸载时直接执行以下命令即可:

sudo rm /usr/local/bin/docker-compose 

Compose核心概念

一、一个文件

 docker-compose.yml 


二、两个要素

1.服务(service)

一个个容器实例,比如微服务、mysql、Nginx、redis等

2.工程(project)

由一组关联的容器组成的一个完整的业务单元,在 docker-compose.yml 文件中进行定义。


Compose的操作步骤

使用的三个步骤如下:

编写Dockerfile定义各个微服务应用并构建出对应的镜像文件

使用 docker-compose.yml 定义一个完整的业务单元,安排好整体应用中的各个容器服务

最后,执行 docker-compose up 命令来启动并运行整个应用程序,完整一键部署


Compose常用命令

docker-compose -h                           # 查看帮助
docker-compose up                           # 启动所有docker-compose服务
docker-compose down                         # 停止并删除容器、网络、卷、镜像
docker-compose logs  yml里面的服务id          # 查看容器输出日志
docker-compose config                       # 检查配置
docker-compose config -q                    # 检查配置,有问题才有输出
docker-compose restart                      # 重启服务
docker-compose start                        # 启动服务
docker-compose stop                         # 停止服务
docker-compose exec  yml里面的服务id          # 进入容器实例内部  
docker-compose exec docker-compose.yml文件中写的服务id /bin/bash
docker-compose ps                           # 展示当前docker-compose编排过的运行的所有容器
docker-compose top                          # 展示当前docker-compose编排过的容器进程
docker-compose up -d                        # 启动所有docker-compose服务并后台运行

Compose编排Java微服务

之前博客里写得发布微服务到容器其实里面也就只输出了一个UUID。这回咱们把mysql那些东西都加上,然后再使用docker compose进行编排管控。类如下:

36a48364b2b74d7d8a4901190065dde0.png

因为本篇文章的重点不在于Java、mysql、redis等方面,直接上代码:

一、sql

CREATE TABLE `t_user` (

  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,

  `username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',

  `password` varchar(50) NOT NULL DEFAULT '' COMMENT '密码',

  `sex` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',

  `deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',

  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表'

二、POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <!--<version>2.3.10.RELEASE</version>-->
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>docker</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.47</mysql.version>
        <druid.version>1.1.16</druid.version>
        <mapper.version>4.1.5</mapper.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>

    <dependencies>
        <!--guava Google 开源的 Guava 中自带的布隆过滤器-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>
        <!-- redisson -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.4</version>
        </dependency>
        <!--SpringBoot通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--swagger2-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--SpringBoot与Redis整合依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--springCache-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!--springCache连接池依赖包-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!-- jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.1.0</version>
        </dependency>
        <!--Mysql数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--SpringBoot集成druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!--mybatis和springboot整合-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.spring.boot.version}</version>
        </dependency>
        <!-- 添加springboot对amqp的支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.10</version>
        </dependency>
        <!--通用基础配置junit/devtools/test/log4j/lombok/hutool-->
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.2.3</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <optional>true</optional>
        </dependency>
        <!--persistence-->
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>persistence-api</artifactId>
            <version>1.0.2</version>
        </dependency>
        <!--通用Mapper-->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>${mapper.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.1.0</version>
            </plugin>
        </plugins>
    </build>
</project>

三、YML

server.port=6001
# ========================alibaba.druid=========================
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.150.30:3306/dbtest?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=你自己的密码
spring.datasource.druid.test-while-idle=false
# ========================redis=========================
spring.redis.database=0
spring.redis.host=192.168.150.30
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
# ========================mybatis=======================
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=docker.entities
# ========================swagger=====================
spring.swagger2.enabled=true

四、主启动类

package docker.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReferenceUtil;
import docker.entities.User;
import docker.entities.UserDTO;
import docker.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.Random;

@Api(description = "用户User接口")
@RestController
@Slf4j
public class UserController
{
    @Resource
    private UserService userService;

    @ApiOperation("数据库新增3条记录")
    @RequestMapping(value = "/user/add",method = RequestMethod.POST)
    public void addUser()
    {
        for (int i = 1; i <=3; i++) {
            User user = new User();

            user.setUsername("van"+i);
            user.setPassword(IdUtil.simpleUUID().substring(0,6));
            user.setSex((byte) new Random().nextInt(2));

            userService.addUser(user);
        }
    }
    
    @ApiOperation("查询1条记录")
    @RequestMapping(value = "/user/find/{id}",method = RequestMethod.GET)
    public User findUserById(@PathVariable Integer id)
    {
        return userService.findUserById(id);
    }
}





五、config配置类

1.RedisConfig

package docker.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.Serializable;

@Configuration
@Slf4j
public class RedisConfig {
    /**
     * @param lettuceConnectionFactory
     * @return redis序列化的工具配置类,下面这个请一定开启配置
     * 127.0.0.1:6379> keys *
     * 1) "ord:102"  序列化过
     * 2) "\xac\xed\x00\x05t\x00\aord:102"   野生,没有序列化过
     */
    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();

        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        //设置key序列化方式string
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置value的序列化方式json
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }

}

2.SwaggerConfig

package docker.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.text.SimpleDateFormat;
import java.util.Date;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Value("${spring.swagger2.enabled}")
    private Boolean enabled;

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(enabled)
                .select()
                .apis(RequestHandlerSelectors.basePackage("docker")) //你自己的package
                .paths(PathSelectors.any())
                .build();
    }

    public ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Java" + "\t" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()))
                .description("docker-compose")
                .version("1.0")
                .termsOfServiceUrl("https://www.baidu.com/")
                .build();
    }
}

六、entities类

1.User

package docker.entities;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;

@Table(name = "t_user")
public class User {
    @Id
    @GeneratedValue(generator = "JDBC")
    private Integer id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 性别 0=女 1=男
     */
    private Byte sex;

    /**
     * 删除标志,默认0不删除,1删除
     */
    private Byte deleted;

    /**
     * 更新时间
     */
    @Column(name = "update_time")
    private Date updateTime;

    /**
     * 创建时间
     */
    @Column(name = "create_time")
    private Date createTime;

    /**
     * @return id
     */
    public Integer getId() {
        return id;
    }

    /**
     * @param id
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取用户名
     *
     * @return username - 用户名
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置用户名
     *
     * @param username 用户名
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取密码
     *
     * @return password - 密码
     */
    public String getPassword() {
        return password;
    }

    /**
     * 设置密码
     *
     * @param password 密码
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * 获取性别 0=女 1=男
     *
     * @return sex - 性别 0=女 1=男
     */
    public Byte getSex() {
        return sex;
    }

    /**
     * 设置性别 0=女 1=男
     *
     * @param sex 性别 0=女 1=男
     */
    public void setSex(Byte sex) {
        this.sex = sex;
    }

    /**
     * 获取删除标志,默认0不删除,1删除
     *
     * @return deleted - 删除标志,默认0不删除,1删除
     */
    public Byte getDeleted() {
        return deleted;
    }

    /**
     * 设置删除标志,默认0不删除,1删除
     *
     * @param deleted 删除标志,默认0不删除,1删除
     */
    public void setDeleted(Byte deleted) {
        this.deleted = deleted;
    }

    /**
     * 获取更新时间
     *
     * @return update_time - 更新时间
     */
    public Date getUpdateTime() {
        return updateTime;
    }

    /**
     * 设置更新时间
     *
     * @param updateTime 更新时间
     */
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    /**
     * 获取创建时间
     *
     * @return create_time - 创建时间
     */
    public Date getCreateTime() {
        return createTime;
    }

    /**
     * 设置创建时间
     *
     * @param createTime 创建时间
     */
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}

2.UserDTO

package docker.entities;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@NoArgsConstructor
@AllArgsConstructor
@Data
@ApiModel(value = "用户信息")
public class UserDTO implements Serializable {
    @ApiModelProperty(value = "用户ID")
    private Integer id;

    @ApiModelProperty(value = "用户名")
    private String username;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "性别 0=女 1=男 ")
    private Byte sex;

    @ApiModelProperty(value = "删除标志,默认0不删除,1删除")
    private Byte deleted;

    @ApiModelProperty(value = "更新时间")
    private Date updateTime;

    @ApiModelProperty(value = "创建时间")
    private Date createTime;

    /**
     * @return id
     */
    public Integer getId() {
        return id;
    }

    /**
     * @param id
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取用户名
     *
     * @return username - 用户名
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置用户名
     *
     * @param username 用户名
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取密码
     *
     * @return password - 密码
     */
    public String getPassword() {
        return password;
    }

    /**
     * 设置密码
     *
     * @param password 密码
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * 获取性别 0=女 1=男
     *
     * @return sex - 性别 0=女 1=男
     */
    public Byte getSex() {
        return sex;
    }

    /**
     * 设置性别 0=女 1=男
     *
     * @param sex 性别 0=女 1=男
     */
    public void setSex(Byte sex) {
        this.sex = sex;
    }

    /**
     * 获取删除标志,默认0不删除,1删除
     *
     * @return deleted - 删除标志,默认0不删除,1删除
     */
    public Byte getDeleted() {
        return deleted;
    }

    /**
     * 设置删除标志,默认0不删除,1删除
     *
     * @param deleted 删除标志,默认0不删除,1删除
     */
    public void setDeleted(Byte deleted) {
        this.deleted = deleted;
    }

    /**
     * 获取更新时间
     *
     * @return update_time - 更新时间
     */
    public Date getUpdateTime() {
        return updateTime;
    }

    /**
     * 设置更新时间
     *
     * @param updateTime 更新时间
     */
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    /**
     * 获取创建时间
     *
     * @return create_time - 创建时间
     */
    public Date getCreateTime() {
        return createTime;
    }

    /**
     * 设置创建时间
     *
     * @param createTime 创建时间
     */
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", sex=" + sex +
                '}';
    }
}

七、mapper类

1.UserMapper

package docker.mapper;

import docker.entities.User;
import tk.mybatis.mapper.common.Mapper;

public interface UserMapper extends Mapper<User> {
}

2.UserMapper.xml

在此处创建UserMapper.xml

9ba908d6b9c84f21afd9bd011962c04e.png

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="docker.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="docker.entities.User">
        <!--
          WARNING - @mbg.generated
        -->
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="username" jdbcType="VARCHAR" property="username" />
        <result column="password" jdbcType="VARCHAR" property="password" />
        <result column="sex" jdbcType="TINYINT" property="sex" />
        <result column="deleted" jdbcType="TINYINT" property="deleted" />
        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    </resultMap>
</mapper>

八、service类

package docker.service;

import docker.entities.User;
import docker.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserService {

    public static final String CACHE_KEY_USER = "user:";

    @Resource
    private UserMapper userMapper;
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * addUser
     *
     * @param user
     */
    public void addUser(User user) {
        //1 先插入mysql并成功
        int i = userMapper.insertSelective(user);

        if (i > 0) {
            //2 需要再次查询一下mysql将数据捞回来并ok
            user = userMapper.selectByPrimaryKey(user.getId());
            //3 将捞出来的user存进redis,完成新增功能的数据一致性。
            String key = CACHE_KEY_USER + user.getId();
            redisTemplate.opsForValue().set(key, user);
        }
    }

    /**
     * findUserById
     *
     * @param id
     * @return
     */
    public User findUserById(Integer id) {
        User user = null;
        String key = CACHE_KEY_USER + id;

        //1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql
        user = (User) redisTemplate.opsForValue().get(key);

        if (user == null) {
            //2 redis里面无,继续查询mysql
            user = userMapper.selectByPrimaryKey(id);
            if (user == null) {
                //3.1 redis+mysql 都无数据
                //你具体细化,防止多次穿透,我们规定,记录下导致穿透的这个key回写redis
                return user;
            } else {
                //3.2 mysql有,需要将数据写回redis,保证下一次的缓存命中率
                redisTemplate.opsForValue().set(key, user);
            }
        }
        return user;
    }
}

九、controller类

package docker;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@MapperScan("docker.mapper") //import tk.mybatis.spring.annotation.MapperScan;
public class DockerBootApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(DockerBootApplication.class, args);
    }

}

十、打成jar包并上传

fde851c088d744c795490044eeaf1ed1.png

直接双机,在打包完成后上传至Linux,记得单独开一个目录来进行存放。 

f93e1102d91e40b59d4e86983d2cbefd.png


十一、编写Dockerfile

在刚刚用来存放jar包的目录下新建Dockerfile文件,注意文件名大小写。

# 基础镜像使用java
FROM java:8

# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp

# 将jar包添加到容器中并更名为van_docker.jar
ADD docker-0.0.1-SNAPSHOT.jar van_docker.jar

# 运行jar包
RUN bash -c 'touch /van_docker.jar'
ENTRYPOINT ["java","-jar","/van_docker.jar"]

#暴露6001端口作为微服务
EXPOSE 6001

427bf4fec60a4717aeab328673b1a3f4.png


十二、构建镜像

注意最后的空格和点

docker build -t van_docker:1.6 .

430d2ee486a24daca77b94e6643e548f.png

构建成功。


不使用Compose

有compose不用就有点类似于有idea不用,偏偏要用记事本写代码

一、mysql容器实例

docker run -p 3306:3306 --name mysql57 --privileged=true -v /jardata/mysql/conf:/etc/mysql/conf.d -v /jardata/mysql/logs:/logs -v /jardata/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=你的密码 -d mysql:5.7

老规矩,别忘了ps看一眼 

0f8e8f13fe674caa9cc6cb023ba8f458.png

然后咱们进入容器看看:

docker exec -it mysql57 bash

mysql -uroot -p

8e15af97a7a54b67a48955be8a97800a.png

然后我们需要开始建库建表,把之前的东西给扔进去:

create database dbtest;

use dbtest;

接着将之前的那一大段sql复制进去:

CREATE TABLE `t_user` (

  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,

  `username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',

  `password` varchar(50) NOT NULL DEFAULT '' COMMENT '密码',

  `sex` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',

  `deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',

  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表'

27607a01e2dc4975be83440544207ca1.png

表结构创建成功 


二、redis容器实例

docker run  -p 6379:6379 --name redis608 --privileged=true -v /app/redis/redis.conf:/etc/redis/redis.conf -v /app/redis/data:/data -d redis:6.0.8 redis-server /etc/redis/redis.conf

 ps看一眼:

9e6e66f25b2941a7abc248158b848cdc.png

进入容器:

docker exec -it redis608 bash

3076c48e7bca4a7099979415de72b12d.png

这时候的mysql和redis都还是空的,而且都已经创建成功


三、微服务

注意自己的实际ID

docker run -d -p 6001:6001 965861d1a066

7402b5836a48428893edb8738986246b.png

到了这里才算是把这三个玩意儿都起起来,但是这只是三个而已。实际的生产环境里有很大的概率会是几十个,要是这样一个一个起真的有点折寿


swagger测试

一、验证写操作

这里要注意自己的真实IP:

http://192.168.150.30:6001/swagger-ui.html#/user-controller

13347185a0c645cc8d2450b75a0320a1.png

点击右侧的Try it now ,然后点击弹出来的Execute

a6e41b512c084364bf912280174e8bb9.png

6dc4391537e145d1a6bd559c179d9d6e.png

 这里是发送了一个post请求

95efef851f924e4b8e419be4f808490d.png

然后咱们返回到mysql查询一下看看:

52a22025a7fa4a9a8fd4417da0d8916b.png

接着去redis看看:

dfdca8c1a07e41ec895d1dbe0fa295f9.png

这里的写操作已经验证成功。 


二、验证读操作

点击get里面的Try it now

7454580b57294ab1a05a1b97567211f0.png

我们这里查找一下1号记录,然后点击Execute 

91277f21046847f3914d40f817fa32a8.png

往下看结果

f6d2c6467ede432d8bcd7ec297b254c0.png

全部查询成功,到这里读操作也完成了。 


存在的问题

咱们这里没有使用compose也成功达到了目的,但是存在着以下问题:

先后顺序要求固定,先mysql+redis才能启动微服务

过多的run 

容器间的启停或宕机,有可能导致IP地址对应容器实例变化,映射出错,这时候就只能写死IP,要么通过服务调用 


使用Compose

本篇的重点到这里才刚刚开始

一、编写docker-compose.yml文件

在之前上传jar包的目录下创建yml文件

vim docker-compose.yml

ceac02f8d5254f659d50a7534f3bb9c6.png

将以下内容复制进去: 

version: "3"



services:
#微服务

  microService:

    image: van_docker:2.0
    #镜像

    container_name: ms01
    #容器名称

    ports:
    #端口映射

      - "6001:6001"

    volumes:
    #容器数据卷

      - /app/microService:/data

    networks:
    #网络模式 

      - van_net 

    depends_on: 
    #依赖关系

      - redis

      - mysql

 

  redis:
  #redis

    image: redis:6.0.8
    #镜像

    ports:
    #端口映射

      - "6379:6379"

    volumes:
    #容器数据卷

      - /app/redis/redis.conf:/etc/redis/redis.conf

      - /app/redis/data:/data

    networks: 
    #网络模式

      - van_net

    command: redis-server /etc/redis/redis.conf

 

  mysql:
  #mysql

    image: mysql:5.7
    #镜像

    environment:
    #环境

      MYSQL_ROOT_PASSWORD: 'zxcvbn0131'

      MYSQL_ALLOW_EMPTY_PASSWORD: 'no'

      MYSQL_DATABASE: 'dbtest'

      MYSQL_USER: 'van'

      MYSQL_PASSWORD: 'zxcvbn0131'

    ports:
    #端口映射

       - "3306:3306"

    volumes:
    #容器数据卷

       - /app/mysql/db:/var/lib/mysql

       - /app/mysql/conf/my.cnf:/etc/my.cnf

       - /app/mysql/init:/docker-entrypoint-initdb.d

    networks:
    #网络模式

      - van_net

    command: --default-authentication-plugin=mysql_native_password #解决外部无法访问

 

networks: 
#网络模式

   van_net: 

二、修改微服务项目

1.修改yml

复制以下内容,并覆盖粘贴(这里只是把原先的IP地址改成了服务名):

server.port=6001

# ========================alibaba.druid相关配置=====================
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://mysql:3306/dbtest?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=自己的密码
spring.datasource.druid.test-while-idle=false

# ========================redis相关配置=====================
spring.redis.database=0
spring.redis.host=redis
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0

# ========================mybatis相关配置===================
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=docker.entities

# ========================swagger=====================
spring.swagger2.enabled=true

接着就跟之前一样,打包jar包,然后上传,替换掉原先的jar包。

Dockerfile这里就不用进行修改了,然后重建一个镜像出来:

docker build -t van_docker:2.0 .

37ab4caaf8fd4eab84cf0b93fead5943.png

然后把之前的三个容器直接停掉:

docker rm -f 1e071bfa04a3 c4855d7f87fd 426eac422e84

af08c4eaec2c4841b45f3de2dcc7a2ff.png

 把之前构建的的1.6版本也给删了:

docker rmi -f 965861d1a066

fad60ac5f50a4888aa8ada464d83d5bf.png

接着要开始使用之前提到的常用命令了(建议在yml文件的目录下执行):

运行一个看看有没有问题:

docker-compose config -q

82c092717de24e808dc886a59acc51d5.png

 只要没有其它的任何提示,就代表操作没有问题。接下来开始正式启动:

docker-compose up -d

 效果如下( 这里如果报错请看文章结尾 ):

40c7c4bf07124a44947e787fa820e43f.png

我们可以顺便去看一眼docker的网络:

ec5e5d19dfc140ed8c78d23c54773806.png

可以看到它在我们刚刚创建的(写在了docker-compose.yml里面)van_net网络模式前面加上了前缀。ps看一眼:

350e5c4c0d4a4451af8267c8ee7eba65.png

进入mysql容器看一眼,然后开始建库建表:

docker exec -it 0fd561357e08 bash
create database dbtest;
use dbtest;

sql:

CREATE TABLE `t_user` (

  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,

  `username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',

  `password` varchar(50) NOT NULL DEFAULT '' COMMENT '密码',

  `sex` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',

  `deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',

  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表'

737d619c6cba41c79b89912666ee8ba7.png

重建一次该表,再进去redis看一眼:

docker exec -it 04a23c342f44 bash

redis-cli

keys *

1b82919f16714a88b810693a42100c56.png

可以看到现在咱们的mysql和redis都是空的,开始重新进行测试。


三、再次进行swagger测试

这里需要注意自己的真实IP,再次访问进行测试:

http://192.168.150.30:6001/swagger-ui.html#/user-controller

写入数据

ea6db65731b049209966281d38cc5ca9.png

0fb76fc18ee64fd1a3efe768bcd99379.png

成功拿到数据,数据写入成功。

88ca36d5335b43d89bc95537da68a9a1.png

成功读取。 


四、关停

docker-compose stop

0645ebd9302844ecac760fc4aacc72db.png


五、报错

Creating jardocker_mysql_1 ... error

ERROR: for mysql  Cannot start service mysql: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/app/mysql/conf/my.cnf" to rootfs at "/etc/my.cnf": mount /app/mysql/conf/my.cnf:/etc/my.cnf (via /proc/self/fd/6), flags: 0x5000: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
ERROR: Encountered errors while bringing up the project.
 

 docker-compose up 的过程中出现了以下报错:

这里的原因是因为创建的 my.cnf 的时候应该是个文件,而这里却是目录,去改一下即可,前往以下目录:

把它删了,然后创建一个同名的文件,然后再次启动 docker-compose up

启动成功问题解决。

案例演示结束。

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会调制解调的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值