Skip to content

Commit 27ee418

Browse files
danielschulzDaniel Schulz
authored andcommitted
original publication
1 parent 730acce commit 27ee418

File tree

54 files changed

+903
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+903
-1
lines changed

.gitignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,22 @@ venv.bak/
102102

103103
# mypy
104104
.mypy_cache/
105+
106+
107+
# password file
108+
resources/presentation/credentials.txt
109+
resources/photos/credentials.txt
110+
111+
# IntelliJ IDEA project files
112+
.idea/*
113+
114+
# for legal licensing reasons only, two images and the source presentation may not be provided in raw format
115+
code/2-uc-deeplearning-image/2-client-calls/1-requests/kitten.jpg
116+
code/2-uc-deeplearning-image/2-client-calls/1-requests/cute-chick-with-hairy-pussy.jpg
117+
resources/presentation/~$DevOps_AiToProduction_DockerizedLeanApproach.pptx
118+
resources/presentation/DevOps_AiToProduction_DockerizedLeanApproach.pptx
119+
resources/presentation/20181114_DevOps_AiToProduction_DockerizedLeanApproach.pdf
120+
121+
# extracted git repos
122+
code/1-uc-structured-data-json/1-server-setup/1-data-to-deploy/raw-binaries/tensorflow_serving_tutorial
123+
code/2-uc-deeplearning-image/1-server-setup/1-data-to-deploy/raw-binaries/mxnet

README.md

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,64 @@
11
# DevOps_AiToProduction_DockerizedLeanApproach
2-
Delivery Excellence, DevOps: Cloud-native Deployments of Tensorflow Models
2+
Delivery Excellence, DevOps: Cloud-native Deployments of Data Science Models
3+
4+
## Setup in static code
5+
Please modify this static git repo's code before using it. For technical and legal reasons, some files are
6+
archived in unencrypted and plain 7z archives.
7+
8+
### Extract
9+
For legal licensing reasons only, two images may not be provided in raw format.
10+
11+
- `code/2-uc-deeplearning-image/2-client-calls/1-requests/kitten.7z` to
12+
`code/2-uc-deeplearning-image/2-client-calls/1-requests/kitten.jpg` to
13+
- `code/2-uc-deeplearning-image/2-client-calls/1-requests/cute-chick-with-hairy-pussy.7z` to
14+
`code/2-uc-deeplearning-image/2-client-calls/1-requests/cute-chick-with-hairy-pussy.jpg`
15+
- `code/1-uc-structured-data-json/1-server-setup/1-data-to-deploy/raw-binaries/tensorflow_serving_tutorial.7z` to
16+
`code/1-uc-structured-data-json/1-server-setup/1-data-to-deploy/raw-binaries/tensorflow_serving_tutorial`
17+
- `code/2-uc-deeplearning-image/1-server-setup/1-data-to-deploy/raw-binaries/mxnet.7z` to
18+
`code/2-uc-deeplearning-image/1-server-setup/1-data-to-deploy/raw-binaries/mxnet`
19+
20+
The extraction keys for the presentation resp. photos may or may not be provided to you for legal reasons. You can find
21+
the applicable keys in
22+
- `resources/presentation/credentials.txt` resp.
23+
- `resources/photos/credentials.txt`
24+
Both latter software archives as git repositories are packaged only for technical reasons and do not need a passphrase
25+
to extract them.
26+
27+
28+
# Prepare your client's host
29+
Amend your OS' `hosts` file like shown in `code/0-meta/client-network/amend-hosts-file/example-hosts-file`.
30+
Your `hosts` file resides in:
31+
- Windows: `C:\Windows\System32\drivers\etc\hosts`
32+
- Linux: `/etc/hosts`
33+
34+
35+
# Getting started deploying Docker Containers
36+
37+
## Establish SSH tunnel
38+
Shown in `code/0-meta/client-network/create-ssh-tunnel/sshConnection.bash`
39+
40+
## Optional: Docker web ui
41+
Shown in `code/0-meta/server-optional-dockerwebui/deepinfrastructure-docker-webui.bash`
42+
43+
## TensorFlow / Iris model
44+
45+
### Distribute Model data
46+
Shown in `code/1-uc-structured-data-json/1-server-setup/1-data-to-deploy`
47+
48+
### Start Docker Container
49+
Shown in `code/1-uc-structured-data-json/1-server-setup/2-bash-commands-to-execute/tf-model-iris.bash`
50+
51+
### Perform client requests
52+
Requests in `code/1-uc-structured-data-json/2-client-calls/1-requests` and respective responses from `code/1-uc-structured-data-json/2-client-calls/2-example-responses`
53+
54+
55+
## MXNet / Deep Learning image classification model
56+
57+
### Distribute Model data
58+
Shown in `code/2-uc-deeplearning-image/1-server-setup/1-data-to-deploy`
59+
60+
### Start Docker Container
61+
Shown in `code/2-uc-deeplearning-image/1-server-setup/2-bash-commands-to-execute/mxnet-model-deeplearning.bash`
62+
63+
### Perform client requests
64+
Requests in `code/2-uc-deeplearning-image/2-client-calls/1-requests` and respective responses from `code/2-uc-deeplearning-image/2-client-calls/2-example-responses`
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
# a default on any Linux or Windows might be
3+
# IPv4
4+
127.0.0.1 localhost.localdomain localhost
5+
6+
7+
# amendments
8+
# you may use those FQDNs from the client (e.g. your local notebook) or
9+
# server (e.g. in a remote Bash session) to access your local SSH tunnel trough it.
10+
#
11+
# Obviously you access your local network interfaces (127.0.0.1) and by proxy your
12+
# SSH tunnel, which is listening on this network interface
13+
# on specific ports (e.g. "127.0.0.1:2308" for port 2308 on localhost).
14+
#
15+
# All FQDNs are convenience only and might be replaced with the associated IP
16+
# respectively -- the amendments are hence optional for the code to work
17+
18+
19+
# the IP and logical FQDN name for Hypervisor, VM or the remote Linux server
20+
# TODO: CHANGE ME
21+
10.2.3.4 host.docker.devops.svc.cluster.local
22+
23+
24+
# this FQDN will be used to access the Docker web ui through my browser
25+
127.0.0.1 webui.docker.devops.svc.cluster.local
26+
27+
# Only for specific development reasons, I have connected my client's local IDE on Windows
28+
# to my server's Docker Daemon -- this lets me interact with my cluster as if it were local.
29+
#
30+
# I must admit, the IDE integration has flaws; additionally the Docker Daemon on the server
31+
# needs to listen to this network's interface: I used localhost on the server, so the
32+
# Docker Daemon is never exposed to outside access -- it will only be accessible through
33+
# my SSH tunnel.
34+
127.0.0.1 daemon.docker.devops.svc.cluster.local
35+
36+
# my convenient access URI for ReST calls to the structured data (JSON)
37+
127.0.0.1 iris.devops.svc.cluster.local
38+
39+
# my convenient access URI for ReST calls to the Deep Learning path (image)
40+
127.0.0.1 deeplearning.devops.svc.cluster.local
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env bash
2+
3+
4+
# 1st SSH session aka the SSH tunnel
5+
ssh \
6+
-L webui.docker.devops.svc.cluster.local:1206:127.0.0.1:1206 \
7+
-L daemon.docker.devops.svc.cluster.local:1906:127.0.0.1:1906 \
8+
-L iris.devops.svc.cluster.local:2008:127.0.0.1:2008 \
9+
-L deeplearning.devops.svc.cluster.local:2308:127.0.0.1:2308 \
10+
root@host.docker.devops.svc.cluster.local \
11+
-i /c/Apps/Current/Keys/ssh-key-8k
12+
13+
14+
# ssh \
15+
# # Docker administrative UI (1206) & Daemon port (1906)
16+
# # my server's Docker Web UI listens on its port 1206 on localhost (IPv4) resp. 127.0.0.1
17+
# # my server's Docker Daemon listens on its port 1906 on localhost (IPv4) resp. 127.0.0.1
18+
# -L webui.docker.devops.svc.cluster.local:1206:127.0.0.1:1206 \
19+
# -L daemon.docker.devops.svc.cluster.local:1906:127.0.0.1:1906 \
20+
# # 2 ReST API ports
21+
# -L iris.devops.svc.cluster.local:2008:127.0.0.1:2008 \
22+
# -L deeplearning.devops.svc.cluster.local:2308:127.0.0.1:2308 \
23+
# # node identity for login
24+
# root@host.docker.devops.svc.cluster.local \
25+
# # secret to login with -- my private OpenSSH keyfile
26+
# -i /c/Apps/Current/Keys/ssh-key-8k
27+
#
28+
#
29+
# line-wise pattern: [local IP/FQDN]:[local port]:[remote IP/FQDN]:[remote port]
30+
# e.g. "deeplearning.devops.svc.cluster.local:2308:cloud.docker.devops.svc.cluster.local:2908"
31+
# - local FQDN: "deeplearning.devops.svc.cluster.local"
32+
# explanation: the SSH tunnel listens locally for all incoming request on this client's FQDN,
33+
# which translates into an IP (localhost, 127.0.0.1)
34+
# - local port: "2308"
35+
# explanation: the SSH tunnel listens locally for all incoming request on this client's port
36+
# - remote FQDN: "cloud.docker.devops.svc.cluster.local"
37+
# explanation: the SSH tunnel forwards all incoming request to this
38+
# - remote port: "2908"
39+
# explanation: the SSH tunnel forwards all incoming request to this
40+
#
41+
# Only for simplicity, all local ports on the SSH tunnel forward to the exact same ports remotely
42+
# on the server. You may change all ports provided. As long as the chains will be kept
43+
# aligned (not broken), everything will keep on working as before.
44+
# The latter are the Docker Container's host ports all on the localhost network
45+
# interface respectively. So all Containers will only be accessible through the SSH tunnel and from
46+
# calls performed by the server itself. No inbound traffic may access them due to the
47+
# "natural firewall" (sic) on Linux -- no IPtables, etc. needed.
48+
49+
50+
# 2nd, 3rd, 4th, etc. SSH session
51+
# establish more SSH sessions wo/ any port forwards as all those ports above will be already blocked
52+
# more SSH sessions "only" help for convenience to work in multiple contexts/in directories at the same time
53+
ssh \
54+
root@host.docker.devops.svc.cluster.local \
55+
-i /c/Apps/Current/Keys/ssh-key-8k
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env bash
2+
3+
4+
# Docker Daemon control via web ui
5+
# - webui.docker.devops.svc.cluster.local:1206
6+
# - bind to localhost (IPv4) resp. 127.0.0.1 network interface --
7+
# will only be accessible from server and through SSH tunnel;
8+
# not even firewall-relevant network traffic from outside needs to be managed for it
9+
# - only map the Docker Daemon's TCP socket inside the Container Jail for it to speak to the Docker Daemon from with
10+
# - no user data mapping through Docker Volumes needed here -- might be better to add in production environments
11+
# - gets 128 MiB of RAM wo/ Swap and 8.125% of one CPU core
12+
docker run \
13+
-d \
14+
--name daniel-docker-daemon-ui \
15+
-h 127.0.0.1 \
16+
-p 1206:9000 \
17+
-v /var/run/docker.sock:/var/run/docker.sock \
18+
--memory="128m" --memory-swap="128m" \
19+
--cpus=".08125" \
20+
portainer/portainer
21+
22+
# inspect Docker Containers 2 secs after starting -- the last command is a comment for the Bash history to reveal it
23+
# later on in case you need to step into the Container for debugging reasons
24+
sleep 2 && docker ps -a | grep daniel-docker-daemon-ui && docker ps -a
25+
# docker exec -it daniel-mxnet-server-deeplearning bash
26+
27+
28+
29+
# Query the deployed service -- from server itself
30+
time curl -X GET \
31+
http://localhost:1206 \
32+
-H 'cache-control: no-cache' \
33+
&& echo $?
34+
35+
36+
37+
38+
# Query the deployed service -- from remote client
39+
time curl -X GET \
40+
http://webui.docker.devops.svc.cluster.local:1206 \
41+
-H 'cache-control: no-cache' \
42+
&& echo $?
43+
44+
45+
46+
47+
# UNDEPLOY --
48+
# WARNING: removes all runtime information again; persistent data (Volumes and Images) will remain untouched
49+
# remove Docker Container
50+
docker rm -f daniel-docker-daemon-ui
51+
docker ps -a
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
$ time curl -X GET \
2+
> http://webui.docker.devops.svc.cluster.local:1206 \
3+
> -H 'cache-control: no-cache' \
4+
> && echo $?
5+
% Total % Received % Xferd Average Speed Time Time Time Current
6+
Dload Upload Total Spent Left Speed
7+
100 2804 100 2804 0 0 30150 0 --:--:-- --:--:-- --:--:-- 30150<!DOCTYPE html>
8+
<html lang="en" ng-app="portainer">
9+
<head>
10+
<meta charset="utf-8">
11+
<title>Portainer</title>
12+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
13+
<meta name="description" content="">
14+
<meta name="author" content="Portainer.io">
15+
16+
<link rel="stylesheet" href="css/app.7d725f5a.css">
17+
18+
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
19+
<!--[if lt IE 9]>
20+
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
21+
<![endif]-->
22+
23+
<script src="js/app.b6e7ddde.js"></script>
24+
25+
<!-- Fav and touch icons -->
26+
<link rel="apple-touch-icon" sizes="180x180" href="ico/apple-touch-icon.png">
27+
<link rel="icon" type="image/png" sizes="32x32" href="ico/favicon-32x32.png">
28+
<link rel="icon" type="image/png" sizes="16x16" href="ico/favicon-16x16.png">
29+
<link rel="manifest" href="ico/manifest.json">
30+
<link rel="mask-icon" href="ico/safari-pinned-tab.svg" color="#5bbad5">
31+
<link rel="shortcut icon" href="ico/favicon.ico">
32+
<meta name="msapplication-config" content="ico/browserconfig.xml">
33+
<meta name="theme-color" content="#ffffff">
34+
</head>
35+
36+
<body ng-controller="MainController">
37+
<div id="page-wrapper" ng-class="{
38+
open: toggle && ['portainer.auth', 'portainer.updatePassword', 'portainer.init.admin', 'portainer.init.endpoint'].indexOf($state.current.name) === -1,
39+
nopadding: ['portainer.auth', 'portainer.updatePassword', 'portainer.init.admin', 'portainer.init.endpoint'].indexOf($state.current.name) > -1 || applicationState.loading
40+
}"
41+
ng-cloak>
42+
<div id="sideview" ui-view="sidebar" ng-if="!applicationState.loading"></div>
43+
44+
<div id="content-wrapper">
45+
<div class="page-content">
46+
47+
<div class="page-wrapper" ng-if="applicationState.loading">
48+
<!-- loading box -->
49+
<div class="container simple-box">
50+
<div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3">
51+
<!-- loading box logo -->
52+
<div class="row">
53+
<img ng-if="logo" ng-src="{{ logo }}" class="simple-box-logo">
54+
<img ng-if="!logo" src="images/logo_alt.png" class="simple-box-logo" alt="Portainer">
55+
</div>
56+
<!-- !loading box logo -->
57+
<!-- panel -->
58+
<div class="row" style="text-align: center">
59+
Connecting to the Docker endpoint...
60+
<i class="fa fa-cog fa-spin" style="margin-left: 5px"></i>
61+
</div>
62+
<!-- !panel -->
63+
</div>
64+
</div>
65+
<!-- !loading box -->
66+
</div>
67+
68+
<!-- Main Content -->
69+
<div id="view" ui-view="content" ng-if="!applicationState.loading"></div>
70+
71+
</div><!-- End Page Content -->
72+
</div><!-- End Content Wrapper -->
73+
</div><!-- End Page Wrapper -->
74+
</body>
75+
</html>
76+
77+
78+
real 0m0.915s
79+
user 0m0.062s
80+
sys 0m0.078s
81+
0
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# time curl -X GET \
2+
> http://localhost:1206 \
3+
> -H 'cache-control: no-cache' \
4+
> && echo $?
5+
<!DOCTYPE html>
6+
<html lang="en" ng-app="portainer">
7+
<head>
8+
<meta charset="utf-8">
9+
<title>Portainer</title>
10+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
11+
<meta name="description" content="">
12+
<meta name="author" content="Portainer.io">
13+
14+
<link rel="stylesheet" href="css/app.7d725f5a.css">
15+
16+
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
17+
<!--[if lt IE 9]>
18+
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
19+
<![endif]-->
20+
21+
<script src="js/app.b6e7ddde.js"></script>
22+
23+
<!-- Fav and touch icons -->
24+
<link rel="apple-touch-icon" sizes="180x180" href="ico/apple-touch-icon.png">
25+
<link rel="icon" type="image/png" sizes="32x32" href="ico/favicon-32x32.png">
26+
<link rel="icon" type="image/png" sizes="16x16" href="ico/favicon-16x16.png">
27+
<link rel="manifest" href="ico/manifest.json">
28+
<link rel="mask-icon" href="ico/safari-pinned-tab.svg" color="#5bbad5">
29+
<link rel="shortcut icon" href="ico/favicon.ico">
30+
<meta name="msapplication-config" content="ico/browserconfig.xml">
31+
<meta name="theme-color" content="#ffffff">
32+
</head>
33+
34+
<body ng-controller="MainController">
35+
<div id="page-wrapper" ng-class="{
36+
open: toggle && ['portainer.auth', 'portainer.updatePassword', 'portainer.init.admin', 'portainer.init.endpoint'].indexOf($state.current.name) === -1,
37+
nopadding: ['portainer.auth', 'portainer.updatePassword', 'portainer.init.admin', 'portainer.init.endpoint'].indexOf($state.current.name) > -1 || applicationState.loading
38+
}"
39+
ng-cloak>
40+
<div id="sideview" ui-view="sidebar" ng-if="!applicationState.loading"></div>
41+
42+
<div id="content-wrapper">
43+
<div class="page-content">
44+
45+
<div class="page-wrapper" ng-if="applicationState.loading">
46+
<!-- loading box -->
47+
<div class="container simple-box">
48+
<div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3">
49+
<!-- loading box logo -->
50+
<div class="row">
51+
<img ng-if="logo" ng-src="{{ logo }}" class="simple-box-logo">
52+
<img ng-if="!logo" src="images/logo_alt.png" class="simple-box-logo" alt="Portainer">
53+
</div>
54+
<!-- !loading box logo -->
55+
<!-- panel -->
56+
<div class="row" style="text-align: center">
57+
Connecting to the Docker endpoint...
58+
<i class="fa fa-cog fa-spin" style="margin-left: 5px"></i>
59+
</div>
60+
<!-- !panel -->
61+
</div>
62+
</div>
63+
<!-- !loading box -->
64+
</div>
65+
66+
<!-- Main Content -->
67+
<div id="view" ui-view="content" ng-if="!applicationState.loading"></div>
68+
69+
</div><!-- End Page Content -->
70+
</div><!-- End Content Wrapper -->
71+
</div><!-- End Page Wrapper -->
72+
</body>
73+
</html>
74+
75+
real 0m0.648s
76+
user 0m0.005s
77+
sys 0m0.009s
78+
0

0 commit comments

Comments
 (0)