本文目录导读:

我来提供一个完整的Ansible自动化部署Java应用的实战案例,涵盖从环境准备到应用部署的全过程。
项目结构
ansible-java-deploy/
├── ansible.cfg
├── inventory/
│ ├── production.ini
│ └── staging.ini
├── playbooks/
│ ├── deploy-java-app.yml
│ ├── setup-java-env.yml
│ └── install-docker.yml
├── roles/
│ ├── java/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ └── templates/
│ │ └── application.yml.j2
│ └── tomcat/
│ ├── tasks/
│ │ └── main.yml
│ ├── handlers/
│ │ └── main.yml
│ └── templates/
│ └── server.xml.j2
└── vars/
└── common.yml
配置文件
ansible.cfg
[defaults] inventory = inventory/production.ini remote_user = deploy host_key_checking = False roles_path = roles gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/ansible_cache fact_caching_timeout = 300 [ssh_connection] pipelining = True control_path = /tmp/ansible-%%h-%%p-%%r
inventory/production.ini
[java-servers] web01 ansible_host=192.168.1.10 web02 ansible_host=192.168.1.11 [tomcat-servers] app01 ansible_host=192.168.1.20 app02 ansible_host=192.168.1.21 [database] db01 ansible_host=192.168.1.30 [all:vars] ansible_python_interpreter=/usr/bin/python3 app_version=1.2.3 DEPLOY_USER=deploy DEPLOY_GROUP=deploy [java-servers:vars] java_version=17 jdk_archive=jdk-17_linux-x64_bin.tar.gz [tomcat-servers:vars] tomcat_version=9.0.85 tomcat_major_version=9
Roles实现
roles/java/tasks/main.yml
---
- name: Create Java installation directory
ansible.builtin.file:
path: "{{ java_install_dir }}"
state: directory
owner: "{{ DEPLOY_USER }}"
group: "{{ DEPLOY_GROUP }}"
mode: '0755'
- name: Download JDK
ansible.builtin.get_url:
url: "{{ java_download_url }}/{{ jdk_archive }}"
dest: "/tmp/{{ jdk_archive }}"
owner: "{{ DEPLOY_USER }}"
group: "{{ DEPLOY_GROUP }}"
mode: '0644'
timeout: 120
register: download_jdk
- name: Extract JDK
ansible.builtin.unarchive:
src: "/tmp/{{ jdk_archive }}"
dest: "{{ java_install_dir }}"
remote_src: yes
owner: "{{ DEPLOY_USER }}"
group: "{{ DEPLOY_GROUP }}"
create: true
when: download_jdk is success
- name: Create symlink for Java
ansible.builtin.file:
src: "{{ java_install_dir }}/jdk-{{ java_version }}"
dest: "{{ java_home }}"
state: link
owner: "{{ DEPLOY_USER }}"
group: "{{ DEPLOY_GROUP }}"
- name: Configure JVM options
ansible.builtin.template:
src: jvm.options.j2
dest: "{{ java_home }}/conf/jvm.options"
owner: "{{ DEPLOY_USER }}"
group: "{{ DEPLOY_GROUP }}"
notify: Restart Java services
- name: Set JAVA_HOME in profile
ansible.builtin.lineinfile:
path: /etc/profile.d/java.sh
line: |
export JAVA_HOME={{ java_home }}
export PATH=$JAVA_HOME/bin:$PATH
create: yes
mode: '0755'
owner: root
group: root
- name: Copy application configuration
ansible.builtin.template:
src: application.yml.j2
dest: "{{ app_home }}/config/application.yml"
owner: "{{ DEPLOY_USER }}"
group: "{{ DEPLOY_GROUP }}"
mode: '0644'
roles/java/templates/application.yml.j2
# Java Application Configuration
server:
port: {{ app_port | default(8080) }}
address: 0.0.0.0
spring:
application:
name: {{ app_name }}
datasource:
url: jdbc:mysql://{{ db_host }}:{{ db_port }}/{{ db_name }}?createDatabaseIfNotExist=true&useSSL=false
username: {{ db_user }}
password: "{{ db_password }}"
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: none
show-sql: false
logging:
level:
com.example: INFO
org.hibernate.SQL: DEBUG
management:
endpoints:
web:
exposure:
include: "*"
roles/tomcat/tasks/main.yml
---
- name: Create Tomcat user
ansible.builtin.user:
name: tomcat
system: yes
shell: /sbin/nologin
home: "{{ tomcat_home }}"
create_home: yes
- name: Download Tomcat
ansible.builtin.get_url:
url: "https://archive.apache.org/dist/tomcat/tomcat-{{ tomcat_major_version }}/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz"
dest: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
timeout: 120
- name: Extract Tomcat
ansible.builtin.unarchive:
src: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
dest: "{{ tomcat_home }}/.."
remote_src: yes
creates: "{{ tomcat_home }}"
- name: Set ownership
ansible.builtin.file:
path: "{{ tomcat_home }}"
owner: tomcat
group: tomcat
recurse: yes
- name: Create systemd service file
ansible.builtin.template:
src: tomcat.service.j2
dest: /etc/systemd/system/tomcat.service
mode: '0644'
notify: Reload systemd
- name: Start and enable Tomcat
ansible.builtin.systemd:
name: tomcat
state: started
enabled: yes
daemon_reload: yes
- name: Configure Tomcat server
ansible.builtin.template:
src: server.xml.j2
dest: "{{ tomcat_home }}/conf/server.xml"
owner: tomcat
group: tomcat
mode: '0644'
notify: Restart Tomcat
roles/tomcat/templates/server.xml.j2
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="{{ app_port | default(8080) }}" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxThreads="200"
minSpareThreads="50"
acceptCount="100" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
Playbook实现
playbooks/setup-java-env.yml
---
- name: Setup Java Environment
hosts: java-servers
become: yes
vars_files:
- ../vars/common.yml
roles:
- role: java
when: ansible_os_family == "RedHat" or ansible_os_family == "Debian"
tasks:
- name: Install MySQL client
ansible.builtin.package:
name: mysql
state: present
when: ansible_os_family == "RedHat"
- name: Install JDBC driver
ansible.builtin.copy:
src: mysql-connector-j-8.0.33.jar
dest: "{{ app_home }}/lib/"
owner: "{{ DEPLOY_USER }}"
group: "{{ DEPLOY_GROUP }}"
mode: '0644'
- name: Setup Tomcat Servers
hosts: tomcat-servers
become: yes
tasks:
- name: Install Tomcat
include_role:
name: tomcat
- name: Deploy application WAR
ansible.builtin.copy:
src: "{{ app_artifact }}"
dest: "{{ tomcat_home }}/webapps/{{ app_name }}.war"
owner: tomcat
group: tomcat
mode: '0644'
when: deploy_type == "war"
notify: Restart Tomcat
playbooks/deploy-java-app.yml
---
- name: Deploy Java Application
hosts: tomcat-servers
become: yes
vars_files:
- ../vars/common.yml
environment:
JAVA_HOME: "{{ java_home }}"
PATH: "{{ java_home }}/bin:{{ ansible_env.PATH }}"
pre_tasks:
- name: Update package cache
ansible.builtin.apt:
update_cache: yes
when: ansible_os_family == "Debian"
- name: Stop Tomcat before deployment
ansible.builtin.systemd:
name: tomcat
state: stopped
when: rolling_update == false
roles:
- java
- tomcat
tasks:
- name: Backup old application
ansible.builtin.command: cp -r {{ tomcat_home }}/webapps/{{ app_name }} /backup/{{ app_name }}-{{ lookup('pipe', 'date +%Y%m%d_%H%M%S') }}
become: yes
- name: Deploy new WAR file
ansible.builtin.copy:
src: "{{ app_artifact }}"
dest: "{{ tomcat_home }}/webapps/{{ app_name }}.war"
backup: yes
- name: Update application configuration
ansible.builtin.template:
src: config.j2
dest: "{{ app_home }}/config/application.properties"
when: update_config
post_tasks:
- name: Start Tomcat if stopped
ansible.builtin.systemd:
name: tomcat
state: started
when: rolling_update == false
- name: Health check
ansible.builtin.uri:
url: "http://{{ inventory_hostname }}:{{ app_port }}/{{ app_name }}/health"
return_content: yes
timeout: 30
register: health_result
until: health_result.status == 200
retries: 10
delay: 5
- name: Report deployment status
ansible.builtin.debug:
msg: "Deployment successful to {{ inventory_hostname }}"
when: health_result.status == 200
playbooks/install-docker.yml (可选容器化部署)
---
- name: Install Docker and Deploy Java App in Container
hosts: docker-hosts
become: yes
tasks:
- name: Install Docker packages
ansible.builtin.package:
name:
- docker
- docker-compose
state: present
- name: Start Docker service
ansible.builtin.systemd:
name: docker
state: started
enabled: yes
- name: Create Dockerfile
ansible.builtin.template:
src: Dockerfile.j2
dest: /opt/{{ app_name }}/Dockerfile
- name: Create docker-compose.yml
ansible.builtin.template:
src: docker-compose.yml.j2
dest: /opt/{{ app_name }}/docker-compose.yml
- name: Build Docker image
ansible.builtin.command:
cmd: docker build -t {{ app_name }}:{{ app_version }} .
chdir: /opt/{{ app_name }}
- name: Deploy container
ansible.builtin.command:
cmd: docker-compose up -d
chdir: /opt/{{ app_name }}
- name: Verify container is running
ansible.builtin.shell: docker ps --filter name=app-{{ app_name }} --format "{{ '{{.Names}}' }} {{ '{{.Status}}' }}"
register: container_status
- name: Show container status
ansible.builtin.debug:
var: container_status.stdout_lines
Variables文件
vars/common.yml
---
# Application settings
app_name: myapp
app_version: 1.2.3
app_port: 8080
app_home: /opt/myapp
deploy_type: war
# Java settings
java_version: 17
java_home: /opt/java/jdk-17
java_install_dir: /opt/java
java_download_url: https://download.java.net/java/GA/jdk17
# Database settings
db_host: db.example.com
db_port: 3306
db_name: myapp
db_user: myapp
db_password: SecurePass123
# Deployment settings
rolling_update: true
update_config: true
artifact_name: myapp-{{ app_version }}.war
app_artifact: /tmp/{{ artifact_name }}
# SMTP settings (if needed)
smtp_host: smtp.example.com
smtp_port: 587
Handler定义
roles/tomcat/handlers/main.yml
---
- name: Restart Tomcat
ansible.builtin.systemd:
name: tomcat
state: restarted
- name: Reload systemd
ansible.builtin.systemd:
daemon_reload: yes
roles/java/handlers/main.yml
---
- name: Restart Java services
ansible.builtin.systemd:
name: "{{ item }}"
state: restarted
loop:
- java-application
- java-service
when: java_services is defined
部署命令
# 检查语法 ansible-playbook playbooks/deploy-java-app.yml --syntax-check # 部署到生产环境 ansible-playbook playbooks/deploy-java-app.yml -i inventory/production.ini # 部署到测试环境 ansible-playbook playbooks/deploy-java-app.yml -i inventory/staging.ini # 只更新配置 ansible-playbook playbooks/deploy-java-app.yml --tags=config # 滚动更新 ansible-playbook playbooks/deploy-java-app.yml -e "rolling_update=true" # 指定特定主机 ansible-playbook playbooks/deploy-java-app.yml --limit web01 # 跳过健康检查 ansible-playbook playbooks/deploy-java-app.yml -e "skip_health_check=true"
高级特性示例
滚动更新策略
- name: Rolling update deployment
hosts: all
serial: 1
strategy: free
tasks:
- name: Deploy to current host
ansible.builtin.include_tasks: deploy.yaml
蓝绿部署
- name: Blue-green deployment
hosts: load_balancer
tasks:
- name: Switch traffic to blue
ansible.builtin.command: ./switch_to_blue.sh
- name: Verify blue deployment
ansible.builtin.uri:
url: "http://blue.example.com/health"
status: 200
这个完整的Ansible Java部署方案包含了:
- 结构化的目录设计 - 便于维护和扩展
- 角色复用 - Java环境和Tomcat配置独立封装
- 环境配置管理 - 通过变量控制不同环境
- 服务管理 - 使用systemd管理服务生命周期
- 健康检查 - 自动验证部署是否成功
- 滚动更新 - 支持零停机部署
- 备份机制 - 部署前自动备份
- Docker容器化 - 支持容器部署方式
你可以根据实际需求调整,比如添加CI/CD集成、监控告警、日志采集等。