Open petermicuch opened 3 years ago
The docker file is an example of what you can do. If you prefer not to have it hosted at ROOT, then rename the war to eg. 'myfancyurl.war'. If you deploy myfancyurl.war to tomcat, the content will be hosted under https://host.docker.internal/myfancyurl (UI) and https://host.docker.internal/myfancyurl/fhir (the actual FHIR endpoint). Remember to reconfigure the tester as well in that case
@jvitrifork thanks for your response. I get what you are saying. I just wanted to somehow bring together all different settings that are in hapi with regards to subpath/base url. I.e. this should also be reflected to tester server settings. But that might be specific to my usecase.
In my case, i am using one hapi docker instance for one fhir version as seen also on this output from my local cluster.
NAME READY STATUS RESTARTS AGE
hapi-postgres-0 1/1 Running 2 40h
hapi-server-dstu2-6869569d9b-sbq8n 1/1 Running 2 40h
hapi-server-dstu3-7d4996994-8xkrx 1/1 Running 1 23h
hapi-server-r4-6d5ff58477-cdsf9 1/1 Running 2 40h
So there is just one tester defined per instance and you can reach different versions on different subpaths i.e. https://docker.host.internal/fhir-dstu2 (+/fhir for tester), https://docker.host.internal/fhir-dstu3 (+/fhir for tester) and https://docker.host.internal/fhir-r4 (+/fhir for tester). Since I am able to achieve this by modifying original docker file, I am closing this issue. Thank you.
After all I have to reopen this to at least get your opinion @jvitrifork and @jamesagnew. I am not sure if this is for starter or for base hapi project. When I try to run hapi server outside of ROOT with the modification to docker file as proposed also by @jvitrifork, then there are some hardcoded relative paths that will return 404
. All works fine if you end your requested URL with /
. I think this would deserve a fix not to force everyone to enforce redirects in the api gateway.
How to reproduce: Try to change Dockerfile#L13 so that application is reachable at "fhir-dstu3":
COPY --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/target/*.war /usr/local/tomcat/webapps/fhir-dstu3.war
Then run this docker command (replace image name at the end of course):
docker run -p 8088:8080 -e hapi_fhir_fhir_version=DSTU3 -e hapi_fhir_server_address="http://localhost:8088/fhir-dstu3/fhir" name_of_your_docker_image
Then open browser and disable cache so that you do not get things loaded from cache and navigate to http://localhost:8088/fhir-dstu3
You will see that some resources are not loaded, like e.g. custom logo and several scripts. If you call http://localhost:8088/fhir-dstu3/ all works fine, since last /
treats path as directory and not file.
+1
I want to use the same dockerfile for multiple instance on the same host. I have an nginx ingress in front of the hapi fhir instances which forwards the traffic based on path to the correct k8s service.
fhir.host.com/r4/fhir
fhir.host.com/stu3/fhir
The hapi fhir server tries to load the resources at root level which doesn't resolve.
fhir.host.com/resources/Eonasdan-bootstrap-datetimepicker/js/bootstrap-datetimepicker.js
Should be some kind of property to configure the location of the resources.
FWIW the paths come from here: https://github.com/hapifhir/hapi-fhir/blob/master/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-head.html#L33
Specifically, using the thymeleaf @{...path....}
syntax should cause the context to be added automatically, provided your server actually knows what context it's running under
@jamesagnew tnx for your reply. How to actually set this context?
@Milzor if you do the same that I did, it will work just fine with very few leftover 404
responses, which are obviously hardcoded and ignoring "base_href" settings - and that is why I reopened this issue.
Find below an example of the modified Dockerfile. Keep everything the same, only change the last part of runtime image, where you need to introduce environment variable (in my case BASE_URI
) and use that one to rename the *.war
file to you specific subpath.
BTW - @jamesagnew could I provide PR for this change or is there more clever way to do this?
#Keep the original Dockerfile content up to tomcat:9.0.38-jdk11-openjdk-slim-buster image
FROM tomcat:9.0.38-jdk11-openjdk-slim-buster
RUN mkdir -p /data/hapi/lucenefiles && chmod 775 /data/hapi/lucenefiles
COPY --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/target/*.war /usr/local/tomcat/webapps/ROOT.tmp
ENV BASE_URI "/ROOT"
EXPOSE 8080
CMD mv /usr/local/tomcat/webapps/ROOT.tmp /usr/local/tomcat/webapps${BASE_URI}.war && exec catalina.sh run
@petermicuch If you feel the ENV-controlled filename feature adds value, then please make a PR ;)
So, I took a different approach and now use a spring-boot war with embedded tomcat to run the application in a docker container. This way you can use the following command to start the container.
java -jar /path/to/war.war
passing an env variable SERVER_SERVLET_CONTEXT_PATH
will make spring initiate the application context on the configured path.
@Milzor where do you copy war file during the docker build? Does this -jar
refer always to root.war file and only specifies different context path or does the file need to be located at that path?
I use a multi stage docker build in my CI.
....stuff before
RUN ./mvnw clean package -Pboot
COPY --from=builder target/ROOT.war /
CMD ["java", "-jar", "/ROOT.war"]
So the name of the war doesn't need to change. Spring boot uses application.yml to load the properties. I even integrated spring-cloud-config-server
to provide configuration for each individual fhir server.
If you don't add -Pboot to your mvn command it won't add the embedded tomcat and then you need an external web server to run the war.
I use a multi stage docker build in my CI.
....stuff before RUN ./mvnw clean package -Pboot COPY --from=builder target/ROOT.war / CMD ["java", "-jar", "/ROOT.war"]
So the name of the war doesn't need to change. Spring boot uses application.yml to load the properties. I even integrated
spring-cloud-config-server
to provide configuration for each individual fhir server.If you don't add -Pboot to your mvn command it won't add the embedded tomcat and then you need an external web server to run the war.
@Milzor Could you please share your entire Dockerfile? They have changed the dockerfile significantly over the last year and I can't successfully reproduce your solution in the current context.
Well the idea is still the same. You can replace the entire dockerfile of this project for your own.
It's quite different in my case now as I removed the boot maven profile from the pom and rely fully on spring boot locally and in docker. Steps to accomplish this:
<properties>
....
<spring-boot.version>2.6.7</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
....
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
FROM adoptopenjdk/openjdk11:jre-11.0.14.1_1-alpine
RUN mkdir -p /data/hapi/lucenefiles && \ chmod 775 /data/hapi/lucenefiles
COPY target/ROOT.war /app/
EXPOSE 8080
CMD ["java", "-jar", "/app/ROOT.war"]
4. run `mvn clean package`
5. run `docker build -t hapi-fhir-jpaserver-starter .`
6. run `docker run -it --rm -e SERVER_SERVLET_CONTEXT_PATH=/pathyoulike -e SERVER_PORT=8080 hapi-fhir-jpaserver-starter`
feel free to use `mvn clean package` in the same dockerfile with a multi stage build or use a CI/CD to build the artifact.
let me know if this works out for you.
@Milzor I have created the following multi stage Docker build. I have also added the depedencies you mentioned in pom.xml. However, I keep getting the following error when trying to run the application.
FROM maven:3.8-openjdk-17-slim as builder
RUN mkdir -p /app/source
COPY . /app/source
WORKDIR /app/source
RUN mvn clean package
FROM adoptopenjdk/openjdk11:jre-11.0.14.1_1-alpine as runtime
RUN mkdir -p /data/hapi/lucenefiles && \
chmod 775 /data/hapi/lucenefiles
COPY --from=builder /app/source/target/ROOT.war /app/
EXPOSE 8080
CMD ["java", "-jar", "/app/ROOT.war"]
***************************
APPLICATION FAILED TO START
***************************
Description:
An attempt was made to call a method that does not exist. The attempt was made from the following location:
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:448)
The following method did not exist:
'boolean org.springframework.util.ClassUtils.isLambdaClass(java.lang.Class)'
The calling method's class, org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator, was loaded from the following location:
jar:file:/app/ROOT.war!/WEB-INF/lib/spring-aop-5.3.19.jar!/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.class
The called method's class, org.springframework.util.ClassUtils, is available from the following locations:
jar:file:/app/ROOT.war!/WEB-INF/lib/spring-core-5.3.15.jar!/org/springframework/util/ClassUtils.class
The called method's class hierarchy was loaded from the following locations:
org.springframework.util.ClassUtils: jar:file:/app/ROOT.war!/WEB-INF/lib/spring-core-5.3.15.jar!/
Action:
Correct the classpath of your application so that it contains compatible versions of the classes org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator and org.springfram
ework.util.ClassUtils
I think I have messed up somewhere in the dependencies section. Could you please share your pom file?
Looks like you have an issue with spring dependencies. Try to run
mvn dependency:tree
and investigate what's wrong.
Facing same issue when deploying HAPI with Helm and using Ingress for access. anybody found solution ?
Right now using docker file as is, it is not possible to define URL other than root to host hapi-server. I.e. we can use https://host.docker.internal, but can not use https://host.docker.internal/fhir-dstu3. If you do that, you will get java scripts downloaded just fine, but all resources are returning 404 (page not found).
One option would be to modify docker file
CMD
part to enable this and user could then specify this using environment:Another option is to rewrite responses in your API gateway (i.e. ingress nginx) to achieve this is working.
But maybe there is better more natural way of doing this in java (i.e. embedded tomcat?) then to move war file around to mimic this behavior or force rewrites in your API gateway. And maybe this could somehow work in cooperation with "hapi.fhir.server_address" setting, otherwise we define the same thing again and again. (i.e. split it to base address and subpath or something similar). The best is not to make assumptions where the server is being deployed, but to make this configurable.
Any ideas how this could be made possible? We are rarely hosting hapi server in the root. All works fine if you only use /fhir endpoints, but we are loosing nice build in UI that comes with hapi-server unless we do workarounds: