├── .gitignore
├── 1startGitLab.sh
├── 2createProjectsAndCommitToGitLab.sh
├── 3startDockerRegistry.sh
├── 4startJenkins.sh
├── README.md
├── hello-world-app-acceptance
├── .gitignore
├── pom.xml
└── src
│ └── test
│ └── java
│ └── de
│ └── philipphauer
│ └── helloworld
│ └── DummyAcceptanceTest.java
├── hello-world-app-deployment
└── runDockerContainer.sh
├── hello-world-app
├── .gitignore
├── HelloWorldApplication.launch
├── README
├── pom.xml
└── src
│ ├── main
│ ├── docker
│ │ └── docker-assembly.xml
│ ├── java
│ │ └── de
│ │ │ └── philipphauer
│ │ │ └── helloworld
│ │ │ ├── HelloWorldApplication.java
│ │ │ ├── HelloWorldConfiguration.java
│ │ │ ├── healthchecks
│ │ │ └── TemplateHealthCheck.java
│ │ │ ├── model
│ │ │ └── Saying.java
│ │ │ └── rest
│ │ │ └── HelloWorldResource.java
│ └── resources
│ │ └── config.yml
│ └── test
│ ├── java
│ └── de
│ │ └── philipphauer
│ │ └── helloworld
│ │ └── rest
│ │ └── HelloWorldResourceTest.java
│ └── resources
│ └── test-config.yml
└── util-scripts
└── docker-util.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | .project
--------------------------------------------------------------------------------
/1startGitLab.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | # https://hub.docker.com/r/sameersbn/gitlab/
3 | # https://github.com/sameersbn/docker-gitlab
4 |
5 | #TODO use docker-compose instead (see doc)
6 |
7 | #postgresql
8 | docker run --name gitlab-postgresql -d \
9 | --env 'DB_NAME=gitlabhq_production' \
10 | --env 'DB_USER=gitlab' --env 'DB_PASS=password' \
11 | --volume /srv/docker/gitlab/postgresql:/var/lib/postgresql \
12 | sameersbn/postgresql:9.4-3
13 |
14 | #redis
15 | docker run --name gitlab-redis -d \
16 | --volume /srv/docker/gitlab/redis:/var/lib/redis \
17 | sameersbn/redis:latest
18 |
19 | #gitlab
20 | docker run --name gitlab -d \
21 | --link gitlab-postgresql:postgresql --link gitlab-redis:redisio \
22 | --publish 10022:22 --publish 10080:80 \
23 | --env 'GITLAB_PORT=10080' --env 'GITLAB_SSH_PORT=10022' \
24 | --volume /srv/docker/gitlab/gitlab:/home/git/data \
25 | sameersbn/gitlab:7.14.3
26 |
27 | echo "NOTE: Please allow a couple of minutes for the GitLab application to start."
28 | echo "Port: 10080. user: root. default pw: 5iveL!fe or changed to 12345678"
29 |
--------------------------------------------------------------------------------
/2createProjectsAndCommitToGitLab.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # use same credentials for git as for gitlab user
4 |
5 | echo "precondition: create project 'hello-world-app' in gitlab"
6 | echo "The default user is 'root' and the pw is shown above"
7 |
8 | TARGETFOLDER=~/Development
9 | GITLAB_USER=root
10 | GITLAB_PW=12345678
11 |
12 | createProjectAndCommitToGitLab(){
13 | local projectName=$1
14 | local targetPath=$TARGETFOLDER/$projectName
15 | echo "=== $targetPath ==="
16 |
17 | rm -fr $targetPath
18 |
19 | echo "--- Cloning..."
20 | git clone http://$GITLAB_USER:$GITLAB_PW@localhost:10080/root/$projectName.git $targetPath
21 | git config user.name $GITLAB_USER #don't use --global!
22 | git config credential.helper cache #caches password for 15 min
23 |
24 | echo "--- Copying..."
25 | cp -r $projectName $TARGETFOLDER
26 | rm -fr $targetPath/target
27 | rm -fr $targetPath/.settings
28 | rm -fr $targetPath/.classpath
29 | rm -fr $targetPath/.project
30 |
31 | echo "--- Committing..."
32 | local currentPath=`pwd`;
33 | cd $targetPath
34 | git add .
35 | git commit -m "inital commit"
36 | git push -u origin master
37 | cd $currentPath
38 | }
39 |
40 | createProjectAndCommitToGitLab hello-world-app
41 | createProjectAndCommitToGitLab hello-world-app-acceptance
42 | createProjectAndCommitToGitLab hello-world-app-deployment
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/3startDockerRegistry.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | PORT=5000
3 | VOLUME_PATH=~/docker-registry-data
4 | docker run -d -p $PORT:5000 --restart=always --name registry \
5 | -v $VOLUME_PATH:/var/lib/registry \
6 | registry:2
7 |
8 | echo "Docker Registry runs on port $PORT"
9 | echo "Prefix tag with localhost:5000 so that it points to the registry"
10 | echo "Access http://localhost:5000/v2/_catalog in the browser to show the content. Or http://localhost:5000/v2/prozu-service/tags/list"
11 | echo "See https://docs.docker.com/registry/spec/api/#detail for documentation"
12 | echo "Also you can see the docker content in $VOLUME_PATH"
13 |
--------------------------------------------------------------------------------
/4startJenkins.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | JENKINS_PORT=8090
3 | JENKINS_CONTAINER_NAME=jenkins
4 | JENKINS_HOME=~/jenkins_home
5 |
6 | mkdir $JENKINS_HOME
7 |
8 | JenkinsContainerId=`docker ps -qa --filter "name=$JENKINS_CONTAINER_NAME"`
9 | if [ -n "$JenkinsContainerId" ]
10 | then
11 | echo "Stopping and removing existing jenkins container"
12 | docker stop $JENKINS_CONTAINER_NAME
13 | docker rm $JENKINS_CONTAINER_NAME
14 | fi
15 |
16 | echo "Starting jenkins container on port $JENKINS_PORT and jenkins home is $JENKINS_HOME"
17 | # https://github.com/jenkinsci/docker
18 | # https://hub.docker.com/r/jenkinsci/jenkins/tags/
19 | # /var/jenkins_home contains all plugins and configuration
20 | docker run -d --name $JENKINS_CONTAINER_NAME \
21 | -p $JENKINS_PORT:8080 -p 50000:50000 \
22 | -v $JENKINS_HOME:/var/jenkins_home \
23 | -v /var/run/docker.sock:/var/run/docker.sock \
24 | -v $(which docker):/bin/docker \
25 | -v /usr/lib/x86_64-linux-gnu/libapparmor.so.1.1.0:/lib/x86_64-linux-gnu/libapparmor.so.1 \
26 | -u root \
27 | jenkins:1.609.3
28 |
29 | #the last 3 volume bindings are important in order to enable jenkins to run docker, see
30 | #http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Continuous Delivery Playground
2 | My playground for setting up a continuous delivery pipeline with Docker.
3 |
4 | - Jenkins
5 | - GitLab
6 | - Using a simple Dropwizard microservice as an example application
7 | - Using a Docker image (containing the microservice) to create reproducible environments for all stages of the continuous delivery pipeline.
8 |
9 | # Step by Step Tutorial
10 | For a step by step tutorial see my blog post ["Tutorial: Continuous Delivery with Docker and Jenkins"](http://blog.philipphauer.de/tutorial-continuous-delivery-with-docker-jenkins/#Setting_up_a_Simple_Continuous_Delivery_Pipeline_with_Docker).
11 |
--------------------------------------------------------------------------------
/hello-world-app-acceptance/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | .settings
3 | .classpath
4 | .project
5 | /target/
6 |
--------------------------------------------------------------------------------
/hello-world-app-acceptance/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | de.philipphauer
5 | hello-world-app-acceptance
6 | 0.0.1-SNAPSHOT
7 |
8 | UTF-8
9 | UTF-8
10 | hello-world-app
11 | 8080
12 | localhost
13 | ${docker.host.address}:5000/
14 | ${docker.registry.name}${target.project}
15 |
16 |
17 |
18 | junit
19 | junit
20 | 4.12
21 |
22 |
23 |
24 |
25 |
26 | org.apache.maven.plugins
27 | maven-surefire-plugin
28 | 2.18.1
29 |
30 | true
31 |
32 |
33 |
34 | org.apache.maven.plugins
35 | maven-failsafe-plugin
36 | 2.18.1
37 |
38 | integration-test
39 |
40 | **/*.java
41 |
42 |
43 | http://${docker.host.address}:${prozu.port}/
44 |
45 |
46 |
47 |
48 |
49 | integration-test
50 | verify
51 |
52 |
53 |
54 |
55 |
56 | org.jolokia
57 | docker-maven-plugin
58 | 0.13.6
59 |
60 |
61 |
62 | ${target.project}
63 | ${docker.repository.name}:${project.version}
64 |
65 | alias
66 |
67 | ${prozu.port}:8080
68 | ${prozu.port.admin}:8081
69 |
70 |
71 |
72 | ${user.home}/logs:/logs
73 |
74 |
75 |
76 | org.eclipse.jetty.server.Server: Started @
77 |
78 |
79 |
80 | ${project.artifactId}
81 | cyan
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | start
90 | pre-integration-test
91 |
92 | start
93 |
94 |
95 |
96 | stop
97 | post-integration-test
98 |
99 | stop
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/hello-world-app-acceptance/src/test/java/de/philipphauer/helloworld/DummyAcceptanceTest.java:
--------------------------------------------------------------------------------
1 | package de.philipphauer.helloworld;
2 |
3 | import java.io.IOException;
4 | import java.net.HttpURLConnection;
5 | import java.net.URL;
6 |
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | public class DummyAcceptanceTest {
11 |
12 | //TODO do real acceptance testing (GUI test with selenium or behavior test with jbehave)
13 |
14 | @Test
15 | public void testConnection() throws IOException {
16 | String urlString = System.getProperty("service.url");
17 | System.out.println("testing url:" + urlString);
18 |
19 | URL serviceUrl = new URL(urlString + "hello-world");
20 | HttpURLConnection connection = (HttpURLConnection) serviceUrl.openConnection();
21 | int responseCode = connection.getResponseCode();
22 | Assert.assertEquals(200, responseCode);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/hello-world-app-deployment/runDockerContainer.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | DOCKER_REPO=192.168.35.217:5000/hello-world-app
4 | TAG=0.0.1-SNAPSHOT
5 | CONTAINER_NAME=hello-world-app-deployment
6 |
7 | containerId=`docker ps -qa --filter "name=$CONTAINER_NAME"`
8 | if [ -n "$containerId" ]
9 | then
10 | echo "Stopping and removing existing hello-world container"
11 | docker stop $CONTAINER_NAME
12 | docker rm $CONTAINER_NAME
13 | fi
14 |
15 | docker run -d --name $CONTAINER_NAME -p 9090:8080 $DOCKER_REPO:$TAG
16 | #use -d to run in background
17 | #use -it to run in foreground and get the output. add --rm to remove the container (container's file system) when it exits.
18 |
19 |
--------------------------------------------------------------------------------
/hello-world-app/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | .settings
3 | .classpath
4 | .project
5 | /target/
6 |
--------------------------------------------------------------------------------
/hello-world-app/HelloWorldApplication.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/hello-world-app/README:
--------------------------------------------------------------------------------
1 | dummy project for continuous delivery
2 |
--------------------------------------------------------------------------------
/hello-world-app/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | de.philipphauer
5 | hello-world-app
6 | 0.0.1-SNAPSHOT
7 |
8 | 0.8.0
9 | 8080
10 | 8081
11 | localhost:5000/
12 | ${docker.registry.name}${project.artifactId}
13 |
14 |
15 |
16 | io.dropwizard
17 | dropwizard-core
18 | ${dropwizard.version}
19 |
20 |
21 | io.dropwizard
22 | dropwizard-testing
23 | ${dropwizard.version}
24 |
25 |
26 | junit
27 | junit
28 | 4.12
29 |
30 |
31 | io.dropwizard
32 | dropwizard-client
33 | ${dropwizard.version}
34 |
35 |
36 |
37 |
38 |
39 | org.apache.maven.plugins
40 | maven-shade-plugin
41 | 1.6
42 |
43 | true
44 |
45 |
46 | *:*
47 |
48 | META-INF/*.SF
49 | META-INF/*.DSA
50 | META-INF/*.RSA
51 |
52 |
53 |
54 |
55 |
56 |
57 | package
58 |
59 | shade
60 |
61 |
62 |
63 |
65 |
67 | de.philipphauer.helloworld.HelloWorldApplication
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | org.apache.maven.plugins
76 | maven-jar-plugin
77 | 2.4
78 |
79 |
80 |
81 | true
82 |
83 |
84 |
85 |
86 |
87 | org.jolokia
88 | docker-maven-plugin
89 | 0.13.3
90 |
91 |
92 |
93 | ${project.artifactId}
94 | ${docker.repository.name}:${project.version}
95 |
96 | java:8-jre
97 | phauer
98 |
99 | docker-assembly.xml
100 |
101 |
102 | 8080
103 | 8081
104 |
105 |
106 | java -jar \
107 | /maven/${project.build.finalName}.jar server \
108 | /maven/config.yml
109 |
110 |
111 |
112 | alias
113 |
114 | ${prozu.port}:8080
115 | ${prozu.port.admin}:8081
116 |
117 |
118 |
119 | ${user.home}/logs:/logs
120 |
121 |
122 |
123 | org.eclipse.jetty.server.Server: Started @
124 |
125 |
126 |
127 | ${project.artifactId}
128 | cyan
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | start
137 | pre-integration-test
138 |
139 | build
140 | start
141 |
142 |
143 |
144 | stop
145 | post-integration-test
146 |
147 | stop
148 |
149 |
150 |
151 | push-to-docker-registry
152 | deploy
153 |
154 | push
155 |
156 |
157 |
158 |
159 |
160 | org.apache.maven.plugins
161 | maven-deploy-plugin
162 | 2.7
163 |
164 | true
165 |
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/hello-world-app/src/main/docker/docker-assembly.xml:
--------------------------------------------------------------------------------
1 |
3 | ${project.artifactId}
4 |
5 |
6 | target/${project.build.finalName}.jar
7 | /
8 |
9 |
10 | src/main/resources/config.yml
11 | /
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/hello-world-app/src/main/java/de/philipphauer/helloworld/HelloWorldApplication.java:
--------------------------------------------------------------------------------
1 | package de.philipphauer.helloworld;
2 |
3 | import de.philipphauer.helloworld.healthchecks.TemplateHealthCheck;
4 | import de.philipphauer.helloworld.rest.HelloWorldResource;
5 | import io.dropwizard.Application;
6 | import io.dropwizard.setup.Bootstrap;
7 | import io.dropwizard.setup.Environment;
8 |
9 | public class HelloWorldApplication extends Application {
10 |
11 | public static void main(String[] args) throws Exception {
12 | new HelloWorldApplication().run(args);
13 | }
14 |
15 | @Override
16 | public String getName() {
17 | return "hello-world";
18 | }
19 |
20 | @Override
21 | public void initialize(Bootstrap bootstrap) {
22 | // nothing to do yet
23 | }
24 |
25 | @Override
26 | public void run(HelloWorldConfiguration configuration, Environment environment) {
27 | TemplateHealthCheck healthCheck = new TemplateHealthCheck(configuration.getTemplate());
28 | environment.healthChecks().register("template", healthCheck);
29 |
30 | HelloWorldResource resource = new HelloWorldResource(configuration.getTemplate(),
31 | configuration.getDefaultName());
32 | environment.jersey().register(resource);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/hello-world-app/src/main/java/de/philipphauer/helloworld/HelloWorldConfiguration.java:
--------------------------------------------------------------------------------
1 | package de.philipphauer.helloworld;
2 |
3 | import io.dropwizard.Configuration;
4 |
5 | import org.hibernate.validator.constraints.NotEmpty;
6 |
7 | import com.fasterxml.jackson.annotation.JsonProperty;
8 |
9 | /** mapping: YAML file to this class */
10 | public class HelloWorldConfiguration extends Configuration {
11 |
12 | @NotEmpty
13 | private String template;
14 |
15 | @NotEmpty
16 | private String defaultName = "Stranger";
17 |
18 | @JsonProperty
19 | public String getTemplate() {
20 | return template;
21 | }
22 |
23 | @JsonProperty
24 | public void setTemplate(String template) {
25 | this.template = template;
26 | }
27 |
28 | @JsonProperty
29 | public String getDefaultName() {
30 | return defaultName;
31 | }
32 |
33 | @JsonProperty
34 | public void setDefaultName(String name) {
35 | this.defaultName = name;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/hello-world-app/src/main/java/de/philipphauer/helloworld/healthchecks/TemplateHealthCheck.java:
--------------------------------------------------------------------------------
1 | package de.philipphauer.helloworld.healthchecks;
2 |
3 | import com.codahale.metrics.health.HealthCheck;
4 |
5 | public class TemplateHealthCheck extends HealthCheck {
6 | private final String template;
7 |
8 | public TemplateHealthCheck(String template) {
9 | this.template = template;
10 | }
11 |
12 | @Override
13 | protected Result check() throws Exception {
14 | final String saying = String.format(template, "TEST");
15 | if (!saying.contains("TEST")) {
16 | return Result.unhealthy("template doesn't include a name");
17 | }
18 | return Result.healthy();
19 | }
20 | }
--------------------------------------------------------------------------------
/hello-world-app/src/main/java/de/philipphauer/helloworld/model/Saying.java:
--------------------------------------------------------------------------------
1 | package de.philipphauer.helloworld.model;
2 |
3 | import org.hibernate.validator.constraints.Length;
4 |
5 | import com.fasterxml.jackson.annotation.JsonProperty;
6 |
7 | public class Saying {
8 | private long id;
9 |
10 | @Length(max = 3)
11 | private String content;
12 |
13 | public Saying() {
14 | // Jackson deserialization
15 | }
16 |
17 | public Saying(long id, String content) {
18 | this.id = id;
19 | this.content = content;
20 | }
21 |
22 | @JsonProperty
23 | public long getId() {
24 | return id;
25 | }
26 |
27 | @JsonProperty
28 | public String getContent() {
29 | return content;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/hello-world-app/src/main/java/de/philipphauer/helloworld/rest/HelloWorldResource.java:
--------------------------------------------------------------------------------
1 | package de.philipphauer.helloworld.rest;
2 |
3 | import java.util.concurrent.atomic.AtomicLong;
4 |
5 | import javax.ws.rs.GET;
6 | import javax.ws.rs.Path;
7 | import javax.ws.rs.Produces;
8 | import javax.ws.rs.QueryParam;
9 | import javax.ws.rs.core.MediaType;
10 |
11 | import com.codahale.metrics.annotation.Timed;
12 | import com.google.common.base.Optional;
13 |
14 | import de.philipphauer.helloworld.model.Saying;
15 |
16 | @Path("/hello-world")
17 | @Produces(MediaType.APPLICATION_JSON)
18 | public class HelloWorldResource {
19 | private final String template;
20 | private final String defaultName;
21 | private final AtomicLong counter;
22 |
23 | public HelloWorldResource(String template, String defaultName) {
24 | this.template = template;
25 | this.defaultName = defaultName;
26 | this.counter = new AtomicLong();
27 | }
28 |
29 | @GET
30 | @Timed
31 | public Saying sayHello(@QueryParam("name") Optional name) {
32 | final String value = String.format(template, name.or(defaultName));
33 | return new Saying(counter.incrementAndGet(), value);
34 | }
35 |
36 | }
37 | // http://localhost:8081/metrics?pretty=true
--------------------------------------------------------------------------------
/hello-world-app/src/main/resources/config.yml:
--------------------------------------------------------------------------------
1 | template: Hello, %s!
2 | defaultName: Stranger
--------------------------------------------------------------------------------
/hello-world-app/src/test/java/de/philipphauer/helloworld/rest/HelloWorldResourceTest.java:
--------------------------------------------------------------------------------
1 | package de.philipphauer.helloworld.rest;
2 |
3 | import static org.hamcrest.CoreMatchers.equalTo;
4 | import static org.hamcrest.MatcherAssert.assertThat;
5 | import io.dropwizard.client.JerseyClientBuilder;
6 | import io.dropwizard.testing.ResourceHelpers;
7 | import io.dropwizard.testing.junit.DropwizardAppRule;
8 |
9 | import javax.ws.rs.client.Client;
10 | import javax.ws.rs.core.Response;
11 |
12 | import org.junit.ClassRule;
13 | import org.junit.Test;
14 |
15 | import de.philipphauer.helloworld.HelloWorldApplication;
16 | import de.philipphauer.helloworld.HelloWorldConfiguration;
17 |
18 | public class HelloWorldResourceTest {
19 |
20 | @ClassRule
21 | public static final DropwizardAppRule RULE =
22 | new DropwizardAppRule(HelloWorldApplication.class,
23 | ResourceHelpers.resourceFilePath("test-config.yml"));
24 |
25 | @Test
26 | public void helloWorld() {
27 | Client client = new JerseyClientBuilder(RULE.getEnvironment()).build("test client");
28 | String url = String.format("http://localhost:%d/hello-world", RULE.getLocalPort());
29 | Response response = client.target(url).request().get();
30 | String json = response.readEntity(String.class);
31 | assertThat(json, equalTo("{\"id\":1,\"content\":\"Hello, Stranger!\"}"));
32 | assertThat(response.getStatus(), equalTo(200));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/hello-world-app/src/test/resources/test-config.yml:
--------------------------------------------------------------------------------
1 | template: Hello, %s!
2 | defaultName: Stranger
3 | server:
4 | applicationConnectors:
5 | - type: http
6 | port: 9000
7 | adminConnectors:
8 | - type: http
9 | port: 9001
--------------------------------------------------------------------------------
/util-scripts/docker-util.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | # add 'source /docker-util.sh' to ~/.bashrc. This makes this functions available in the current shell.
3 | docker-stop-remove(){
4 | docker stop $1 && docker rm $1
5 | }
6 | docker-remove-stopped(){
7 | #docker rm $(docker ps -a -q)
8 | docker ps -a -q | xargs docker rm
9 | }
10 | docker-nuke() {
11 | docker ps -q | xargs docker stop
12 | docker ps -q -a | xargs docker rm
13 | }
14 | docker-rmi-none() {
15 | docker images | grep '' | \
16 | awk '{ print $3 }' | \
17 | xargs docker rmi
18 | }
19 | docker-go() {
20 | docker run --rm -t -i $@
21 | }
22 | docker-go-bg() {
23 | docker run -d --name $@ $@
24 | }
25 | # append bash in a running container
26 | docker-exec-bash(){
27 | if [ $# -lt 1 ] ; then
28 | echo "Please provide a container id or name. Usage: docker-exec-bash "
29 | else
30 | docker exec -it $1 bash
31 | fi
32 | }
33 | # show running processes within a running container
34 | docker-container-ps(){
35 | docker exec $1 ps -f
36 | }
37 |
38 |
39 |
--------------------------------------------------------------------------------