Container-less Java services

Container-less Java services

Development and deployment strategies for building container-less apps in Java inspired by lessons from the 12 Factor App, a methodology for building software-as-a-service apps for the cloud.

Cross posted from https://slides.com/gilday/container-less-java-services

0e8b10cb5bf78ffa5669bb0d770d0ba9?s=128

Johnathan Gilday

July 14, 2016
Tweet

Transcript

  1. 2.

    Some motivation Some motivation At , we build a lot

    of services for our customers, often in Java Our services are getting trimmer and more plentiful Our development teams continue to become more involved with the infrastructure management and production operations We need to optimize the way we build and deploy these services. Abandoning the shared application container is one way Next Century
  2. 3.

    What do I mean by What do I mean by

    container? container? Also called "application server" One process Manages one or more archives (WAR, EAR) Examples: Apache Tomcat, Wildfly, Glassfish
  3. 4.

    To deploy application To deploy application archives to running archives

    to running containers: containers: 1. Install container 2. Build WAR 3. Push WAR to container
  4. 5.

    Containers Manage your Containers Manage your app's lifecycle app's lifecycle

    @WebListener public class AppInitializer implements ServletContextListener { @Override public void contextInitialized(final ServletContextEvent sce) { // container calls this method to initialize app } @Override public void contextDestroyed(final ServletContextEvent sce) { // container calls this method to shutdown app } } @WebServlet(name = "my-servlet", urlPatterns = "/my-servlet") public class MyServlet extends HttpServlet { @Override public void doGet(final HttpServletRequest req, final HttpServletResponse resp) { // container calls this method on incoming request } }
  5. 7.

    Development Team Development Team Delivers a packaged application with dependencies

    outside of those provided by the container Delivers a configuration files and documentation for running the service Operations Team Operations Team Configures the operating system Configures the container May run multiple applications on the container Containers work best when there is a clear Containers work best when there is a clear separation of responsibilities between teams separation of responsibilities between teams developing and maintaining the application developing and maintaining the application
  6. 9.

    Discrepancies between Discrepancies between application and container application and container

    cause headaches cause headaches Logging Configuration Dependencies (classpath) Test environments (mvn jetty:run vs container)
  7. 10.

    Environment discrepancies Environment discrepancies cause headaches cause headaches Different versions

    of dependencies on the classpath Example: container provides a different version of servlet than the one used in testing Containers use varying strategies for classpath loading
  8. 12.

    Self-contained apps Self-contained apps Live in their own process Embed

    their own web server Embed all their own dependencies Define their own means of configuration
  9. 14.

    Runs in its own process Runs in its own process

    /** * Point of entry. Configure and start app */ public class App { public static void main(final String[] args) { // configure application // register signal handlers if desired // run web server until app terminates } } Embraces Unix process model: use of environment variables, command-line args, signals, STDOUT, STDERR Easy to run Development and Production parity
  10. 15.

    Embeds its own server Embeds its own server public static

    void main(final String[] args) { // start jetty logger.info("listening on port {}", port); final ResoureConfig rc = ResourceConfig.forApplication(app); final Server server = JettyHttpContainerFactory.createServer(baseUri, rc); try { server.start(); server.join(); } catch (Exception e) { throw new RuntimeException(e); } } "Don’t deploy your application in Jetty, deploy Jetty in your application!" Integrates well with Jersey via JettyHttpContainerFactory Jetty is not the only solution, but it’s mature and lightweight
  11. 16.

    Bundles its dependencies Bundles its dependencies $ gradle shadowJar $

    java -jar build/libs/my-app.jar listening on port 8000 Sometimes called "fat jar", "uber jar", or "shadow jar" Prefer all dependencies bundled together: we don't want dependencies to change after building Easier than using shell scripts to build up a classpath argument for java Include main class in jar manifest
  12. 17.

    Logs to STDOUT Logs to STDOUT <?xml version="1.0" encoding="UTF-8"?> <configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-5level %logger{5} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="STDOUT" /> </root> </configuration> Easy debugging One stream of log statements with flexible downstream processing Let production environment worry about filtering, rotation
  13. 18.

    What does What does running running a self- a self-

    contained app contained app look like? look like?
  14. 19.

    systemd managed daemon systemd managed daemon [Unit] Description=my-app service [Service]

    EnvironmentFile=-/etc/sysconfig/my-app ExecStart=/usr/bin/java \$JVM_OPTS -jar /opt/my-app/my-app-${version}-all.jar User=my-app Restart=on-failure [Install] WantedBy=multi-user.target Much easier than systemctl for start, stop, status, restart Automatic start on system boot Note: same command to start as used in development init scripts
  15. 20.

    don't forget journald don't forget journald [vagrant@localhost ~]$ journalctl -fu

    how-to-microservice -- Logs begin at Thu 2016-07-14 00:07:10 UTC. -- Jul 14 00:08:44 localhost.localdomain systemd[1]: Started how-to-microservice ser Jul 14 00:08:44 localhost.localdomain systemd[1]: Starting how-to-microservice se Jul 14 00:08:44 localhost.localdomain java[11726]: 2016-07-14T00:08:44,718Z INFO Captures STDOUT, STDERR from systemd managed daemons Easy to tail and grep your app's logs Highly configurable: log rotation, format, forwarding
  16. 22.

    RPM RPM $ rpm -qlp build/distributions/how-to-microservice-0.0.4-1.e7.noarch.rpm /etc/how-to-microservice /etc/how-to-microservice/settings.conf /etc/sysconfig /etc/sysconfig/how-to-microservice

    /etc/systemd /etc/systemd/system /etc/systemd/system/how-to-microservice.service /opt/how-to-microservice /opt/how-to-microservice/how-to-microservice-0.0.4-SNAPSHOT-all.jar /opt/how-to-microservice/settings.conf Packages user install, config file template, systemd unit file, binaries, package dependencies (java) Host your own yum repository for easy updates (jenkins, Nexus) Establishes a common means for deploying your container-less apps
  17. 23.

    Build RPM with Gradle Build RPM with Gradle task rpm(type:

    Rpm) { it.dependsOn shadowJar packageName = project.name version = '0.0.4' release = '1.e7' os = LINUX requires('java-1.8.0-openjdk') ... Netflix OSS nebula-ospackage-plugin gradle plugin Builds deb and rpm Host your own yum repository with Nexus, deploy with Gradle
  18. 24.

    Test RPM with Vagrant Test RPM with Vagrant Vagrant.configure(2) do

    |config| config.vm.box = "geerlingguy/centos7" ... config.vm.provision "shell", inline: <<-SHELL sync_dir=/vagrant rpm=($sync_dir/build/distributions/*.rpm) if [ ! -f $rpm ]; then echo "how-to-microservice RPM not found" exit 1 fi sudo yum erase -y how-to-microservice sudo yum install -y $rpm sudo systemctl restart how-to-microservice SHELL end Vagrant defines development virtual machines with Ruby DSL Vagrant shell provisioner installs RPM in the Vagrant virtual machine Rebuild virtual machine: vagrant destroy ­f && vagrant up
  19. 26.

    Dockerfile Dockerfile FROM java:8 MAINTAINER Johnathan Gilday COPY ./build/libs/how-to-microservice.jar /opt/how-to-microservice/

    EXPOSE 8000 WORKDIR /opt/how-to-microservice CMD ["java", "-jar", "how-to-microservice.jar"] Manage a docker container instead of a systemd service Manage logs with docker log driver instead of journald Deploy a docker image instead of an RPM
  20. 27.

    Thanks! Thanks! Sample self-contained jersey service Gradle plugin for building

    "fat jars" Netflix OSS Gradle plugins (including gradle-os-package) Nexus yum repository hosting Vagrant shell provisioner https://github.com/gilday/how-to-microservice https://github.com/johnrengelman/shadow https://nebula-plugins.github.io/ https://books.sonatype.com/nexus-book/reference/yum-configuration.html https://www.vagrantup.com/docs/provisioning/shell.html