Writing a Bash Script to Run Docker - VTAstrobotics/Documentation GitHub Wiki
Contents
Prerequisites
To understand the content in this page, you should know how to use
- Basic Linux commands (cd, ls, that kind of stuff)
- Basic Docker commands (docker build, run)
- Note that you do NOT need to know any bash :)
How this changed our process
- Before:
- sudo docker build -t testbotdev -f dockerfile-name
- [type in password]
- [wait]
- sudo ufw disable
- sudo docker run -it --net=host --rm --privileged -v /dev/bus/usb:/dev/bus/usb testbotdev
- [wait]
- . install/setup.bash (I know I never forgot this step. Ever.)
- [ready]
- [ctrl + D to exit]
- And of course, you wouldn’t forget to re-enable the firewall:
- sudo ufw enable
- After:
- ./startup dockerfile-name
- [type in password]
- [ wait]
- [ready]
- [ctrl + D to exit]
Let’s Get Started So you have a Dockerfile, but you’ve gotten tired of typing in docker build and docker run. Over and over. Now, we’ll shorten this to just one command. In fact, it’s so simple that you can turn this into a keyboard shortcut if you’d like.
Writing the Basic Script
Basic bash scripting is pretty easy. Since we don’t want to do anything too fancy, this is little more than a newline-separated (press enter) list of commands. Genuinely, all you need to do is:
- Create a new text file with a list of commands that you want to run. Example:
docker build -t mytag .
docker run mytag
- Now, tell Linux “hey, this file is a bash script” by using the shebang interpreter directive on the first line of the file.
#!/bin/bash
- Exit and save your file. Now make it an executable
chmod u+x your-text-file
- Run file
./your-text-file
And you’re done!
Upgrades
I mean that’s cool and all… but come on. That was so easy… why not make it a little fancier? For example, instead of having a different executable that does the exact same thing for each Dockerfile, what if we just took it in as a command-line argument?
Dockerfile Command-Line Argument
The way bash handles command-line arguments is by splitting on whitespace. So every time there is a space, tab, etc., that denotes the beginning or end of a term.
- This is basically just an array, where the first term is arr[0]. To do this in our bash script, we use the $ followed by the index. And since we’re not monsters, we start at index zero. So in this case, is our Dockerfile name.
sudo docker build -t testbotdev -f "" .
Now that that works, I should warn you that this does not let you pass in flags, so you will be running each Dockerfile with the same ones. This isn’t an issue unless you want to run multiple Dockerfiles on the same machine (because -t tagname). Oh wait, that's totally an issue (i.e. robot vs testbot). So let’s fix that:
Bash has a built-in getopts command made for this. In our case, we can do something like:
`tag = ‘’
while getopts ‘-t:’ arg; do
case “$flag” in
t) tag=”${OPTARG}” ;;
And now we have our tag in a variable, tag! Why am I doing it like this, and not like we did with the dockerfile name? With the dockerfile, we just passed it as an argument, not a flag. Well, this will actually allow someone to use the same old command (./startup dockerfile), but it will also allow us to use this script “properly” with
./startup dockerfile -t tag.
You can of course continue this process with as many cases (one per flag) as you’d like. This will make the bash script more customizable, but, also make it harder to use. The entire point is to make this easy and user-friendly. If you want to have one that lets you input more tags, I would support you, but make that a different bash script so the CPEs are still able to run the code. ;)
Firewall Management
This one is pretty easy. Using sudo ufw enable
and sudo ufw disable
you can toggle the firewall (this allows ROS2 to UDP through the router to itself).
I know what you’re thinking: I don’t need to read this. I can just put sudo ufw disable
somewhere near the top and sudo ufw enable
after the docker run command. Wrong. For god knows what reason, that doesn’t work.
Luckily, there’s a solution. Just run the enable command one more time. Seriously, I don’t know why this works. I only have to run it once on my VM, and only once on Will’s Linux machine. But the Jetson needs it twice. If it works, it works, right?
Automating Sourcing Your ROS Workspace
You ever get tired of having to type in ./install setup.bash
once in your container? Me too. Unfortunately, simply adding this as a RUN Dockerfile line does not work.
This is because the way Docker creates your final container is by running each line in a new, intermediary container, which is useful when you rebuild since it caches the container associated with each step (so, it can skip to the first change in your Dockerfile, if any, without wasting time rebuilding the same thing).
So, what to do? Well, we’re going to cheat a little by modifying our Dockerfile, not a bash script. But the reason why this is in this Google Doc is because we’ll be writing a bash script within our Dockerfile. Since this documentation is all about being a little extra, I think it fits better here.
So, how do we run a command in our final container automatically? Luckily for us, Docker has a tool just for that: CMD. CMD will essentially RUN any file in our final container. For now, we just want to run a simple terminal command, but eventually we may want to add a bunch of stuff. So let’s make a bash script to run within our container, where we can easily add commands as needed. For now,
#!/bin/bash
. install/setup.bash
will suffice. To add to a new file, we can echo our command(s) like so:
RUN /bin/echo -e "#!/bin/bash\n. install/setup.bash\n/bin/bash" > /usr/local/bin/startup_cmds
RUN chmod 777 /usr/local/bin/startup_cmds
Condensing that,
RUN /bin/echo -e "#!/bin/bash\n. install/setup.bash\n/bin/bash" > /usr/local/bin/startup_cmds && chmod 777 /usr/local/bin/startup_cmds
and we’re done! To be clear, just that one last (very long) line is all you need. Okay, now let’s tell Docker to run that bash script:CMD /usr/local/bin/startup_cmds.
Just make sure this is at the end of the Dockerfile. The. Last. Line.
Run With a Keyboard Shortcut
Go to keyboard shortcuts. Add new. Make whatever shortcut. Run the command. Lol. The only downside is to use another Dockerfile, you’d need to add another shortcut. So it’s great to have for the default testbot Dockerfile, but not much else. Running It (Putting It All Together) As mentioned in the intro, run ./startup docker_driver (where docker_driver is the name of the Dockerfile) If prompted, type in password, let it run, and you’re good to go. You should now be able to run ros commands directly (i.e. ros2 run joy joy_node). And ctrl + D to exit