Deploying an app
Templates are an important concept within the OpenShift world. They are, however, more of an advanced topic and outside of the scope of this "quick start". You can find more information regarding templates here:
https://docs.openshift.com/enterprise/latest/dev_guide/templates.html
Creating a Build
Builds are the foundation of any type of CI/CD ("cloud") work flow. This is a huge topic and I would suggest reading the full documentation to start:
https://docs.openshift.com/enterprise/3.2/dev_guide/builds.html
That said I will do my best to summarize some of what I think are the more important points
OpenShift Enterprise has three build strategies:
1. Source to image (usually involves linking a source repository into the build config)
2. Docker
3. Custom
You can have the following sources in a build:
1. Git repo
2. Dockerfile
3. Image
4. Binary (this is usually where Jenkins/Bamboo or other has already built your artifact and you want to deploy that)
Build Configuration
At the heart of every build is the build config. Every build config needs the following pieces:
Code:
- apiVersion: v1
kind: BuildConfig
metadata:
annotations:
description: Defines how to build the application
labels:
template: <name of template>
name: <name of the build config>
spec:
output:
to:
kind: ImageStreamTag
name: <name of repository to push built docker image>
resources: {}
source:
git:
uri: <location of git repo with source code>
type: Git
strategy:
sourceStrategy:
from:
kind: ImageStreamTag
name: <name of docker image to base build on>
type: Source
triggers:
- imageChange: {}
type: ImageChange
- type: ConfigChange
- github:
secret: <secret for git>
type: GitHub
status:
lastVersion: 0
I am not going to go into huge detail here as I believe the documentation does a good job of explaining this fairly well. However, to start off with, I suggest basing your build config on one that gets generated by one of the "instant app" templates. Get used to manipulating those before attempting to create your own from scratch
Understanding Secrets
The Secret object type provides a mechanism to hold sensitive information such as passwords, OpenShift client config files, dockercfg files, etc. Secrets decouple sensitive content from the pods that use it and can be mounted into containers using a volume plug-in or used by the system to perform actions on behalf of a pod.
IMPORTANT NOTE: A secret must be created before the pods that depend on it. Once a pod is created, its secret volumes do not change, even if the secret resource is modified. To change the secret used, the original pod must be deleted, and a new pod must be created.
Docker Secrets
The official documentation demonstrates how to create a .dockercfg json file:
Code:
{
"https://index.docker.io/v1/": {
"auth": "YWRfbGzhcGU6R2labnRib21ifTE=",
"email": "[email protected]"
}
}
NOTE: You can define multiple Docker registry entries in this file. Alternatively, you can also add authentication entries to this file by running the docker login command. The file will be created if it does not exist.
Once you have created your .dockercfg create the secret:
Code:
oc secrets new dockersecret ~/.dockercfg
Add the secret to the service accounts:
Code:
oc secrets add serviceaccount/default secrets/dockersecret --for=pull
oc secrets add serviceaccount/builder secrets/dockersecret
In order to use the secret you will need to add the pushSecret and pullSecret sections to your build config
Code:
spec:
output:
to:
kind: ImageStreamTag
name: cakephp-example:latest
pushSecret:
name: dockersecret
postCommit: {}
resources: {}
source:
git:
uri: https://github.com/openshift/cakephp-ex.git
secrets: null
type: Git
strategy:
sourceStrategy:
from:
kind: ImageStreamTag
name: php:5.6
namespace: openshift
pullSecret:
name: dockersecret
BasicAuth/Git Secrets
To create a basic auth secret, you have 3 options:
1. username and password
Code:
oc secrets new-basicauth basicsecret --username=USERNAME --password=PASSWORD
2. token
Code:
oc secrets new-basicauth basicsecret --password=TOKEN
3. CA Certificate
Code:
oc secrets new-basicauth basicsecret --username=USERNAME --password=PASSWORD --ca-cert=FILENAME
Once you create your secret make sure the builder service account has access to it
Below is an example of a basicauth secret being called within a buildconfig:
Code:
apiVersion: "v1"
kind: "BuildConfig"
metadata:
name: "sample-build"
spec:
output:
to:
kind: "ImageStreamTag"
name: "sample-image:latest"
source:
git:
uri: "https://github.com/user/app.git"
sourceSecret:
name: "basicsecret"
type: "Git"
Config Maps
The ConfigMap object provides mechanisms to inject containers with configuration data while keeping containers agnostic of OpenShift Enterprise. A ConfigMap can be used to store fine-grained information like individual properties or coarse-grained information like entire configuration files or JSON blobs. The ConfigMap API object holds key-value pairs of configuration data that can be consumed in pods or used to store configuration data for system components such as controllers. ConfigMap is similar to secrets, but designed to more conveniently support working with strings that do not contain sensitive information.
A configmap can be a plain yaml file:
Code:
apiVersion: v1
data:
game-settings: |
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
kind: ConfigMap
metadata:
creationTimestamp: 2016-05-30T22:52:06Z
name: game-settings
namespace: project1
resourceVersion: "281368"
selfLink: /api/v1/namespaces/project1/configmaps/game-settings
uid: 21e809ec-26b9-11e6-86bc-0800271c5c7b
You can import them in the usual fashion
Code:
oc create -f game-settings.yaml
However, you can also create configmaps from a flat file
Code:
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
And then import them with
Code:
oc create configmap game-settings --from-file=game-settings.txt
You can also create configmaps of all files in a directory:
Code:
oc create configmap game-config --from-file=example-files-directory/
For more information see the
official documentation
Deployment Configuration
The deployment config is what you would be concerned about duplicating if you are moving an image that has already been built, from one environment to another. Think of the deployment config as a method for "promoting" an image. In the deployment config you can specify things like environment variables inside the container (which is often used for database related information, program startup parameters etc), the deployment strategy (see:
https://docs.openshift.com/enterprise/latest/dev_guide/deployments.html#strategies), resource limits and a host of other related information. For demo purposes deployment configs are created for you from a template and you don't usually have to worry about changing these directly.
However should you want to create a template or a yaml file from an existing deployment config, you can always dump it to the command line.
To dump the template
Code:
oc export deploymentconfig/cakephp-example --as-template=mytemplate > deploymentconfig_template.yaml
Or to simply create a deployment config yaml file
Code:
oc get deploymentconfig/cakephp-example -o yaml |grep -v selfLink |grep -v namespace |grep -v uid |grep -v resrouceVersion |grep -v creationTimestamp > cakephp-deploymentconfig.yaml
The difference between these methods is how they are used and stored. In general, you probably want to dump the deployment config yaml file instead of the template. The keen observer will notice that the
oc get command has some greps in there. This is because you will want to remove any uniquely identifying references or else the import will fail. You cannot have duplicates of uid, selfLink, resourceVersion etc, for obvious reasons. There should be only a single object with these exact values.
Deploying images between projects
There are two ways of achieving this.
1. Using a docker style methodology whereby an image that has been deployed and tested, is retagged for the new project
2. Sharing the image stream
Of the two, the former provides the best granularity and security. This deployment process is done with a series of docker commands
First identify the image to deploy to a different project (this is done on the app node where the image is currently deployed)
Code:
[root@application01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
172.50.254.67:5000/project1/cakephp-example latest 12956f73e415 18 minutes ago 508.7 MB
registry.access.redhat.com/openshift3/ose-sti-builder v3.1.1.6 cad03b82e5ad 12 days ago 442.2 MB
registry.access.redhat.com/openshift3/ose-pod v3.1.1.6 077b7021c72c 12 days ago 428.2 MB
registry.access.redhat.com/rhscl/php-56-rhel7 latest bbfc4eb8005b 3 weeks ago 491.4 MB
Next login to docker
Code:
docker login -u <username> -e <email> -p <token from oc whoami -t> 172.50.254.67:5000
Login Succeeded
Tag the image and then push it into the new project
Code:
[root@application01 ~]# docker tag 12956f73e415 172.50.254.67:5000/project2/cakephp-example
[root@application01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
172.50.254.67:5000/project1/cakephp-example latest 12956f73e415 21 minutes ago 508.7 MB
172.50.254.67:5000/project2/cakephp-example latest 12956f73e415 21 minutes ago 508.7 MB
registry.access.redhat.com/openshift3/ose-sti-builder v3.1.1.6 cad03b82e5ad 12 days ago 442.2 MB
registry.access.redhat.com/openshift3/ose-pod v3.1.1.6 077b7021c72c 12 days ago 428.2 MB
registry.access.redhat.com/rhscl/php-56-rhel7 latest bbfc4eb8005b 3 weeks ago 491.4 MB
[root@application01 ~]# docker push 172.50.254.67:5000/project2/cakephp-example
The push refers to a repository [172.50.254.67:5000/project2/cakephp-example] (len: 1)
12956f73e415: Pushed
bbfc4eb8005b: Pushed
6bcf1d53eb78: Pushed
c453594215e4: Pushed
latest: digest: sha256:7f822812f569841176e0326fdee6a46d85da3c22495688a2de60615a22b829be size: 11241
Now that the image is pushed, log out of the application node and return to working on the master (or whichever machine you are issuing the oc commands from)
Generate the deployment config from project1:
Code:
oc get deploymentconfig/cakephp-example --namespace project1 -o yaml |grep -v selfLink |grep -v namespace |grep -v uid |grep -v resrouceVersion |grep -v creationTimestamp > cakephp-deploymentconfig.yaml
You will have to change the references from project1 to project2 in this file. Once you have done this, you are ready to create the deployment config in the new project:
Code:
oc create -f cakephp-deploymentconfig.yaml --namespace project2
OpenShift by default, will detect the deployment config and begin deploying it. The image should deploy successfully. The route and service entry are not copied during this operation, so they will need to be created if you want to access the pod externally.
A basic service can be obtained from the original running pod. Below is an example of a basic service definition (extracted from
oc export service/cakephp-example --namespace project1 --as-template=newtemp
Code:
apiVersion: v1
kind: Service
metadata:
annotations:
description: Exposes and load balances the application pods
creationTimestamp: null
labels:
template: cakephp-example
name: cakephp-example
spec:
ports:
- name: web
port: 8080
protocol: TCP
targetPort: 8080
selector:
name: cakephp-example
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
The clusterIP and portalIP are dynamically allocated from the pool and should not be specified manually. Finally you need to expose the route, you can either do this manually via the
oc expose command, or get it from dumping the route to a file
Code:
oc get route/cakephp-example --namespace project1 -o yaml |grep -v selfLink |grep -v namespace |grep -v uid |grep -v resrouceVersion |grep -v creationTimestamp > cakephp-route.yaml
This will produce a file which needs to have the
hosts: section edited or else it will produce the same URL that already exists and thus will point to the wrong project's application.
Finally import the
edited route into the new project
Code:
oc create -f cakephp-route.yaml --namespace project2
At this point your image has now been "promoted" into a new project/environment.
IMPORTANT NOTE: For manageability, while your routes, services, deployment configs, build configs etc. can all have different names to identify them, I
STRONGLY recommend naming them the exact same as the app the belong to, or at very least follow, and stick to, a naming convention. When it comes to automating tasks in the future you will be glad you did!
Routes and TLS
For the purpose of this discussion, I assume that you either have a CA to generate your own certificates, or you have been given proper certificates from an authority. In any event, there are a few ways that this can be done.
1. passthrough. This requires each one of your apps to manage their own certificates
2. re-encryption. This also requires certificates to be inside the pod as well as outside
3. edge termination.
For simplicity sake I am only going to talk about edge termination in this post. For more details on various options please see:
https://docs.openshift.com/enterprise/latest/architecture/core_concepts/routes.html#secured-routes
The easiest way to do edge termination is to create a route yaml file. If you have an existing route you have 2 options, edit the current route in place (either through the webUI or
oc edit) or dump the current route to a file, delete the route, edit the yaml file created and then reload it with the
oc create command. I prefer editing from within the webUI as the tab key is already set to help with the appropriate spacing (which is important in yaml)
Here is what a completed edge termination file may look like (for security reasons large sections of the certificates have been deleted... THESE ARE NOT VALID CERTS)
Code:
apiVersion: v1
kind: Route
metadata:
annotations:
openshift.io/host.generated: "true"
creationTimestamp: 2016-05-29T16:49:28Z
labels:
template: cakephp-example
name: cakephp-example
namespace: project1
resourceVersion: "121790"
selfLink: /oapi/v1/namespaces/project1/routes/cakephp-example
uid: 4edb34e2-25bd-11e6-aaf6-080027e32c95
spec:
host: cakephp-example-project1.apps.example.com
tls:
caCertificate: |-
-----BEGIN CERTIFICATE-----
MIIGAzCCA+ugAwIBAgIJAMdWmtLRwBKfMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNV
BAYTAkNBMQ8wDQYDVQQIDAZDYW5hZGExGDAWBgNVBAoMD3g4NiBJbm5vdmF0aW9u
czEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNjA1MjQxODEzNDNaFw0zNjA1MTkxODEz
NDNaMEoxCzAJBgNVBAYTAkNBMQ8wDQYDVQQIDAZDYW5hZGExGDAWBgNVBAoMD3g4
q0uB9lHFo/9WMswQJM+IJKrJkNwOb2RmTPJCujDrXs/xDRI0RxaoWUsHA1islU62
Jhk5q3Q8yNU7VAvF+98ZoRVzQO/D2jU1tpW5zurNBpFYrCF0lPJM2silbriWvzqD
VYUEHMQQ6qozdkeNQd1T4qhWKZsiSEIBDSjeANzJ/eZelwcUqhCH0F7+eBUFV3Rm
IAdZNmvmIl9pMkosgjpDpZNMNYC2tVI2SopMN/zqsXNGYN3fo1x9j6O6lS2tchnA
dNo4Gly8my+n662ZaamiIp2HytfZda8AwEyTkUja0xSC0IlSgAD7CKzQ9gj0u5JJ
gn3zFMa7eg==
-----END CERTIFICATE-----
certificate: |-
-----BEGIN CERTIFICATE-----
MIIGdDCCBFygAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwUjELMAkGA1UEBhMCQ0Ex
DzANBgNVBAgMBkNhbmFkYTEYMBYGA1UECgwPeDg2IElubm92YXRpb25zMRgwFgYD
VQQDDA9JbnRlcm1lZGlhdGUgQ0EwHhcNMTYwNTI0MTgxNjE4WhcNMTcwNjAzMTgx
NjE4WjBOMQswCQYDVQQGEwJDQTEPMA0GA1UECAwGQ2FuYWRhMRgwFgYDVQQKDA94
ODYgSW5ub3ZhdGlvbnMxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG
db5soDkIrCmzG3JeU2sCj6iaf2GrQjPlmdTM99w7nsKpwRagNVtb8o+uE8uz838S
SJHONCS8HDLpT9nqXmMN2WcWk/VaA4O5xYFAWx80Sy/AxM4Fs3jXxD9+bWPbDjAa
zA/0RfSZnOyG0HksmGY0bE2LBNjJzNuRvQceGWm40RfUd3CIBzwSAebjAbbXx9Oj
woRNiE4snyeFtXInd42V9hTYLJxd8ECsaWiGFtyk2FwJszCTSpA4OmZN+5Px8PR+
iaScGTYrXo63CNI+0DbGFJ3i8Dybwgmo
-----END CERTIFICATE-----
key: |-
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAvAfZZkXk5y/5kvZBEp/KyEFyWUqZ8JxNs7nS5xoqdVaS8t0K
5LHjQ5KYK4kp9wvB4rupLfkYyk8tx1FkmOS+Oc1DqEs41dwzOrhity1Z7VjAII2Q
A++fq1VOcMnKS9snZeGiThmy7SSQ8CCT4tnFWN20nU4yka2/RExZblNn14fe5bFu
i8otZT0MHv3g3UN32D2uWiZobdjkar8ooNwXeaW0eEFpuuAH1ydAAutwc7ZDpDl+
6bm1J52thocgj7PWQdjjQ4PNJeNEhe49g7YHqeCRBXqj6NXRTZDQbpdxA3HszZZ1
FIAJZobwfDsGlNIpa4mCtJJGXxgVQgGJPu1noQIDAQABAoIBAFIyODX+Ld9mWHqH
BZ9pCQKBgGT56lJEsQhL8/+AmjmILIcTppX1pOCaLA7VwpL73h+qTKA1QPB8Qu9m
VxfRtYr/4/gPtdHvCySScFxgnVdvqHwJ+rRFfSdvOQB0+8B+UfqsZyC6K02XDp6Y
hshsaAAuYx30j4vQLYvneCUAvU/6AQI2E8X1cr768tNFCwMgG0rI
-----END RSA PRIVATE KEY-----
termination: edge
to:
kind: Service
name: cakephp-example
status: {}
For re-encryption, it is almost exactly the same as the above, however you add one section which represents the CA public key certificate (usually named ca.crt) which is associated with the certificate inside of the pod.
The TLS section would look like this
Code:
tls:
termination: reencrypt
key: [as in edge termination]
certificate: [as in edge termination]
caCertificate: [as in edge termination]
destinationCaCertificate: |-
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----