在这个例子中,我们要用Ansible和Jenkins将一个Docker化的Symfony应用部署到生产服务器上。当涉及到生产环境时,总是很棘手,所以Docker相关的文件与开发环境以及其他构建文件相比,有点不同。你会看到下面的区别。
Jenkins服务器的必备条件
确保你涵盖了以下几点。
-
完成了GitHub集成。
-
Ansible已经安装。
-
Jenkins用户可以运行
ansible命令。 -
Ansible可以通过SSH进入生产服务器。
-
在终端上登录Docker Hub,这样
jenkins用户就可以推送镜像。
我们有Jenkins和生产,详情如下。
-
Jenkins:
192.168.99.40/jenkins(IP/用户)。 -
生产:
192.168.99.30/vagrant(IP/用户)。
我们也有一个MySQL服务器(而不是一个容器),详情如下。
DB_VERS=5.7.24
生产服务器的先决条件
准备SSH GitHub集成,并确保它可以运行docker 命令。
流程
Jenkins和GitHub的集成已经设置好了,所以当我把一个PR合并到master 分支时,GitHub会与Jenkins沟通,在那里运行部署管道。
**替代方案。**这个例子利用git将仓库克隆到生产服务器上,并构建docker镜像/容器。然而,如果你更喜欢把应用程序复制到容器图像中,并消除存储库克隆步骤,你可以阅读这篇文章。在这种情况下,你的Jenkinsfile将改变为构建和推送图像到Docker Hub。之后,一个阶段需要把相关的docker文件复制到生产服务器,然后运行docker pull/build/up命令。
-
Jenkins检查到
master分支。 -
Ansible SSH进入生产服务器并开始部署过程。
-
创建应用程序目录。
-
克隆应用程序仓库。
-
拷贝金库加密的
.env文件。 -
启动应用程序。
-
安装
secure-delete包。 -
安全地删除所有复制过来的东西。
MySQL服务器的先决条件
下面的步骤是一次性的,必须在一切之前完成。
允许外部连接
修改bind-address ,如下所示。
mysql-server$ cat /etc/mysql/my.cnf | grep bind-address
用mysql-server$ mysql -u root -proot 登录到MySQL服务器,并做以下工作。
新的数据库和有权限的用户
创建一个名为sport 的新数据库。
mysql> CREATE DATABASE `sport` CHARACTER SET `UTF8mb4` COLLATE `utf8mb4_unicode_ci`;
创建一个新的用户inanzzz ,在sport 数据库上拥有所有权限。
mysql> USE mysql;
结构
我将列出dev 环境文件,只是为了让你与prod 环境文件进行比较,以便你知道它们有什么不同。主要的区别在于Dockerfile、Makefile和docker-compose.yml文件。
.
开发文件
docker/dev/docker-compose.yml
version: "3.4"
docker/dev/Makefile
PHP_SERVICE := football_php
docker/dev/nginx/app.conf
server {
docker/dev/nginx/nginx.conf
user nginx;
docker/dev/nginx/Dockerfile
FROM nginx:1.15.8-alpine
docker/dev/php/php.ini
[PHP]
docker/dev/php/www.conf
[global]
docker/dev/php/Dockerfile
FROM php:7.2.13-fpm-alpine3.8
程序文件
docker/prod/docker-compose.yml
如果我们不为每次部署的 "命名卷 "使用不同的名称,我们在应用程序代码中所做的修改将无法在生产中使用。因此,我用来自Jenkins的BUILD_ID 环境变量作为它的后缀。Ansible playbook将其传递给Makefile,然后Makefile将其传递给docker-compose.yml。另外,我把应用程序的源代码放在名为football_php 的 "php容器 "中,让nginx服务通过 "命名卷 "访问它。
重要提示: net.core.somaxconn 是可选的,但很重要。这是一个Linux内核参数,指定了TCP/IP套接字的最大积压量。默认值是128 。基于我们的修改,内核将允许一次最多有1000 个连接被 "积压"。这个值不能超过USHRT_MAX常数的最大限制,即65535 。在/usr/local/etc/php-fpm.d/www.conf 文件中的listen.backlog 选项也是同样的值。你可以在PHP-FPM容器中运行sysctl net.core.somaxconn 命令来验证它。简而言之,如果你期望有太多的并发请求,那么最好保持高值。我用$ ab -n 10000 -c 1000 http://0.0.0.0:80/ 命令总共发送了10000:1000 (total:concurrent)个请求,全部通过。如果我把这个值降低到500 ,那么大约有300个请求失败。另一个例子,Redis需要很多连接,所以它也需要一个高数值。
version: "3.4"
docker/prod/Makefile
build:
docker/prod/nginx/app.conf
不要像我在生产中那样忽略了SSL证书和端口443!
server {
docker/prod/nginx/nginx.conf
user nginx;
docker/prod/nginx/Dockerfile
FROM nginx:1.15.8-alpine
docker/prod/php/php.ini
[PHP]
docker/prod/php/www.conf
[global]
docker/prod/php/Dockerfile
FROM php:7.2.13-fpm-alpine3.8
CICD文件
cicd/merge/master/Jenkinsfile
pipeline {
cicd/provision/prod/hosts.yml
如果你愿意,你可以用Ansible vault来加密。
all:
cicd/provision/prod/site.yml
如果你愿意,你可以在这里准备MySQL服务器,也可以使用一些环境变量。
---
其他文件
.dockerignore
cicd/
.env
APP_ENV=dev
config/packages/doctrine.yaml
doctrine:
DefaultContainer.php
class EnvironmentController
Ansible Vault 加密
生产环境的.env 文件包含秘密/重要的值,所以我们需要在Jenkins服务器上创建它,并使用ansible-vault 命令对其进行加密。当部署开始时,它将在幕后被解密(在stage('Deploy') )并传输到生产服务器。这些值将被设置为php容器中的环境变量,我们的Symfony应用程序将使用它们而不是在.env 文件中设置的相同变量。这就是它的工作原理,环境变量有优先权!
jenkins@jenkins-server:$ mkdir -p ~/app-secrets/sport
实际内容如下所示。
APP_ENV=prod
测试生产管道
假设我们已经合并了一个PR信息master 分支。
Running on Jenkins in /var/lib/jenkins/workspace/sport-merge-master
检查生产服务器
正如你所看到的,我们在开始时创建的/home/vagrant/sport ,在部署过程结束时已被删除。
vagrant@production:~$ ls -l
vagrant@production:~$ docker images
vagrant@production:~$ docker ps -a
vagrant@production:~$ docker volume ls
vagrant@production:~$ docker inspect football_source_code_17
vagrant@production:~$ sudo ls -l /var/lib/docker/volumes/football_source_code_17/_data
检查应用程序
vagrant@production:~$ curl -i http://0.0.0.0:80 # or http://0.0.0.0 | http://localhost