In most cases when deploying a container there is no need to modify or run scripts remotely. Essentially doing so is the antithesis of why you would deploy a docker image in the first place. However there is maybe a 0.001% chance that you may need to, like the example I am about to share.
Situation
Last year my current employer wished to run a live Jacoco(Java code coverage) scan in our preproduction environments. What this essentially means is that there is a java binary that runs along with your application that reviews the execution of the target applications code and builds a report using the source code. The report generated highlights the portion of the code that was triggered during the run. This is a great way of determining if you have unused code or verifying code behaviour after build.
Retrieving the report is fine enough when running on your local or even a VM. All you would have to do is run the commands to build the report and fetch them from the location they were published. But a container is a whole different story. One solution would be to setup ssh on the container and map a port on the host to the ssh port on the container. I really can’t speak to this implementation as I chose a different path.
To begin we must properly prep the environment by downloading the jacoco binaries and copying the source and jacoco directories into the docker folder to be built into the docker image
To copy directories into a docker image you need to us the “/” instead of “*” like in the code to the right. And add the -javagent line to the options when initiating the java application.
jamd5check=$(curl https://www.jacoco.org/jacoco/download/jacoco-0.8.8.zip.mdz.txt | awk {'print $1'})
curl -LO https://search.maven.org/remote?filepath=org/jacoco/0.8.8/jacoco-0.8.8.zip
jamd5rep=$(md5sum jacoco-0.8.8.zip | awk {'print $1'})
if [ "$jamd5check" = "$jamdrep" ]; then
unzip jacoco-0.8.8.zip -d jacoco
fi
mkdir source
for directory in $(find . -name com); do cp -r $folder source; done
cp -r jacoco docker
cp -r source docker
COPY / /path/to/main/directory
java <app_name> -javaagent: jacoco/lib/jacocoagent.jar=address=*,port=3620,destfile=jacoco-it.exec,output=tcpserver
My solution
Configuration management are amazing tools. My experience is limited mostly to Ansible but on this occasion I was working on a self managed ECS cluster. Which meant I had the luxury of using AWS’ built services. Specifically System Manager Documents. Documents is a configuration management solution that works in tandem with the AWS SSM agent that is deployed on ECS clusters and it can be installed on any EC2 instance for that matter. It has it’s own built in version control, which took some time to get used to, because it doesn’t automatically migrate to the new code. But enough about the tool, let’s move on to the actual solution.
- container=$(sudo docker ps | grep environment | grep -v pause | awk {'print $1'})
- |
if [ -z "$container" ]; then
exit
Let’s break down this code. Line 1 of the above snippet parses the running docker containers and stores the docker hash of the target container in the variable container. The if statement checks if the variable container is null. If it is the code then exits. Else we write a script using cat EOF.
else
cat > script.sh << \EOF
#! /bin/bash
java -jar jacoco/lib/jacococli.jar dump --address localhost --port 36320 --destfile jacoco-it.exec
sleep 5
Line 3 will be used to create the file script.sh which will contain all the content found between the 2 EOF flags. The script begins with the shebang that is need for all shell scripts. The script will eventually be copied to the container for execution, but lets first start by going over the contents of the script.
The first command that will be ran in the container takes a dump from the Jacoco server running alongside the target application.
mkdir classes
mkdir decompress
cd decompress
file=$(ls ../*.*ar | grep -v dd)
jar -xvf $file
cd ../
cp decompress/*-INF/lib/*-* classes
Next the war and or jar files located in the main directory are expanded into the decompress directory and then the library files are copied to the classes directory.
list=$(ls classes | grep -v public)
mkdir jar_sources
cd jar_sources
jsecret=$(aws secretsmanager get-secret-value --secret-id <secret_name> --region <region> --query SecretString | jq -r)
jpass=$(echo $jsecret | jq -r .<password>)
juser=$(echo $jsecret | jq -r .<username>)
for line in $list; do art=$(echo $line | sed "s/\.[a-z]/ /g" | awk {'print $1'}); fold=$(echo $art | sed "s/-[0-9]/ /g" | awk {'print $1'}); version=$(echo $art | awk -F '[a-z]-' '{print $NF}');\
curl --user "$juser:$jpass" -LO https://<url>/path/to/file/lib/$fold/$version/$art-sources.jar; jar -xvf "$art-sources.jar"; done
cp com ../source
Afterwards we store the names of all the public libraries from the files in the classes directory and then download the source code from a private artifact store. We then copy the com directory and store it in source directory.
cd ../
java -jar jacoco/lib/jacococli.jar report jacoco-it.exec --classfiles classes/ --sourcefiles source/ --html jacoco-report
rm -rf classes
rm -rf decompress
rm -rf jar_sources
rm script.sh
folder=$( echo $file | sed -e "s/\./ /g" | awk {'print $1'} | sed -e "s/\///g")
aws s3 cp jacoco-report s3://<bucket>/$folder/ --recursive
EOF
We now have what we need to generate the report which is what we do on line 4. Once the report has been generated we clean up all the files that we created and publish the the report to an s3 bucket. The EOF on line 18 signifies the end of the script.sh file that will be created.
chmod +x script.sh
sudo docker cp script.sh $container:/path/to/main/folder
sudo docker exec $container /path/to/main/folder/script.sh
Now that the script has been generated the file is then made executable, copiedĀ into the container and then executed within the container.
Conclusion
There may never be a reason that you would ever need to remotely run code on a docker container. If that’s the case I hope you found the artical insightful and were at leastĀ somewhat amused. For those of you that encounter the 0.001% of situations where this use case is viable. I hope that this article was helpful.
Nice One Kurt.
Thank you. I really appreciate the compliment
Awesome page with genuinely good material for readers wanting to gain some useful insights on that topic! But if you want to learn more, check out Webemail24 about Social Media Marketing. Keep up the great work!