解决方案:
-
To start the Postgres Docker container, first determine what environment variables are required to set the default username and password credentials for the database. Reading through the official Docker Hub page, you can see that you have configuration options for the
POSTGRES_USER
andPOSTGRES_PASSWORD
environment variables. Pass the environment variables using the-e
flag. The final command to start our Postgres Docker container will be as follows:docker run -itd -e "POSTGRES_USER=panoramic" -e "POSTGRES_PASSWORD=trekking" postgres:12
运行此命令将启动容器。
-
Execute the
docker ps
command to verify that it is running and healthy:$ docker ps
该命令应该返回如下输出:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 29f115af8cdd postgres:12 "docker-entrypoint.s…" 4 seconds ago Up 2 seconds 5432/tcp blissful_kapitsa
从前面的输出可以看出,ID 为
29f115af8cdd
的容器已经启动并运行。
在本练习中,您已经成功启动了一个 PostgreSQL 版本 12 容器,它是全景徒步应用的一部分,将在本书的过程中构建。
解决方案:
-
Log in to the database instance using
docker exec
to start the PSQL shell inside the container, passing in the--username
flag and leaving the--password
flag empty:$ docker exec -it <containerID> psql --username panoramic --password
这应该会提示您输入密码,并启动一个 PSQL shell。
-
Use the
\l
command to list all the databases:psql (12.2 (Debian 12.2-2.pgdg100+1)) Type "help" for help. panoramic=# \l
将返回容器中运行的数据库列表:
图 1.4:数据库列表
-
最后,使用
\q
快捷键退出外壳。 -
使用
docker stop
和docker rm
命令停止并清理容器实例。
在本活动中,您通过使用在活动 1.01 、中设置的凭据登录并从 Docker Hub 中提取和运行 PostgreSQL 容器映像来访问容器中运行的数据库。您还列出了容器中运行的数据库。该活动让您亲身体验了如何使用 PSQL shell 访问在任何容器中运行的数据库。
解决方案:
-
为此活动创建一个名为
activity-02-01
的新目录:mkdir activity-02-01
-
导航至新创建的
activity-02-01
目录:cd activity-02-01
-
在
activity-02-01
目录下,创建一个名为welcome.php
:touch welcome.php
的文件
-
现在,使用您最喜欢的文本编辑器打开【T0:
vim welcome.php
-
用活动开始时提供的内容创建
welcome.php
文件,然后保存并退出welcome.php
文件:<?php $hourOfDay = date('H'); if($hourOfDay < 12) { $message = «Good Morning»; } elseif($hourOfDay > 11 && $hourOfDay < 18) { $message = «Good Afternoon»; } elseif($hourOfDay > 17){ $message = «Good Evening»; } echo $message; ?>
-
在
activity-02-01
目录下,创建一个名为Dockerfile
:touch Dockerfile
的文件
-
现在,使用您最喜欢的文本编辑器打开【T0:
vim Dockerfile
-
Add the following content to the
Dockerfile
, and then save and exit from theDockerfile
:# Start with Ubuntu base image FROM ubuntu:18.04 # Set labels LABEL maintainer=sathsara LABEL version=1.0 # Set environment variables ENV DEBIAN_FRONTEND=noninteractive # Install Apache, PHP, and other packages RUN apt-get update && \ apt-get -y install apache2 \ php \ curl # Copy all php files to the Docker image COPY *.php /var/www/html # Set working directory WORKDIR /var/www/html # Create health check HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD curl -f http://localhost || exit 1 # Expose Apache EXPOSE 80 # Start Apache ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
我们使用
ubuntu
基础映像开始这个Dockerfile
,然后设置几个标签。接下来,DEBIAN_FRONTEND
环境变量被设置为noninteractive
以使软件包安装非交互。然后安装apache2
、php
、curl
包,将 PHP 文件复制到/var/www/html
目录。接下来,配置健康检查并暴露端口80
。最后,使用apache2ctl
命令启动 Apache 网络服务器。 -
Now, build the Docker image:
$ docker image build -t activity-02-01 .
运行
build
命令后,应该会得到如下输出:图 2.22:构建活动-02-01 Docker 映像
-
Execute the
docker container run
command to start a new container from the Docker image that you built in the previous step:
```
$ docker container run -p 80:80 --name activity-02-01-container -d activity-02-01
```
由于您是在分离模式下启动 Docker 容器的(带有`-d`标志),前面的命令将输出结果 Docker 容器的 ID。
- Now, you should be able to view the Apache home page. Go to the
http://127.0.0.1/welcome.php
endpoint from your favorite web browser:

图 2.23: PHP 应用页面
注意,默认的 Apache 主页是可见的。在前面的输出中,您收到的输出为`Good Morning`。该输出可能会有所不同,根据运行该容器的时间,显示为`Good Afternoon`或`Good Evening`。
- 现在,清理容器。首先,用 docker 容器停止命令停止 Docker 容器:
```
$ docker container stop activity-02-01-container
```
- 最后,用
docker container rm
命令
```
$ docker container rm activity-02-01-container
```
移除 Docker 容器
在本练习中,我们学习了如何使用本章中所学的Dockerfile
指令来记录一个示例 PHP 应用。我们使用了多个Dockerfile
指令,包括FROM
、LABEL
、ENV
、RUN
、COPY
、WORKDIR
、HEALTHCHECK
、EXPOSE
和ENTRYPOINT
。
解决方案:
您可以通过多种方式完成本活动。这里有一个例子:
-
创建新的构建脚本。第一行显示设置
–ex
命令,将每一步打印到屏幕上,如果任何一步失败,脚本将失败。第 3 行和第 4 行设置注册表和服务名称的变量:1 set -ex 2 3 REGISTRY=dev.docker.local:5000 4 SERVICENAME=postgresql
-
在第 6 行中,将
GIT_VERSION
变量设置为指向您的短 Git 提交散列。构建脚本然后将该值打印到第 7 行 :6 GIT_VERSION=`git log -1 --format=%h` 7 echo "version: $GIT_VERSION "
中的屏幕上
-
Use the
docker build
command in line 9 to create your new image and add thedocker push
command in line 11 to push the image to your local Docker registry:9 docker build -t $REGISTRY/$SERVICENAME:$GIT_VERSION . 10 11 docker push $REGISTRY/$SERVICENAME:$GIT_VERSION
脚本文件如下所示:
1 set -ex 2 3 REGISTRY=dev.docker.local:5000 4 SERVICENAME= postgresql 5 6 GIT_VERSION=`git log -1 --format=%h` 7 echo "version: $GIT_VERSION " 8 9 docker build -t $REGISTRY/$SERVICENAME:$GIT_VERSION . 10 11 docker push $REGISTRY/$SERVICENAME:$GIT_VERSION
-
Run the following command to ensure that the script has been built and runs successfully:
./build.sh
您应该得到如下输出:
./BuildScript.sh ++ REGISTRY=dev.docker.local:5000 ++ SERVICENAME=basic-app +++ git log -1 --format=%h ++ GIT_VERSION=49d3a10 ++ echo 'version: 49d3a10 ' version: 49d3a10 ++ docker build -t dev.docker.local:5000/basic-app:49d3a10 . Sending build context to Docker daemon 3.072kB Step 1/1 : FROM postgres ---> 873ed24f782e Successfully built 873ed24f782e Successfully tagged dev.docker.local:5000/basic-app:49d3a10 ++ docker push dev.docker.local:5000/basic-app:49d3a10 The push refers to repository [dev.docker.local:5000/basic-app]
解决方案:
以下步骤描述了实现活动目标的方法之一:
-
在你的主目录中创建
test_registry
目录:mkdir /home/vincesesto/test_registry/
-
运行本地注册表,但在这种情况下,包括
-v
选项,该选项将您在上一步中创建的目录连接到/var/lib/registry
的容器目录。另外,使用:rw
选项确保您可以读写目录:docker run -d -p 5000:5000 --restart=always --name registry -v /home/vincesesto/test_registry/registry:/var/lib/registry:rw registry
-
现在,像平常一样将映像推送到新安装的注册表中:
docker push dev.docker.local:5000/basic-app:ver1
-
To verify that the images are now being stored in your newly mounted directory, list the files in the
registry/docker/registry/v2/repositories/
directory.ls ~/test_registry/registry/docker/registry/v2/repositories/
您应该会看到您在上一步中刚刚推送的新映像:
basic-app
这项活动让我们开始使用一些更高级的 Docker 选项。不要担心,在运行容器时,将有更多的章节专门帮助您理解卷装载和存储。
解决方案:
-
为此活动创建一个名为
activity-04-01
的新目录:mkdir activity-04-01
-
导航至新创建的
activity-04-01
目录:cd activity-04-01
-
在
activity-04-01
目录下,创建一个名为main.go
:$ touch main.go
的文件
-
现在,使用您最喜欢的文本编辑器打开
main.go
文件:$ vim main.go
-
将以下内容添加到
main.go
文件,然后保存并退出该文件:package main import ( "net/http" "fmt" "log" "os" ) func main() { http.HandleFunc("/", defaultHandler) http.HandleFunc("/contact", contactHandler) http.HandleFunc("/login", loginHandler) port := os.Getenv("PORT") if port == "" { port = "8080" } log.Println("Service started on port " + port) err := http.ListenAndServe(":"+port, nil) if err != nil { log.Fatal("ListenAndServe: ", err) return } } func defaultHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Home Page</h1>") } func contactHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Contact Us</h1>") } func loginHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Login Page</h1>") }
-
在
activity-04-01
目录下,创建一个名为Dockerfile
的文件。该文件将是多阶段的Dockerfile
:touch Dockerfile
-
现在,使用您最喜欢的文本编辑器打开【T0:
vim Dockerfile
-
Add the following content to the
Dockerfile
and save the file:FROM golang:1.14.2-alpine AS builder WORKDIR /myapp COPY main.go . RUN go build -o main . FROM alpine:latest AS runtime WORKDIR /myapp COPY --from=builder /myapp/main . ENTRYPOINT ["./main"] EXPOSE 8080
这个
Dockerfile
有两个阶段,分别叫做builder
和runtime
。构建器阶段使用 Golang Docker 映像作为父映像,并负责从 Golang 源文件创建可执行文件。运行时阶段使用alpine
Docker 映像作为父映像,并执行从builder
阶段复制的可执行文件。 -
Now, build the Docker image with the
docker build
command:docker build -t activity-04-01:v1 .
您应该会得到以下输出:
图 4.14:构建 Docker 映像
-
Use the
docker image
ls command to list all the Docker images available on your computer. Verify the size of the image:
```
docker images
```
该命令将返回所有可用 Docker 映像的列表:

图 4.15:列出所有 Docker 映像
在前面的输出中,您可以看到名为`activity-04-01`的优化 Docker 映像的大小为 13.1 MB,而构建器阶段使用的父映像(Golang 映像)的大小为 370 MB。
- Execute the
docker container run
command to start a new container from the Docker image that you built in the previous step:
```
$ docker container run -p 8080:8080 --name activity-04-01-container activity-04-01:v1
```
您应该会得到类似如下的输出:
```
2020/08/30 05:14:10 Service started on port 8080
```
- View the application at the following URL in your favorite web browser:
```
http://127.0.0.1:8080/
```
下图显示了我们导航到网址`http://127.0.0.1:8080/`时的主页:

图 4.16: Golang 应用–主页
- Now, browse to the following URL on your favorite web browser:
```
http://127.0.0.1:8080/contact
```
下图显示了我们导航到网址`http://127.0.0.1:8080/contact`时的联系人页面:

图 4.17: Golang 应用-联系我们页面
- Now, enter the following URL in your favorite web browser:
```
http://127.0.0.1:8080/login
```
下图显示了我们导航到网址`http://127.0.0.1:8080/login`时的登录页面:

图 4.18: Golang 应用–登录页面
在本练习中,我们学习了如何部署一个可以根据调用 URL 返回不同响应的 Golang HTTP 服务器。在本活动中,我们使用多阶段 Docker 构建来创建最小大小的 Docker 映像。
解决方案:
可以通过以下步骤创建数据库并安装 WordPress:
-
创建所需的目录,并使用
cd
命令导航到该目录:mkdir wordpress cd wordpress
-
创建一个
docker-compose.yaml
文件,内容如下:version: "3" services: database: image: mysql:5.7 volumes: - data:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: db MYSQL_USER: user MYSQL_PASSWORD: password wordpress: depends_on: - database image: wordpress:latest ports: - "8080:80" restart: always environment: WORDPRESS_DB_HOST: database:3306 WORDPRESS_DB_USER: user WORDPRESS_DB_PASSWORD: password WORDPRESS_DB_NAME: db volumes: data: {}
-
Start the application with the
docker-compose up --detach
command:图 5.22:应用的启动
-
Check for the running containers with the
docker-compose ps
command. You should get the following output:图 5.23: WordPress 和数据库容器
-
Open
http://localhost:8080
in your browser to check the WordPress setup screen:
图 5.24: WordPress 设置屏幕
在本练习中,您已经使用 Docker Compose 为实际应用创建了一个部署。该应用由一个数据库容器和一个 WordPress 容器组成。两个容器服务都使用环境变量进行配置,通过 Docker Compose 网络和卷进行连接。
解决方案:
可以通过以下步骤创建数据库和全景徒步旅行应用:
-
创建所需的目录,并将其更改为:
mkdir pta-compose cd pta-compose
-
创建一个
docker-compose.yaml
文件,内容如下:version: "3" services: db: image: postgres volumes: - db_data:/var/lib/postgresql/data/ environment: - POSTGRES_PASSWORD=docker web: image: packtworkshops/the-docker-workshop:chapter5-pta-web volumes: - static_data:/service/static depends_on: - db nginx: image: packtworkshops/the-docker-workshop:chapter5-pta-nginx volumes: - static_data:/service/static ports: - 8000:80 depends_on: - web volumes: db_data: static_data:
-
Start the application with the
docker-compose up --detach
command. You should get output similar to the following:图 5.25:应用的启动
注意
也可以使用
docker-compose up -d
命令启动应用。 -
Check for the running containers with the
docker-compose ps
command. You should get output similar to the following:图 5.26 应用、数据库和 nginx 容器
-
Open the administration section of the Panoramic Trekking App in the browser with the address of
http://0.0.0.0:8000/admin
:图 5.27:管理员设置登录
注意
也可以运行
firefox http://0.0.0.0:8000/admin
命令,打开全景徒步应用的管理部分。使用用户名
admin
和密码changeme
登录,添加新的照片和国家。将出现以下屏幕:图 5.28:管理设置视图
-
Open the Panoramic Trekking App at the address
http://0.0.0.0:8000/photo_viewer
in the browser:
图 5.29:应用视图
在本练习中,您已经使用 Docker Compose 创建了一个三层应用,其中包含 PostgreSQL 数据库、后端和代理服务层。所有服务都使用 Docker Compose 及其网络和存储功能进行配置和连接。
解决方案:
以下是根据最佳实践完成本活动的最常见方式:
-
Use the
docker network create
command to create a network for the NGINX web server. Call itwebservernet
and give it a subnet of192.168.1.0/24
and a gateway of192.168.1.1
:$ docker network create webservernet --subnet=192.168.1.0/24 --gateway=192.168.1.1
这应该会创建
bridge
网络,webservernet
。 -
Use the
docker run
command to create an NGINX web server. Use the-p
flag to forward port8080
on the host to port80
on the container instance:$ docker run -itd -p 8080:80 --name webserver1 --network webservernet nginx:latest
这将启动
webservernet
网络中的webserver1
容器。 -
Use the
docker run
command to start an Alpine Linux container namedmonitor
inhost
networking mode. This way, you will know that the container has access to the host ports of the main system as well as access to thebridge
network IP address:$ docker run -itd --name monitor --network host alpine:latest
这将在
host
网络模式下启动一个 Alpine Linux 容器实例。 -
Use
docker inspect
to find the IP address of thewebserver1
container:$ docker inspect webserver1
容器的详细信息将以 JSON 格式显示;从
IPAddress
参数获取 IP 地址:图 6.27:检查 webserver1 容器实例
-
Access an
sh
shell inside the monitoring container using thedocker exec
command:$ docker exec -it monitor /bin/sh
这会让你陷入一个根壳。
-
Use the
apk install
command to install thecurl
command inside this container:/ # apk add curl
这将安装
curl
实用程序:fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/main /x86_64/APKINDEX.tar.gz fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/community /x86_64/APKINDEX.tar.gz (1/4) Installing ca-certificates (20191127-r1) (2/4) Installing nghttp2-libs (1.40.0-r0) (3/4) Installing libcurl (7.67.0-r0) (4/4) Installing curl (7.67.0-r0) Executing busybox-1.31.1-r9.trigger Executing ca-certificates-20191127-r1.trigger OK: 7 MiB in 18 packages
-
Use the
curl
command to validate connectivity works at the host level, by calling port8080
on the host machine:/ # curl -v http://localhost:8080
您应该会收到来自 NGINX 的
200 OK
响应,表明在主机级别成功连接:图 6.28:从主机上公开的端口访问 webserver1 容器
-
Likewise, use the
curl
command to access the IP address of the container in the Dockerbridge
network directly over port80
:/ # curl -v 192.168.1.2:80
同样,您应该会收到另一个
200 OK
响应,表示连接成功:
图 6.29:从容器实例的 IP 地址访问 NGINX 网络服务器
在本练习中,我们能够使用不同的 Docker 网络驱动程序来说明容器之间的连接。这个场景适用于现实世界的生产基础设施,因为当部署一个容器化的解决方案时,工程师将努力部署一个尽可能不可变的基础设施。借助在 Docker 中部署完全模拟主机级网络的容器的能力,可以设计出在主机操作系统上只需极少配置的基础架构。这使得部署和扩展 Docker 所部署的主机变得非常容易。诸如curl
和其他监控工具的包可以部署到在 Docker 主机上运行的容器中,而不是安装在主机本身上。这保证了部署和维护的便利性,并提高了部署更多主机以满足不断增长的需求的速度。
解决方案:
-
使用
docker network create
命令,通过传递自定义的subnet
、gateway
和overlay
网络驱动程序:$ docker network create panoramic-net --subnet=10.2.0.0/16 --gateway=10.2.0.1 --driver overlay
,在 Docker 集群的
Machine1
上创建一个名为panoramic-net
的 Dockeroverlay
网络 -
Use the
docker service create
command onMachine1
to create a service namedtrekking-app
in thepanoramic-net
network:$ docker service create -t --name trekking-app --replicas=1 --network panoramic-net alpine:latest
这将在
panoramic-net
overlay
网络中启动一项名为trekking-app
的服务。 -
使用
Machine1
上的docker service create
命令在panoramic-net
网络中创建名为database-app
的服务。设置默认凭证并指定 Docker 映像的postgres:12
版本:$ docker service create -t --name database-app --replicas=1 --network panoramic-net -e "POSTGRES_USER=panoramic" -e "POSTGRES_PASSWORD=trekking" postgres:12
-
Use
docker exec
to access ansh
shell inside thetrekking-app
service container:$ docker exec -it trekking-app.1.qhpwxol00geedkfa9p6qswmyv /bin/sh
这将把你放到
trekking-app
容器实例中的一个根外壳中。 -
Use the
ping
command to validate network connectivity to thedatabase-app
service:/ # ping database-app
ICMP 回复应表明连接成功:
PING database-app (10.2.0.5): 56 data bytes 64 bytes from 10.2.0.5: seq=0 ttl=64 time=0.261 ms 64 bytes from 10.2.0.5: seq=1 ttl=64 time=0.352 ms 64 bytes from 10.2.0.5: seq=2 ttl=64 time=0.198 ms
在本练习中,我们利用了一个跨 Docker 集群的定制 Docker overlay
网络来说明使用 Docker DNS 的两个 Docker 集群服务之间的连接。在现实世界的多层应用中,许多微服务可以部署在大型 Docker 集群中,这些集群使用overlay
网络网格直接相互对话。了解overlay
网络如何与 Docker DNS 协同工作,对于实现高效的可扩展性至关重要,因为您的容器化基础架构在持续增长。
解决方案:
-
运行以下命令删除主机中的所有对象:
$ docker container rm -fv $(docker container ls -aq) $docker image rm $(docker image ls -q)
-
获取卷名,然后使用以下命令删除所有卷:
$docker volume ls $docker volume rm <volume names separated by spaces>
-
获取网络名称,然后使用以下命令删除所有网络:
$docker network ls $docker network rm <network names separated by spaces>
-
打开两个终端,一个专用于看到
docker events --format '{{json .}}'
生效。另一个应该打开以执行前面提到的高级步骤。 -
In the first terminal, run the following command:
docker events --format '{{json .}}'.
您应该得到如下输出:
图 7.11:docker 事件命令的输出
-
Run the following command to start the
ubuntu
container in the second terminal:$docker run -d ubuntu:14.04
您应该得到如下输出:
图 7.12:Docker 运行命令的输出
-
在第二个终端中使用以下命令创建名为
vol1
的卷:$docker volume create vol1
-
在第二个终端中使用以下命令创建名为
net1
的网络:$docker network create net1
-
使用以下命令移除容器:
$docker container rm -fv <container ID>
-
使用以下命令删除卷和网络:
```
$docker volume rm vol1
$docker network rm net1
```
- 在
docker events
终端点击 Ctrl + C 终止。 - Check the following two examples to understand the JSON output:
**例 1** :
```
{"status":"create","id":"43903b966123a7c491b50116b40827daa03
da5d350f8fef2a690fc4024547ce2","from":"ubuntu:14.04","Type":
"container","Action":"create","Actor":{"ID":"43903b966123a7c
491b50116b40827daa03da5d350f8fef2a690fc4024547ce2","Attributes":
{"image":"ubuntu:14.04","name":"upbeat_johnson"}},"scope":"local",
"time":1592516703,"timeNano":1592516703507582404}
```
**例 2** :
```
{"Type":"network","Action":"connect","Actor":{"ID":"52855e1561
8e37b7ecc0bb26bc42847af07cae65ddd3b68a029e40006364a9bd",
"Attributes":{"container":"43903b966123a7c491b50116b40827daa03d
a5d350f8fef2a690fc4024547ce2","name":"bridge","type":"bridge"}},
"scope":"local","time":1592516703,"timeNano":1592516703911851347}
```
你会发现不同的对象有不同的属性和结构。
- 运行带有卷的 PostgreSQL 容器。命名容器
db1
:
```
$docker container run --name db1 -v db:/var/lib/postgresql/data -e POSTGRES_PASSWORD=password -d postgres
```
- 运行
exec
命令,以便用要执行的命令替换 bash。外壳将变为posgres=#
表示您在容器内:
```
$ docker container exec -it db1 psql -U postgres
```
- 创建一个包含两列的表格:
serial
类型的ID
和json
类型的info
:
```
CREATE TABLE events (ID serial NOT NULL PRIMARY KEY, info json NOT NULL);
```
- 将第一个例子输出的第一行
JSON
插入表格:
```
INSERT INTO events (info) VALUES ('{"status":"create","id":"43903b966123a7c491b50116b40827daa03da 5d350f8fef2a690fc4024547ce2","from":"ubuntu:14.04","Type":"container","Action":"create","Actor":{"ID":"43903b966123a7c49 1b50116b40827daa03da5d350f8fef2a690fc4024547ce2","Attributes":{"image":"ubuntu:14.04","name":"upbeat_johnson"}},"scope":"local","time":1592516703,"timeNano":1592516703507582404}');
```
- Verify that the row is saved in the database by typing the following SQL statement:
```
select * from events;
```
您应该得到如下输出:

图 7.13:验证该行是否保存在数据库中
- Insert Docker events into the
events
table using the SQLinsert
command.
注意
请参考位于[https://packt.live/2ZKfGgB](https://packt.live/2ZKfGgB)的`events.txt`文件,使用`insert`命令插入 Docker 事件。
您应该得到如下输出:

图 7.14:在数据库中插入多行
从这个输出中,很明显有 11 个事件被成功地插入到 PostgreSQL 数据库中。
- Run the following three queries one by one.
**查询 1** :
```
SELECT * FROM events WHERE info ->> 'status' = 'pull';
```
输出如下所示:

图 7.15:查询 1 的输出
查询 2 :
SELECT * FROM events WHERE info ->> 'status' = 'destroy';
输出如下所示:
图 7.16:查询 2 的输出
查询 3 :
SELECT info ->> 'id' as id FROM events WHERE info ->> 'status'= 'destroy';
输出如下所示:
图 7.17:查询 3 的输出
在本练习中,您学习了如何记录和监视容器,如何使用 SQL 语句查询容器的事件,以及如何获取事件的 JSON 输出并保存在 PostgreSQL 数据库中。您还学习了 JSON 输出结构,并学习了如何查询它。
解决方案:
-
Verify that you do not have the
/var/mylogs
folder on your host by running the following command:$cd /var/mylogs
您应该得到如下输出:
Bash: cd: /var/mylogs: No such file or directory
-
Run a container based on the NGINX image. Specify the path of the shared volumes on the host and inside the container in the
run
command. Inside the container, NGINX uses the/var/log/nginx
path for the log files. Specify the path on the host as/var/mylogs
:$docker container run -d -v /var/mylogs:/var/log/nginx nginx
如果本地没有映像,Docker 引擎将自动提取映像:
图 7.18:Docker 运行命令的输出
-
Go to the path of
/var/mylogs
. List all the files in that directory:$cd /var/mylogs $ls
你应该在那里找到两个文件:
access.log error.log
-
(Optional) If no errors were generated, the two files will be empty. You check the contents by using the
cat
Linux command or by using thetail
Linux command. As we used thecat
command before, let's use thetail
command for this example:$tail -f *.log
您应该得到如下输出:
==> access.log <== ==> error.log <==
由于该 NGINX 服务器未产生任何错误或未被访问,文件当前为空。但是,如果 NGINX 在任何时刻崩溃,产生的错误将保存在
error.log
中。
在本练习中,您学习了如何将容器的日志文件共享给主机。您使用的是 NGINX 服务器,所以如果它崩溃了,您可以从它的日志文件中追踪发生了什么。
解决方案:
-
Install SonarQube and run it as a container using the following command:
docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 sonarqube
您应该获得容器标识作为输出:
4346a99b506b1bec8000e429471dabac57e3f565b154ee921284ec685497bfae
-
Log in to SonarQube by using
admin/admin
credentials:图 8.38:登录到 SonarQube
成功登录后,将出现类似以下内容的屏幕:
图 8.39:SonarQube 仪表板
-
在右上角,单击用户。将出现一个下拉菜单。点击
My Account
: -
Scroll down and click on
Generate
underSecurity
to generate a token. You must copy it now because you will not be able to access it later:图 8.40:生成令牌
-
In Jenkins, click on
Manage Jenkins
>Plugin Manager
. Search forSonar
in theAvailable
list. Install theSonarQube Scanner
plugin.图 8.41:安装 SonarQube 扫描仪插件
-
Verify that the installation is correct by clicking on the
hit_count
project and then clicking theConfigure
option. Click onAdd build step
and thenExecute SonarQube Scanner
on theBuild
tab, as in Figure 8.43:图 8.42:选择执行声纳立方体扫描仪
-
However, the new box will generate errors, like the one shown in the following screenshot. To rectify that, integrate SonarQube and Jenkins through the
system configuration
andglobal tool configuration
options:图 8.43:由于尚未配置 SonarQube 而产生的错误
-
In Jenkins, click on
Manage Jenkins
. Click theGlobal Tool Configuration
option and then clickAdd SonarQube Scanner
:图 8.44:在全局工具配置页面上添加 SonarQube 扫描仪
-
Enter the name
SonarQube Scanner
. CheckInstall automatically
. UnderInstall from Maven Central
, inVersion
, selectSonarQube Scanner 3.2.0.1227
. Click onAdd Installer
. In theLabel
field, enterSonarQube
. In theDownload URL for binary archive
field, enter the linkhttps://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.2.0.1227-linux.zip
.点击
Save
。图 8.45:添加 SonarQube 扫描仪的详细信息
您现在已经完成了
Global Tool Configuration
选项,所以是时候转到Configure System
选项了。 -
In
Manage Jenkins
, clickConfigure System
:

图 8.46:单击管理詹金斯页面上的配置系统
- You cannot enter the system configuration right now as it asks for
Server Authentication Token
. When you click theAdd
button, it will do nothing. Enter the token as secret text in the following steps, and then return toManage Jenkins
:

图 8.47:在 Jenkins 配置中插入 SonarQube 令牌
- Click on
Manage Credentials
:

图 8.48:管理詹金斯页面
- Click on
Jenkins
:

图 8.49:詹金斯证书页面
- Click on
Global credentials (unrestricted)
:

图 8.50:全局凭证(无限制)域
- Click on
adding some credentials
:

图 8.51:添加一些凭证
- In the
Kind
drop-down menu, click onSecret text
:

图 8.52:为“种类”选择机密文本
- In the
Secret
textbox, paste the token that you copied in Step 5 in this activity. In theID
field, enterSonarQubeToken
. ClickOK
:

图 8.53:将令牌添加到机密文本框
`SonarQubeToken`将保存在`Global credentials`选项中。您将看到类似于以下内容的屏幕:

图 8.54:保存在全局凭证中的 SonarQubeToken
- Return to
Manage Jenkins
. ClickConfiguration System
and thenRefresh
. Now, in theServer Authentication Token
drop-down menu, you will findSonarQubeToken
. CheckEnable injection of SonarQube server configuration as build environment variables
. EnterSonarQube
in theName
field. Enterhttp://<your IP>:9000
in theServer URL
field. Then clickSave
:
您可以运行`ifconfig`命令获取您的 IP。您将在输出的`en0`部分找到该 IP:
```
$ ifconfig
```
这是将 Jenkins 与 SonarQube 集成的最后一步。让我们回到项目上来。
- 在
Build Environment
中,勾选Prepare SonarQube Scanner environment
。将Server authentication token
设置为SonarQubeToken
: - Now, click on the project name and then
Configure
. In theBuild
step, enter the following code in theAnalysis Properties
field:
```
sonar.projectKey=hit_count
sonar.projectName=hit_count
sonar.projectVersion=1.0
sonar.sources=.
sonar.language=py
sonar.sourceEncoding=UTF-8
# Test Results
sonar.python.xunit.reportPath=nosetests.xml
# Coverage
sonar.python.coverage.reportPath=coverage.xml
# Linter (https://docs.sonarqube.org/display/PLUG/Pylint+Report)
#sonar.python.pylint=/usr/local/bin/pylint
#sonar.python.pylint_config=.pylintrc
#sonar.python.pylint.reportPath=pylint-report.txt
```
点击`Save`。
- After saving, you will find the SonarQube logo showing on the project page, as in Figure 8.55. Click on
Build Now
:

图 8.55:显示在我们项目仪表板上的 SonarQube 选项
- In
Build History
, click onConsole Output
. You should get the screen similar to the following:

图 8.56:控制台输出
- 查看
SonarQube
上的报告。在浏览器中,键入http://<ip>:9000
或http://localhost:9000
。你会发现詹金斯自动将你的hit_count
项目添加到了索纳库: - 点击
hit_count
。你会发现一份详细的报告。每当 Jenkins 构建项目时,SonarQube 都会自动分析代码
在本练习中,您学习了如何将 Jenkins 与 SonarQube 集成并安装所需的插件,您通过在浏览器中检查 SonarQube 验证了这一点。您还将 SonarQube 应用于您的简单网络应用hit_counter
。
解决方案:
-
在詹金斯中创建一个名为
trekking
的新项目。选择它作为FREESTYLE
项目。点击OK
。 -
在
General
选项卡中,选择Discard old builds
。 -
In the
Source Code Management
tab, selectGIT
. Then enter the URLhttp://github.com/efoda/trekking_app
:图 8.57:插入 GitHub 网址
-
In
Build Triggers
, selectPoll SCM
and enterH/15 * * * *
:图 8.58:插入调度代码
-
In the
Build Environment
tab, selectPrepare SonarQube Scanner environment
. Select theServer authentication token
from the drop-down menu:图 8.59:选择 SonarQubeToken 作为服务器认证令牌
-
In the
Build
tab, enter the following code inAnalysis properties
:sonar.projectKey=trekking sonar.projectName=trekking sonar.projectVersion=1.0 sonar.sources=. sonar.language=py sonar.sourceEncoding=UTF-8 # Test Results sonar.python.xunit.reportPath=nosetests.xml # Coverage sonar.python.coverage.reportPath=coverage.xml # Linter (https://docs.sonarqube.org/display/PLUG/Pylint+Report) #sonar.python.pylint=/usr/local/bin/pylint #sonar.python.pylint_config=.pylintrc #sonar.python.pylint.reportPath=pylint-report.txt
点击
Save
。 -
Select
Build Now
. When the build is done successfully, selectConsole Output
. The following output will indicate that it finished successfully:图 8.60:验证 Jenkins 是否成功构建了映像
-
Switch to the
SonarQube
tab in the browser and check the output. The following report indicates that the trekking app has two bugs and zero security vulnerabilities:图 8.61:显示在 SonarQube 浏览器选项卡中的报告
如果您点击
New Code
,它将是空白的,因为您只构建了一次项目。当詹金斯另一次构建它时,你会发现两个构建之间的比较。 -
如果你想编辑项目的代码,把 GitHub 代码转移到你的账户上,然后编辑代码来修复错误和漏洞。编辑项目的配置,使您的 GitHub 代码代替
Source Code
选项卡中提供的代码。
在本练习中,您将 Jenkins 与 SonarQube 集成在一起,并将其应用于全景徒步旅行应用。在活动结束时,您检查了 SonarQube 生成的报告,这些报告显示了代码中的错误和漏洞。
解决方案:
您可以通过多种方式来执行此活动。这些步骤是一种方法:
-
为应用创建一个目录。在这种情况下,您将创建一个名为
Activity1
的目录,并使用cd
命令进入新目录:mkdir Activity1; cd Activity1
-
从其 GitHub 存储库中克隆应用,以确保您将拥有想要部署到群中的全景徒步应用服务所需的所有相关信息和应用:
git clone https://github.com/vincesesto/trekking_app.git
-
You won't need any of the supporting directories for NGINX, but ensure that your web service and database running are listed here, including the
panoramic_trekking_app
andphoto_viewer
directories and theDockerfile
,entrypoint.sh
,manage.py
, andrequirements.txt
scripts and files:ls -l
该命令应返回类似如下的输出:
-rw-r--r-- 1 vinces staff 533 12 Mar 15:02 Dockerfile -rwxr-xr-x 1 vinces staff 1077 12 Mar 15:02 entrypoint.sh -rwxr-xr-x 1 vinces staff 642 12 Mar 15:02 manage.py drwxr-xr-x 9 vinces staff 288 12 Mar 15:02 panoramic_trekking_app drwxr-xr-x 12 vinces staff 384 12 Mar 15:02 photo_viewer -rw-r--r-- 1 vinces staff 105 12 Mar 15:02 requirements.txt
-
在目录中创建
.env.dev
文件,并添加以下详细信息以供panoramic_trekking_app
在其settings.py
文件中使用。这些环境变量将设置数据库名称、用户、密码和其他数据库设置:SQL_ENGINE=django.db.backends.postgresql SQL_DATABASE=pta_database SQL_USER=pta_user SQL_PASSWORD=pta_password SQL_HOST=db SQL_PORT=5432 PGPASSWORD=docker
-
Create a new
docker-compose.yml
file and open it with your text editor and add in the following details:version: '3.3' services: web: build: . image: activity_web:latest command: python manage.py runserver 0.0.0.0:8000 volumes: - static_volume:/service/static ports: - 8000:8000 environment: - PGPASSWORD=docker env_file: - ./.env.dev depends_on: - db db: image: postgres volumes: - postgres_data:/var/lib/postgresql/data/ environment: - POSTGRES_PASSWORD=docker ports: - 5432:5432 volumes: postgres_data: static_volume:
从
docker-compose.yml
文件中突出显示的行可以看出,web
服务依赖于activity_web:latest
Docker 映像。 -
运行以下
docker build
命令构建映像并适当标记:docker build -t activity_web:latest .
-
It's now time to deploy the stack to Swarm. Run the following
stack deploy
command usingthe docker-compose.yml
file you have created:docker stack deploy --compose-file docker-compose.yml activity_swarm
一旦创建了网络,您应该会看到
activity_swarm_web
和activity_swarm_db
服务可用:Creating network activity_swarm_default Creating service activity_swarm_web Creating service activity_swarm_db
-
Run the
service ls
command:docker service ls
验证所有服务已成功启动并显示
1/1
副本,如我们这里所示:ID NAME MODE REPLICAS IMAGE k6kh… activity_swarm_db replicated 1/1 postgres:latest copa… activity_swarm_web replicated 1/1 activity_web:latest
-
最后,打开你的网络浏览器,确认你可以从
http://localhost:8000/admin/
和http://localhost:8000/photo_viewer/
访问网站。
全景徒步旅行应用的创建和设置方式类似于您在本章中已经完成的其他一些服务。
解决方案:
我们可以通过多种方式进行这项活动。以下步骤详细介绍了一种方法:
-
If you do not have a Swarm running, deploy the
docker-compose.yml
file you created in Activity 9.01, Deploying the Panoramic Trekking App to a Single-Node Docker Swarm:docker stack deploy --compose-file docker-compose.yml activity_swarm
如您所见,这三项服务现在都在运行:
Creating network activity_swarm_default Creating service activity_swarm_web Creating service activity_swarm_db
-
In the same directory where you have performed the
stack deploy
command, open thephoto_viewer/templates/photo_index.html
file with your text editor and change line four to match the following details, basically adding the wordPatch
to the main heading:photo_index.html
1 {% extends "base.html" %} 2 {% load static %} 3 {% block page_content %} 4 <h1>Patch Panoramic Trekking App - Photo Viewer</h1>
你可以在这里找到完整的代码https://packt.live/3ceYnta。
-
构建一个新的映像,这次使用以下命令将映像标记为【T0:
docker build -t activity_web:patch_1 .
-
Deploy the patch to your Swarm web service using the
service update
command. Provide the image name and the service the update is going to be applied to as well:docker service update --image activity_web:patch_1 activity_swarm_web
输出应该如下所示:
… activity_swarm_web overall progress: 1 out of 1 tasks 1/1: running [=======================================>] verify: Service converged
-
List the services running and verify that the new image is running as part of the
activity_swarm_web
service:docker service ls
从输出中可以看到,web 服务不再用
latest
标记。它现在显示patch_1
映像标签:ID NAME MODE REPLICAS IMAGE k6kh… activity_swarm_db replicated 1/1 postgres:latest cu5p… activity_swarm_web replicated 1/1 activity_web:patch_1
-
Verify that the changes have been applied to the image by accessing
http://localhost:8000/photo_viewer/
and seeing that the heading now showsPatch Panoramic Trekking App
:
图 9.10:全景徒步应用的补丁版本
在本活动中,您对全景徒步旅行应用进行了微小的更改,以便对服务进行滚动更新。然后,您将映像部署到运行环境中,并执行滚动更新以验证更改是否成功。标题中的更改表明滚动更新已成功执行。
10。KubernetesKubernetesKubernetesKubernetesKubernetesKubernetesKubernetesKubernetesKubernetesKubernetes
解决方案:
可以通过以下步骤创建数据库和全景徒步旅行应用:
-
Install the database with the following
helm
command:helm install database stable/postgresql --set postgresqlPassword=kubernetes
这将为 PostgreSQL 安装多个 Kubernetes 资源,并显示如下摘要:
图 10.23:数据库安装
该输出首先列出与 Helm 图表相关的信息,如名称、部署时间、状态和版本,然后是与 PostgreSQL 实例相关的信息以及如何访问它。在 Helm 图表中,这是一种广泛接受的方法,用于在安装图表后提供此类信息。否则,将很难学习如何连接到 Helm 安装的应用。
-
Create a
statefulset.yaml
file with the following content:apiVersion: apps/v1 kind: StatefulSet metadata: name: panoramic-trekking-app spec: serviceName: panoramic-trekking-app replicas: 1 selector: matchLabels: app: panoramic-trekking-app template: metadata: labels: app: panoramic-trekking-app spec: containers: - name: nginx image: packtworkshops/the-docker-workshop: chapter10-pta-nginx ports: - containerPort: 80 name: web volumeMounts: - name: static mountPath: /service/static - name: pta image: packtworkshops/the-docker-workshop: chapter10-pta-web volumeMounts: - name: static mountPath: /service/static volumeClaimTemplates: - metadata: name: static spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi
该文件创建一个名为
panoramic-trekking-app
的状态集。在spec
部分定义了两个容器,名称分别为nginx
和pta
。此外,体积索赔以名称static
定义,并安装在两个容器上。 -
Deploy the
panoramic-trekking-app
StatefulSet with the following command:kubectl apply -f statefulset.yaml
这将为我们的应用创建一个状态集:
StatefulSet.apps/panoramic-trekking-app created
-
Create a
service.yaml
file with the following content:apiVersion: v1 kind: Service metadata: name: panoramic-trekking-app labels: app: panoramic-trekking-app spec: ports: - port: 80 name: web type: LoadBalancer selector: app: panoramic-trekking-app
该服务定义有一个
LoadBalancer
类型来访问带有标签app: panoramic-trekking-app
的容器。港口80
将可以进入吊舱的web
港口。 -
Deploy the
panoramic-trekking-app
Service with the following command:kubectl apply -f service.yaml
这将创建如下服务资源:
Service/panoramic-trekking-app created
-
Get the IP of the Service with the following command:
minikube service panoramic-trekking-app --url http://192.168.64.14:32009
按照以下步骤存储 IP 以访问全景徒步应用。
-
Open the administration section of the Panoramic Trekking App in the browser with
http://$SERVICE_IP/admin
:图 10.24:管理员登录视图
-
Log in with the username
admin
and the passwordchangeme
and add new photos and countries:图 10.25:管理设置视图
-
Open the Panoramic Trekking App at
http://$SERVICE_IP/photo_viewer
in the browser:
图 10.26:应用视图
照片查看器应用显示照片和国家已从数据库中检索。它还表明应用设置正确,运行正常。
在本练习中,您已经将全景徒步旅行应用部署到了 Kubernetes 集群。您从使用 Helm 图表的数据库开始,然后为应用创建 Kubernetes 资源。最后,您从浏览器访问了该应用,并通过添加新照片对其进行了测试。在本练习结束时,您已经发现了如何使用正式的 Helm 图表来部署数据库,创建了一系列 Kubernetes 资源来连接数据库和部署应用,并从集群中收集信息来访问应用。活动中的步骤涵盖了部署在 Kubernetes 集群中的容器化应用的生命周期。
解决方案:
您可以通过多种方式创建seccomp
配置文件,阻止用户执行mkdir
、kill
和uname
命令。这些步骤展示了实现这一点的一种方法:
-
如果本地还没有
postgres
映像,执行以下命令:docker pull postgres
-
使用系统上的
wget
命令获取默认seccomp
配置文件的副本。将您正在下载的文件命名为activity1.json
:wget https://raw.githubusercontent.com/docker/docker/v1.12.3/profiles/seccomp/default.json - O activity1.json
-
Remove the following three commands from the profile to allow us to further lock down our image. Open the
activity1.json
file with your favorite text editor and remove the following lines from the file. You should look to remove lines 1500 to 1504 to remove theuname
command, 669 to 673 to remove themkdir
command, and lines 579 to 583 to remove thekill
command from being available:1500 { 1501 "name": "uname", 1502 "action": "SCMP_ACT_ALLOW", 1503 "args": [] 1504 }, 669 { 670 "name": "mkdir", 671 "action": "SCMP_ACT_ALLOW", 672 "args": [] 673 }, 579 { 580 "name": "kill", 581 "action": "SCMP_ACT_ALLOW", 582 "args": [] 583 },
您可以在以下链接找到修改后的
activity1.json
文件:https://packt.live/32BI3PK。 -
使用
postgres
映像测试新配置文件,方法是在运行时分配一个新配置文件,在运行映像时使用–-security-opt seccomp=activity1.json
选项:docker run --rm -it --security-opt seccomp=activity1.json postgres sh
-
As you are now logged on to the running container, test the new permissions of the profile you have now assigned to the container. Perform a
mkdir
command to create a new directory on the system:~ $ mkdir test
该命令应显示一个
Operation not permitted
输出:mkdir: can't create directory 'test': Operation not permitted
-
To test that you are no longer able to kill the running processes, you need to start something up. Start the
top
process and run it in the background. Do this by typingtop
into the command line and then adding&
, before pressing Enter to run the process in the background. The following command then provides the process command (ps
) to see what processes are running on the container:~ $ top & ps
从下面的输出可以看出,
top
进程运行为PID 8
:PID USER TIME COMMAND 1 20002 0:00 sh 8 20002 0:00 top 10 20002 0:00 ps [1]+ Stopped (tty output) top
注意
基于
postgres
映像的容器中没有ps
和top
命令。但是,这不会导致任何问题,因为运行带有任何随机 pid 号的kill
命令就足以证明不允许运行该命令。 -
Kill the top process by using the
kill -9
command followed by the PID number of the process you want to kill. Thekill -9
command will try to force the command to stop:~ $ kill -9 8
你应该看到
Operation not permitted
:sh: can't kill pid 8: Operation not permitted
-
Test the
uname
command. This is a little different from the other commands:~ $ uname
您将得到一个
Operation not permitted
输出:Operation not permitted
这是一个很好的活动,表明如果映像被攻击者访问,我们仍然可以做很多事情来限制对映像的处理。
解决方案:
我们可以通过多种方式扫描映像中的漏洞。以下步骤是实现这一点的一种方法,使用 Anchore 来验证postgres-app
映像对于我们的应用来说是否安全:
-
标记映像并将其推送到 Docker Hub 存储库中。在这种情况下,用我们的存储库名称标记
postgres-app
映像,并将其标记为activity2
。我们还将其推送到我们的 Docker 中心仓库:docker tag postgres <your repository namespace>/postgres-app:activity2 ; docker push <your repository name>/postgres-app:activity2
-
你应该还保留着本章中最初使用的
docker-compose.yaml
文件。如果您还没有运行 Anchore,运行docker-compose
命令并导出ANCHORE_CLI_URL
、ANCHORE_CLI_URL
和ANCHORE_CLI_URL
变量,就像您之前所做的那样,以允许我们运行anchore-cli
命令:docker-compose up -d
-
通过运行
anchore-cli system status
命令来检查锚定应用的状态:anchore-cli system status
-
使用
feeds list
命令检查订阅源列表是否全部更新:anchore-cli system feeds list
-
一旦所有的提要都被更新,添加我们已经推送到 Docker Hub 的
postgres-app
映像。使用anchore-cli
提供的image add
命令,提供我们要扫描的映像的存储库、映像和标签。这将把映像添加到我们的 Anchore 数据库,准备扫描:anchore-cli image add <your repository namespace>/postgres-app:activity2
-
使用
image list
命令,允许我们验证我们的映像已经被分析。一旦完成,您应该会在Analysis Status
栏中看到analyzed
字样:anchore-cli image list
-
Use the
image vuln
command with our image name to see a list of all the vulnerabilities found on ourpostgres-app
image. This image is a lot larger and a lot more complex than the images we have tested previously, so there is a long list of vulnerabilities found when we use theall
option. Fortunately, most of the vulnerabilities present eitherNegligible
orUnknown
. Run theimage vuln
command and pipe out the results to thewc -l
command:anchore-cli image vuln <your repository namespace>/postgres-app:activity2 all | wc -l
这将为我们提供已发现漏洞的数量。在这种情况下有 100 多个值:
108
-
Finally, use the
evaluate check
command to see whether the vulnerabilities found will give us a pass or fail:anchore-cli evaluate check <your repository namespace>/postgres-app:activity2
幸运的是,从下面的输出可以看出,我们通过了:
Image Digest: sha256:57d8817bac132c2fded9127673dd5bc7c3a97654 636ce35d8f7a05cad37d37b7 Full Tag: docker.io/vincesestodocker/postgres-app:activity2 Status: pass Last Eval: 2019-11-23T06:15:32Z Policy ID: 2c53a13c-1765-11e8-82ef-23527761d060
由于映像是由大型组织提供的,确保您可以安全使用符合他们的最佳利益,但是由于扫描映像非常容易,我们应该仍然在扫描它们,以验证它们 100%可以安全使用。
解决方案:
我们可以通过多种方式来完成本章的第一项活动。以下步骤是一种方法,使用docker stats
命令查看全景徒步应用中服务正在使用的资源。在本例中,我们将使用postgresql-app
服务,该服务作为全景徒步旅行应用的一部分运行:
-
Create a script that will create a new table and fill it with random values. The following script does exactly what we want in this situation as we want to create a long processing query and see how it affects the resources on our container. Add in the following details and save the file as
resource_test.sql
using your favorite editor:1 CREATE TABLE test_data 2 ( 3 random_value NUMERIC NOT NULL, 4 row1 NUMERIC NOT NULL, 5 row2 NUMERIC NOT NULL 6 ); 7 8 INSERT INTO test_data 9 SELECT random_value.*, 10 gen.* , 11 CEIL(RANDOM()*100) 12 FROM GENERATE_SERIES(1, 300) random_value, 13 GENERATE_SERIES(1, 900000) gen 14 WHERE gen <= random_value * 300;
第 1 行到第 6 行创建新表并设置它包含的三个不同行,而第 8 行 到第 14 行遍历一个新表,用随机值填充它。
-
如果您尚未获得 PostgreSQL Docker 映像的副本,请使用以下命令从支持的 PostgreSQL Docker Hub 存储库中提取映像:
docker pull postgres
-
Move into a new terminal window and run the
docker stats
command to view theCPU
percentage being used, as well as the memory and memory percentage being used:docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemPerc}}\t{{.MemUsage}}"
在下面的命令中,我们没有显示容器标识,因为我们想要限制输出中显示的数据量:
NAME CPU % MEM % MEM USAGE / LIMIT
-
为了简单地测试这个映像,您不需要将正在运行的容器装载到特定的卷上,就可以使用您以前用于这个映像的数据。移动到一个不同的终端来监控你的中央处理器和内存。启动容器并将其命名为
postgres-test
,并通过公开运行psql
命令所需的端口来确保可以从您的主机系统访问数据库。我们还使用环境变量(-e
)选项指定了docker
的临时密码:docker run --rm --name postgres-test -v ${PWD}/resource_test.sql:/resource_test.sql -e POSTGRES_PASSWORD=docker -d -p 5432:5432 postgres
-
在运行测试脚本之前,请转到监控 CPU 和内存使用情况的终端。您可以看到,我们的容器已经在使用一些资源,甚至没有真正做任何事情:
NAME CPU % MEM % MEM USAGE / LIMIT postgres-test 0.09% 0.47% 9.273MiB / 1.943GiB
-
使用以下命令进入容器内的 Docker:
docker exec -it postgres-test /bin/bash
-
使用
psql
命令发送postgres-test
容器命令,创建一个名为resource_test
:psql -h localhost -U postgres -d postgres -c 'create database resource_test;' Password for user postgres: CREATE DATABASE
的新数据库
-
Run the script you created earlier. Make sure you include the
time
command before you run the script as this will allow you to see the time it takes to complete:time psql -h localhost -U postgres -d resource_test -a -f resource_test.sql
我们在下面的代码块中减少了命令的输出。用数据填满
resource_database
表格需要 50 秒:Password for user postgres: … INSERT 0 13545000 real 0m50.446s user 0m0.003s sys 0m0.008s
-
移动到运行
docker stats
命令的终端。您将看到一个输出,具体取决于系统运行的内核数量和可用内存。正在运行的脚本似乎不是很占用内存,但是它将容器可用的 CPU 提升到了 100%NAME CPU % MEM % MEM USAGE / LIMIT postgres-test 100.66% 2.73% 54.36MiB / 1.943GiB
-
在可以使用对 CPU 和内存配置的更改运行容器之前,使用以下命令删除正在运行的容器,以确保有一个新的数据库正在运行:
```
docker kill postgres-test
```
- 再次运行容器。在这种情况下,您将把可用的 CPU 限制在主机系统上一个内核的一半,并且由于测试不是太占用内存,所以将内存限制设置为
256MB
:
```
docker run --rm --name postgres-test -e POSTGRES_PASSWORD=docker -d -p 5432:5432 --cpus 0.5 --memory 256MB postgres
```
- 使用
exec
命令进入容器:
```
docker exec -it postgres-test /bin/bash
```
- 同样,在运行测试之前,创建
resource_test
数据库:
```
psql -h localhost -U postgres -d postgres -c 'create database resource_test;'
Password for user postgres:
CREATE DATABASE
```
- 现在,要查看我们的资源发生了什么变化,请限制容器可以使用的内容。再次运行
resource_test.sql
脚本,通过限制资源,特别是 CPU,我们可以看到现在需要 1 分多钟才能完成:
```
time psql -h localhost -U postgres -d resource_test -a -f resource_test.sql
Password for user postgres:
…
INSERT 0 13545000
real 1m54.484s
user 0m0.003s
sys 0m0.005s
```
- 移动到运行
docker stats
命令的终端。它也应该看起来不同,因为可使用的 CPU 百分比将减半。您对 CPU 所做的更改减缓了脚本的运行速度,因此似乎也减少了正在使用的内存:
```
NAME CPU % MEM % MEM USAGE / LIMIT
postgres-test 48.52% 13.38% 34.25MiB / 256MiB
```
这个活动很好地说明了在监视和配置容器资源时,有时需要执行的平衡操作。它确实阐明了您需要了解您的服务正在执行的任务,以及配置的更改将如何影响您的服务的运行。
解决方案
我们可以通过多种方式进行这项活动。以下步骤展示了一种实现方法:
-
使用以下
docker pull
命令从hadolint
存储库中提取映像:docker pull hadolint/hadolint
-
Use
hadolint
to lint thedocker-stress
Dockerfile
we have been using throughout this chapter and document the warnings presented:docker run --rm -i hadolint/hadolint < Dockerfile
您将收到如下警告:
/dev/stdin:1 DL3006 Always tag the version of an image explicitly /dev/stdin:2 DL3008 Pin versions in apt get install. Instead of 'apt-get install <package>' use 'apt-get install <package>=<version>' /dev/stdin:2 DL3009 Delete the apt-get lists after installing something /dev/stdin:2 DL3015 Avoid additional packages by specifying '--no-install-recommends' /dev/stdin:2 DL3014 Use the '-y' switch to avoid manual input 'apt-get -y install <package>' /dev/stdin:3 DL3025 Use arguments JSON notation for CMD and ENTRYPOINT arguments
与最初测试映像时相比,没有真正的变化。但是
Dockerfile
中只有三行代码,看能否减少hadolint
呈现的警告数量。 -
如本章前面所述,
hadolint
维基页面将为您提供如何解决每个警告的详细信息。但是,如果您遍历每一行,您应该能够解决所有这些警告。第一个显示的是DL3006
,要求标记您正在使用的 Docker 映像的版本,这是 Ubuntu 映像的新版本。将您的Dockerfile
的第 1 行更改为现在包括18.08
映像版本,如图所示:1 FROM ubuntu:18.08
-
接下来的四个警告都和我们
Dockerfile
的第二行有关。DL3008
要求锁定正在安装的应用版本。在以下情况下,将应力应用固定到版本 1.0.3。DL3009
声明你应该删除任何列表。这就是我们在下面的代码中添加了第 4 行和第 5 行的地方。DL3015
声明你也应该使用--no-install-recommends
,确保你没有安装你不需要的应用。最后,DL3014
建议您包括-y
选项,以确保不会提示您验证应用的安装。编辑Dockerfile
如下所示:2 RUN apt-get update \ 3 && apt-get install -y stress=1.0.4 --no-install-recommends \ 4 && apt-get clean \ 5 && rm -rf /var/lib/apt/lists/*
-
DL3025
is your last warning and states that you need to have yourCMD
instruction in JSON format. This could cause issues as you are trying to use environment variables with your stress application. To clear up this warning, run thestress
command with thesh -c
option. This should still allow you to run the command with environment variables:6 CMD ["sh", "-c", "stress ${var}"]
您现在遵循最佳实践的完整
Dockerfile
应该如下所示:FROM ubuntu:18.04 RUN apt-get update \ && apt-get install -y stress=1.0.4 --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* CMD ["sh", "-c", "stress ${var}"]
-
现在,再次使用
hadolint
去除Dockerfile
的绒毛,不再出现警告:docker run --rm -i hadolint/hadolint < Dockerfile
-
If you want to be 100% sure that the
Dockerfile
is looking as good as it can be, perform one final test. OpenFROM:latest
in your browser and you will see theDockerfile
with the latest changes showingNo problems or suggestions found!
:
图 12.4:Docker 压力 Docker 文件现在坚持最佳实践
您的Dockerfiles
可能比本章中介绍的要大得多,但是正如您所看到的,系统的逐行方法将帮助您纠正您的Dockerfiles
可能存在的任何问题。使用诸如hadolint
和FROM latest
之类的应用,以及它们关于如何解决警告的建议,将使您熟悉最佳实践。这就把我们的活动和这一章带到了最后,但还有更多有趣的内容要走,所以现在不要停下来。
解决方案:
您可以通过多种方式来执行此活动。以下步骤就是这样一种方法:
-
确保普罗米修斯正在运行并收集数据,Docker 和
cAdvisor
被配置为公开度量,Grafana 正在运行并以普罗米修斯为数据源进行配置。 -
打开 Grafana 网络界面和您在练习 13.05:在您的系统上安装和运行 Grafana中创建的
Container Monitoring
仪表板 -
There is an
Add panel
option at the top of the dashboard and to the right of the dashboard name. Click theAdd panel
icon to add in your new dashboard panel:图 13.26:向容器监控仪表板添加一个新面板
-
从下拉列表中选择
Prometheu
s 作为我们将用于生成新仪表板面板的数据源。 -
在
metrics
部分,添加以下 PromQL 查询,container_memory_usage_bytes
,只搜索具有名称值的条目。然后,对每个名称求和,为每个容器提供一个线图:sum by (name) (container_memory_usage_bytes{name!=""})
-
Depending on the amount of data you have available in your time-series database, adjust the relative time if needed. Perhaps set the relative time to
15m
. The previous three steps are captured in the following diagram:图 13.27:向容器监控仪表板添加一个新面板
-
选择
Show options
并添加Memory Container Usage
的标题。 -
If you click on
Save
, you will notice that you cannot save the panel as the dashboard has been provisioned on startup. You can export the JSON, which you can then add to your provisioning directory. Click theShare Dashboard
button and export the JSON. SelectSave JSON to file
and store the dashboard file in the/tmp directory
:图 13.28:警告我们不能保存新的仪表板
-
停止运行您的 Grafana 容器,以便可以在您的环境中添加到配置文件。使用以下
docker kill
命令进行操作:docker kill grafana
-
在
provisioning/dashboards
目录中已经有一个名为ContainerMonitoring.json
的文件。从您的tmp
目录复制您刚刚创建的 JSON 文件,并替换provisioning/dashboards
目录中的原始文件:
```
cp /tmp/ContainerMonitoring-1579130313205.json provisioning/dashboards/ContainerMonitoring.json
```
- 再次启动 Grafana 映像,并使用默认管理密码登录应用:
```
docker run --rm -d --name grafana -p 3000:3000 -e "GF_SECURITY_ADMIN_PASSWORD=secret" -v ${PWD}/provisioning:/etc/grafana/provisioning grafana/Grafana
```
- Log in to Grafana one more time and move to the
Container Monitoring
dashboard you have been provisioning. You should now see the newly createdMemory Container usage
panel at the top of our dashboard, similar to the following screenshot:

图 13.29:显示内存使用情况的新仪表板面板
现在,这应该可以更容易地监控系统上运行的容器的内存和 CPU 使用情况。仪表板提供了比浏览docker stats
命令更简单的界面,尤其是当您开始在系统上运行更多的容器时。
解决方案:
我们可以通过多种方式进行这项活动。在这里,我们选择向我们作为全景徒步应用的一部分运行的 PostgreSQL 容器中添加一个导出器:
-
If you don't have the panoramic trekking app running, make sure that at least the PostgreSQL container is running so that you can complete this activity. You won't need to have Prometheus running yet as you will need to make some changes to the configuration file first. Run the following command to verify that the PostgreSQL database is running:
docker run --rm -d --name postgres-test -e POSTGRES_PASSWORD=docker -p 5432:5432 postgres
为了从您的 PostgreSQL 容器中收集更多的指标,您可以在 GitHub 上找到用户已经创建的导出器
albertodonato
。使用别人已经创建的比创建自己的要容易得多。文档和详细信息可在以下网址找到:https://github.com/albertodonato/query-exporter。 -
The aforementioned GitHub account has a good breakdown of how to set up the configuration and metrics. Set up a basic configuration file to get started. Find the IP address that the PostgreSQL container is running on by running the following
docker inspect
command. This gives you the internal IP address your container is running on. You'll also need to substitute the container name you have running for<container_name>
:docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container_name>
您的 IP 地址可能与这里的不同:
172.17.0.3
-
对于这个导出器,您需要设置一些额外的配置来输入导出器。首先,在工作目录中创建一个名为
psql_exporter_config.yml
的配置文件,并用文本编辑器打开该文件。 -
在下面的配置文件中输入前四行。这是导出器连接到数据库的方式。您需要提供可以访问数据库的密码,以及您在上一步中获得的 IP 地址,或者是否为数据库分配了域:
1 databases: 2 pg: 3 dsn: postgresql+psycopg2://postgres:<password>@<ip|domain>/ postgres 4
-
将第一个指标添加到配置文件中。输入以下行来添加您的度量名称、仪表类型、描述和标签:
5 metrics: 6 pg_process: 7 type: gauge 8 description: Number of PostgreSQL processes with their states 9 labels: [state] 10
-
Set up a database query to gather the metric details you want for the
pg_process
gauge. Line 13 shows that you want to create a database query with lines 14 and 15, assigning the results to the metric you created earlier. Lines 16 to 23 are the query we want to run on our database in order to create a gauge for the number of processes running on the database:psql_exporter_config.yml
11 queries: 12 process_stats: 13 databases: [pg] 14 metrics: 15 - pg_process 16 sql: > 17 SELECT 18 state, 19 COUNT(*) AS pg_process 20 FROM pg_stat_activity 21 WHERE state IS NOT NULL 22 GROUP BY state 23 FROM pg_stat_database
你可以在这里找到完整的代码https://packt.live/32C47K3。
-
保存配置文件,并从命令行运行导出程序。出口商将在港口公布其指标。装载您在本练习前面创建的配置文件。您还将获得最新版本的
adonato/query-exporter
映像:docker run -p 9560:9560/tcp -v --name postgres-exporter ${PWD}/psql_exporter_config.yml:/psql_exporter_config.yml --rm -itd adonato/query-exporter:latest -- /psql_exporter_config.yml
-
打开网络浏览器,使用网址
http://0.0.0.0:9560/metrics
查看您为作为全景徒步旅行应用的一部分运行的 PostgreSQL 容器设置的新指标:# HELP database_errors_total Number of database errors # TYPE database_errors_total counter # HELP queries_total Number of database queries # TYPE queries_total counter queries_total{database="pg",status="success"} 10.0 queries_total{database="pg",status="error"} 1.0 # TYPE queries_created gauge queries_created{database="pg",status="success"} 1.5795789188074727e+09 queries_created{database="pg",status="error"} 1.57957891880902e+09 # HELP pg_process Number of PostgreSQL processes with their states # TYPE pg_process gauge pg_process{database="pg",state="active"} 1.0
-
移动到安装了普罗米修斯的目录中,用文本编辑器打开
prometheus.yml
文件,并添加导出器详细信息,让普罗米修斯开始收集数据:45 - job_name: 'postgres-web' 46 scrape_interval: 5s 47 static_configs: 48 - targets: ['0.0.0.0:9560']
-
保存您对
prometheus.yml
文件所做的更改,并从命令行再次启动普罗米修斯应用,如下所示:
```
./prometheus --config.file=prometheus.yml
```
- If everything has worked as it should, you should now see the
postgres-web
target displayed on the PrometheusTargets
page, as demonstrated here:

图 13.30:普罗米修斯上显示的新 postgres-web 目标页面
这就把我们带到了活动的结尾和本章的结尾。这些活动应该有助于巩固之前所学的知识,并为您提供收集应用和运行系统的指标以及以更方便用户的方式显示它们的经验。
解决方案:
我们可以通过多种方式进行这项活动。以下步骤概述了一种可能的方法。
在这里,您将设置一个docker-compose.yml
文件,该文件将至少以本章中相同的方式运行您的 Splunk 容器。为了安装/opt/splunk/etc
目录和/opt/splunk/var
目录,您将设置两个卷。您需要公开端口8000
、9997
和8088
,以允许访问您的 web 界面并允许数据转发到 Splunk 实例。最后,您需要设置一些环境变量来接受 Splunk 许可证并添加管理员密码。让我们开始吧:
-
创建一个名为
docker-compose.yml
的新文件,用你喜欢的文本编辑器打开。 -
从您喜欢的版本
Docker Compose
开始,创建您要使用的卷,以便挂载var
和ext
目录:1 version: '3' 2 3 volumes: 4 testsplunk: 5 testsplunkindex: 6
-
为 Splunk 安装设置服务,使用
splunk
作为主机名,使用splunk/splunk
作为您安装时使用的映像。另外,设置SPLUNK_START_ARGS
和SPLUNK_PASSWORD
的环境变量,如下图:7 services: 8 splunk: 9 hostname: splunk 10 image: splunk/splunk 11 environment: 12 SPLUNK_START_ARGS: --accept-license 13 SPLUNK_PASSWORD: changeme
-
最后,安装卷并展示您的安装访问 web 界面和转发来自转发器和容器的数据所需的端口:
14 volumes: 15 - ./testsplunk:/opt/splunk/etc 16 - ./testsplunkindex:/opt/splunk/var 17 ports: 18 - "8000:8000" 19 - "9997:9997" 20 - "8088:8088"
-
Run the
docker-compose up
command to make sure it is all working correctly. Use the-d
option to make sure it is running as a daemon in the background of our system:docker-compose up -d
该命令应返回类似如下的输出:
Creating network "chapter14_default" with the default driver Creating chapter14_splunk_1 ... done
-
Once your Splunk installation is running again, it's time to get one of your services from the Panoramic Trekking App running so that you can forward logs to Splunk to be indexed. When using the
docker run
command, add the log driver details, as you did previously in this chapter, and make sure you include the correct token for yourHTTP Event Collector
:docker run --rm -d --name postgres-test \ -e POSTGRES_PASSWORD=docker -p 5432:5432 \ --log-driver=splunk \ --log-opt splunk-url=http://127.0.0.1:8088 \ --log-opt splunk-token=5c051cdb-b1c6-482f-973f-2a8de0d92ed8 \ --log-opt splunk-insecureskipverify=true \ --log-opt tag="{{.Name}}/{{.FullID}}" \ postgres -c log_statement=all
注意
请注意,我们在
docker run
命令中使用了-c log_statement=all
,因为这将确保我们所有的 PostgreSQL 查询都被记录并发送到 Splunk。 -
Log in to the Splunk web interface and access the
Search & Reporting
app. Enter thesource="http:docker logs" AND postgres-test
query into the interface and press Enter. Since you have tagged our container, you should see your containers tagged with the name and full ID, so addingpostgres-test
to your search will make sure only your PostgreSQL logs are visible:
图 14.48:Splunk 中显示的 PostgreSQL 日志
从前面的截图中可以看到,我们的日志成功地流过了 Splunk。记下添加到日志条目中的标记,如前面的截图所示。
这个活动教我们如何使用 Docker Compose 在我们的开发项目中实现日志记录过程。
解决方案:
您可以通过多种方式来执行此活动。以下步骤是实现这一点的一种方法。在这里,您将向作为全景徒步应用一部分运行的PostgreSQL
容器添加一个导出器:
-
确保 Splunk 正在运行,并且您一直监控的服务已经运行了一段时间,以确保您正在为此活动收集一些日志。
-
Log in to the Splunk web interface. From the Splunk home screen, click on the cog icon next to the
Apps
menu; you will be presented with theApps
page for your Splunk environment:图 14.49:Splunk 环境的应用页面
-
Click the
Create
app button and fill in the form. It will be similar to the following, in whichName
is set toPanoramic Trekking App
,Folder name
is set topanoramic_trekking_app
, andVersion
is set to1.0.0
. ClickSave
to create the new app:图 14.50:在 Splunk 中创建新应用
-
Return to the Splunk home page and make sure your
Panoramic Trekking App
is visible from theApps
menu. ClickPanoramic Trekking App
to bring up theSearch & Reporting
page so that you can start querying your data:图 14.51:选择全景徒步应用
-
Type
source="http:docker logs" AND postgres-test AND INSERT AND is_superuser | stats count
into the query bar and press Enter. The search will look for anySuper Users
that were created as part of the application. When your data comes up, click theVisualization
tab and change it to display a single-value visualization:图 14.52:在查询栏中输入查询
-
Click the
Save As
button at the top of the screen and select theDashboards
panel. When you are presented with this screen, select the panel to be added to a new dashboard and give it the namePTA Monitoring
. Also, give the panel the titleSuper User Access
and clickSave
:图 14.53:向仪表板面板添加细节
-
When you are presented with your new dashboard, click the
Edit
andAdd
panel buttons. SelectNew
and thenSingle Value
as the visualization type. SetContent Title
toDatabase Creation
. Add thesource="http:docker logs" AND postgres-test AND CREATE DATABASE | stats count
source string and clickSave
. This will search through your logs to show if anyone has created any databases on the PostgreSQL database, which should only happen when the app is set up and created:图 14.54:编辑仪表板面板
-
再次单击
New Panel
按钮,从可视化效果中选择New
和Column Chart
。添加App Usage
的Content Title
,添加source="http:docker logs" AND postgres-test AND SELECT AND photo_viewer_photo earliest=-60m | timechart span=1m count
搜索查询,点击Save
。该搜索将为您提供一段时间内使用该应用查看您照片的人数。 -
Feel free to move the panels around the dashboard. When you are happy with the changes, click the
Save
button. Your dashboard should look similar to the following:
图 14.55:用于监控 PostgreSQL 使用情况的新仪表板面板
这项活动帮助您为全景徒步旅行应用收集日志数据,并使用 Splunk 以更加用户友好的方式显示这些数据。
解决方案:
通过以下步骤,可以使用卷和网络插件为数据库和 WordPress 博客创建容器:
-
Create a network with the following command:
docker network create \ --driver=store/weaveworks/net-plugin:2.5.2 \ --attachable \ wp-network
该命令使用编织网插件创建一个网络,用
driver
标志指定。此外,卷被指定为attachable
,这意味着您将来可以连接到 Docker 容器。最后,容器的名称将是wp-network
。您应该得到如下输出:mk0pmhpb2gx3f6s00o57j2vd
-
Create a volume with the following command:
docker volume create -d vieux/sshfs \ --name wp-content \ -o sshcmd=root@localhost:/tmp \ -o password=root \ -o port=2222
该命令使用
vieux/sshfs
插件通过 SSH 创建一个卷。该卷的名称为wp-content
,并为ssh
命令、端口和密码传递了其他选项:wp-content
-
Create the
mysql
container with the following command:docker run --name mysql -d \ -e MYSQL_ROOT_PASSWORD=wordpress \ -e MYSQL_DATABASE=wordpress \ -e MYSQL_USER=wordpress \ -e MYSQL_PASSWORD=wordpress \ --network=wp-network \ mysql:5.7
该命令以分离模式运行
mysql
容器,使用环境变量和wp-network
连接。 -
Create the
wordpress
container with the following command:docker run --name wordpress -d \ -v wp-content:/var/www/html/wp-content \ -e WORDPRESS_DB_HOST=mysql:3306 \ -e WORDPRESS_DB_USER=wordpress \ -e WORDPRESS_DB_PASSWORD=wordpress \ -e WORDPRESS_DB_NAME=wordpress \ --network=wp-network \ -p 8080:80 \ wordpress
该命令使用环境变量和
wp-network
连接以分离模式运行wordpress
容器。此外,容器的80
港在主机系统的8080
港可用。成功启动后,您将有两个容器运行
mysql
和wordpress
:docker ps
图 15.17:WordPress 和数据库容器
-
Open
http://localhost:8080
in your browser to check the WordPress setup screen:
图 15.18: WordPress 设置屏幕
WordPress 安装屏幕验证是否使用网络和卷插件安装了 WordPress。
在本练习中,您已经使用编织网插件创建了一个自定义网络,并使用sshfs
插件创建了一个自定义卷。您创建了一个使用自定义网络的数据库容器和一个使用自定义网络和自定义卷的 WordPress 容器。设置成功后,您的 Docker 容器通过自定义网络相互连接,并通过 SSH 使用卷。在本练习中,您将 Docker 扩展用于实际应用。现在,您可以放心地用您的定制业务需求和技术扩展 Docker。