使用Docker部署能够给我们带来方便、快捷的体验

作者: 操作系统  发布:2019-11-05

一、前言

    之前我们公司部署服务,就是大家都懂的那一套(安装JDK、Tomcat —> 编译好文件或者打war包上传 —> 启动Tomcat),这种部署方式一直持续了很久,带来的问题也很多:

1、繁重的发布任务。微服务一多,就要每个服务都要重启一遍,而且要是集群的话,那要启动的服务就更多了。

2、环境迁移报错。经常发生的一件事,同样的一套代码,这台服务器上就是能跑起来,换个服务器就是报错了。

3、士气低落。小公司没有正经的运维,都是让开发兼并着做这方面的工作,然后负责这块的同事怨言很多(因为这种发布部署实在太无趣了)。

    所以领导决定引起 Docker 作为我们的部署方式,一来可以很好的解决目前项目部署存在的问题,二来为项目注入新鲜血液。

    从上个月15号开始接触 Docker,到现在把我们系统的微服务架构初步搭建好,折腾了好久,踩了很多坑。纪念一下小成就,写了这篇博客。为了避免涉嫌泄露公司机密,就小而全的做一些简单介绍哈,以下面这张最小微服务架构图为例,部署一套 Dubbo 微服务。

9159金沙官网 1

热部署

pom.xml文件中添加spring-boot-devtools依赖即可实现页面和代码的热部署。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

9159金沙官网 2

一、应用场景

在做项目的过程中,服务器端的部署是一个非常重要的场景,当客户的服务器是Linux/Unix系统时,用Docker容器来部署最方便不过了,Windows场景下Docker的支持也比较完善了,使用Docker部署能够给我们带来方便、快捷的体验。


二、服务镜像打包

常规部署

如今前端工程越来越复杂,打造一个好用的工作流也显得越来越重要。
本文讲分成二个部分,通过简单例子,来介绍上图中那条鱼和狐狸 :)

二、应用项目综述

本文中项目的开发环境是基于spring+springMVC+mybatis的maven项目。开发的IDE是IntelliJ IDEA 2017.1.4,服务器是使用Tomcat 9.0.0.M21,数据库使用MySQL 5.7。最后部署测试环境是MacOS Sierra 10.12.3


     1、Tomcat 基础环境搭建

    我们系统的每个微服务都部署运行在 Tomcat 上(听说这种方式很不好,对于一些不是web工程的,没必要搭建成 web 服务,增加复杂性,也浪费系统资源),所以我的想法是:先搭建一套 Tomcat 环境镜像,然后每个微服务都基于这个环境镜像去构建。所以写了一个 tomcat-env 的镜像,思路如下:

    -- 基于 JDK 的 Tomcat 容器(主要参考官网 Tomcat 镜像的 Dockerfile)。

    -- 在上下文目录存放项目编译文件,并重命名为 ROOT(不放 war 包的原因是考虑调试的时候方便,不用改一个文件,就打个war包)。

    -- 删除原本 Tomcat 容器 webapps 目录下的 ROOT 文件,并将上下文目录中项目的 ROOT 文件夹上传到容器 webapps 目录下。

    -- 启动服务。

9159金沙官网 39159金沙官网 4

FROM openjdk:8-jre

ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
RUN mkdir -p "$CATALINA_HOME"
WORKDIR $CATALINA_HOME

# let "Tomcat Native" live somewhere isolated
ENV TOMCAT_NATIVE_LIBDIR $CATALINA_HOME/native-jni-lib
ENV LD_LIBRARY_PATH ${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOMCAT_NATIVE_LIBDIR

# runtime dependencies for Tomcat Native Libraries
# Tomcat Native 1.2+ requires a newer version of OpenSSL than debian:jessie has available
# > checking OpenSSL library version >= 1.0.2...
# > configure: error: Your version of OpenSSL is not compatible with this version of tcnative
# see http://tomcat.10.x6.nabble.com/VOTE-Release-Apache-Tomcat-8-0-32-tp5046007p5046024.html (and following discussion)
# and https://github.com/docker-library/tomcat/pull/31
ENV OPENSSL_VERSION 1.1.0f-3+deb9u2
RUN set -ex; 
    currentVersion="$(dpkg-query --show --showformat '${Version}n' openssl)"; 
    if dpkg --compare-versions "$currentVersion" '<<' "$OPENSSL_VERSION"; then 
        if ! grep -q stretch /etc/apt/sources.list; then 
# only add stretch if we're not already building from within stretch
            { 
                echo 'deb http://deb.debian.org/debian stretch main'; 
                echo 'deb http://security.debian.org stretch/updates main'; 
                echo 'deb http://deb.debian.org/debian stretch-updates main'; 
            } > /etc/apt/sources.list.d/stretch.list; 
            { 
# add a negative "Pin-Priority" so that we never ever get packages from stretch unless we explicitly request them
                echo 'Package: *'; 
                echo 'Pin: release n=stretch*'; 
                echo 'Pin-Priority: -10'; 
                echo; 
# ... except OpenSSL, which is the reason we're here
                echo 'Package: openssl libssl*'; 
                echo "Pin: version $OPENSSL_VERSION"; 
                echo 'Pin-Priority: 990'; 
            } > /etc/apt/preferences.d/stretch-openssl; 
        fi; 
        apt-get update; 
        apt-get install -y --no-install-recommends openssl="$OPENSSL_VERSION"; 
        rm -rf /var/lib/apt/lists/*; 
    fi

RUN apt-get update && apt-get install -y --no-install-recommends 
        libapr1 
    && rm -rf /var/lib/apt/lists/*

# see https://www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/KEYS
# see also "update.sh" (https://github.com/docker-library/tomcat/blob/master/update.sh)
ENV GPG_KEYS 05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23

ENV TOMCAT_MAJOR 8
ENV TOMCAT_VERSION 8.0.53
ENV TOMCAT_SHA512 cd8a4e48a629a2f2bb4ce6b101ebcce41da52b506064396ec1b2915c0b0d8d82123091242f2929a649bcd8b65ecf6cd1ab9c7d90ac0e261821097ab6fbe22df9

ENV TOMCAT_TGZ_URLS 
# https://issues.apache.org/jira/browse/INFRA-8753?focusedCommentId=14735394#comment-14735394
    https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz 
# if the version is outdated, we might have to pull from the dist/archive :/
    https://www-us.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz 
    https://www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz 
    https://archive.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz

ENV TOMCAT_ASC_URLS 
    https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc 
# not all the mirrors actually carry the .asc files :'(
    https://www-us.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc 
    https://www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc 
    https://archive.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc

RUN set -eux; 
    
    savedAptMark="$(apt-mark showmanual)"; 
    apt-get update; 
    
    apt-get install -y --no-install-recommends gnupg dirmngr; 
    
    export GNUPGHOME="$(mktemp -d)"; 
    for key in $GPG_KEYS; do 
        gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; 
    done; 
    
    apt-get install -y --no-install-recommends wget ca-certificates; 
    
    success=; 
    for url in $TOMCAT_TGZ_URLS; do 
        if wget -O tomcat.tar.gz "$url"; then 
            success=1; 
            break; 
        fi; 
    done; 
    [ -n "$success" ]; 
    
    echo "$TOMCAT_SHA512 *tomcat.tar.gz" | sha512sum -c -; 
    
    success=; 
    for url in $TOMCAT_ASC_URLS; do 
        if wget -O tomcat.tar.gz.asc "$url"; then 
            success=1; 
            break; 
        fi; 
    done; 
    [ -n "$success" ]; 
    
    gpg --batch --verify tomcat.tar.gz.asc tomcat.tar.gz; 
    tar -xvf tomcat.tar.gz --strip-components=1; 
    rm bin/*.bat; 
    rm tomcat.tar.gz*; 
    command -v gpgconf && gpgconf --kill all || :; 
    rm -rf "$GNUPGHOME"; 
    
    nativeBuildDir="$(mktemp -d)"; 
    tar -xvf bin/tomcat-native.tar.gz -C "$nativeBuildDir" --strip-components=1; 
    apt-get install -y --no-install-recommends 
        dpkg-dev 
        gcc 
        libapr1-dev 
        libssl-dev 
        make 
        "openjdk-${JAVA_VERSION%%[.~bu-]*}-jdk=$JAVA_DEBIAN_VERSION" 
    ; 
    ( 
        export CATALINA_HOME="$PWD"; 
        cd "$nativeBuildDir/native"; 
        gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; 
        ./configure 
            --build="$gnuArch" 
            --libdir="$TOMCAT_NATIVE_LIBDIR" 
            --prefix="$CATALINA_HOME" 
            --with-apr="$(which apr-1-config)" 
            --with-java-home="$(docker-java-home)" 
            --with-ssl=yes; 
        make -j "$(nproc)"; 
        make install; 
    ); 
    rm -rf "$nativeBuildDir"; 
    rm bin/tomcat-native.tar.gz; 
    
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
    apt-mark auto '.*' > /dev/null; 
    [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; 
    apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; 
    rm -rf /var/lib/apt/lists/*; 
    
# sh removes env vars it doesn't support (ones with periods)
# https://github.com/docker-library/tomcat/issues/77
    find ./bin/ -name '*.sh' -exec sed -ri 's|^#!/bin/sh$|#!/usr/bin/env bash|' '{}' +

# verify Tomcat Native is working properly
RUN set -e 
    && nativeLines="$(catalina.sh configtest 2>&1)" 
    && nativeLines="$(echo "$nativeLines" | grep 'Apache Tomcat Native')" 
    && nativeLines="$(echo "$nativeLines" | sort -u)" 
    && if ! echo "$nativeLines" | grep 'INFO: Loaded APR based Apache Tomcat Native library' >&2; then 
        echo >&2 "$nativeLines"; 
        exit 1; 
    fi

EXPOSE 8080
RUN rm -rf /usr/local/tomcat/webapps/ROOT/
ONBUILD COPY ROOT /usr/local/tomcat/webapps/ROOT/
ONBUILD ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]

tomcat-env

看起来很复杂,不要被吓到,其实都是抄的官网 Tomcat 镜像的Dockerfile,然后改动了一点,主要是后面三句:删除容器 ROOT 文件夹,拷贝上下文目录的 ROOT 文件夹到 wenapps 目录下,重启服务。

RUN rm -rf /usr/local/tomcat/webapps/ROOT/
ONBUILD COPY ROOT /usr/local/tomcat/webapps/ROOT/
ONBUILD ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]

tips:1、ONBUILD 命令本次镜像不会被执行,只有以这个镜像为基础镜像的时候才会被执行。

          2、上下文目录指的是 Dockerfile 文件所在的目录。

          3、该镜像已上传到 DockerHub 上:https://hub.docker.com/r/jmcui/tomcat-env/

jar

  • 打包
mvn package
  • 运行
java -jar xxx.jar
  • war 转 jar

    • pom.xml文件中将<packaging>war</packaging>改为<packaging>jar</packaging>
    • 去掉ServletInitializer类
    • 去掉如下依赖,恢复默认内嵌Tomcat依赖

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
          <scope>provided</scope>
      </dependency>
      
  • 注册Linux服务

    • 修改spring-boot-maven-plugin配置

      <build>
          <plugins>
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
                  <configuration>
                      <executable>true</executable>
                  </configuration>
              <plugin>
          </plugins>
      </build>
      
    • 使用mvn package打包

    • 使用init.d或systemd注册服务

      • init.d部署
        注册服务

        sudo ln -s /var/apps/xxx.jar /etc/init.d/xxx
        

        启动服务

        service xxx start
        

        停止服务

        service xxx stop
        

        服务状态

        service xxx status
        

        开机启动

        chkconfig xxx on
        

        日志存放在/var/log/xxx.log。

      • systemd部署
        注册服务
        在/etc/systemd/system/目录下新建文件xxx.service,xxx.service内容如下:

        [Unit]
        Description=xxx
        After=syslog.target
        
        [Service]
        ExecStart= /usr/bin/java -jar /var/apps/xxx.jar
        
        [Install]
        WantedBy=multi-user.target
        

        启动服务

        systemctl start xxx
        
        systemctl start xxx.service
        

        停止服务

        systemctl stop xxx
        
        systemctl stop xxx.service
        

        服务状态

        systemctl status xxx
        
        systemctl status xxx.service
        

        9159金沙官网 ,开机启动

        systemctl enable xxx
        
        systemctl enable xxx.service
        

        项目日志

        journalctl -u xxx
        
        journalctl -u xxx.service
        

part1 : docker 本文的项目代码
part2 : gitlab-ci

三、安装Docker

工欲善其事,必先利其器。首先我们应该在开发机器上安装Docker。关于Docker的安装,这里就不详细叙述了,官网的步骤都很详细,给大家搬运一下网址:

1、Docker官网:

2、Mac安装方式:

3、Linux安装方式:

4、Windows安装方式:


     2、微服务镜像打包

    有了基础环境镜像 tomcat-env,那么打包一个服务镜像就是一件再简单不过的事情了:

9159金沙官网 5

FROM tomcat-env:1.0

    没错,就是这么简单,因为我们把所有的工作都放在 tomcat-env 中了,其实就是那个 ONBUILD 命令的效果啦~~ 

war

  • 打包
mvn package
  • 运行
    将war包丢到支持war文件的Servlet容器执行。
  • jar 转 war

    • pom.xml文件中将<packaging>jar</packaging>改为<packaging>war</packaging>
    • 增加ServletInitializer类

      import org.springframework.boot.builder.SpringApplicationBuilder;
      import org.springframework.boot.context.web.SpringBootServletInitializer;
      public class ServletInitializer extends SpringBootServletInitializer {
          @Override
          protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
              return application.source(XxxApplication.class)
          }
      }
      
    • 增加如下依赖,覆盖默认内嵌Tomcat依赖

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
          <scope>provided</scope>
      </dependency>
      

ok, 那么现在就开始第一部分

四、部署正式开始

好了,做好了前三步准备工作,我们假定项目已经开发完毕,Docker也正确安装在了你的测试机器上,你也学习了一些Docker的操作命令,不是一个完全的小白了,我们就可以开始部署应用了。

三、编排文件 docker-compose.yml

    微服务项目要部署起来,主要是靠 docker-compose.yml 文件进行编排,规定服务之间的关联以及先后启动顺序,然后把几十个零散的微服务当成一个整体来统一管理。

    首先,困扰我的是网络问题。做过开发的都知道,要在项目中指定(Spring 在 applicationContext.xml)数据库地址和 Zookeeper 地址,那么我怎么知道容器的 ip 地址是多少呢?先来了解下 Docker 的网络模式?

    Docker 的默认网络配置是 "bridge",当 Docker 启动时,会自动在主机上创建一个 docker0 虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机。Docker 会随机分配一个本地未占用的私有网段(在 RFC1918 中定义)中的一个地址给 docker0 接口,它会在挂载到它的网口之间进行转发。当创建一个 Docker 容器的时候,同时会创建了一对 veth pair 接口。这对接口一端在容器内,即 eth0;另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头(例如 vethAQI2QT)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。

     也就是说,每次容器启动以后的 ip 地址是不固定的,这该怎么办呢?当然可以写死 IP 地址,规定局域网网段,给每个服务编排 IP 地址;当然也可以把network_mode="host",统一用宿主机的网络地址。当然!这些都不是最好的办法:

version: '3.7'
#服务列表
services:
  #基础组件 zookeeper  
  zookeeper:
    image: zookeeper
    restart: always
    ports:
      - 4181:2181
  #基础组件 MySQL
  db:
    image: mysql:5.7.17
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --init-connect='SET NAMES utf8mb4;'
    ports:
     - "3636:3306"
    volumes:
     - /var/mysqldb:/var/lib/mysql
     - /docker/mysql/my.cnf:/etc/mysql/mysql.conf.d/mysqld.cnf
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: password
  #消费者服务1 admin
  admin:
    image: "admin:2.3.1"
    ports:
     - "7575:8080"
    depends_on:
     - zookeeper
    restart: always
    environment:
      zookeeper.host: zookeeper://zookeeper:2181
  #提供者服务1 system
  system:
    image: "system:2.3.1"
    depends_on:
     - db
     - zookeeper
    restart: always
    environment:
      zookeeper.host: zookeeper://zookeeper:2181
      mysql.address: db:3306

    看到了吗?IP 地址直接由 服务名 指定就可以了。另外, Docker 中设置的环境变量,竟然能被 applicationContext.xml 中读取,我也是蛮诧异的!(在代码和 Docker 中都配置了mysql.address 的话,以 Docker 中设置的生效)。

    然后 docker-compose up -d 启动微服务项目就可以了~~

    容器部署的一个原则:尽量不要在容器内部做文件的修改,要修改的内容用数据卷的方式映射到宿主机上,比如上面的MySQL配置文件和数据仓库。

9159金沙官网 6

    在 Docker 上部署 MySQL 遇到了几个问题,简单罗列下:

1、Navicat 连接的时候: Client does not support authentication protocol requested by server ?

解决:进入 MySQL 容器,运行

ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password';

2、Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggre 的问题?

原因:MySQL 5.7.5及以上功能依赖检测功能。如果启用了ONLY_FULL_GROUP_BY SQL模式(默认情况下),MySQL将拒绝选择列表,HAVING条件或ORDER BY列表的查询引用在GROUP BY子句中既未命名的非集合列,也不在功能上依赖于它们。

解决:在MySQL的配置文件中加上:

sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

3、MySQL 连接参数useSSL=true 和 useSSL=false 的区别?

    建议不要在没有服务器身份验证的情况下建立SSL连接(同一个 Docker-compose 中是内网环境)。根据 MySQL 5.5.45 +,5.6.26 +和5.7.6+ 要求如果未设置显式选项,则必须默认建立SSL连接。为了符合不使用SSL的现有应用程序。您需要通过设置useSSL = false显式禁用SSL,或者设置useSSL = true并为服务器证书验证提供信任库。

云部署

一、什么是 Docker ?

Docker是一个基于轻量级虚拟化技术的容器引擎开源项目,可以轻松的为任何应用创建一个容器
具体做的就是快速的帮助开发者搭建应用周期里所需的各种环境,快速地部署项目以缩短开发周期

1、创建MySQL容器

四、结语

    总算是把一个微服务项目部署运行起来了,几乎是用了最少的 Docker-compose 模板文件,所以还是有很多地方可以完善的,比如说 MySQL 密码没有加密处理、服务没有做健康检查、集群方面还没怎么考虑(用 Docker Swarm 实现)等等......路漫漫其修远兮,吾将上下而求索。共勉!

Docker

使用Dockerfile编译Docker镜像。

docker 具备以下几个优势:

1.配置简单
2.可移植
3.独立自给自足
4.轻量级

下载MySQL镜像

启动Docker的情况下,在终端中输入 docker search mysql命令可以搜索到各式各样的MySQL镜像,我们选择star最多的MySQL官方镜像。

然后输入:docker pull mysql命令,将这个镜像拉到本地(有可能会比较慢,可以使用阿里云加速或者fanqiang)。 

拉取完成后,在本地输入:docker images 查看镜像可以看到如下图中所示,说明你的镜像已经拉取成功。

9159金沙官网 7

Dockerfile指令

  • FROM
    指明当前镜像继承的基镜像,编译当前镜像时会自动下载基镜像。

    FROM java:8
    
  • MAINTAINER
    指明当前镜像的作者。

    MAINTAINER linliangsheng
    
  • RUN
    当前镜像上执行Linux命令并形成一个新的层,编译时(build)动作。

    RUN /bin/bash -c "echo hello"
    
    RUN ["/bin/bash", "-c", "echo hello"]
    
  • CMD
    启动镜像容器时的默认行为,一个Dockerfile只能有一个CMD指令,可在运行镜像时使用参数覆盖,运行时(run)动作。

    CMD echo "hello"
    

    参数覆盖写法:

    docker run -d image_name echo "docker-hello"
    
  • EXPOSE
    指明镜像运行时的容器必需监听指定的端口。

    EXPOSE 8080
    
  • ENV
    设置环境变量。

    ENV name=linliangsheng
    
    ENV name linliangsheng
    
  • ADD
    从当前工作目录复制文件到镜像目录。

    ADD xxx.jar app.jar
    
  • ENTRYPOINT
    让容器像可执行程序一样运行,镜像运行时可接收参数,运行时(run)动作。

    ENTRYPOINT ["java"]
    

    参数接收写法:

    docker run -d image_name "-jar app.jar"
    
Docker 的应用场景

1.web应用工作流中的各种环境快速搭建

  1. 自动化测试和持续集成、发布

创建MySQL容器

启动容器:MySQL可以直接把端口地址映射到宿主机上,但是在生产环境中映射宿主主机有可能会造成端口冲突等一系列问题,所以我们用容器互联的方法,让MySQL的端口只暴露给Tomcat服务器,命令如下:

docker run --name health-tomcat  -v /home/mysql_data:/var/lib/mysql --restart=always -e MYSQL_ROOT_PASSWORD=123456 -d <IMAGE-ID>

    -v:容器的/var/lib/mysql目录挂载在主机的/home/mysql_data目录

    -e 设置默认参数,支持参数:

    • MYSQL_ROOT_PASSWORD

    • MYSQL_DATABASE

    • MYSQL_USER, MYSQL_PASSWORD

    • MYSQL_ALLOW_EMPTY_PASSWORD

    • MYSQL_RANDOM_ROOT_PASSWORD

    • MYSQL_ONETIME_PASSWORD

我们用MYSQL_ROOT_PASSWORD=123456是给默认的root用户设置密码为123456.

<IMAGE-ID>为你的镜像的名字,这里为mysql。

进入容器:

使用命令 docker exec -it health-mysql mysql -uroot -p进入MySQL容器,在Enter password:输入你刚刚设置的密码。进入之后的操作跟你在自己电脑上安装MySQL后的操作一样。

导入宿主机.sql文件到MySQL容器的数据库中:

先查看MySQL运行名称:docker ps, NAMES栏底下为名称

进入刚刚创建的容器:docker exec -it health-mysql mysql -uroot -p

创建一个数据库:create database health_data

退出MySQL环境

将宿主机文件导入:MySQL容器名称为:health-mysql,  容器中数据库名称为:health_data

执行:docker exec -i health-mysql mysql -u root -p123456 health_data < /Users/jacob/Desktop/health_data.sql

Docker安装

  • 安装Docker

    yum install docker
    
  • 启动Docker

    systemctl start docker
    
  • 开机自启动

    systemctl enable docker
    

二、安装 docker

2、创建Tomcat容器并且用--link连接到MySQL容器

1、下载镜像:docker pull tomcat

2、创建一个本地目录:/Users/jacob/Desktop/tomcat_data,把项目的war包放到这个本机文件夹。需要注意的是,在容器互联的时候需要为MySQL容器创建一个别名,在项目的jdbc.properties中jdbc.url=jdbc:mysql://tomysql:3306/guotai?useUnicode=true&characterEncoding=utf-8,3306端口前的tomysql就是通过数据库别名,这样才能访问项目,他相当于localhost/你的IP地址。所以在导出war包之前,先想好你的数据库别名,然后把3306前面的名称和你的数据库别名设置成一致然后导出(很关键!!!)

3、创建Tomcat容器并连接到MySQL

直接线上命令,然后解释

docker run -d -p 8888:8080 -v /Users/jacob/Desktop/tomcat_data:/usr/Downloads --name health-tomcat --link health-mysql:tomysql tomcat

1、-p后面是把Tomcat的8080端口映射到8888端口,部署后用8888来访问。

2、-v /Users/jacob/Desktop/tomcat_data:/usr/Downloads 把/usr/Downloads目录挂载到你的房war包的本地目录,为一会拷贝war包到Tomcat容器做准备。

3、--name health-tomcat 为你的Tomcat容器起一个名字

4、--link health-mysql:tomysql 这步就是实现互联,health-mysql就是你一开始第一步创建的MySQL容器的名称,冒号后面就是MySQL容器在Tomcat容器中的别名,这个别名要和你项目的jdbcurl 3306端口前的名称一致,这样才可以建立数据库连接(谨记)

5、tomcat 这个就是你的镜像名称

执行完容器创建后,我们需要把war包从本机拷贝到Tomcat服务器中。

首先:执行以下命令进入容器:docker exec -it health-tomcat /bin/bash

进入容器后再执行以下命令将war包复制到tomcat容器目录下:cp /usr/Downloads/Demo.war /usr/local/tomcat/webapps/

/usr/Downloads/Demo.war是你刚刚挂在的本地存放war报的目录

/usr/local/tomcat/webapps/是Tomcat容器中你要把war包拷贝到这里

此时,准备工作已经全部结束,享受成果吧!

在浏览器输入URL:

9159金沙官网 8

8888是你刚刚在创建容器时给8080端口映射的端口值,Demo是你的war包的名字,在访问你的API的时候要加上war包名字这个路径。


好了,这是我部署的一些经验。每个项目具体的差异会造成操作步骤和命令上的差异,才疏学浅,错误在所难免,希望大家批评指正!

Docker实践

首先下载安装包

这里以 mac 为例子

Mac 客户端:https://store.docker.com/editions/community/docker-ce-desktop-mac
其他版本:https://www.docker.com/get-docker

9159金沙官网 9

这时候打开终端,就可以使用docker命令了。

9159金沙官网 10

Spring Boot实践

在xxx.jar包同级目录建立Dockerfile文件,Dockerfile文件内容如下:

FROM java:8
MAINTAINER linliangsheng
ADD xxx.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

编译镜像:

docker build -t wisely/xxx .

运行镜像:

docker run -d --name xxx -p 8080:8080 wisely/xxx
性能和网络设置

安装后,最好进行一些基本的性能和网络设置(在 docker app的设置菜单里,快捷键 commond + ,)

9159金沙官网 11

设置镜像加速(很重要),这里利用阿里云的加速 https://cr.console.aliyun.com/#/accelerator

9159金沙官网 12

Spring Cloud实践

  1. 编写runboot.sh脚本
    位于src/main/docker下:

    sleep 10
    java -Djava.security.egd=file:/dev/./urandom -jar /app/app.jar
    

    根据启动顺序调整sleep时间

  2. 编写Dockerfile
    位于src/main/docker下:

    FROM java:8
    VOLUME /tmp
    RUN mkdir /app
    ADD xxx.jar /app/app.jar
    ADD runboot.sh /app/
    RUN bash -c 'touch /app/app.jar'
    WORKDIR /app
    RUN chmod a+x runboot.sh
    EXPOSE 8888
    CMD /app/runboot.sh
    

    不同的微服务只需修改

    ADD xxx.jar /app/app.jar
    EXPOSE 8888
    
  3. Docker插件
    在pom.xml中增加docker-maven-plugin插件

    <build>
        <plugins>
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <configuration>
                    <imageName>${project.name}:${project.version}</imageName>
                    <dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
                    <skipDockerBuild>false</skipDockerBuild>
                    <resources>
                        <resource>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
  4. 编译镜像
    docker-maven-plugin默认将Docker编译到localhost,如果是远程Linux服务器,需要配置环境变量DOCKER_HOST,变量值tcp://IP地址:端口号
    执行mvn命令进行镜像编译:

    mvn clean package docker:build -DskipTests
    
  5. Docker Compose
    Docker Compose是用来定义和运行多容器应用的工具,使用一个docker-compose.yml来描述多容器定义,使用docker-compose up -d运行整个应用,-d表示后台运行。
    docker-compose.yml参考内容:

    postgresdb:
      image: busybox
      volumes:
        - /var/lib/postgresql/data
    postgres:
      name: postgres
      image: postgres
      hostname: postgres
      volumes_from:
        - postgresdb
      environment:
        - POSTGRES_USER=postgres
        - POSTGRES_PASSWORD=postgres
    discovery:
      name: discovery
      image: "discovery:1.0.0-SNAPSHOT"
      hostname: discovery
      ports:
        - "8761:8761"
    config:
      name: config
      image: "config:1.0.0-SNAPSHOT"
      hostname: config
      links:
        - discovery
      environment:
        - EUREKA_HOST: discovery
        - EUREKA_PORT: 8761
      ports:
        - "8888:8888"
    person:
      name: person
      image: "person:1.0.0-SNAPSHOT"
      hostname: person
      links:
        - discovery
        - config
        - postgres
      environment:
        - EUREKA_HOST: discovery
        - EUREKA_PORT: 8761
        - SPRING_PROFILES_ACTIVE: docker
      ports:
        - "8082:8082"
    some:
      name: some
      image: "some:1.0.0-SNAPSHOT"
      hostname: some
      links:
        - discovery
        - config
      environment:
        - EUREKA_HOST: discovery
        - EUREKA_PORT: 8761
        - SPRING_PROFILES_ACTIVE: docker
      ports:
        - "8083:8083"
    ui:
      name: ui
      image: "ui:1.0.0-SNAPSHOT"
      hostname: ui
      links:
        - discovery
        - config
        - person
        - some
      environment:
        - EUREKA_HOST: discovery
        - EUREKA_PORT: 8761
        - SPRING_PROFILES_ACTIVE: docker
      ports:
        - "80:80"
    monitor:
      name: monitor
      image: "monitor:1.0.0-SNAPSHOT"
      hostname: monitor
      links:
        - discovery
        - config
        - person
        - some
        - ui
      environment:
        - EUREKA_HOST: discovery
        - EUREKA_PORT: 8761
        - SPRING_PROFILES_ACTIVE: docker
      ports:
        - "8989:8989"
    

三、Docker 的几个核心概念

在使用 docker 之前,有必要先了解 docker 的几个核心概念。

FAQ

  1. 编译打包报错I/O exception (java.io.IOException) caught when processing request to {}->unix://localhost:80: Permission denied
    需要配置DOCKER_HOST环境变量,Ubuntu16.04LTS中环境变量配置是修改~/.bashrc文件,增加export DOCKER_HOST=tcp://127.0.0.1:5555,使配置的环境变量立即生效source ~/.bashrc。同时,需要修改Docker后台进程监听5555端口,编辑/etc/default/docker,增加DOCKER_OPTS="-H 0.0.0.0:5555",然后service docker restart重启Docker。
  2. 编译打包报错Exception caught: Get https://registry-1.docker.io/v2/: net/http: TLS handshake timeout
    多次编译打包就可以解决。
    Tip:
    查看本地镜像命令:docker images
    查看当前容器状态:docker ps
1.镜像 images

Images 镜像是 Docker 容器的模板文件,用来创建和运行Docker容器。

镜像可以从 Docker Hub 下载:
我们可以先用 docker search 命令来搜索 ubuntu 镜像

9159金沙官网 13

然后我们可以用 docker pull ubuntu 来获取那个 images

9159金沙官网 14

各个选项说明:

  • REPOSTITORY:表示镜像的仓库源
  • TAG:镜像的标签
  • IMAGE ID:镜像ID
  • CREATED:镜像创建时间
  • SIZE:镜像大小

参考资料

Java EE开发的颠覆者-Spring Boot实战.汪云飞.407-424,478-484

2.容器 container

Container 容器是 Docker 镜像的一个运行实例,一个实例相当于创建了一个独立的环境,我们可以在
里面运行操作系统、程序应用、修改文件数据等等。

当你用 docker run运行 images 的时候,就会创建对应的容器:

docker run -ti ubuntu:latest /bin/bash

9159金沙官网 15

-ti参数可以让容器保持终端交互 ( 退出在容器内输入 exit )
ubuntu:latest就是镜像名,/bin/bash 则是运行命令

想查看运行中的容器:docker ps

9159金沙官网 16

退出容器:docker stop 或者 docker kill 加上对应容器的ID (一般输入开头3~4个字母就行了)
重新启动容器: docker start加上对应容器的ID
更多容器相关命令,请查看官网文档,或者--help查看命令帮助

3.使用 Dockerfile 和 Docker-compose 定制镜像

Dockerfile 是一个文本格式的配置文件,用于快速方便的创建自定义镜像。
Docker-compose 则是用于组合多个镜像,创建一个模块化的容器集合。

Dockerfile 常用有以下指令选项:

- FROM
指定构建镜像的基础源镜像,如果本地没有指定的镜像,则会从 docker hub pull 镜像下来。

FROM ubuntu

- RUN
创建镜像过程中,用来执行命令,通常用于安装程序(RUN 会被缓存,可以使用docker build --no-cache 清除缓存)

RUN apt-get update && apt-get install git

- CMD
CMD可以让容器在启动时默认执行一个命令。如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令。

CMD ["/bin/bash","/etc/php.sh"]

- EXPOSE
容器对外映射的本地端口,需要在 docker run 的时候使用-p或者-P选项生效。

EXPOSE 8080

- ENV
设置环境变量

ENV <key> <value>       # 只能设置一个变量
ENV <key>=<value> ...   # 允许一次设置多个变量

- ADD
- COPY
ADD 和 COPY 都是本地主机文件、目录到容器指定路径中 。,不同的是,ADD可以复制远程文件 URL,并且支持 Go 的正则模糊匹配,具体规则可参见 Go filepath.Match

ADD hom* /mydir/        # adds all files starting with "hom"
ADD hom?.txt /mydir/    # ? is replaced with any single character
COPY <src>... <dest>

- VOLUME
本地目录到容器的映射

VOLUME ["/src","/www"]

- WORKDIR
初始执行命令的路径

WORKDIR /www/server
RUN pwd # 打印/www/server

以上配置 也可以在 Docker-compose 完成,只是关键字和值的写法不太一样,具体可以参考它们的文档:
Dockerfile: https://docs.docker.com/engine/reference/builder/
Docker-compose: https://docs.docker.com/compose/compose-file/

四、一个简单案例

使用 docker 为一个spa应用起一个开发环境+测试环境

简单说要做的是:

  1. 在 docker 里起一个 node 服务热加载项目源码。
  2. 用 docker 起一个 nginx 服务,代理项目编译后的 dist 目录。

在这,我用 vue-cli 初始化了一个项目,然后新建了个 docker-compose 文件夹(用来配置docker)如下图:

9159金沙官网 17

上图是源码目录,

另外,docker-compose 的目录结构如下

  • docker-compose
    • docker-compose.yml
    • nginx
      • Dockerfile
      • nginx.conf
      • sites-enabled
        • www.docker-test.com.conf
    • node
      • Dockerfile
      • start.sh

nginx 、node 文件夹下各有一份 Dockerfile 文件,可以创建两个 images 镜像,docker-compose.yml 则用于将两个镜像服务整合使用。

我们先看 node 文件夹下的
Dockerfile:

# docker-compose/php/Dockerfile

#基于 node 镜像
FROM node  

# 复制宿主机的 start.sh 到 容器 /etc/start.sh
ADD start.sh /etc/start.sh

# 设置初始命令执行目录
WORKDIR /www

# 通过 RUN 可以在容器里执行自定义命令
RUN node -v
RUN pwd

CMD ["/bin/bash","/etc/start.sh"]

start.sh:

#!/bin/bash

# 启动 php 服务
npm run dev 

再来看 nginx 下的
Dockerfile:

# docker-compose/nginx/Dockerfile

#基于 nginx 镜像
FROM nginx  

#基于 nginx 相关配置复制到容器
ADD nginx.conf /etc/nginx/nginx.conf
ADD sites-enabled/* /etc/nginx/conf.d/

#创建 nginx log 和用户相关路径
RUN mkdir /opt/htdocs && mkdir /opt/log && mkdir /opt/log/nginx
RUN chown -R www-data.www-data /opt/htdocs /opt/log

这样,两个服务的 Dockerfile 都创建完成了,但是我们还没暴露端口,也没配置 volumes 映射,这里我们可以在 docker-compose.yml 中设置:

nginx:
  build: ./nginx
  ports:
    - "80:80" #映射到本地的端口
  volumes: 
    - /Users/mr.yun/docker-test/docker-demo/dist:/www

node:
  build: ./node
  ports: 
    - "8085:8080"   #映射到本地的端口 本地访问8085,即访问容器内的8080
  volumes:
    - /Users/mr.yun/docker-test/docker-demo:/www

注意,上面代码中,volumes 的值,要根据你自己的实际项目目录来配置。

配置完以上变量后,cd 进入 docker-compose 文件目录

直接运行命令

# 启动容器集合,会同时创建两个image,并启动两个容器,也可以加 -d 在后台运行
# 运行后可以通过 docker images,docker ps查看生成的镜像和容器
docker-compose up --biuld

等待下载完成,并自动运行

然后在浏览器输入 127.0.0.1:8050 ,就能看到 vue项目,并且修改源码能热加载。

9159金沙官网 18

输入 127.0.0.1,则可以看到静态资源 hash 过的项目。(别忘了在本地先 npm run build)

9159金沙官网 19

哦了,以上就是 docker 的基本使用介绍,更多玩法和技巧,到实际项目中探索。
个人感觉在项目多、协作人数多的情况下,docker 还是很方便的。

本文由9159.com发布于操作系统,转载请注明出处:使用Docker部署能够给我们带来方便、快捷的体验

关键词: