Contents
  1. 1. 使用Docker构建Web应用程序sinatra
    1. 1.1. 构建Sinatra应用程序镜像
    2. 1.2. 创建Sinatra容器
    3. 1.3. 拓展Sinatra应用程序来使用Redis
    4. 1.4. 将Sinatra应用程序连接到Redis容器
    5. 1.5. 中途遇到的问题(避免踩坑):

使用docker构建ruby + redis环境

使用Docker构建Web应用程序sinatra

测试一个基于Sinatra的Web应用程序,基于Docker来对这个应用进行测试,我们将创建一个应用程序,它接收输入的URL参数,并以JSON散列的结构输出到客户端。

构建Sinatra应用程序镜像

为测试Web应用程序创建目录

1
2
3
mkdir -p sinatra
cd sinatra
touch Docekrfile

测试用web应用程序的Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM ubuntu:18.04
MAINTAINER flag0 "root@flag0.com"
ENV REFRESHED_AT 2019-8-27

RUN apt-get -yqq update
RUN
RUN apt-get -yqq install ruby-dev build-essential redis-tools
RUN gem install --no-rdoc --no-ri sinatra json redis
RUN mkdir -p /opt/webapp

EXPOSE 4567

CMD ["/opt/webapp/bin/webapp"]

创建了另外一个基于ubuntu的镜像,安装了Ruby和RubyGem,并且使用gem命令安装了sinatra、json和redis gem。(sinatra是Sinatra的库,json用来提供对JSON的支持,redis gem用来和Redis数据库进行集成)

创建了一个目录来存放新的Web应用程序,并公开了WEBrick的默认端口4567

使用CMD指定/opt/webapp/bin/webapp作为Web应用程序的启动文件

构建新的Sinatra镜像

1
docker build -t flag0/sinatra .

创建Sinatra容器

下载Sinatra Web应用程序的源代码,将其下载到sinatar目录

下载Sinatra Web应用程序

下载webapp代码到sinatra目录下

1
https://github.com/turnbullpress/dockerbook-code/tree/master/code/5/sinatra/webapp

修改目录权限,确保webapp/bin/webapp可执行

1
chmod +x webapp/bin/webapp

启动第一个Sinatra容器

我们需要将webapp目录下的源代码通过卷挂载到容器中去

1
docker run -d -p 4567 --name webapp -v $PWD/webapp:/opt/webapp flag0/sinatra

查看Sinatra容器的日志

1
2
3
4
5
[root@iz0201z3qoku45z sint]# docker logs webapp
[2019-09-01 05:00:40] INFO WEBrick 1.4.2
[2019-09-01 05:00:40] INFO ruby 2.5.1 (2018-03-29) [x86_64-linux-gnu]
== Sinatra (v2.0.7) has taken the stage on 4567 for development with backup from WEBrick
[2019-09-01 05:00:40] INFO WEBrick::HTTPServer#start: pid=1 port=4567

跟踪(持续输出)Sinatra容器的日志

1
docker logs -f webapp
  • -f 在运行docker logs 时,可以达到与执行tail -f 命令一样的效果—持续输出容器的STDERR和STDOUT里的内容

使用docker top来列出Sinatra进程

1
2
3
docker top webapp
UID PID PPID C STIME TTY TIME CMD
root 24943 24927 0 13:00 ? 00:00:00 /usr/bin/ruby /opt/webapp/bin/webapp

检查端口映射情况

1
2
docker port webapp 4567
0.0.0.0:32777

测试Sinatra应用程序

1
2
3
4
5
6
7
8
9
10
11
12
[root@iz0201z3qoku45z ~]# curl -i -H 'Accept:application/json' -d 'name=Foo&status=Bar' http://localhost:32777/json
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Length: 29
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Server: WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29)
Date: Sun, 01 Sep 2019 10:05:15 GMT
Connection: Keep-Alive

{"name":"Foo","status":"Bar"}

拓展Sinatra应用程序来使用Redis

拓展Sinatra应用程序,加入Redis后端数据库,并在Redis数据库中存储输入的URL参数。

升级Sinatra应用程序

下载应用程序

1
2
mkdir -p sinatra/webapp_redis
https://github.com/turnbullpress/dockerbook-code/tree/master/code/5/sinatra/webapp_redis

新版本的代码中增加了对redis的支持

设置执行权限

1
chmod +x webapp_redis/bin/webapp

构建Redis数据库镜像

创建目录

1
mkdir -p sinatra/redis

sinatra/redis目录下创建用于Redis镜像的 Dockerfile

1
2
3
4
5
6
7
FROM ubuntu
MAINTAINER flag0 "root@flag.com"
ENV REFRESHED_AT 2019-9-1
RUN apt-get -yqq update && apt-get -yqq install redis-server redis-tools
EXPOSE 6379
ENTRYPOINT ["/usr/bin/redis-server"]
CMD []

构建Redis镜像

1
docker build -t flag0/redis .

启动redis容器

1
docker run -d -p 6379 --name redis flag0/redis --protected-mode no

检查映射端口

1
2
docker port redis 6379
0.0.0.0:32778

在本地安装Redis客户端

1
2
3
4
//ubuntu
apt-get -y -q install redis-tools
//Centos
yum install -y -q redis

测试Redis链接

1
2
redis-cli -h 127.0.0.1 -p 32778
127.0.0.1:32778>

将Sinatra应用程序连接到Redis容器

创建Docker网络

1
docker network create app

在Docker网络中创建Redis容器

1
docker run -d --net=app --name db flag0/redis --protected-mode no

这里基于flag0/redis 镜像创建了一个名为db的新容器,--net制定了新容器将会在哪个网络中运行

--protected-mode no关闭redis保护模式

查看更新后的app网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
docker network inspect app
[
{
"Name": "app",
"Id": "212ddbcc32ce24a5b5cde3f2e233555ac5ff9189cd0fba79c8e333b374701ff4",
"Created": "2019-09-01T22:54:59.573164465+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"e094cb58dbbd88bfadcc460f7cbfd3ae4ba93c8bdacd80441ede503c2ffe8f40": {
"Name": "db",
"EndpointID": "ec0ee61af53a4a5af349da7ad206a896ffa333a59bf14dc6c352e639fba72d55",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

可以看到Containers里面已经我们新添加的容器

链接redis容器

回到sinatra/webapp

首先删除原有的webapp容器

1
docker rm -f webapp

创建新的,在app网络下的webapp容器

1
docker run -p 4567 --net=app --name webapp -it -v $PWD/webapp_redis:/opt/webapp flag0/sinatra /bin/bash

在webapp容器中测试与redis容器的连通性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apt-get install inetutils-ping //安装ping命令
ping db
PING db (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.087 ms
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.091 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.086 ms
64 bytes from 172.18.0.2: icmp_seq=3 ttl=64 time=0.092 ms
64 bytes from 172.18.0.2: icmp_seq=4 ttl=64 time=0.086 ms
64 bytes from 172.18.0.2: icmp_seq=5 ttl=64 time=0.097 ms
64 bytes from 172.18.0.2: icmp_seq=6 ttl=64 time=0.079 ms
^C--- db ping statistics ---
7 packets transmitted, 7 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.079/0.088/0.097/0.000 ms
root@b2f281b42b71:/# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.18.0.3 b2f281b42b71


ping db.app
PING db.app (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.067 ms
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.082 ms
^C--- db.app ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.067/0.075/0.082/0.000 ms

代码中指定Redis DB主机名

1
redis = Redis.new(:host => 'db',port => '6379')

启动新版的Sinatra应用程序

1
nohup /opt/webapp/bin/webapp

以后台方式运行了Sinatra应用程序

查看Sinatra容器的端口映射情况

1
2
docker port webapp 4567
0.0.0.0:32781

测试启用了Redis的Sinatra应用程序

1
2
3
4
5
6
7
8
9
10
11
12
curl -i -H 'Accept: application/json' -d 'name=Foo&status=Bar' http://localhost:32781/json
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Length: 29
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Server: WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29)
Date: Tue, 03 Sep 2019 12:40:41 GMT
Connection: Keep-Alive

{"name":"Foo","status":"Bar"}

确认Redis容器数据

1
2
3
4
5
6
7
8
9
10
11
12
![QQ图片20190901000907](\img\images\C:/Users/GetFlag/Desktop/QQ图片20190901000907.png)curl -i http://localhost:32781/json
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Length: 41
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Server: WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29)
Date: Tue, 03 Sep 2019 12:43:47 GMT
Connection: Keep-Alive

"[{\"name\":\"Foo\",\"status\":\"Bar\"}]"

安装成功!

中途遇到的问题(避免踩坑):

1.构建flag0/sinatra时,提示 rack requires Ruby version >= 2.2.2

原因在于按照《第一本Docker书》上的Dockerfile来生成镜像,其中FROM ubuntu:14.04使用了低版本的ubuntu镜像导致了该问题,使用18.08可以解决该问题

2.构建redis容器时,连接后执行命令提示(error) DENIED Redis is running in protected mode because protected mode is enabled

1567516369515

这里是因为没有关闭redis默认的保护模式,中途我尝试了替换配置文件,运行后本地连接后执行命令关闭等方式均没有成功,最后在启动容器的时候使用--protected-mode no命令成功关闭

3.运行webapp容器后,docker ps可以看到映射的宿主机端口,但是curl连接后提示Recv failure:Connection reset by peer

1567516669995

这个问题困扰了我一个晚上,最终怀疑是文件夹里的webapp坏掉了,尝试了去运行没有redis的sinatra应用程序,可以访问,替换掉webapp果然解决了

但是这个问题解决的不够透彻,后续我把有问题的目录删除后重新上传,问题就没有再出现过了,在出现上面这种情况的时候,我进入容器内部进行curl可以成功访问,由于无法重新复现且通过替换webapp程序解决了问题,就把原因大概的解释为webapp出问题了吧,如果你同时出现了这个问题,请联系我,一块研究研究,一探究竟 email:cm9vdEBmbGFnMC5jb20=

4.运行容器时出现OCI runtime create failed: container_linux.go:345:...

1567517446884

原因在于没有给webapp程序执行权限,容器启动后运行该程序时发现无法运行才爆出的错误,chmod +x webapp后解决

Contents
  1. 1. 使用Docker构建Web应用程序sinatra
    1. 1.1. 构建Sinatra应用程序镜像
    2. 1.2. 创建Sinatra容器
    3. 1.3. 拓展Sinatra应用程序来使用Redis
    4. 1.4. 将Sinatra应用程序连接到Redis容器
    5. 1.5. 中途遇到的问题(避免踩坑):