1、使用Docker测试静态网站

Eg:将Nginx安装到容器来架构一个简单的网站Sample:

(1)先创建一个目录来保存Dockerfile:

~$ mkdir sample      //创建目录,存放Dockerfile文件
~$ cd sample      
~$ touch Dockerfile   //touch:修改指定文件的时间标签,把已存在的文件的时间标签更新为系统当前时间(默认方式),
                     //它们的数据原封不动的保存下来;若该文件尚未存在,则创建一个空新文件;

(2)配置Nginx:下载配置文件;

~$ cd sample   
~$ mkdir nginx && cd nginx  //创建nginx目录,存放nginx配置文件,并进入目录
~$ wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/nginx/global.conf     //获取nginx配置文件
~$ wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/nginx/nginx.conf
// 配置文件`global.conf`:
server {
        listen          0.0.0.0:80;    //将Nginx设置为监听80端口
        server_name     _;

        root            /var/www/html/website;   //设置网络服务的根路径(RUN指令创建的) 
        index           index.html index.htm;

        access_log      /var/log/nginx/default_access.log;
        error_log       /var/log/nginx/default_error.log;
}

// 配置文件nginx.conf
user www-data;
worker_processes 4;
pid /run/nginx.pid;
daemon off;     //阻止Nginx进入后台,强制其在前台运行;
                //这是因为要想保持Docker容器活跃状态,需要其中运行的进程不能中断;
                //默认情况下,Nginx会以守护进程的方式启动,这会导致容器只是短暂运行;
                //将Nginx配置为非守护进程的模式,so可以让Nginx在Docker容器中工作;
events {  }

http {
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  types_hash_max_size 2048;
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;
  gzip on;
  gzip_disable "msie6";
  include /etc/nginx/conf.d/*.conf;
}

(3)编写Dockerfile文件:

~$ cd sample && vi Dockerfile    //使用vi编辑器编写Dockerfile内容,然后保存退出

FROM ubuntu:14.04                //基础镜像为ubuntu
MAINTAINER zxj "13116088888"               
ENV REFRESHED_AT 2018-06-29      //设置环境变量
RUN apt-get update               //更新数据源库
RUN apt-get -y -q install nginx  //安装nginx,-y:一路yes,-q:输出到日志 - 无进展指示;
RUN mkdir -p /var/www/html       //-p:若路径中的父目录不存在,则创建
ADD nginx/global.conf /etc/nginx/conf.d/    //复制文件到指定路径,并自动解压
ADD nginx/nginx.conf /etc/nginx/nginx.conf  //将已下载的本地文件的Nginx的配置文件添加到镜像中;
EXPOSE 80     //公开镜像的80端口

Nginx配置文件是为了运行sample网站而配置的;将文件nginx/global.conf用ADD指令复制到/etc/nginx/conf.d/目录下,将文件/etc/nginx.conf复制到/etc/nginx目录下;

(4)利用Dockerfile文件构建Sample网站和Nginx镜像

~$ docker build -t zxj/nginx .    //在当前目录(.)下构建镜像并命名为zxj/nginx

(5)创建一个被测的Sample网站:

~$ cd sample  
~$ mkdir website && cd website  //在构建环境下创建目录website,用于存放sample网站的东西
~$ wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/website/index.html   //下载Sample网站的index.html文件到website目录中

(6)根据镜像创建Nginx测试容器:

~$ cd sample     //进入到构建环境目录下,下面一条指令$PWD获取到目录应该是/home/zxj/sample路径
~$ docker run -d -p 80 --name website -v $PWD/website:/var/www/html/website  zxj/nginx nginx // `$PWD` 获得当前工作目录路径的字符串值;
  • 执行docker run命令时传入了nginx作为容器的启动命令;一般情况下,这个命令无法让Nginx以交互式的方式运行,但是我们已经在提供给Docker的配置(nginx.conf)里面加入了指令daemon off,这个指令让Nginx启动后以交互式的方式在前台运行;
  • -v:将宿主机的目录作为卷,挂在到容器里; 指定卷的源目录(本地宿主机的目录)和容器里的目的目录,两个目录通过冒号来分隔;若目的目录不存在,Docker会自动创建;可以通过在目的目录后面加上:rw / :ro来指定目的目录的读写状态;
  • 在Nginx网站容器里,我们通过卷将$PWD/website挂载到容器的/var/www/html/website目录,顺利挂载了正在开发的本地网站;在Nginx配置里(global.conf中),已经指定了这个目录为Nginx服务器的工作目录;

(7)使用ps 命令查看正在运行的容器:

~$ docker ps   //可以看到名为website的容器正在处于活跃状态,其80端口被映射到32770端口;

(8)在Docker宿主机上浏览32770端口,可以看到Sample网站:

docker重新构建某个容器 docker 构建_docker重新构建某个容器

(9)修改网站:

网站已经运行起来了,若现在要修改网站,可以直接打开本地宿主机的website目录下的index.html文件并修改:~$ vi $PWD/website/index.html,This is a website for Docker.然后刷新浏览器即可;

docker重新构建某个容器 docker 构建_Docker_02


2、使用Docker构建并测试Web应用程序

接下来要测试一个基于Sinatra的Web应用程序,而不是静态网站;演示如何在Docker里开发并测试应用程序,这个应用程序会接收输入的参数,并使用JSON散列输出这些参数;

(1)构建Sinatra应用程序 - 编写Dockerfile - 构建镜像:

~$ mkdir sinatra     //创建Sinatra目录
~$ cd sinatra        //进入Sinatra目录
~$ touch Dockerfile  //创建Dockerfile文件
~$ vi Dockerfile     //使用vi编辑器编写dockerfile文件,然后保存并退出
~$ sudo docker build -t zxj5203/sinatra .     //编写完Dockerfile文件之后,构建新镜像
-------------------------------------------------------------------------
FROM ubuntu:14.04
MAINTAINER zxj "13116088888"               
ENV REFRESHED_AT 2018-06-29      //设置环境变量
//安装Ruby和RubyGem,并使用gem安装sinatra、json、redis包;
RUN apt-get update
RUN apt-get -y install ruby ruby-dev build-essential redis-tools
RUN gem install --no-rdoc --no-ri sinatra json redis

RUN mkdir -p /opt/webapp    //创建新目录来存放新的Web应用程序

EXPOSE 4567                 //公开WEBrick的默认端口4567
CMD ["/opt/webapp/bin/webapp"]   //指定[]里面内容作为Web应用程序的启动文件
  • 注意:gem安装Redis的时候,可能会出错:redis requires Ruby version >= 2.2.2.;
  • 原因:默认支持ruby到2.0.0,可gem 安装redis需要最低是2.2.2;
  • 解决办法:先安装rvm,再把ruby版本提升至2.3.3:
  • 参考链接:
  • curl -sSL https://get.rvm.io | bash -s stable
  • 若执行命令出错:
  • 则执行下面命令:gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
  • source /home/zxj/.rvm/scripts/rvm
  • 查看rvm库中已知的ruby版本:rvm list known
  • 选择>=2.2.2的Ruby版本进行安装:rvm install 2.3.7
  • 使用刚才下载的ruby版本:rvm use 2.3.7
  • 查看安装的Ruby版本:ruby --version;
  • 重新安装Redis:gem install redis,或者重新build Dockerfile:cd sinatra && docker build -t zxj/sinatra .;

(2)创建Sinatra容器: 创建镜像之后,下载Sinatra Web程序的源代码,这个应用程序在webapp目录下,由bin和lib两个目录组成;

  • GitHub仓库:https://github.com/jamtur01/dockerbook-code
  • 官网:http://dockerbook.com/code/5/sinatra/webapp/ (失效)
  • bin目录内容:https://github.com/turnbullpress/dockerbook-code/blob/master/code/5/sinatra/webapp/bin/webapp
  • lib目录内容:< https://github.com/turnbullpress/dockerbook-code/blob/master/code/5/sinatra/webapp/lib/app.rb>
~/sinatra$ wget --cut-dirs=7 -nH -r --no-parent https://github.com/turnbullpress/dockerbook-code/tree/master/code/5/sinatra/webapp/      // 下载的内容有错误;不用这种方式下载;
-------------------------------------------------------------------------
// 手动创建目录,分别下载文件;
~/sinatra$ mkdir webapp && cd webapp
~/sinatra/webapp$ mkdir bin && cd bin
~/sinatra/webapp/bin$ wget https://raw.githubusercontent.com/turnbullpress/dockerbook-code/master/code/5/sinatra/webapp/bin/webapp

~/sinatra/webapp$ mkdir lib && cd lib 
~/sinatra/webapp/lib$ wget https://raw.githubusercontent.com/turnbullpress/dockerbook-code/master/code/5/sinatra/webapp/lib/app.rb

~/sinatra$ ls -l webapp      //列出webapp文件夹内容
~/sinatra$ chmod +x $PWD/webapp/bin/webapp    //修改webapp文件的属性为可执行的
  • --cut-dirs=7:减掉主机名后面的相对目录数,避免创建n多层目录;
  • nH:不创建主机名目录;
  • r:递归下载子目录;
  • --no-parent: Don´t download something from the parent directory;

(3)从镜像创建新sinatra容器:sudo docker run -d -p 4567 --name webapp -v $PWD/webapp:/opt/webapp zxj5203/sinatra

  • 创建了一个新的名为webapp的容器,制定了一个新卷$PWD/webapp,来存放sinatra应用程序;并将这个卷挂在Dockerfile里创建的目录/opt/webapp;
  • 没有在命令行中提供要运行的命令,而是在Dockerfile里的CMD指令中提供了这个命令:CMD ["/opt/webapp/bin/webapp"]
  • sudo docker logs webapp:查看被执行的命令都输出了什么;
  • sudo docker top webapp:查看容器里正在运行的进程;
  • sudo docker port webapp 4567:查看容器的端口映射到了本地宿主机的哪个端口;

(4)使用curl命令测试应用程序:

curl -i -H 'Accept:application/json' -d 'name=Foo&status=Bar' http://localhost:32779/json:给sinatra应用程序传入一些参数,这些参数转化成JSON散列后输出;

docker重新构建某个容器 docker 构建_nginx_03

(5)构建Redis镜像和容器: 扩展sinatra应用程序,加入Redis后端数据库,并在数据库中存储输入的参数;

需要构建新的镜像和容器来运行Redis数据库,然后利用Docker的特性来关联两个容器;

  • Dockerfile:
FROM ubuntu:18.04
MAINTAINER zxj "13116088888"               
ENV REFRESHED_AT 2019-06-10
RUN apt-get update
RUN apt-get -y install redis-server redis-tools
EXPOSE 6379
ENTRYPOINT ["/usr/bin/redis-server"]
CMD []
  • sudo docker build -t zxj5203/redis .:构建Redis镜像;
  • sudo docker run -d -p 6379 --name redis zxj5203/redis:启动Redis容器;
  • sudo docker port redis 6379:查看容器端口映射到宿主机的哪个端口;
  • sudo apt-get -y install redis-tools:在本地安装Redis客户端做测试,客户端程序一般在Redis-tools包里;
  • redis-cli -h 127.0.0.1 -p 32780:使用redis-cli命令来确认Redis服务器是否正常工作;

(6)连接Redis容器:sudo docker inspect redis:查看Redis容器的网络配置;sudo docker inspect -f '{{.NetworkSettings.IPAddress}}' redis:查看Redis容器的IP地址;redis-cli -h 172.17.0.4:直接与Redis容器通信;

使用这种方式连接Redis有问题:要在应用程序中对Redis容器的IP地址做硬编码,如重启Redis容器,docker会改变Redis容器的IP地址;

(7)让Docker容器互连:

  • 停止并删除webapp、redis两个容器;
  • sudo docker run -d --name redis zxj5203/redis:重新创建一个Redis容器,不用公开容器的端口;
  • sudo docker run -i -t -p 4567 --name webapp --link redis:db -v $PWD/webapp:/opt/webapp zxj5203/sinatra /bin/bash:启动web应用程序,并把它连接到Redis容器中;
  • root@deec416db260:/# cat /etc/hosts:在父容器里面查看连接信息;信息包括容器自己的IP地址和主机名、子容器的IP地址和该连接的别名衍生的主机名; 重启子容器时容器的IP地址会发生变化,若被连接的容器重启了,/etc/hosts文件中的IP地址会同时更新;
  • root@deec416db260:/# env:显示用于连接的环境变量; Docker在连接父子容器时,会自动创建一些以子容器别名开头的环境变量;自动创建的环境变量包括:子容器的别名,容器里运行的服务所使用的协议、IP和端口号,容器里运行的不同服务所指定的协议、IP和端口号,容器里由Docker设置的环境变量的值;

(8)使用容器连接来通信: 给父容器应用程序加入一些连接信息,以便与子容器通信;

  • 方法1:使用环境变量里的一些连接信息:
  • 方法2:使用DNS和/etc/hosts信息:

(9)测试支持Redis的sinatrat应用程序:

docker重新构建某个容器 docker 构建_应用_04

docker重新构建某个容器 docker 构建_Docker_05


3、构建自己的镜像 - myforum

~ (1)制作镜像Tomcat - 将项目war包文件 装进镜像中

1、进入到/usr/local/docker/tomcat目录,创建Dockerfile文件;

2、将项目的打包文件上传到服务器的这个目录;(本地项目目录下的taget目录下的,与war包同名的文件夹打包zip,是进入到文件夹里面,选中所有文件,右键压缩)

3、在/usr/local/docker/tomcat下docker build -t myforum .

4、docker run -it --rm myforum bash

FROM tomcat

WORKDIR /usr/local/tomcat/webapps/ROOT

RUN rm -rf *

COPY myforum-0.0.1-SNAPSHOT.zip .

RUN unzip myforum-0.0.1-SNAPSHOT.zip

RUN rm -f myforum-0.0.1-SNAPSHOT.zip

WORKDIR /usr/local/tomcat

EXPOST 8080

部署项目到容器时,不使用这种方法,因为这个是把项目的war包直接拷贝到了镜像中,修改项目代码时就需要重新制作镜像;一般是将ROOT目录以数据卷的形式挂载;

~ (2)拉取镜像MySQL - 配置mysql

1、拉取镜像:docker pull mysql:5.7.26 需要指定5.x.x版本;因为默认拉取的最新版本是8.x.x版本的;

8.x.x比5.x.x快2倍;8.x.x新特性:取消了MyISAM引擎;5.x.x有MyISAM和InnoDB两种引擎;InnoDB引擎支持事务,所以效率会降低,MyISAM不支持事务,查询效率高;8.x.x取消了MyISAM引擎,所以InnoDB既支持事务,效率也提高了;

现在的企业在向互联网转型,所以需要互联网的开发技能:分布式系统开发; 分布式系统开发就需要数据库也支持分布式,现在的数据库分布式解决方案使用的是第三方方案,8.0是支持原生分布式数据库解决方案的;

2、运行镜像:(-d模式)

docker run -p 3306:3306 --name mysql \
-v /usr/local/docker/mysql/conf:/etc/mysql \
-v /usr/local/docker/mysql/logs:/var/log/mysql \
-v /usr/local/docker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \     // 环境变量:在MySQL官方镜像中的启动文档中可以查看;
-d mysql:5.7.26

3、在MySQL的workbench测试连接;

4、以交互式的方式创建一个新容器:docker run -it --rm mysql:5.7.26 bash 查看MySQL路径:whereis mysql mysq配置:root@4cbd6e7e4548:/etc/mysql/mysql.conf.d# cat mysqld.cnf 因为运行的这个容器没有指定数据卷,所以/etc/mysql下有配置文件;

docker重新构建某个容器 docker 构建_docker_06

5、使用workbench导入数据库:会报错; 原因是MySQL在配置里面默认情况下,服务器可接受的文件比较小,root@4cbd6e7e4548:/etc/mysql/conf.d# cat mysqldump.cnf 查看:

docker重新构建某个容器 docker 构建_docker_07

docker重新构建某个容器 docker 构建_Docker_08

6、设置MySQL初始时,服务器可以接收的文件的大小:将配置这部分做成数据卷,放到宿主机上,让配置可以被共享;不然的话容器一旦销毁,所有的配置就都没有了;

  • 首先退出并删除刚才启动的容器,
  • 然后以交互式方式重新启动一个容器:
docker run -p 3306:3306 --name mysql \
-v /usr/local/docker/mysql/logs:/var/log/mysql \
-v /usr/local/docker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7.26
  • 以交互式方式进入容器:docker exec -it mysql bashroot@8dda2ce3956b:/# cd /etc/mysql/conf.droot@8dda2ce3956b:/etc/mysql/conf.d# cat mysqldump.cnf:复制max_allowed_packet = 16Mroot@8dda2ce3956b:/etc/mysql# cd mysql.conf.droot@8dda2ce3956b:/etc/mysql/mysql.conf.d# echo "max_allowed_packet = 128M" >> mysqld.cnf,追加到mysqld.cnf;cat mysqld.cnf
  • 退出容器,并重启:docker restart mysql

7、将容器里的文件复制到宿主机:将容器的/etc/mysql文件夹整个复制; 在/usr/local/docker/mysql/conf目录下操作;root@Ubuntu:/usr/local/docker/mysql/conf# docker cp mysql:/etc/mysql .:将mysql容器的/etc/mysql目录拷贝到宿主机的当前目录;root@Ubuntu:/usr/local/docker/mysql/conf# cd mysqlroot@Ubuntu:/usr/local/docker/mysql/conf/mysql# mv *.* ..root@Ubuntu:/usr/local/docker/mysql/conf/mysql# cd ..root@Ubuntu:/usr/local/docker/mysql/conf# rm -fr mysql

8、退出并删除刚才启动的容器;

9、删除data文件夹:root@Ubuntu:/usr/local/docker/mysql# rm -rf data

10、重新执行第二步,运行一个带有配置文件的容器;然后workbench测试连接,导入数据库,成功则说明数据卷里面的配置文件是生效的;

(3)项目的容器化部署

不使用(1)中制作的镜像,而是直接运行tomcat镜像,运行时ROOT目录一数据卷的形式挂载;

  • pull tomcat;
  • /usr/local/docker/tomcat:进入到目录中
  • 不用创建Dockerfile;
  • 将项目war包导入到该目录;
  • 然后创建ROOT目录;
  • 将war包复制到ROOT目录,并解压缩,删除ROOT下的压缩包;
  • 修改WEB_INF/classes/db.properties文件中的数据库地址;
  • docker run -d -p 8080:8080 --name myforum -v /usr/local/docker/tomcat/ROOT/:/usr/local/tomcat/webapps/ROOT tomcat:直接使用pull的Tomcat镜像run一个容器,以守护进程的方式,指定端口映射,挂载ROOT目录数据卷;
  • 浏览器访问:192.168.141.176:8080