├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── configs └── build.cnf ├── destroy_vms.sh ├── docker_101_tutorial.py └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "shutit-library"] 2 | path = shutit-library 3 | url = https://github.com/ianmiell/shutit-library 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ian Miell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Docker 101 Tutorial 2 | =================== 3 | 4 | [![asciicast](https://asciinema.org/a/45918.png)](https://asciinema.org/a/45918) 5 | 6 | Ideally you will have vagrant and virtualbox pre-installed, but ShutIt will try and install this for you. 7 | 8 | ``` 9 | sudo pip install shutit 10 | git clone --recursive https://github.com/ianmiell/docker-101-tutorial && cd docker-101-tutorial 11 | ./run.sh 12 | ``` 13 | 14 | 15 | -------------------------------------------------------------------------------- /configs/build.cnf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # PLEASE NOTE: This file should be changed only by the maintainer. 3 | # PLEASE NOTE: This file is only sourced if the "shutit build" command is run 4 | # and this file is in the relative path: configs/build.cnf 5 | # This is to ensure it is only sourced if _this_ module is the 6 | # target. 7 | ############################################################################### 8 | # When this module is the one being built, which modules should be built along with it by default? 9 | # This feeds into automated testing of each module. 10 | [tk.shutit.docker_101_tutorial] 11 | shutit.core.module.build:yes -------------------------------------------------------------------------------- /destroy_vms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #set -x 3 | if [[ $(which VBoxManage) != '' ]] 4 | then 5 | while true 6 | do 7 | VBoxManage list runningvms | grep docker_101_tutorial | awk '{print $1}' | xargs -IXXX VBoxManage controlvm 'XXX' poweroff && VBoxManage list vms | grep docker_101_tutorial | awk '{print $1}' | xargs -IXXX VBoxManage unregistervm 'XXX' --delete 8 | if [[ $(VBoxManage list vms | grep docker_101_tutorial | wc -l) -eq '0' ]] 9 | then 10 | break 11 | else 12 | ps -ef | grep virtualbox | grep docker_101_tutorial | awk '{print $2}' | xargs kill 13 | sleep 10 14 | fi 15 | done 16 | fi 17 | -------------------------------------------------------------------------------- /docker_101_tutorial.py: -------------------------------------------------------------------------------- 1 | """ShutIt module. See http://shutit.tk 2 | """ 3 | 4 | from shutit_module import ShutItModule 5 | import random 6 | import string 7 | 8 | class docker_101_tutorial(ShutItModule): 9 | 10 | def build(self, shutit): 11 | # Some useful API calls for reference. See shutit's docs for more info and options: 12 | # 13 | # ISSUING BASH COMMANDS 14 | # shutit.send(send,expect=) - Send a command, wait for expect (string or compiled regexp) 15 | # to be seen before continuing. By default this is managed 16 | # by ShutIt with shell prompts. 17 | # shutit.multisend(send,send_dict) - Send a command, dict contains {expect1:response1,expect2:response2,...} 18 | # shutit.send_and_get_output(send) - Returns the output of the sent command 19 | # shutit.send_and_match_output(send, matches) 20 | # - Returns True if any lines in output match any of 21 | # the regexp strings in the matches list 22 | # shutit.send_until(send,regexps) - Send command over and over until one of the regexps seen in the output. 23 | # shutit.run_script(script) - Run the passed-in string as a script 24 | # shutit.install(package) - Install a package 25 | # shutit.remove(package) - Remove a package 26 | # shutit.login(user='root', command='su -') 27 | # - Log user in with given command, and set up prompt and expects. 28 | # Use this if your env (or more specifically, prompt) changes at all, 29 | # eg reboot, bash, ssh 30 | # shutit.logout(command='exit') - Clean up from a login. 31 | # 32 | # COMMAND HELPER FUNCTIONS 33 | # shutit.add_to_bashrc(line) - Add a line to bashrc 34 | # shutit.get_url(fname, locations) - Get a file via url from locations specified in a list 35 | # shutit.get_ip_address() - Returns the ip address of the target 36 | # shutit.command_available(command) - Returns true if the command is available to run 37 | # 38 | # LOGGING AND DEBUG 39 | # shutit.log(msg,add_final_message=False) - 40 | # Send a message to the log. add_final_message adds message to 41 | # output at end of build 42 | # shutit.pause_point(msg='') - Give control of the terminal to the user 43 | # shutit.step_through(msg='') - Give control to the user and allow them to step through commands 44 | # 45 | # SENDING FILES/TEXT 46 | # shutit.send_file(path, contents) - Send file to path on target with given contents as a string 47 | # shutit.send_host_file(path, hostfilepath) 48 | # - Send file from host machine to path on the target 49 | # shutit.send_host_dir(path, hostfilepath) 50 | # - Send directory and contents to path on the target 51 | # shutit.insert_text(text, fname, pattern) 52 | # - Insert text into file fname after the first occurrence of 53 | # regexp pattern. 54 | # shutit.delete_text(text, fname, pattern) 55 | # - Delete text from file fname after the first occurrence of 56 | # regexp pattern. 57 | # shutit.replace_text(text, fname, pattern) 58 | # - Replace text from file fname after the first occurrence of 59 | # regexp pattern. 60 | # ENVIRONMENT QUERYING 61 | # shutit.host_file_exists(filename, directory=False) 62 | # - Returns True if file exists on host 63 | # shutit.file_exists(filename, directory=False) 64 | # - Returns True if file exists on target 65 | # shutit.user_exists(user) - Returns True if the user exists on the target 66 | # shutit.package_installed(package) - Returns True if the package exists on the target 67 | # shutit.set_password(password, user='') 68 | # - Set password for a given user on target 69 | # 70 | # USER INTERACTION 71 | # shutit.get_input(msg,default,valid[],boolean?,ispass?) 72 | # - Get input from user and return output 73 | # shutit.fail(msg) - Fail the program and exit with status 1 74 | # 75 | vagrant_image = shutit.cfg[self.module_id]['vagrant_image'] 76 | vagrant_provider = shutit.cfg[self.module_id]['vagrant_provider'] 77 | module_name = 'docker_101_tutorial_' + ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(6)) 78 | shutit.send('rm -rf /tmp/' + module_name + ' && mkdir -p /tmp/' + module_name + ' && cd /tmp/' + module_name) 79 | shutit.send('vagrant init ' + vagrant_image) 80 | shutit.send('vagrant up --provider virtualbox',timeout=99999) 81 | shutit.login(command='vagrant ssh') 82 | shutit.login(command='sudo su -',password='vagrant') 83 | 84 | # DOCKER SETUP 85 | shutit.install('docker.io') 86 | shutit.send('docker pull centos') 87 | shutit.send('cat /etc/issue',note='We are in an ubuntu vm') 88 | shutit.send('yum',check_exit=False,note='yum is not available, for example') 89 | 90 | # DOCKER RUN 91 | shutit.login('docker run -ti --name docker-101-centos centos /bin/bash',note='Run up a centos docker image and attach to it with the bash command.',timeout=999) 92 | shutit.install('iproute') 93 | shutit.send('ps -ef',note='We are in a container, only the bash process is running') 94 | shutit.send('ip route',note='We have our own network stack') 95 | shutit.send('whoami',note='By default I am root') 96 | shutit.send('ls',note='And we have our own filesystem') 97 | shutit.logout(note='log out of the bash shell, terminating the container - type exit') 98 | 99 | # DOCKER PS 100 | shutit.send('docker ps -a',note='That container is still there, but not running because the bash process terminated when we logged out.') 101 | 102 | # DOCKER RM 103 | shutit.send('docker rm docker-101-centos',note='Remove the container.') 104 | shutit.send('docker ps -a',note='The container has gone.') 105 | shutit.send('echo',note='Next we start up 100 containers.') 106 | for i in range(1,100): 107 | shutit.send('docker run -d --name centos_container_' + str(i) + ' centos sleep infinity',timeout=500) 108 | 109 | shutit.send('docker ps',note='Show all 100 containers.') 110 | 111 | # DOCKER EXEC 112 | shutit.login(command='docker exec -ti centos_container_1 /bin/bash',note='Log onto container 1 to create a file that only exists in that container') 113 | shutit.send('pwd',note='We start in the root folder.') 114 | shutit.send('touch myfile',note='Create file: myfile.') 115 | shutit.send('ls',note='The file is there.') 116 | shutit.logout(note='log out of the bash shell - type exit') 117 | 118 | shutit.login('docker exec -ti centos_container_2 /bin/bash',note='Log onto container 2 to show the file is not there.') 119 | shutit.send('pwd',note='We start in the root folder here too.') 120 | shutit.send('ls',note='The file "myfile" is not here.') 121 | shutit.logout(note='log out of the bash shell - type exit') 122 | 123 | shutit.login(command='docker exec -ti centos_container_1 /bin/bash',note='Log onto container 1 again') 124 | shutit.send('ls',note='The file "myfile" is still there in the first container.') 125 | shutit.logout(note='log out of the bash shell - type exit') 126 | 127 | # DOCKER IMAGES 128 | shutit.send('docker images',note='We can list the images we have on this _host_. We have one image (the centos one) which is the source of all our containers.') 129 | shutit.send('docker ps',note='But we have 100 containers.') 130 | 131 | # DOCKER COMMIT / HISTORY / LAYERS 132 | shutit.send('docker history centos',note='Containers are composed of _layers_. Each one represents a set of file changes analagous (but not the same as) a git commit. This is the history of the "centos" image layers.') 133 | shutit.send('docker commit centos_container_1 docker_101_image',note='To create a new image with myfile in, commit the container.') 134 | shutit.send('docker ps -q -a | xargs -n 1 docker rm -f 2>&1 > /dev/null &') 135 | shutit.send('docker images',note='That image is now listed alongside the centos one on our host.') 136 | shutit.send('docker history centos',note='Show the centos history.') 137 | shutit.send('docker history docker_101_image',note='Show the docker_101_image history. It is the same as the centos image with our extra layer.') 138 | 139 | # DOCKER LOGIN 140 | shutit.pause_point('Now log in to docker with "docker login" please.') 141 | docker_username = shutit.get_input('Please input your dockerhub username: ') 142 | shutit.send('docker tag docker_101_image ' + docker_username + '/docker_101_image',note='Re-tag the image with your docker username-space.') 143 | shutit.send('docker images',note='It is listed as a separate image with the same ID.') 144 | shutit.send('docker push ' + docker_username + '/docker_101_image',note='It is listed as a separate image with the same ID.') 145 | 146 | # PULL 147 | shutit.send('docker rmi ' + docker_username + '/docker_101_image',note='Delete our newly-created image.') 148 | shutit.send('docker rmi docker_101_image',note='Delete the previously-created identical image also.') 149 | shutit.send('docker images',note='Neither image is now available. Only the centos image remains, which we keep to avoid re-downloading.') 150 | shutit.send('docker pull ' + docker_username + '/docker_101_image',note='Pull the image back down. Notice it is a lot faster as only the extra layer we created is required.') 151 | shutit.login('docker run -ti ' + docker_username + '/docker_101_image /bin/bash',note='Run up bash in a new container from that image.') 152 | shutit.send('pwd',note='I am in the root folder again') 153 | shutit.send('ls',note='And the file we create earlier is there') 154 | shutit.logout(note='exit the container') 155 | shutit.send('docker ps -a -q | xargs docker rm -f') 156 | shutit.send('docker rmi ' + docker_username + '/docker_101_image',note='Remove the image we just pulled.') 157 | 158 | # DOCKERFILE 159 | shutit.send('mkdir -p docker_build && cd docker_build',note='Create a folder for our build.') 160 | shutit.send('''cat > Dockerfile << END 161 | FROM centos 162 | RUN touch /root/myfile 163 | CMD ['/bin/bash'] 164 | END''') 165 | shutit.send('cat Dockerfile',note='Cat the Dockerfile created for you. This Dockerfile that does the same action as before.') 166 | docker_image_name = shutit.get_input('Please input a new image name.') 167 | shutit.send('docker build -t ' + docker_username + '/' + docker_image_name + ' .',note='Build the docker image using the Dockerfile (rather than running and committing), and tag it with a new image name') 168 | shutit.send('docker push ' + docker_username + '/' + docker_image_name,note='Push the image to the dockerhub') 169 | shutit.send('docker rmi ' + docker_username + '/' + docker_image_name,note='Remove the image from our local machine') 170 | shutit.send('docker images',note='It is no longer available. Only the centos image remains.') 171 | shutit.send('rm -rf Dockerfile',note='Remove the Dockerfile we created') 172 | 173 | # DOCKERFILE FROM THAT BASE 174 | shutit.send('''cat > Dockerfile << END 175 | FROM ''' + docker_username + '/' + docker_image_name + ''' 176 | RUN touch /root/myfile2 177 | CMD ['/bin/bash'] 178 | END''') 179 | shutit.send('cat Dockerfile',note='Dockerfile created for you that builds on the last one (rather than centos).') 180 | shutit.send('docker build -t newimage .',note='Build this new image from the new Dockerfile.') 181 | shutit.login('docker run -ti newimage /bin/bash',note='Run the newly-create image.') 182 | shutit.send('ls',note='The file myfile2 (from our new layer) and myfile (from our old image) is there.') 183 | shutit.logout(note='Log out of the new container - type exit') 184 | shutit.send('docker ps -a -q | xargs docker rm -f') 185 | shutit.send('docker rmi newimage',note='Destroy this new image') 186 | 187 | shutit.logout() 188 | shutit.logout() 189 | return True 190 | 191 | def get_config(self, shutit): 192 | # CONFIGURATION 193 | # shutit.get_config(module_id,option,default=None,boolean=False) 194 | # - Get configuration value, boolean indicates whether the item is 195 | # a boolean type, eg get the config with: 196 | # shutit.get_config(self.module_id, 'myconfig', default='a value') 197 | # and reference in your code with: 198 | # shutit.cfg[self.module_id]['myconfig'] 199 | shutit.get_config(self.module_id,'vagrant_image',default='ubuntu/trusty64') 200 | shutit.get_config(self.module_id,'vagrant_provider',default='virtualbox') 201 | return True 202 | 203 | def module(): 204 | return docker_101_tutorial( 205 | 'tk.shutit.docker_101_tutorial', 1845506479.0001123, 206 | description='', 207 | maintainer='', 208 | delivery_methods=['bash'], 209 | depends=['shutit.tk.setup','shutit-library.virtualbox.virtualbox.virtualbox','tk.shutit.vagrant.vagrant.vagrant'] 210 | ) 211 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | bash ./destroy_vms.sh 3 | [[ -z "$SHUTIT" ]] && SHUTIT="$1/shutit" 4 | [[ ! -a "$SHUTIT" ]] || [[ -z "$SHUTIT" ]] && SHUTIT="$(which shutit)" 5 | if [[ ! -a "$SHUTIT" ]] 6 | then 7 | echo "Must have shutit on path, eg export PATH=$PATH:/path/to/shutit_dir" 8 | exit 1 9 | fi 10 | $SHUTIT build -d bash -m shutit-library/vagrant:shutit-library/virtualbox "$@" --training 11 | if [[ $? != 0 ]] 12 | then 13 | exit 1 14 | fi 15 | --------------------------------------------------------------------------------