Ansible Java案例

wen java案例 1

本文目录导读:

Ansible Java案例

  1. 项目结构
  2. 配置文件
  3. Roles实现
  4. Playbook实现
  5. Variables文件
  6. Handler定义
  7. 部署命令
  8. 高级特性示例

我来提供一个完整的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 &quot;%r&quot; %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部署方案包含了:

  1. 结构化的目录设计 - 便于维护和扩展
  2. 角色复用 - Java环境和Tomcat配置独立封装
  3. 环境配置管理 - 通过变量控制不同环境
  4. 服务管理 - 使用systemd管理服务生命周期
  5. 健康检查 - 自动验证部署是否成功
  6. 滚动更新 - 支持零停机部署
  7. 备份机制 - 部署前自动备份
  8. Docker容器化 - 支持容器部署方式

你可以根据实际需求调整,比如添加CI/CD集成、监控告警、日志采集等。

抱歉,评论功能暂时关闭!